* This file is part of the MindStudio project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* 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 <climits>
#include <unistd.h>
#include <fstream>
#include <nlohmann/json.hpp>
#include <unordered_map>
#include "securec.h"
#include "msServiceProfiler/Log.h"
#include "msServiceProfiler/SecurityUtils.h"
#include "msServiceProfiler/SecurityUtilsLog.h"
#include "msServiceProfiler/Utils.h"
#include "msServiceProfiler/Config.h"
namespace msServiceProfiler {
constexpr int MILLISECONDS_IN_SECOND = 1000;
constexpr int ACL_PROF_ENABLE_TASK_TIME = 1;
constexpr int MSPTI_ENABLE_TASK_TIME = 2;
constexpr int TORCH_PROFILER_ENABLE_TASK_TIME = 3;
constexpr int MAX_TIME_LIMIT = 7200;
static std::string TrimWhitespace(const std::string& str)
{
std::string result = str;
result.erase(0, result.find_first_not_of(" \t\n\r\f\v"));
result.erase(result.find_last_not_of(" \t\n\r\f\v") + 1);
return result;
}
Config::Config()
{
ReadConfigPath();
}
void Config::ReadAndSaveConfig()
{
PROF_LOGD("isServiceProfConfigPathSet: %s", isServiceProfConfigPathSet ? "true" : "false");
if (!isServiceProfConfigPathSet) {
InitProfPathDateTail();
ParseProfPath(Json());
return;
}
InitProfPathDateTail();
auto configJson = ReadConfigFile();
ParseConfig(configJson);
SaveConfigToJsonFile();
ReadConfigPath();
}
void Config::ReadConfigPath()
{
configPath_ = MsUtils::GetEnvAsString("SERVICE_PROF_CONFIG_PATH");
isServiceProfConfigPathSet = !configPath_.empty();
if (isServiceProfConfigPathSet && access(configPath_.c_str(), F_OK) != 0) {
configPath_ = "";
}
}
nlohmann::ordered_json Config::ReadConfigFile()
{
nlohmann::ordered_json jsonData;
if (configPath_.empty()) {
return jsonData;
}
if (access(configPath_.c_str(), F_OK) != 0) {
LOG_ONCE_E("SERVICE_PROF_CONFIG_PATH : %s is not file or Permission Denied",
configPath_.c_str());
return jsonData;
}
std::ifstream configFile;
char realConfigPath[PATH_MAX] = {0};
if (realpath(configPath_.c_str(), realConfigPath) == nullptr) {
LOG_ONCE_E("Failed to get real path of: %s", configPath_.c_str());
return jsonData;
}
configPath_ = realConfigPath;
LOG_ONCE_D("SERVICE_PROF_CONFIG_PATH : %s", configPath_.c_str());
try {
configFile.open(configPath_);
if (!configFile.good()) {
LOG_ONCE_E("Fail to open: %s", configPath_.c_str());
return jsonData;
}
} catch (const std::exception &e) {
LOG_ONCE_E("Fail to open config file: %s, error: %s",
configPath_.c_str(), e.what());
return jsonData;
}
try {
configFile >> jsonData;
} catch (const std::exception &e) {
PROF_LOGE("Fail to parse file content as json object, config path: %s, error: %s",
configPath_.c_str(), e.what());
configFile.close();
return jsonData;
}
configFile.close();
if (jsonData.empty()) {
PROF_LOGE("Parsed json object is empty, config path: %s", configPath_.c_str());
return jsonData;
}
return jsonData;
}
void Config::ParseConfig(const Json& configJson)
{
ParseTimeLimit(configJson);
ParseAclTaskTime(configJson);
ParseProfPath(configJson);
ParseLevel(configJson);
ParseDomain(configJson);
ParseCollectConfig(configJson);
ParseMspti(configJson);
ParseAicoreMetrics(configJson);
ParseDataTypeConfig(configJson);
ParseTorchProfStack(configJson);
ParseTorchProfModules(configJson);
ParseTorchProfStepNum(configJson);
ParseProfilerStepNum(configJson);
ParseEnable(configJson);
ParseMetricEnable(configJson);
}
bool Config::ParseTorchProfStack(const Json& config, bool justParse)
{
bool torch_prof_stack = false;
if (config.contains("torch_prof_stack")) {
if (config["torch_prof_stack"].is_boolean()) {
torch_prof_stack = config["torch_prof_stack"];
} else {
PROF_LOGW("torch_prof_stack value is not a boolean, will set false.");
}
}
if (!justParse) {
torch_prof_stack_ = torch_prof_stack;
PROF_LOGD("torch_prof_stack_: %s", torch_prof_stack_ ? "true" : "false");
}
return torch_prof_stack;
}
bool Config::ParseTorchProfModules(const Json& config, bool justParse)
{
bool torch_prof_modules = false;
if (config.contains("torch_prof_modules")) {
if (config["torch_prof_modules"].is_boolean()) {
torch_prof_modules = config["torch_prof_modules"];
} else {
PROF_LOGW("torch_prof_modules value is not a boolean, will set false.");
}
}
if (!justParse) {
torch_prof_modules_ = torch_prof_modules;
PROF_LOGD("torch_prof_modules_: %s", torch_prof_modules_ ? "true" : "false");
}
return torch_prof_modules;
}
void Config::ParseTorchProfStepNum(const Json& config)
{
int torch_prof_step_num = 0;
if (config.contains("torch_prof_step_num")) {
if (config["torch_prof_step_num"].is_number_integer()) {
torch_prof_step_num = config["torch_prof_step_num"];
if (torch_prof_step_num < 1) {
PROF_LOGD("Torch Profiler will collect all steps data.");
torch_prof_step_num = 0;
}
} else {
PROF_LOGW("torch_prof_step_num is not an integer, "
"using default value 0 (collect all steps)");
torch_prof_step_num = 0;
}
}
torch_prof_step_num_ = torch_prof_step_num;
PROF_LOGD("torch_prof_step_num_: %d", torch_prof_step_num_);
}
void Config::ParseProfilerStepNum(const Json& config)
{
int profiler_step_num = -1;
if (config.contains("profiler_step_num")) {
if (config["profiler_step_num"].is_number_integer()) {
profiler_step_num = config["profiler_step_num"];
if (profiler_step_num < 1) {
PROF_LOGD("Profiler will collect all steps data.");
profiler_step_num = 0;
}
} else {
PROF_LOGW("profiler_step_num is not an integer, "
"using default value 0 (collect all steps)");
profiler_step_num = 0;
}
}
profiler_step_num_ = profiler_step_num;
PROF_LOGD("profiler_step_num_: %d", profiler_step_num_);
}
void Config::ParseAicoreMetrics(const Json& config)
{
if (config.contains("aclprofAicoreMetrics")) {
aclprofAicoreMetrics_ = ConvertStringToAicoreMetrics(config["aclprofAicoreMetrics"]);
} else {
aclprofAicoreMetrics_ = ACL_AICORE_PIPE_UTILIZATION;
}
}
void Config::ParseDataTypeConfig(const Json& config)
{
if (config.contains("aclDataTypeConfig")) {
aclDataTypeConfig_ = ConvertStringToAclDataType(config["aclDataTypeConfig"]);
} else {
aclDataTypeConfig_ = 0;
}
}
uint32_t Config::ConvertStringToAclDataType(const std::string& configStr) const
{
uint32_t profSwitch = 0;
static const std::unordered_map<std::string, uint32_t> flagMap = {
{"ACL_PROF_ACL_API", ACL_PROF_ACL_API},
{"ACL_PROF_TASK_TIME", ACL_PROF_TASK_TIME},
{"ACL_PROF_TASK_TIME_L0", ACL_PROF_TASK_TIME_L0},
{"ACL_PROF_OP_ATTR", ACL_PROF_OP_ATTR},
{"ACL_PROF_AICORE_METRICS", ACL_PROF_AICORE_METRICS},
{"ACL_PROF_TASK_MEMORY", ACL_PROF_TASK_MEMORY},
{"ACL_PROF_AICPU", ACL_PROF_AICPU},
{"ACL_PROF_L2CACHE", ACL_PROF_L2CACHE},
{"ACL_PROF_HCCL_TRACE", ACL_PROF_HCCL_TRACE},
{"ACL_PROF_TRAINING_TRACE", ACL_PROF_TRAINING_TRACE},
{"ACL_PROF_RUNTIME_API", ACL_PROF_RUNTIME_API},
{"ACL_PROF_MSPROFTX", ACL_PROF_MSPROFTX}
};
const auto& tokens = SplitAndTrimString(configStr, ',');
if (tokens.size() > flagMap.size()) {
PROF_LOGW("Too many aclDataTypeConfig provided, check if there are repeated values.");
}
for (size_t i = 0; i < tokens.size(); ++i) {
const auto& flagName = tokens[i];
auto it = flagMap.find(flagName);
if (it != flagMap.end()) {
profSwitch |= it->second;
} else {
PROF_LOGE("Unknown profiling flag: %s", flagName.c_str());
}
}
return profSwitch;
}
uint32_t Config::GetProfilingSwitch() const
{
uint32_t profSwitch = aclDataTypeConfig_ | ACL_PROF_MSPROFTX;
const std::string& taskTimeLevel = GetAclTaskTimeLevel();
PROF_LOGD("In GetProfilingSwitch, taskTimeLevel: %s", taskTimeLevel.c_str());
if (taskTimeLevel == "L0") {
profSwitch |= ACL_PROF_TASK_TIME_L0;
} else if (taskTimeLevel == "L1") {
profSwitch |= (ACL_PROF_TASK_TIME | ACL_PROF_ACL_API);
}
PROF_LOGD("In GetProfilingSwitch, profSwitch: 0x%x", profSwitch);
return profSwitch;
}
aclprofAicoreMetrics Config::ConvertStringToAicoreMetrics(const std::string& configStr) const
{
if (configStr.empty()) {
return ACL_AICORE_NONE;
}
std::string upperStr;
upperStr.reserve(configStr.size());
for (char c : configStr) {
upperStr.push_back(static_cast<char>(std::toupper(static_cast<unsigned char>(c))));
}
static const std::unordered_map<std::string, aclprofAicoreMetrics> metricMap = {
{"ACL_AICORE_PIPE_UTILIZATION", ACL_AICORE_PIPE_UTILIZATION},
{"ACL_AICORE_MEMORY_BANDWIDTH", ACL_AICORE_MEMORY_BANDWIDTH},
{"ACL_AICORE_L0B_AND_WIDTH", ACL_AICORE_L0B_AND_WIDTH},
{"ACL_AICORE_RESOURCE_CONFLICT_RATIO", ACL_AICORE_RESOURCE_CONFLICT_RATIO},
{"ACL_AICORE_MEMORY_UB", ACL_AICORE_MEMORY_UB},
{"ACL_AICORE_L2_CACHE", ACL_AICORE_L2_CACHE},
{"ACL_AICORE_NONE", ACL_AICORE_NONE}
};
auto it = metricMap.find(upperStr);
if (it != metricMap.end()) {
return it->second;
}
PROF_LOGE("Unknown profiling flag: %s", configStr.c_str());
return ACL_AICORE_NONE;
}
void Config::ParseMspti(const Json& config)
{
if (config.contains("api_filter")) {
if (config["api_filter"].is_string()) {
apiFilter_ = config["api_filter"];
} else {
PROF_LOGW("Unknown api_filter type. api_filter set to nullptr.");
}
}
if (config.contains("kernel_filter")) {
if (config["kernel_filter"].is_string()) {
kernelFilter_ = config["kernel_filter"];
} else {
PROF_LOGW("Unknown kernel_filter type. kernel_filter set to nullptr.");
}
}
}
bool Config::ParseEnable(const Json& config, bool justParse)
{
bool enable = false;
if (config.contains("enable")) {
if (config["enable"].is_number_integer()) {
enable = config["enable"] == 1;
} else {
PROF_LOGW("enable value is not an integer, will set false.");
}
}
if (justParse) {
return enable;
}
enable_ = enable;
PROF_LOGI("profile enable_: %s", enable_ ? "true" : "false");
return enable;
}
bool Config::ParseMetricEnable(const Json& config, bool justParse)
{
bool metric_enable = false;
if (config.contains("metric_enable")) {
if (config["metric_enable"].is_number_integer()) {
metric_enable = config["metric_enable"] == 1;
} else if (config["metric_enable"].is_boolean()) {
metric_enable = config["metric_enable"];
} else {
PROF_LOGW("metric_enable value is not an integer or boolean, will set false.");
}
}
if (justParse) {
return metric_enable;
}
metric_enable_ = metric_enable;
PROF_LOGI("profile metric_enable_: %s", metric_enable_ ? "true" : "false");
return metric_enable;
}
void Config::ParseTimeLimit(const Json& config)
{
timeLimit_ = 0;
if (config.contains("timelimit")) {
if (config["timelimit"].is_number_integer()) {
PROF_LOGD("Got timelimit value: %d", static_cast<int>(config["timelimit"]));
if (config["timelimit"] <= 0) {
timeLimit_ = 0;
} else if (config["timelimit"] > 0 && config["timelimit"] <= MAX_TIME_LIMIT) {
timeLimit_ = config["timelimit"];
PROF_LOGD("profile timeLimit_: %u", timeLimit_);
} else {
timeLimit_ = MAX_TIME_LIMIT;
PROF_LOGW("timelimit value is higher than %d, will set %d", MAX_TIME_LIMIT, MAX_TIME_LIMIT);
}
} else {
PROF_LOGW("timelimit value is not an integer, the profiling time is not assigned.");
}
}
}
std::string Config::GetDefaultProfPath() const
{
std::string profPath;
std::string homePath = MsUtils::GetEnvAsString("HOME");
profPath.append(homePath).append("/.ms_server_profiler/");
return profPath;
}
std::string Config::GetDirPath(std::string configPath) const
{
std::string dirPath;
size_t lastSlash = configPath.find_last_of("/\\");
if (lastSlash != std::string::npos) {
dirPath = configPath.substr(0, lastSlash);
} else {
dirPath = ".";
}
return dirPath;
}
void Config::ParseProfPath(const Json& config)
{
if (config.contains("prof_dir")) {
profPath_ = config["prof_dir"];
if (profPath_.back() != '/') {
profPath_.append("/");
}
} else {
profPath_ = GetDefaultProfPath();
}
profPath_.append(profPathDateTail_);
}
void Config::CheckMsptiConflict()
{
std::string ld_preload_str = MsUtils::GetEnvAsString("LD_PRELOAD");
if (ld_preload_str.find("libmspti.so") != std::string::npos) {
PROF_LOGW("Detected mspti is enabled, which conflicts with acl prof. "
"`acl_task_time` has been reset to the default value 0. If you need to enable it,"
"check the loading of libmspti.so in LD_PRELOAD.");
enableAclTaskTime_ = false;
}
}
* @brief 检查msprof是否开启了动态或静态采集,如果开启则不读取配置文件以防采集冲突
*/
void Config::CheckAclKernelConflict()
{
const char* profilerSampleConfig = getenv("PROFILER_SAMPLECONFIG");
if (profilerSampleConfig != nullptr) {
enableAclTaskTime_ = false;
msptiEnable_ = false;
PROF_LOGE("Failed to initialize acl_task_time, env variable `PROFILER_SAMPLECONFIG` is set."
"This causes conflicts with kernels profiling. ");
return;
}
const char* profilingMode = getenv("PROFILING_MODE");
if (profilingMode != nullptr && std::string(profilingMode) == "dynamic") {
enableAclTaskTime_ = false;
msptiEnable_ = false;
PROF_LOGE("Failed to initialize acl_task_time, env variable `PROFILING_MODE` is set to dynamic."
"This causes conflicts with kernels profiling. ");
return;
}
}
void Config::ParseAclTaskTime(const Json &config)
{
enableAclTaskTime_ = false;
if (config.contains("acl_task_time")) {
if (config["acl_task_time"].is_number_integer()) {
enableAclTaskTime_ = config["acl_task_time"] == ACL_PROF_ENABLE_TASK_TIME;
if (enableAclTaskTime_) {
CheckMsptiConflict();
}
msptiEnable_ = config["acl_task_time"] == MSPTI_ENABLE_TASK_TIME;
CheckAclKernelConflict();
torchProfilerEnable_ = config["acl_task_time"] == TORCH_PROFILER_ENABLE_TASK_TIME;
} else {
PROF_LOGW("Unknown acl_task_time type. acl_task_time disabled.");
}
}
PROF_LOGD("profile enableAclTaskTime_: %s", enableAclTaskTime_ ? "true" : "false");
PROF_LOGD("profile msptiEnable_: %s", msptiEnable_ ? "true" : "false");
PROF_LOGD("profile torchProfilerEnable_: %s", torchProfilerEnable_? "true" : "false");
if (config.contains("acl_prof_task_time_level")) {
ParseAclProfTaskTimeLevel(config["acl_prof_task_time_level"]);
}
}
void Config::ParseAclProfTaskTimeLevel(const Json &configValue)
{
auto aclProfTaskTimeLevel = MsUtils::SplitStr(configValue, ';');
if (aclProfTaskTimeLevel.first.empty()) {
aclProfTaskTimeLevel.first = "L0";
} else if (aclProfTaskTimeLevel.first != "L0" && aclProfTaskTimeLevel.first != "L1") {
PROF_LOGW("aclProfTaskTimeLevel should be L0 or L1, now it is %s, default to L0",
aclProfTaskTimeLevel.first.c_str());
aclProfTaskTimeLevel.first = "L0";
}
aclTaskTimeLevel_ = aclProfTaskTimeLevel.first;
PROF_LOGD("profile aclTaskTimeLevel: %s", aclTaskTimeLevel_.c_str());
if (aclProfTaskTimeLevel.second == "") {
PROF_LOGD("Not set aclTaskTimeDuration value");
return;
}
try {
aclTaskTimeDuration_ = std::stoi(aclProfTaskTimeLevel.second);
} catch (const std::invalid_argument& e) {
PROF_LOGW("aclTaskTimeDuration value is Invalid argument, now it is %s",
aclProfTaskTimeLevel.second.c_str());
return;
} catch (const std::out_of_range& e) {
PROF_LOGW("aclTaskTimeDuration value is Out of range, now it is %s",
aclProfTaskTimeLevel.second.c_str());
return;
}
constexpr int maxAclTaskTimeDuration = 999;
if (aclTaskTimeDuration_ > maxAclTaskTimeDuration || aclTaskTimeDuration_ < 1) {
PROF_LOGW("aclTaskTimeDuration value should between 1 ~ 999, now it is %d",
aclTaskTimeDuration_);
}
PROF_LOGD("profile aclTaskTimeDuration: %d", aclTaskTimeDuration_);
}
void Config::ParseLevel(const Json &config)
{
level_ = Level::INFO;
static const std::map<std::string, Level> ENUM_MAP = {
{"ERROR", Level::ERROR},
{"INFO", Level::INFO},
{"DETAILED", Level::DETAILED},
{"VERBOSE", Level::VERBOSE},
{"LEVEL_CORE_TRACE", Level::LEVEL_CORE_TRACE},
{"LEVEL_OUTLIER_ENENT", Level::LEVEL_OUTLIER_ENENT},
{"LEVEL_NORMAL_TRACE", Level::LEVEL_NORMAL_TRACE},
{"LEVEL_DETAILED_TRACE", Level::LEVEL_DETAILED_TRACE},
{"L0", Level::L0},
{"L1", Level::L1},
{"L2", Level::L2},
};
if (config.contains("profiler_level")) {
const auto profilerLevel = config["profiler_level"];
if (profilerLevel.is_number_integer()) {
int level = profilerLevel.get<int>();
if (level >= 0) {
level_ = static_cast<uint32_t>(level);
}
} else if (profilerLevel.is_string()) {
std::string valueUpper = profilerLevel;
std::transform(valueUpper.begin(), valueUpper.end(), valueUpper.begin(), [](char const &c) {
return std::toupper(c);
});
if (ENUM_MAP.find(valueUpper) != ENUM_MAP.end()) {
level_ = ENUM_MAP.at(valueUpper);
} else {
PROF_LOGW("Unknown profiler_level. Use the default profiler level.");
}
}
}
PROF_LOGD("profiler_level: %u", level_);
}
std::vector<std::string> Config::SplitAndTrimString(const std::string& str, char delimiter) const
{
std::vector<std::string> tokens;
size_t start = 0;
size_t end = str.find(delimiter);
while (end != std::string::npos) {
std::string token = str.substr(start, end - start);
token = TrimWhitespace(token);
if (!token.empty()) {
tokens.push_back(token);
}
start = end + 1;
end = str.find(delimiter, start);
}
std::string lastToken = str.substr(start);
lastToken = TrimWhitespace(lastToken);
if (!lastToken.empty()) {
tokens.push_back(lastToken);
}
return tokens;
}
void Config::LogDomainInfo() const
{
PROF_LOGD("profile enableDomainFilter_: %s", enableDomainFilter_ ? "true" : "false");
std::string combined;
for (const auto& domain : validDomain_) {
if (!combined.empty()) {
combined += ", ";
}
combined += domain;
}
if (!combined.empty()) {
PROF_LOGD("profiler validDomain_: %s", combined.c_str());
}
}
void Config::ParseDomain(const Json& config)
{
enableDomainFilter_ = false;
validDomain_.clear();
if (!config.contains("domain")) {
LogDomainInfo();
return;
}
if (!config["domain"].is_string()) {
PROF_LOGW("Invalid 'domain' format, expected string. Domain filter will be disabled.");
LogDomainInfo();
return;
}
std::string domainStr = config["domain"];
std::vector<std::string> domains = SplitAndTrimString(domainStr, ';');
for (const auto& domain : domains) {
if (!domain.empty()) {
validDomain_.insert(domain);
enableDomainFilter_ = true;
}
}
LogDomainInfo();
}
void Config::InitProfPathDateTail(bool forceReinit)
{
const size_t tailMaxSize = 32;
if (profPathDateTail_.empty() || forceReinit) {
time_t now = time(nullptr);
auto ltm = std::localtime(&now);
char pStrDateTail[tailMaxSize + 1] = {0};
int ret = sprintf_s(pStrDateTail, tailMaxSize + 1, "%02d%02d-%02d%02d/",
ltm->tm_mon + 1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min);
if (ret == -1) {
PROF_LOGW("ProfPathDateTail init failed.");
}
profPathDateTail_ = pStrDateTail;
}
}
bool Config::ParseCollectConfig(const Json &config)
{
bool retHost = ParseHostConfig(config);
bool retNpu = ParseNpuConfig(config);
return retHost && retNpu;
}
bool Config::ParseHostConfig(const Json &config)
{
bool ret = true;
hostCpuUsage_ = false;
hostMemoryUsage_ = false;
if (config.contains("host_system_usage_freq")) {
try {
uint32_t hostFreq = config["host_system_usage_freq"];
if (hostFreq >= hostFreqMin_ && hostFreq <= hostFreqMax_) {
hostFreq_ = hostFreq;
hostCpuUsage_ = true;
hostMemoryUsage_ = true;
} else if (static_cast<int32_t>(hostFreq) == -1) {
ret = false;
} else {
LOG_ONCE_E("To enable host cpu or host memory usage collection, set host_system_usage_freq "
"between %u and %u. To disable it, set this value to -1. "
"host cpu or host memory usage collection is now disabled.",
hostFreqMin_, hostFreqMax_);
ret = false;
}
} catch (const std::exception &e) {
LOG_ONCE_E("fail to convert host_system_usage_freq config to uint,"
"will not collect host cpu or host memory usage.");
ret = false;
}
} else {
ret = false;
}
PROF_LOGD("host_system_usage_freq %s", ret ? "Enabled" : "Disabled");
return ret;
}
bool Config::ParseNpuConfig(const Json &config)
{
bool ret = true;
npuMemoryUsage_ = false;
if (config.contains("npu_memory_usage_freq")) {
try {
uint32_t npuMemoryFreq = config["npu_memory_usage_freq"];
if (npuMemoryFreq >= npuMemoryFreqMin_ && npuMemoryFreq <= npuMemoryFreqMax_) {
npuMemoryFreq_ = npuMemoryFreq;
npuMemoryUsage_ = true;
} else if (static_cast<int32_t>(npuMemoryFreq) == -1) {
ret = false;
} else {
LOG_ONCE_E("To enable npu memory usage collection, set npu_memory_usage_freq "
"between %u and %u. To disable it, set this value to -1. "
"npu memory usage collection is now disabled.",
npuMemoryFreqMin_, npuMemoryFreqMax_);
ret = false;
}
} catch (const std::exception &e) {
LOG_ONCE_E("Fail to convert npu_memory_usage_freq config to uint, "
"will not collect npu memory usage.");
ret = false;
}
npuMemorySleepMilliseconds_ = static_cast<uint32_t>(std::round(MILLISECONDS_IN_SECOND / npuMemoryFreq_));
} else {
ret = false;
}
PROF_LOGD("npu_memory_usage_freq %s", ret ? "Enabled" : "Disabled");
return ret;
}
bool Config::PrepareConfigAndPath(std::string& configPath) const
{
const int jsonSuffixSize = 5;
if (configPath.empty()) {
PROF_LOGD("Cannot save config to JSON file - no config path specified");
return false;
}
if (configPath.size() < jsonSuffixSize ||
configPath.substr(configPath.size() - jsonSuffixSize) != ".json") {
PROF_LOGW("Config path must end with .json: %s", SecurityUtils::ToSafeString(configPath).c_str());
return false;
}
if (access(configPath.c_str(), F_OK) == 0) {
PROF_LOGD("Config path: %s already exists", SecurityUtils::ToSafeString(configPath).c_str());
return false;
}
std::string dirPath = GetDirPath(configPath);
if (access(dirPath.c_str(), W_OK) != 0) {
PROF_LOGW("Directory of Config path is invalid for writing: %s", dirPath.c_str());
return false;
}
if (!SecurityUtils::IsOwner(dirPath.c_str())) {
return false;
}
return true;
}
nlohmann::ordered_json Config::GetConfigData() const
{
return {
{"enable", enable_ ? 1 : 0},
{"prof_dir", GetDefaultProfPath()},
{"profiler_level", "INFO"},
{"acl_task_time", enableAclTaskTime_ ? 1 : 0},
{"acl_prof_task_time_level", "L0"},
{"timelimit", 0},
{"domain", ""},
};
}
void Config::SetFileEnable(bool enable)
{
SetEnable(enable);
const int jsonIndentSize = 4;
std::string configPath = MsUtils::GetEnvAsString("SERVICE_PROF_CONFIG_PATH");
auto configJson = ReadConfigFile();
configJson["enable"] = 0;
if (!SecurityUtils::IsPathLenLegal(configPath)) {
PROF_LOGE("Invalid config path due to excessive length: %s", SecurityUtils::ToSafeString(configPath).c_str());
return;
}
if (!SecurityUtils::IsPathDepthLegal(configPath)) {
PROF_LOGE("Invalid config path due to excessive depth: %s", SecurityUtils::ToSafeString(configPath).c_str());
return;
}
std::ofstream outputFile(configPath.c_str());
if (!outputFile.is_open()) {
PROF_LOGW("Automatic config file update failed %s", SecurityUtils::ToSafeString(configPath).c_str());
return;
}
outputFile << configJson.dump(jsonIndentSize);
outputFile.close();
}
void Config::SaveConfigToJsonFile() const
{
const int jsonIndentSize = 4;
std::string configPath = MsUtils::GetEnvAsString("SERVICE_PROF_CONFIG_PATH");
if (!PrepareConfigAndPath(configPath)) {
return;
}
try {
std::string dirPath = GetDirPath(configPath);
std::string tempDir = dirPath + "/";
std::vector<char> tempPath(tempDir.begin(), tempDir.end());
const size_t TEMP_TEMPLATE_LENGTH = 11;
tempPath.insert(tempPath.end(), "temp_XXXXXX", "temp_XXXXXX" + TEMP_TEMPLATE_LENGTH);
tempPath.push_back('\0');
const int fd = mkstemp(tempPath.data());
if (fd == -1) {
PROF_LOGW("mkstemp failed: %s", strerror(errno));
return;
}
close(fd);
std::string tempFilePathStr(tempPath.data());
char realTempPath[PATH_MAX + 1] = {0};
if (realpath(tempPath.data(), realTempPath) == nullptr) {
PROF_LOGW("Failed to canonicalize path: %s", strerror(errno));
remove(tempFilePathStr.c_str());
return;
}
if (!SecurityUtils::CheckFileBeforeWrite(realTempPath)) {
remove(tempFilePathStr.c_str());
return;
}
PROF_LOGD("file generation in the path %s", realTempPath);
std::ofstream outputFile(realTempPath);
if (!outputFile.is_open()) {
PROF_LOGW("Automatic config file generation failed %s", realTempPath);
remove(tempFilePathStr.c_str());
return;
}
outputFile << GetConfigData().dump(jsonIndentSize);
outputFile.close();
auto ret = rename(realTempPath, configPath.c_str());
if (ret != 0 && errno != ENOENT) {
PROF_LOGW("Automatic config file generation failed: %s", strerror(errno));
remove(realTempPath);
return;
}
PROF_LOGD("Successfully saved profiler configuration to: %s", SecurityUtils::ToSafeString(configPath).c_str());
} catch (const std::exception& e) {
PROF_LOGE("Failed to save config to JSON file: %s", e.what());
}
}
}