* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This file is part of the MindStudio project.
*
* MindStudio is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------*/
#include "analysis/csrc/domain/services/environment/context.h"
#include <unordered_set>
#include "analysis/csrc/infrastructure/dfx/error_code.h"
#include "analysis/csrc/infrastructure/dfx/log.h"
#include "analysis/csrc/infrastructure/utils/config.h"
#include "analysis/csrc/infrastructure/utils/utils.h"
#include "analysis/csrc/viewer/database/finals/unified_db_constant.h"
namespace Analysis
{
namespace Domain
{
namespace Environment
{
using namespace Analysis;
using namespace Analysis::Utils;
using namespace Viewer::Database;
namespace
{
const uint32_t ALL_EXPORT_VERSION = 0x072211;
const uint64_t DEFAULT_DURATION_TIME_US = DEFAULT_DURATION_TIME_NS / MILLI_SECOND;
const std::string DEFAULT_HOST_UID = "0";
const std::string INFO_JSON = "info.json";
const std::string SAMPLE_JSON = "sample.json";
const std::string START_INFO = "start_info";
const std::string END_INFO = "end_info";
const std::string HOST_START_LOG = "host_start.log";
const std::string DEVICE_START_LOG = "dev_start.log";
const std::set<std::string> CHECK_VALUES = {"platform_version",
"startCollectionTimeBegin",
"startClockMonotonicRaw",
"pid",
"CPU",
"DeviceInfo",
"llc_profiling",
"ai_core_profiling_mode",
"hostname",
"memoryTotal",
"netCard"};
}
bool Context::Load(const std::set<std::string> &profPaths)
{
for (const auto &profPath : profPaths)
{
std::vector<std::string> deviceDirs = File::GetOriginData(profPath, {HOST, DEVICE_PREFIX}, {});
for (const auto &deviceDir : deviceDirs)
{
uint16_t deviceId = Utils::GetDeviceIdByDevicePath(deviceDir);
if (deviceId == INVALID_DEVICE_ID)
{
ERROR("The prof path's deviceId is invalid.");
return false;
}
if (!LoadJsonData(profPath, deviceDir, deviceId))
{
return false;
}
if (!LoadLogData(profPath, deviceDir, deviceId))
{
return false;
}
if (!CheckInfoValueIsValid(profPath, deviceId))
{
return false;
}
}
}
return true;
}
bool Context::LoadJsonData(const std::string &profPath, const std::string &deviceDir, uint16_t deviceId)
{
for (const auto &fileName : {INFO_JSON, SAMPLE_JSON, START_INFO, END_INFO})
{
std::vector<std::string> files = File::GetOriginData(deviceDir, {fileName}, {"done"});
if (files.size() != 1)
{
ERROR("The number of % in % is invalid, file num is: %.", fileName, deviceDir, files.size());
if (fileName == END_INFO)
{
continue;
}
return false;
}
FileReader fd(files.back());
nlohmann::json content;
if (fd.ReadJson(content) != ANALYSIS_OK)
{
ERROR("Load json context failed: '%'.", files.back());
return false;
}
if (fileName == START_INFO && content.contains("clockMonotonicRaw") && content.contains("collectionTimeBegin"))
{
content["startClockMonotonicRaw"] = content["clockMonotonicRaw"];
content["startCollectionTimeBegin"] = content["collectionTimeBegin"];
}
else if (fileName == END_INFO && content.contains("clockMonotonicRaw") && content.contains("collectionTimeEnd"))
{
content["endClockMonotonicRaw"] = content["clockMonotonicRaw"];
content["endCollectionTimeEnd"] = content["collectionTimeEnd"];
}
context_[profPath][deviceId].merge_patch(content);
}
return true;
}
bool Context::LoadLogData(const std::string &profPath, const std::string &deviceDir, uint16_t deviceId)
{
const int expectTokenSize = 2;
std::vector<std::string> fileNameList{HOST_START_LOG};
if (deviceId != HOST_ID)
{
fileNameList.emplace_back(DEVICE_START_LOG);
}
for (const std::string &fileName : fileNameList)
{
std::vector<std::string> files = File::GetOriginData(deviceDir, {fileName}, {"done"});
if (files.size() != 1)
{
ERROR("The number of % in % is invalid, file num is: %.", fileName, deviceDir, files.size());
return false;
}
FileReader fd(files.back());
std::vector<std::string> text;
if (fd.ReadText(text) != ANALYSIS_OK)
{
ERROR("Load log text failed: '%'.", files.back());
return false;
}
for (const auto &line : text)
{
auto tokens = Utils::Split(line, ":");
if (tokens.size() != expectTokenSize)
{
continue;
}
context_[profPath][deviceId][tokens[0]] = tokens[1];
}
if (!context_[profPath][deviceId].contains("clock_monotonic_raw") ||
!context_[profPath][deviceId].contains("cntvct"))
{
return false;
}
if (fileName == DEVICE_START_LOG)
{
context_[profPath][deviceId]["devMonotonic"] = context_[profPath][deviceId]["clock_monotonic_raw"];
context_[profPath][deviceId]["devCntvct"] = context_[profPath][deviceId]["cntvct"];
}
else if (fileName == HOST_START_LOG)
{
context_[profPath][deviceId]["hostMonotonic"] = context_[profPath][deviceId]["clock_monotonic_raw"];
context_[profPath][deviceId]["hostCntvct"] = context_[profPath][deviceId]["cntvct"];
if (context_[profPath][deviceId].contains("cntvct_diff"))
{
context_[profPath][deviceId]["hostCntvctDiff"] = context_[profPath][deviceId]["cntvct_diff"];
}
else
{
context_[profPath][deviceId]["hostCntvctDiff"] = "0";
}
}
}
return true;
}
bool Context::CheckInfoValueIsValid(const std::string &profPath, uint16_t deviceId)
{
const auto &info = GetInfoByDeviceId(deviceId, profPath);
for (auto &valueName : CHECK_VALUES)
{
if (!info.contains(valueName))
{
ERROR(
"The key called %, not in the context info, "
"the ProfPath is %, DeviceId is %.",
valueName, profPath, deviceId);
return false;
}
}
if (deviceId == HOST_ID)
{
if (!info.at("CPU").is_array())
{
ERROR(
"CPU's value is invalid, "
"the ProfPath is %, DeviceId is %.",
profPath, deviceId);
return false;
}
if (!info.at("CPU").back().contains("Frequency"))
{
ERROR(
"There is no Frequency in context info, "
"the ProfPath is %, DeviceId is %.",
profPath, deviceId);
return false;
}
}
else
{
if (!info.at("DeviceInfo").is_array() || (info.at("DeviceInfo").size() != 1))
{
ERROR(
"DeviceInfo's value is invalid, "
"the ProfPath is %, DeviceId is %.",
profPath, deviceId);
return false;
}
auto freqArr = info.at("DeviceInfo").back();
if (!freqArr.contains("hwts_frequency") || freqArr.at("hwts_frequency").empty())
{
ERROR(
"There is no hwts_frequency in context info, "
"the ProfPath is %, DeviceId is %.",
profPath, deviceId);
return false;
}
if (!freqArr.contains("aic_frequency") || freqArr.at("aic_frequency").empty())
{
ERROR(
"There is no aic_frequency in context info, "
"the ProfPath is %, DeviceId is %.",
profPath, deviceId);
return false;
}
}
return true;
}
bool Context::IsAllExport()
{
const auto &info = GetInfoByDeviceId();
if (info.empty())
{
ERROR("IsAllExport device info is empty.");
return false;
}
auto drvVersion = info.value("drvVersion", 0u);
if (drvVersion < ALL_EXPORT_VERSION)
{
WARN("DrvVersion not support all export, ALL_EXPORT_VERSION is %", ALL_EXPORT_VERSION);
return false;
}
uint16_t chip;
if (StrToU16(chip, info.at("platform_version")) != ANALYSIS_OK)
{
ERROR("str to uint16_t failed.");
return false;
}
if (chip == static_cast<uint16_t>(Chip::CHIP_V1_1_0) || chip == static_cast<uint16_t>(Chip::CHIP_V3_1_0) ||
chip == static_cast<uint16_t>(Chip::CHIP_V1_1_3))
{
WARN("Platform_version not support all export.");
return false;
}
return true;
}
nlohmann::json Context::GetInfoByDeviceId(uint16_t deviceId, const std::string &profPath)
{
nlohmann::json emptyJson;
auto profInfo = profPath.empty() ? context_.begin() : context_.find(profPath);
if (profInfo == context_.end())
{
ERROR("Can't find PROF file. input path %.", profPath);
return emptyJson;
}
auto deviceInfo = profInfo->second;
if (deviceInfo.begin() == deviceInfo.end())
{
ERROR("Can't find host or device file. input path %.", profPath);
return emptyJson;
}
if (deviceId == DEFAULT_DEVICE_ID || deviceInfo.find(deviceId) == deviceInfo.end())
{
return deviceInfo.begin()->second;
}
return deviceInfo[deviceId];
}
uint16_t Context::GetPlatformVersion(uint16_t deviceId, const std::string &profPath)
{
const auto &info = GetInfoByDeviceId(deviceId, profPath);
uint16_t platformVersion = UINT16_MAX;
if (info.empty())
{
ERROR("GetPlatformVersion device info is empty, input path %, deviceId %", profPath, deviceId);
return platformVersion;
}
if (StrToU16(platformVersion, info.at("platform_version")) != ANALYSIS_OK)
{
ERROR("PlatformVersion to uint16_t failed, input path %, deviceId %", profPath, deviceId);
}
return platformVersion;
}
bool Context::GetProfTimeRecordInfo(Utils::ProfTimeRecord &record, const std::string &profPath, uint16_t deviceId)
{
auto info = GetInfoByDeviceId(deviceId, profPath);
if (info.empty())
{
WARN("There is no host time log in %, it will use device time log!", profPath);
info = GetInfoByDeviceId(DEFAULT_DEVICE_ID, profPath);
if (info.empty())
{
ERROR("No device time log in %, device id is %.", profPath, deviceId);
return false;
}
}
uint64_t startTimeUs = UINT64_MAX;
if (StrToU64(startTimeUs, info.at("startCollectionTimeBegin")) != ANALYSIS_OK)
{
ERROR("StartTime to uint64_t failed. Prof path is %, device id is %.", profPath, deviceId);
return false;
}
uint64_t endTimeUs = DEFAULT_DURATION_TIME_US + startTimeUs;
if (StrToU64(endTimeUs, info.value("endCollectionTimeEnd", std::to_string(endTimeUs))) != ANALYSIS_OK)
{
ERROR("EndTime to uint64_t failed. Prof path is %, device id is %.", profPath, deviceId);
return false;
}
uint64_t baseTimeNs = UINT64_MAX;
if (StrToU64(baseTimeNs, info.at("startClockMonotonicRaw")) != ANALYSIS_OK)
{
ERROR("BaseTime to uint64_t failed. Prof path is %, device id is %.", profPath, deviceId);
return false;
}
if ((startTimeUs * MILLI_SECOND < baseTimeNs))
{
ERROR("The value of startTimeUs and baseTimeNs is invalid. Path is %, device id is %.", profPath, deviceId);
return false;
}
record.startTimeNs = startTimeUs * MILLI_SECOND;
record.endTimeNs = endTimeUs * MILLI_SECOND;
record.baseTimeNs = baseTimeNs;
return true;
}
uint32_t Context::GetPidFromInfoJson(uint16_t deviceId, const std::string &profPath)
{
const auto &info = GetInfoByDeviceId(deviceId, profPath);
uint32_t pid = 0;
if (info.empty())
{
ERROR("GetPidFromInfoJson device info is empty, input path %, deviceId %", profPath, deviceId);
return pid;
}
const std::string strPid = info.at("pid");
if (strPid == "NA")
{
WARN("pid is NA and will be set 0.");
return pid;
}
if (StrToU32(pid, strPid) != ANALYSIS_OK)
{
ERROR("Pid to uint32_t failed, input path %, deviceId %", profPath, deviceId);
}
return pid;
}
std::string Context::GetPidNameFromInfoJson(uint16_t deviceId, const std::string &profPath)
{
const auto &info = GetInfoByDeviceId(deviceId, profPath);
if (info.empty())
{
ERROR("GetPidNameFromInfoJson device info is empty.");
return "";
}
return info.value("pid_name", "");
}
int64_t Context::GetMsBinPid(const std::string &profPath)
{
const auto &info = GetInfoByDeviceId(DEFAULT_DEVICE_ID, profPath);
if (info.empty())
{
PRINT_ERROR("Samplejson is empty, path %.", profPath);
return analysis::dvvp::common::config::MSVP_MMPROCESS;
}
return info.value("msprofBinPid", analysis::dvvp::common::config::MSVP_MMPROCESS);
}
std::string Context::GetLLCProfiling(uint16_t deviceId, const std::string &profPath)
{
const auto &info = GetInfoByDeviceId(deviceId, profPath);
if (info.empty())
{
ERROR("Samplejson is empty, path %.", profPath);
return "";
}
return info.value("llc_profiling", "");
}
bool Context::GetSyscntConversionParams(Utils::SyscntConversionParams ¶ms, uint16_t deviceId,
const std::string &profPath)
{
auto info = GetInfoByDeviceId(deviceId, profPath);
if (info.empty())
{
ERROR("GetSyscntConversionParams device info is empty, input path %, deviceId %", profPath, deviceId);
return false;
}
std::string hostFreqStr = info.at("CPU").back().at("Frequency");
double hostFreq = DEFAULT_FREQ;
if (hostFreqStr.empty())
{
INFO("HostFreq is empty, it will be set 1000.0 .");
}
else if (StrToDouble(hostFreq, hostFreqStr) != ANALYSIS_OK)
{
ERROR("HostFreq to double failed, input path %, deviceId %", profPath, deviceId);
return false;
}
if (deviceId == HOST_ID)
{
params.freq = hostFreq;
}
else
{
if (StrToDouble(params.freq, info.at("DeviceInfo").back().at("hwts_frequency")) != ANALYSIS_OK)
{
ERROR("DeviceFreq to double failed, input path %, deviceId %", profPath, deviceId);
return false;
}
}
if (IsDoubleEqual(params.freq, 0) || IsDoubleEqual(hostFreq, 0))
{
ERROR("Freq is 0, can't be used to calculate, input path %, deviceId %", profPath, deviceId);
return false;
}
params.hostFreq = hostFreq;
std::string cntName = (deviceId == HOST_ID) ? "hostCntvct" : "devCntvct";
if (StrToU64(params.sysCnt, info.value(cntName, "0")) != ANALYSIS_OK)
{
ERROR("SysCnt to uint64_t failed, input path %, deviceId %", profPath, deviceId);
return false;
}
if (StrToU64(params.hostCnt, info.value("hostCntvct", "0")) != ANALYSIS_OK)
{
ERROR("hostCnt to uint64_t failed, input path %, deviceId %", profPath, deviceId);
return false;
}
if (StrToU64(params.hostMonotonic, info.value("hostMonotonic", "0")) != ANALYSIS_OK)
{
ERROR("HostMonotonic to uint64_t failed, input path %, deviceId %", profPath, deviceId);
return false;
}
uint64_t diff = 0;
if (StrToU64(diff, info.value("hostCntvctDiff", "0")) != ANALYSIS_OK)
{
WARN("HostCntvctDiff to uint64_t failed, input path %, deviceId %", profPath, deviceId);
return true;
}
params.hostMonotonic += (diff * MILLI_SECOND / hostFreq);
return true;
}
bool Context::GetPmuFreq(double &freq, uint16_t deviceId, const std::string &profPath)
{
if (deviceId == HOST_ID)
{
ERROR("Host do not have aic or aiv frequency!");
return false;
}
auto info = GetInfoByDeviceId(deviceId, profPath);
if (info.empty())
{
ERROR("GetPmuFreq device info is empty, input path %, deviceId %", profPath, deviceId);
return false;
}
if (StrToDouble(freq, info.at("DeviceInfo").back().at("aic_frequency")) != ANALYSIS_OK)
{
ERROR("DeviceFreq to double failed, input path %, deviceId %", profPath, deviceId);
return false;
}
return true;
}
bool Context::GetMetricMode(std::string &metricMode, const std::string &profPath)
{
auto info = GetInfoByDeviceId(DEFAULT_DEVICE_ID, profPath);
if (info.empty())
{
ERROR("GetMetricMode device info is empty.");
return false;
}
metricMode = info.at("ai_core_profiling_mode");
return true;
}
bool Context::GetClockMonotonicRaw(uint64_t &monotonicRaw, bool isHost, uint16_t deviceId, const std::string &profPath)
{
if (!isHost && (deviceId == HOST_ID))
{
ERROR("GetClockMonotonicRaw host do not have device monotonic!");
return false;
}
auto info = GetInfoByDeviceId(deviceId, profPath);
if (info.empty())
{
ERROR("GetClockMonotonicRaw device info is empty.");
return false;
}
std::string monotonic = isHost ? "hostMonotonic" : "devMonotonic";
if (StrToU64(monotonicRaw, info.value(monotonic, "0")) != ANALYSIS_OK)
{
ERROR("Monotonic to uint64_t failed, input path %, deviceId %", profPath, deviceId);
return false;
}
if (!isHost)
{
return true;
}
std::string hostFreqStr = info.at("CPU").back().at("Frequency");
double hostFreq = DEFAULT_FREQ;
if (hostFreqStr.empty())
{
INFO("HostFreq is empty, it will be set 1000.0 .");
}
else if (StrToDouble(hostFreq, hostFreqStr) != ANALYSIS_OK)
{
ERROR("HostFreq to double failed, input path %, deviceId %", profPath, deviceId);
return false;
}
if (IsDoubleEqual(hostFreq, 0))
{
ERROR("Freq is 0, can't be used to calculate.");
return false;
}
uint64_t diff = 0;
if (StrToU64(diff, info.value("hostCntvctDiff", "0")) != ANALYSIS_OK)
{
WARN("HostCntvctDiff to uint64_t failed, input path %, deviceId %", profPath, deviceId);
return true;
}
monotonicRaw += (diff * MILLI_SECOND / hostFreq);
return true;
}
std::string Context::GetHostUid(uint16_t deviceId, const std::string &profPath)
{
const auto &info = GetInfoByDeviceId(deviceId, profPath);
if (info.empty())
{
ERROR("GetHostUid InfoJson info is empty, input path %, deviceId %", profPath, deviceId);
return DEFAULT_HOST_UID;
}
return info.value("hostUid", DEFAULT_HOST_UID);
}
std::string Context::GetHostName(uint16_t deviceId, const std::string &profPath)
{
const auto &info = GetInfoByDeviceId(deviceId, profPath);
if (info.empty())
{
ERROR("GetHostName InfoJson info is empty.");
return "";
}
return info.at("hostname");
}
std::vector<std::string> Context::GetQosEvents(uint16_t deviceId, const std::string &profPath)
{
if (deviceId == HOST_ID)
{
ERROR("Host do not have qosEvents!");
return {};
}
auto info = GetInfoByDeviceId(deviceId, profPath);
if (info.empty())
{
ERROR("GetQosEvents device info is empty.");
return {};
}
std::string qosEvents = info.value("qosEvents", "");
if (qosEvents.empty())
{
INFO("Check qosProfiling is on or off, if it is on, maybe some mistakes have happened");
return {};
}
else
{
return Split(qosEvents, ",");
}
}
void Context::Clear() { context_.clear(); }
bool Context::IsStarsChip(uint16_t platformVersion)
{
return IsChipV1(platformVersion) || IsChipV4(platformVersion) || IsChipV6(platformVersion);
}
bool Context::IsChipV1(uint16_t platformVersion)
{
auto chipV1_1_1 = static_cast<uint16_t>(Chip::CHIP_V1_1_1);
auto chipV1_1_2 = static_cast<uint16_t>(Chip::CHIP_V1_1_2);
auto chipV1_1_3 = static_cast<uint16_t>(Chip::CHIP_V1_1_3);
std::unordered_set<uint16_t> checkList{chipV1_1_1, chipV1_1_2, chipV1_1_3};
return static_cast<bool>(checkList.count(platformVersion));
}
bool Context::IsChipV4(uint16_t platformVersion) { return platformVersion == static_cast<int>(Chip::CHIP_V4_1_0); }
bool Context::IsChipV6(uint16_t platformVersion)
{
auto chipV6_1_0 = static_cast<uint16_t>(Chip::CHIP_V6_1_0);
auto chipV6_2_0 = static_cast<uint16_t>(Chip::CHIP_V6_2_0);
std::unordered_set<uint16_t> checkList{chipV6_1_0, chipV6_2_0};
return static_cast<bool>(checkList.count(platformVersion));
}
bool Context::IsFirstChipV1(uint16_t platformVersion) { return platformVersion == static_cast<int>(Chip::CHIP_V1_1_0); }
uint16_t Context::GetAiCoreNum(uint16_t deviceId, const std::string &profPath)
{
if (deviceId == HOST_ID)
{
ERROR("Host do not have ai core num!");
return 0;
}
auto info = GetInfoByDeviceId(deviceId, profPath);
if (info.empty())
{
ERROR("GetAiCoreNum device info is empty, input path %, deviceId %", profPath, deviceId);
return 0;
}
return info.at("DeviceInfo").back().value("ai_core_num", 0);
}
uint64_t Context::GetTotalMem(uint16_t deviceId, const std::string &profPath)
{
auto info = GetInfoByDeviceId(deviceId, profPath);
if (info.empty())
{
ERROR("info is empty, input path %, deviceId %", profPath, deviceId);
return 0;
}
return info.at("memoryTotal");
}
uint64_t Context::GetNetCardTotalSpeed(uint16_t deviceId, const std::string &profPath)
{
auto info = GetInfoByDeviceId(deviceId, profPath);
if (info.empty())
{
ERROR("info is empty, input path %, deviceId %", profPath, deviceId);
return 0;
}
uint64_t totalSpeed = 0;
for (const auto netCard : info.at("netCard"))
{
auto speed = netCard.value("speed", 0);
totalSpeed += (speed < 0) ? 0 : static_cast<uint64_t>(speed);
}
return totalSpeed;
}
bool Context::IsLevel0(const std::string &profPath)
{
auto info = GetInfoByDeviceId(DEFAULT_DEVICE_ID, profPath);
if (info.empty())
{
ERROR("info is empty, input path %", profPath);
return true;
}
std::set<std::string> level0Set = {"l0", "level0"};
std::string profLevel = info.contains("profLevel") ? info.value("profLevel", "") : info.value("prof_level", "");
return (level0Set.find(profLevel) != level0Set.end());
}
std::vector<uint16_t> Context::GetCannVersion(uint16_t deviceId, const std::string &profPath)
{
std::vector<uint16_t> emptyVersion;
std::vector<uint16_t> retVersion;
constexpr size_t CANN_VERSION_CNT = 2;
auto info = GetInfoByDeviceId(deviceId, profPath);
if (info.empty())
{
ERROR("GetCannVersion device info is empty, input path %, deviceId %", profPath, deviceId);
return emptyVersion;
}
if (info.contains("cannVersion") && info.at("cannVersion").is_string())
{
std::string verStr = info.at("cannVersion").get<std::string>();
uint16_t verVal = 0;
for (size_t i = 0; i < CANN_VERSION_CNT; ++i)
{
size_t pos = verStr.find(".");
std::string seg;
if (pos == std::string::npos)
{
seg = verStr;
}
else
{
seg = verStr.substr(0, pos);
verStr = verStr.substr(pos + 1);
}
if (seg.empty())
{
return emptyVersion;
}
for (char c : seg)
{
if (!std::isdigit(c))
{
return emptyVersion;
}
}
if (StrToU16(verVal, seg) != ANALYSIS_OK)
{
ERROR("CannVersion segment to uint16_t failed, input path %, deviceId %", profPath, deviceId);
return emptyVersion;
}
retVersion.push_back(verVal);
if (verStr.empty())
{
break;
}
}
return retVersion.size() == CANN_VERSION_CNT ? retVersion : emptyVersion;
}
return emptyVersion;
}
}
}
}