* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* 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 FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
#include "utils/cann_info_utils.h"
#include <fstream>
#include <sys/stat.h>
#include "acl_rt_impl.h"
#include "utils/file_utils.h"
#include "common/json_parser.h"
namespace acl {
namespace {
#if defined(ONLY_ENABLE_ACL_UT)
constexpr const char_t* const SW_CONFIG_FILE = "tmp_run_data/ascendcl_config/swFeatureList.json";
constexpr const char_t* const RUNTIME_VERSION_PATH = "tests/tmp_run_data/share/info/runtime/version.info";
#else
constexpr const char_t* const SW_CONFIG_FILE = "data/ascendcl_config/swFeatureList.json";
constexpr const char_t* const RUNTIME_VERSION_PATH = "share/info/runtime/version.info";
#endif
constexpr const char_t* const VERSION_INFO_KEY = "Version=";
constexpr size_t MAX_INSTALL_PATH_SEARCH_DEPTH = 8U;
std::string StripTrailingSlash(const std::string& path)
{
if ((path.size() > 1UL) && (path.back() == '/')) {
return path.substr(0, path.size() - 1UL);
}
return path;
}
std::string GetParentDir(const std::string& path)
{
const std::string strippedPath = StripTrailingSlash(path);
const size_t pos = strippedPath.rfind('/');
if (pos == std::string::npos) {
return "";
}
return strippedPath.substr(0, pos + 1UL);
}
bool IsRegularFile(const std::string& path)
{
struct stat fileStat = {};
return (stat(path.c_str(), &fileStat) == 0) && S_ISREG(fileStat.st_mode);
}
bool FindFileFromCurrentToParents(const std::string& startDir, const std::string& relativePath, std::string& matchedDir)
{
std::string currentDir = startDir;
for (size_t depth = 0U; depth < MAX_INSTALL_PATH_SEARCH_DEPTH; ++depth) {
if (currentDir.empty()) {
return false;
}
if (IsRegularFile(currentDir + relativePath)) {
matchedDir = currentDir;
return true;
}
const std::string parentDir = GetParentDir(currentDir);
if ((parentDir.empty()) || (parentDir == currentDir)) {
return false;
}
currentDir = parentDir;
}
return false;
}
}
std::mutex CannInfoUtils::mutex_;
bool CannInfoUtils::initFlag_ = false;
int32_t CannInfoUtils::currentRuntimeVersion_ = UNKNOWN_VERSION;
std::string CannInfoUtils::swConfigPath_;
std::string CannInfoUtils::defaultInstallPath_;
aclCannAttr CannInfoUtils::attrArray_[MAX_CANN_ATTR_SIZE];
size_t CannInfoUtils::attrNum_ = 0;
std::map<aclCannAttr, CannInfo> CannInfoUtils::attrToCannInfo_ = {
{ACL_CANN_ATTR_INF_NAN, CannInfo("INF_NAN", "SoCInfo", "support_inf_nan")},
{ACL_CANN_ATTR_BF16, CannInfo("BF16", "SoCInfo", "support_bf16")},
{ACL_CANN_ATTR_JIT_COMPILE, CannInfo("JIT_COMPILE", "", "")},
};
aclError CannInfoUtils::GetAttributeList(const aclCannAttr** cannAttr, size_t* num)
{
const aclError ret = Initialize();
if (ret != ACL_SUCCESS) {
ACL_LOG_INNER_ERROR("initialize CannInfoUtils failed, ret = %d", static_cast<int32_t>(ret));
return ret;
}
*cannAttr = attrArray_;
*num = attrNum_;
return ACL_SUCCESS;
}
aclError CannInfoUtils::GetAttribute(aclCannAttr cannAttr, int32_t* value)
{
const aclError ret = Initialize();
if (ret != ACL_SUCCESS) {
ACL_LOG_INNER_ERROR("initialize CannInfoUtils failed, ret = %d", static_cast<int32_t>(ret));
return ret;
}
auto iter = attrToCannInfo_.find(cannAttr);
if (iter == attrToCannInfo_.end()) {
ACL_LOG_WARN("find cann attr failed, attr value = %d", static_cast<int32_t>(cannAttr));
return ACL_ERROR_INVALID_PARAM;
}
*value = iter->second.isAvailable;
return ACL_SUCCESS;
}
aclError CannInfoUtils::Initialize()
{
std::lock_guard<std::mutex> lock(mutex_);
if (initFlag_) {
ACL_LOG_INFO("CannInfoUtils has already initialized.");
return ACL_SUCCESS;
}
ACL_LOG_INFO("Start to initialize CannInfoUtils.");
auto ret = GetConfigInstallPath();
if (ret != ACL_SUCCESS) {
ACL_LOG_INNER_ERROR("Failed to get swFeatureList.json, please check ascendcl_config path.");
return ret;
}
ret = JsonParser::GetAttrConfigFromFile(swConfigPath_.c_str(), attrToCannInfo_);
if (ret != ACL_SUCCESS) {
ACL_LOG_INNER_ERROR("Failed to parse requirements of Cann attrs, ret = %d.", ret);
return ret;
}
const std::string runtimeVersionPath = defaultInstallPath_ + RUNTIME_VERSION_PATH;
ret = ParseVersionInfo(runtimeVersionPath, ¤tRuntimeVersion_);
if (ret != ACL_SUCCESS) {
ACL_LOG_WARN("cannot get runtime version in current environment!");
return ACL_ERROR_INTERNAL_ERROR;
}
CheckAndUpdateAttrAvailability();
initFlag_ = true;
ACL_LOG_INFO(
"Successfully initialized CannInfoUtils: current CannInfo[runtime = %d, attrNum = %zu]", currentRuntimeVersion_,
attrNum_);
return ACL_SUCCESS;
}
aclError CannInfoUtils::GetConfigInstallPath()
{
std::string path;
const aclError ret = file_utils::GetSoRealPath(path);
if (ret != ACL_SUCCESS) {
ACL_LOG_WARN("Failed to get libascendcl.so file path.");
return ret;
}
ACL_LOG_DEBUG("current path = %s", path.c_str());
const std::string soDir = path;
path = path.substr(0, path.rfind('/'));
path = path.substr(0, path.rfind('/') + 1UL);
swConfigPath_ = path + SW_CONFIG_FILE;
if (!IsRegularFile(swConfigPath_)) {
std::string matchedDir;
if (FindFileFromCurrentToParents(soDir, SW_CONFIG_FILE, matchedDir)) {
swConfigPath_ = matchedDir + SW_CONFIG_FILE;
path = matchedDir;
ACL_LOG_INFO("fallback to swConfigPath = %s", swConfigPath_.c_str());
}
}
ACL_LOG_DEBUG("swConfigPath = %s", swConfigPath_.c_str());
path.pop_back();
defaultInstallPath_ = path.substr(0, path.rfind('/') + 1UL);
if (!IsRegularFile(defaultInstallPath_ + RUNTIME_VERSION_PATH)) {
std::string matchedDir;
if (FindFileFromCurrentToParents(soDir, RUNTIME_VERSION_PATH, matchedDir)) {
defaultInstallPath_ = matchedDir;
ACL_LOG_INFO("fallback to defaultInstallPath = %s", defaultInstallPath_.c_str());
}
}
ACL_LOG_DEBUG("defaultInstallPath = %s", defaultInstallPath_.c_str());
return ACL_SUCCESS;
}
aclError CannInfoUtils::ParseVersionInfo(const std::string& path, int32_t* version)
{
std::ifstream ifs(path, std::ifstream::in);
if (!ifs.is_open()) {
ACL_LOG_WARN("Open file [%s] failed.", path.c_str());
return ACL_ERROR_INTERNAL_ERROR;
}
std::string line;
while (std::getline(ifs, line)) {
if (line.find(VERSION_INFO_KEY) != std::string::npos) {
ACL_LOG_DEBUG("Parse version success, content is [%s].", line.c_str());
ifs.close();
const size_t prefixLen = strlen(VERSION_INFO_KEY);
line = line.substr(prefixLen);
const size_t pos = line.find('.', line.find('.') + 1UL);
line = line.substr(0, pos);
return ParseVersionValue(line, version);
}
}
ifs.close();
ACL_LOG_WARN("cannot find valid Version info, please check path = %s", path.c_str());
return ACL_ERROR_INTERNAL_ERROR;
}
aclError CannInfoUtils::ParseVersionValue(const std::string& str, int32_t* value)
{
const size_t pos = str.find('.');
try {
const int32_t major = std::stoi(str.substr(0, pos));
const int32_t minor = std::stoi(str.substr(pos + 1UL));
*value = 1000 * major + 10 * minor;
} catch (...) {
ACL_LOG_WARN("strVal[%s] can not be converted to version value", str.c_str());
return ACL_ERROR_INTERNAL_ERROR;
}
return ACL_SUCCESS;
}
bool CannInfoUtils::MatchVersionInfo(const CannInfo& configCannInfo)
{
if (configCannInfo.minimumRuntimeVersion == UNKNOWN_VERSION) {
return true;
}
return (currentRuntimeVersion_ >= configCannInfo.minimumRuntimeVersion);
}
bool CannInfoUtils::CheckNPUFeatures(const CannInfo& configInfo)
{
if (configInfo.socSpecLabel.empty() || configInfo.socSpecKey.empty()) {
return true;
}
constexpr uint32_t kMaxValueLen = 16U;
char_t value[kMaxValueLen] = {0};
const auto ret = rtGetSocSpec(configInfo.socSpecLabel.c_str(), configInfo.socSpecKey.c_str(), value, kMaxValueLen);
if (ret != RT_ERROR_NONE) {
ACL_LOG_WARN(
"Cannot get platform info, label = [%s], key = [%s]", configInfo.socSpecLabel.c_str(),
configInfo.socSpecKey.c_str());
return false;
}
const std::string strVal(value);
return strVal == "1";
}
void CannInfoUtils::CheckAndUpdateAttrAvailability()
{
for (auto& item : attrToCannInfo_) {
auto& swConfigInfo = item.second;
if (MatchVersionInfo(swConfigInfo) && CheckNPUFeatures(swConfigInfo)) {
ACL_LOG_INFO("support cann attribute [%s]", swConfigInfo.readableAttrName.c_str());
swConfigInfo.isAvailable = 1;
attrArray_[attrNum_] = item.first;
++attrNum_;
}
}
}
}