LCOV - code coverage report
Current view: top level - adump/exception - kernel_info_collector.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 95.4 % 238 227
Test Date: 2026-08-31 10:09:28 Functions: 100.0 % 16 16

            Line data    Source code
       1              : /**
       2              :  * Copyright (c) 2025 Huawei Technologies Co., Ltd.
       3              :  * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
       4              :  * CANN Open Software License Agreement Version 2.0 (the "License").
       5              :  * Please refer to the License for details. You may not use this file except in compliance with the License.
       6              :  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
       7              :  * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
       8              :  * See LICENSE in the root of the software repository for the full text of the License.
       9              :  */
      10              : #include <elf.h>
      11              : #include <functional>
      12              : #include <fstream>
      13              : #include <mutex>
      14              : #include <memory>
      15              : #include <string>
      16              : #include <stack>
      17              : #include <thread>
      18              : #include <dirent.h>
      19              : #include <sys/stat.h>
      20              : #include "path.h"
      21              : #include "file.h"
      22              : #include "sys_utils.h"
      23              : #include "str_utils.h"
      24              : #include "log/adx_log.h"
      25              : #include "log/hdc_log.h"
      26              : #include "extra_config.h"
      27              : #include "thread_manager.h"
      28              : #include "adump_dsmi.h"
      29              : #include "runtime/base.h"
      30              : #include "exception_info_common.h"
      31              : #include "kernel_symbol_locator.h"
      32              : #include "kernel_info_collector.h"
      33              : 
      34              : namespace Adx {
      35              : namespace {
      36              : const std::set<char> INVALID_FILE_NAME_CHAR = {' ', '.', '/', '\\'};
      37              : constexpr uint64_t JSON_SUFFIX_LEN = 5; // length of ".json"
      38              : } // namespace
      39              : 
      40           32 : int32_t StartCollectKernelAsync(std::shared_ptr<KernelInfoCollector> collector, const std::string& dumpPath)
      41              : {
      42           32 :     int32_t ret = collector->LoadKernelBinBuffer();
      43              :     // _host.o 已由编排层(DumpHostKernelBinBeforeSymbolize)在符号化前无条件同步落盘,
      44              :     // 这里不再重复落盘,只把慢的 kernel_meta 搜索拷贝丢到异步线程。
      45           32 :     IDE_LOGD("Start collecting kernel file.");
      46           64 :     std::thread collectKernel([collector, dumpPath]() {
      47           32 :         int32_t tid = mmGetTid();
      48           32 :         ThreadManager::Instance().TaskAdd(tid);
      49           32 :         (void)collector->StartCollectKernel(dumpPath);
      50           32 :         ThreadManager::Instance().TaskDone(tid);
      51           32 :     });
      52           32 :     collectKernel.detach();
      53           32 :     return ret;
      54           32 : }
      55              : 
      56          134 : void KernelInfoCollector::LoadKernelInfo(const rtExceptionArgsInfo& argsInfo)
      57              : {
      58          134 :     kernelBinHandle_ = nullptr;
      59          134 :     kernelBinData_.clear();
      60          134 :     kernelBinSize_ = 0;
      61          134 :     kernelName_.clear();
      62              : 
      63          134 :     kernelBinHandle_ = argsInfo.exceptionKernelInfo.bin; // binHandle
      64          134 :     kernelBinSize_ = argsInfo.exceptionKernelInfo.binSize;
      65          134 :     if ((argsInfo.exceptionKernelInfo.kernelName != nullptr) && (argsInfo.exceptionKernelInfo.kernelNameSize != 0)) {
      66           74 :         kernelName_ = std::string(argsInfo.exceptionKernelInfo.kernelName, argsInfo.exceptionKernelInfo.kernelNameSize);
      67              :     }
      68          134 :     IDE_LOGI(
      69              :         "Kernel handle: %p, kernel size: %u, name addr: %p, name size: %u, kernel name: %s", kernelBinHandle_,
      70              :         kernelBinSize_, argsInfo.exceptionKernelInfo.kernelName, argsInfo.exceptionKernelInfo.kernelNameSize,
      71              :         kernelName_.c_str());
      72          134 : }
      73              : 
      74            8 : int32_t KernelInfoCollector::InitFromBinHandle(rtBinHandle BinHandle, const std::string& kernelName)
      75              : {
      76            8 :     kernelBinHandle_ = BinHandle;
      77            8 :     kernelName_ = kernelName;
      78              : 
      79            8 :     return LoadKernelBinBuffer();
      80              : }
      81              : 
      82           91 : int32_t KernelInfoCollector::LoadKernelBinBuffer()
      83              : {
      84           91 :     if (kernelBinHandle_ == nullptr) {
      85           50 :         return ADUMP_SUCCESS;
      86              :     }
      87           41 :     std::string binData;
      88           41 :     uint32_t binSize = 0;
      89           41 :     int32_t ret = ExceptionInfoCommon::GetBinDataFromHandle(kernelBinHandle_, binData, binSize);
      90           41 :     if (ret != ADUMP_SUCCESS) {
      91            2 :         kernelBinData_ = "";
      92            2 :         return ADUMP_FAILED;
      93              :     }
      94           39 :     kernelBinSize_ = binSize;
      95           39 :     kernelBinData_ = binData;
      96           39 :     return ADUMP_SUCCESS;
      97           41 : }
      98              : 
      99           50 : std::string KernelInfoCollector::GetProcessedKernelName() const
     100              : {
     101           50 :     return ExceptionInfoCommon::GetKernelNameWithoutMixSuffix(kernelName_);
     102              : }
     103              : 
     104           35 : int32_t KernelInfoCollector::StartCollectKernel(const std::string& dumpPath) const
     105              : {
     106           35 :     if (kernelName_.empty() || kernelBinHandle_ == nullptr || kernelBinSize_ == 0) {
     107           12 :         IDE_LOGI("Kernel name or kernel bin is empty, skip dump kernel.");
     108           12 :         return ADUMP_SUCCESS;
     109              :     }
     110              : 
     111           23 :     std::string kernelName = GetProcessedKernelName();
     112              : 
     113              :     // _host.o 已由调用方通过 DumpHostKernelBin 单独同步落盘,这里只做慢的 kernel_meta 搜索拷贝。
     114           23 :     int32_t ret = ADUMP_SUCCESS;
     115           23 :     if (CollectKernelFile(kernelName, dumpPath) != ADUMP_SUCCESS) {
     116            7 :         IDE_LOGE("Collect kernel file failed.");
     117            7 :         ret = ADUMP_FAILED;
     118              :     }
     119           23 :     return ret;
     120           23 : }
     121              : 
     122       139373 : bool KernelInfoCollector::ContainsString(const std::string& filePath, const std::string& targetString) const
     123              : {
     124       139373 :     Path jsonFilePath(filePath);
     125       139575 :     if (!jsonFilePath.RealPath()) {
     126         6401 :         IDE_LOGD("The json file path[%s] is invalid, please check the path.", jsonFilePath.GetCString());
     127         6401 :         return false;
     128              :     }
     129              : 
     130       133056 :     std::ifstream file(jsonFilePath.GetString());
     131       133393 :     if (!file.is_open()) {
     132        10724 :         IDE_LOGD("The json file path[%s] open failed, please check the permission.", jsonFilePath.GetCString());
     133        10724 :         return false;
     134              :     }
     135       122669 :     std::string line;
     136    100509233 :     while (std::getline(file, line)) {
     137    101742363 :         if (line.find(targetString) == std::string::npos) {
     138    100178524 :             continue;
     139              :         }
     140       197255 :         if (IsTargetLine(line, "kernelName", targetString)) {
     141           10 :             file.close();
     142           10 :             return true;
     143              :         }
     144              :     }
     145        94815 :     file.close();
     146       122600 :     return false;
     147       139735 : }
     148              : 
     149           16 : bool KernelInfoCollector::IsTargetLine(
     150              :     const std::string& currentLine, const std::string& key, const std::string& value) const
     151              : {
     152           16 :     size_t curPlace = 0;
     153           16 :     if (GetFirstItem(currentLine, curPlace) != key) {
     154            6 :         return false;
     155              :     }
     156           10 :     if (GetFirstItem(currentLine, curPlace) != value) {
     157            0 :         return false;
     158              :     }
     159           10 :     return true;
     160              : }
     161              : 
     162           26 : std::string KernelInfoCollector::GetFirstItem(const std::string& curLine, size_t& curPlace) const
     163              : {
     164           26 :     size_t head = curLine.find_first_of('"', curPlace);
     165           26 :     if (head == std::string::npos) {
     166            0 :         return "";
     167              :     }
     168           26 :     size_t end = curLine.find_first_of('"', head + 1);
     169           26 :     if (end == std::string::npos) {
     170            0 :         return "";
     171              :     }
     172           26 :     curPlace = end + 1;
     173           26 :     return StrUtils::Trim(curLine.substr(head + 1, end - head - 1));
     174              : }
     175              : 
     176           85 : std::string KernelInfoCollector::SearchJsonFiles(const std::string& rootPath, const std::string& targetString) const
     177              : {
     178           85 :     std::stack<std::string> directories;
     179           85 :     directories.push(rootPath);
     180              : 
     181        50080 :     while (!directories.empty()) {
     182        49964 :         std::string currentDir = directories.top();
     183        49959 :         directories.pop();
     184              : 
     185        50037 :         DIR* dir = opendir(currentDir.c_str());
     186        50103 :         if (!dir) {
     187           28 :             IDE_LOGW("Unable to open directory[%s].", currentDir.c_str());
     188           28 :             continue;
     189              :         }
     190              : 
     191              :         struct dirent* entry;
     192       643409 :         while ((entry = readdir(dir)) != nullptr) {
     193       595442 :             std::string name = entry->d_name;
     194      1077861 :             if (name == "." || name == ".." ||
     195       489522 :                 ((currentDir == "./") && (name.find("kernel_meta") == std::string::npos))) {
     196        99429 :                 continue;
     197              :             }
     198              : 
     199       486170 :             std::string fullPath = currentDir + "/" + name;
     200              :             struct stat fileStat;
     201       484777 :             if (stat(fullPath.c_str(), &fileStat) == -1) {
     202            1 :                 IDE_LOGD("Unable to stat file[%s].", fullPath.c_str());
     203            1 :                 continue;
     204              :             }
     205              : 
     206       495436 :             if (S_ISDIR(fileStat.st_mode)) {
     207        50014 :                 directories.push(fullPath);
     208        49909 :                 continue;
     209              :             }
     210       885187 :             if (!S_ISREG(fileStat.st_mode) || name.size() <= JSON_SUFFIX_LEN ||
     211       884818 :                 name.substr(name.size() - JSON_SUFFIX_LEN) != ".json") {
     212       304167 :                 continue;
     213              :             }
     214              : 
     215              :             // If the file is a JSON file, check whether the file contains the target character string.
     216       139382 :             if (ContainsString(fullPath, targetString)) {
     217           10 :                 (void)closedir(dir);
     218           10 :                 return fullPath;
     219              :             }
     220      1086963 :         }
     221              : 
     222        50189 :         (void)closedir(dir);
     223        50010 :     }
     224              : 
     225           77 :     IDE_LOGI("Can not find kernel file in %s, targetString: %s", rootPath.c_str(), targetString.c_str());
     226          150 :     return "";
     227           84 : }
     228              : 
     229           19 : std::string KernelInfoCollector::GetHostOFilePath(const std::string& dumpPath) const
     230              : {
     231           19 :     std::string kernelName = GetProcessedKernelName();
     232           19 :     Path hostKernelBinPath(dumpPath);
     233           19 :     std::string hostKernelBinName = StrUtils::Replace(kernelName, INVALID_FILE_NAME_CHAR, '_') + "_host.o";
     234           19 :     hostKernelBinPath.Concat(hostKernelBinName);
     235           38 :     return hostKernelBinPath.GetString();
     236           19 : }
     237              : 
     238              : /**
     239              :  * @brief       : dump the kernel bin file runtime load from host
     240              :  * @param [in]  : dumpPath      path of the exception dump file
     241              :  * @param [out] : outHostOPath  绝对/拼接后的 _host.o 落盘路径,供后续 symbolize 使用
     242              :  * @return      : ADUMP_SUCCESS succeed; ADUMP_FAILED failed
     243              :  * @note        : 从 StartCollectKernel 拆出,供调用方在慢搜索/修正 PC 之前单独同步落 _host.o。
     244              :  */
     245           57 : int32_t KernelInfoCollector::DumpHostKernelBin(const std::string& dumpPath, std::string& outHostOPath) const
     246              : {
     247           57 :     outHostOPath.clear();
     248              :     // 与拆分前 StartCollectKernel 入口保持一致的前置校验:kernelName 为空会退化成非唯一的 "_host.o",
     249              :     // 不同异常/算子会互相覆盖并污染幂等判断,故此处直接跳过。
     250           57 :     if (kernelName_.empty() || kernelBinHandle_ == nullptr || kernelBinSize_ == 0) {
     251           42 :         IDE_LOGI("Kernel name or kernel bin is empty, skip dump host kernel bin.");
     252           42 :         return ADUMP_SUCCESS;
     253              :     }
     254           15 :     if (kernelBinData_.empty()) {
     255            0 :         IDE_LOGE("Kernel bin data is empty.");
     256            0 :         return ADUMP_FAILED;
     257              :     }
     258           15 :     std::string hostKernelBinPath = GetHostOFilePath(dumpPath);
     259              :     // 幂等:_host.o 可能已由调用方在符号化前提前同步落盘。仅当磁盘上文件大小与待写入一致时才跳过,
     260              :     // 避免上次部分写(如磁盘满)残留的截断/空文件被 Exist() 误判为已完成而永久跳过重写。
     261              :     struct stat hostBinStat;
     262           20 :     if (stat(hostKernelBinPath.c_str(), &hostBinStat) == 0 &&
     263            5 :         static_cast<uint64_t>(hostBinStat.st_size) == static_cast<uint64_t>(kernelBinSize_)) {
     264            4 :         IDE_LOGI("[Dump][Exception] host kernel bin already dumped, skip. file: %s", hostKernelBinPath.c_str());
     265            4 :         outHostOPath = hostKernelBinPath;
     266            4 :         return ADUMP_SUCCESS;
     267              :     }
     268           11 :     File hostKernelBinFile(hostKernelBinPath, M_WRONLY | M_CREAT | M_TRUNC);
     269           11 :     int32_t ret = hostKernelBinFile.IsFileOpen();
     270           11 :     if (ret != ADUMP_SUCCESS) {
     271            0 :         IDE_LOGE("Open host kernel bin file[%s] failed.", hostKernelBinPath.c_str());
     272            0 :         return ADUMP_FAILED;
     273              :     }
     274              :     static std::mutex KernelBinLock;
     275           11 :     std::lock_guard<std::mutex> lock(KernelBinLock);
     276           11 :     auto writeSize = hostKernelBinFile.Write(kernelBinData_.c_str(), static_cast<int64_t>(kernelBinSize_));
     277           11 :     if (writeSize < 0) {
     278              :         // 不清理残留文件(与拆分前行为一致):M_TRUNC 已把文件截为 0,失败后盘上会留下空/截断的 _host.o。
     279              :         // 下次调用因大小不匹配会重写,不会误判为完整;但符号化阶段若在此期间读取会拿到残缺文件,故打印告警提醒。
     280            0 :         IDE_LOGE(
     281              :             "Write host kernel bin file[%s] failed, an incomplete _host.o may remain on disk.",
     282              :             hostKernelBinPath.c_str());
     283            0 :         return ADUMP_FAILED;
     284              :     }
     285              :     // File::Write 内部循环处理短写、返回的是最后一次 mmWrite 的长度而非累计值,不能用它判完整性;
     286              :     // 改为写后 stat 校验最终文件大小,截断/短写(如磁盘满)时文件大小不足即判失败,避免残留截断文件被幂等误判为完整。
     287           22 :     if (stat(hostKernelBinPath.c_str(), &hostBinStat) != 0 ||
     288           11 :         static_cast<uint64_t>(hostBinStat.st_size) != static_cast<uint64_t>(kernelBinSize_)) {
     289              :         // 同上:残留的截断文件不主动删除,靠下次大小校验重写;此处打印告警提醒盘上存在不完整的 _host.o。
     290            1 :         IDE_LOGE(
     291              :             "Write host kernel bin file[%s] incomplete, fileSize=%lld, expect=%u, "
     292              :             "an incomplete _host.o may remain on disk.",
     293              :             hostKernelBinPath.c_str(), static_cast<long long>(hostBinStat.st_size), kernelBinSize_);
     294            1 :         return ADUMP_FAILED;
     295              :     }
     296           10 :     IDE_LOGE("[Dump][Exception] dump host kernel to file, file: %s", hostKernelBinPath.c_str());
     297           10 :     (void)mmChmod(hostKernelBinPath.c_str(), M_IRUSR); // 安全要求,落盘文件置为最小权限:用户只读, 400
     298           10 :     outHostOPath = hostKernelBinPath;
     299              : 
     300           10 :     return ADUMP_SUCCESS;
     301           15 : }
     302              : 
     303           13 : std::vector<std::string> KernelInfoCollector::SplitString(const std::string& str, char delimiter) const
     304              : {
     305           13 :     std::vector<std::string> result;
     306           13 :     std::stringstream ss(str);
     307           13 :     std::string item;
     308              : 
     309           39 :     while (std::getline(ss, item, delimiter)) {
     310           26 :         result.push_back(item);
     311              :     }
     312              : 
     313           13 :     return result;
     314           13 : }
     315              : 
     316              : /*
     317              :     按照指定的路径优先级来进行.o和.json文件搜索
     318              : */
     319           31 : std::vector<std::string> KernelInfoCollector::GetSearchPath() const
     320              : {
     321           31 :     std::string envStr;
     322           31 :     std::vector<std::string> searchPath;
     323              : 
     324           31 :     ADX_GET_ENV(MM_ENV_ASCEND_CACHE_PATH, envStr);
     325           31 :     if (!envStr.empty()) {
     326           13 :         searchPath.emplace_back(envStr);
     327           13 :         IDE_LOGI("Add ASCEND_CACHE_PATH[%s] to search path.", envStr.c_str());
     328              :     }
     329              : 
     330           31 :     ADX_GET_ENV(MM_ENV_HOME, envStr);
     331           31 :     if (!envStr.empty()) {
     332           26 :         envStr += "/atc_data";
     333           26 :         searchPath.emplace_back(envStr);
     334           26 :         IDE_LOGI("Add HOME[%s] to search path.", envStr.c_str());
     335              :     }
     336              : 
     337              :     // custom install path
     338           31 :     ADX_GET_ENV(MM_ENV_ASCEND_CUSTOM_OPP_PATH, envStr);
     339           31 :     if (!envStr.empty()) {
     340           13 :         std::vector<std::string> customPaths = SplitString(envStr, ':');
     341           52 :         for (const auto& customPath : customPaths) {
     342           26 :             searchPath.emplace_back(customPath);
     343           26 :             IDE_LOGI("Add ASCEND_CUSTOM_OPP_PATH[%s] to search path.", customPath.c_str());
     344              :         }
     345           13 :     }
     346              : 
     347           31 :     ADX_GET_ENV(MM_ENV_ASCEND_OPP_PATH, envStr);
     348           31 :     if (!envStr.empty()) {
     349           26 :         std::string vendorsPath = envStr + "/vendors";
     350           26 :         searchPath.emplace_back(vendorsPath);
     351           26 :         IDE_LOGI("Add ASCEND_OPP_PATH[%s] to search path.", vendorsPath.c_str());
     352              : 
     353           26 :         std::string buildInPath = envStr + "/built-in";
     354           26 :         searchPath.emplace_back(buildInPath);
     355           26 :         IDE_LOGI("Add ASCEND_OPP_PATH[%s] to search path.", buildInPath.c_str());
     356           26 :     }
     357              : 
     358              :     // kernel meta generate in current path
     359           31 :     searchPath.emplace_back("./");
     360           31 :     IDE_LOGI("Add current path[./] to search path.");
     361              : 
     362           31 :     ADX_GET_ENV(MM_ENV_ASCEND_WORK_PATH, envStr);
     363           31 :     if (!envStr.empty()) {
     364           11 :         envStr += "/kernel_meta";
     365           11 :         searchPath.emplace_back(envStr);
     366              :     }
     367           31 :     IDE_LOGI("Add current path[%s] to search path.", envStr.c_str());
     368              : 
     369           31 :     return searchPath;
     370           31 : }
     371              : 
     372           23 : int32_t KernelInfoCollector::CollectKernelFile(const std::string& kernelName, const std::string& dumpPath) const
     373              : {
     374           23 :     std::vector<std::string> searchPath = GetSearchPath();
     375              : 
     376           23 :     bool failFlag = false;
     377          121 :     for (const auto& path : searchPath) {
     378           82 :         std::string jsonFilePath = SearchJsonFiles(path, kernelName);
     379           82 :         if (jsonFilePath.empty()) {
     380           75 :             continue;
     381              :         }
     382              : 
     383            7 :         Path jsonPath(jsonFilePath);
     384            7 :         Path dumpJsonPath(dumpPath);
     385            7 :         dumpJsonPath.Concat(jsonPath.GetFileName());
     386              :         static std::mutex KernelfileLock;
     387            7 :         std::lock_guard<std::mutex> lock(KernelfileLock);
     388            7 :         if (File::Copy(jsonFilePath, dumpJsonPath.GetString()) != ADUMP_SUCCESS) {
     389            3 :             IDE_LOGE("Copy kernel file[%s] to dump path[%s] failed.", jsonFilePath.c_str(), dumpJsonPath.GetCString());
     390            3 :             failFlag = true;
     391              :         } else {
     392            4 :             IDE_LOGE("[Dump][Exception] dump kernel json to file, file: %s", dumpJsonPath.GetCString());
     393            4 :             (void)mmChmod(dumpJsonPath.GetCString(), M_IRUSR); // 安全要求,落盘文件置为最小权限:用户只读, 400
     394              :         }
     395              : 
     396            7 :         std::string kernelBinPath = jsonFilePath.substr(0, jsonFilePath.size() - JSON_SUFFIX_LEN) + ".o";
     397            7 :         Path kernelPath(kernelBinPath);
     398            7 :         Path dumpKernelPath(dumpPath);
     399            7 :         dumpKernelPath.Concat(kernelPath.GetFileName());
     400            7 :         if (File::Copy(kernelBinPath, dumpKernelPath.GetString()) != ADUMP_SUCCESS) {
     401            7 :             IDE_LOGE(
     402              :                 "Copy kernel file[%s] to dump path[%s] failed.", kernelBinPath.c_str(), dumpKernelPath.GetCString());
     403            7 :             failFlag = true;
     404              :         } else {
     405            0 :             IDE_LOGE("[Dump][Exception] dump kernel to file, file: %s", dumpKernelPath.GetCString());
     406            0 :             (void)mmChmod(dumpKernelPath.GetCString(), M_IRUSR); // 安全要求,落盘文件置为最小权限:用户只读, 400
     407              :         }
     408            7 :         break;
     409           89 :     }
     410           23 :     if (failFlag) {
     411            7 :         return ADUMP_FAILED;
     412              :     }
     413              : 
     414           16 :     return ADUMP_SUCCESS;
     415           23 : }
     416              : 
     417              : } // namespace Adx
        

Generated by: LCOV version 2.0-1