LCOV - code coverage report
Current view: top level - adump/exception - kernel_info_collector.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 94.8 % 232 220
Test Date: 2026-08-12 11:04:53 Functions: 100.0 % 15 15

            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           32 :     int32_t ret = collector->LoadKernelBinBuffer();
      42              :     // 先同步落 _host.o(纯内存 buffer 写,快),再把慢的 kernel_meta 搜索丢到异步线程。
      43           32 :     (void)collector->DumpHostKernelBin(dumpPath);
      44           32 :     IDE_LOGD("Start collecting kernel file.");
      45           64 :     std::thread collectKernel([collector, dumpPath](){
      46           32 :         int32_t tid = mmGetTid();
      47           32 :         ThreadManager::Instance().TaskAdd(tid);
      48           32 :         (void)collector->StartCollectKernel(dumpPath);
      49           32 :         ThreadManager::Instance().TaskDone(tid);
      50           32 :     });
      51           32 :     collectKernel.detach();
      52           32 :     return ret;
      53           32 : }
      54              : 
      55          131 : void KernelInfoCollector::LoadKernelInfo(const rtExceptionArgsInfo &argsInfo)
      56              : {
      57          131 :     kernelBinHandle_ = nullptr;
      58          131 :     kernelBinData_.clear();
      59          131 :     kernelBinSize_ = 0;
      60          131 :     kernelName_.clear();
      61              : 
      62          131 :     kernelBinHandle_ = argsInfo.exceptionKernelInfo.bin;  // binHandle
      63          131 :     kernelBinSize_ = argsInfo.exceptionKernelInfo.binSize;
      64          131 :     if ((argsInfo.exceptionKernelInfo.kernelName != nullptr) && (argsInfo.exceptionKernelInfo.kernelNameSize != 0)) {
      65           68 :         kernelName_ = std::string(argsInfo.exceptionKernelInfo.kernelName, argsInfo.exceptionKernelInfo.kernelNameSize);
      66              :     }
      67          131 :     IDE_LOGI("Kernel handle: %p, kernel size: %u, name addr: %p, name size: %u, kernel name: %s", kernelBinHandle_,
      68              :              kernelBinSize_, argsInfo.exceptionKernelInfo.kernelName, argsInfo.exceptionKernelInfo.kernelNameSize,
      69              :              kernelName_.c_str());
      70          131 : }
      71              : 
      72            5 : int32_t KernelInfoCollector::InitFromBinHandle(rtBinHandle BinHandle, const std::string &kernelName)
      73              : {
      74            5 :     kernelBinHandle_ = BinHandle;
      75            5 :     kernelName_ = kernelName;
      76              : 
      77            5 :     return LoadKernelBinBuffer();
      78              : }
      79              : 
      80           87 : int32_t KernelInfoCollector::LoadKernelBinBuffer()
      81              : {
      82           87 :     if (kernelBinHandle_ == nullptr) {
      83           50 :         return ADUMP_SUCCESS;
      84              :     }
      85           37 :     std::string binData;
      86           37 :     uint32_t binSize = 0;
      87           37 :     int32_t ret = ExceptionInfoCommon::GetBinDataFromHandle(kernelBinHandle_, binData, binSize);
      88           37 :     if (ret != ADUMP_SUCCESS) {
      89            2 :         kernelBinData_ = "";
      90            2 :         return ADUMP_FAILED;
      91              :     }
      92           35 :     kernelBinSize_ = binSize;
      93           35 :     kernelBinData_ = binData;
      94           35 :     return ADUMP_SUCCESS;
      95           37 : }
      96              : 
      97           65 : std::string KernelInfoCollector::GetProcessedKernelName() const
      98              : {
      99           65 :     return ExceptionInfoCommon::GetKernelNameWithoutMixSuffix(kernelName_);
     100              : }
     101              : 
     102           35 : int32_t KernelInfoCollector::StartCollectKernel(const std::string &dumpPath) const
     103              : {
     104           35 :     if (kernelName_.empty() || kernelBinHandle_ == nullptr || kernelBinSize_ == 0) {
     105           12 :         IDE_LOGI("Kernel name or kernel bin is empty, skip dump kernel.");
     106           12 :         return ADUMP_SUCCESS;
     107              :     }
     108              : 
     109           23 :     std::string kernelName = GetProcessedKernelName();
     110              : 
     111              :     // _host.o 已由调用方通过 DumpHostKernelBin 单独同步落盘,这里只做慢的 kernel_meta 搜索拷贝。
     112           23 :     int32_t ret = ADUMP_SUCCESS;
     113           23 :     if (CollectKernelFile(kernelName, dumpPath) != ADUMP_SUCCESS) {
     114            6 :         IDE_LOGE("Collect kernel file failed.");
     115            6 :         ret = ADUMP_FAILED;
     116              :     }
     117           23 :     return ret;
     118           23 : }
     119              : 
     120       138791 : bool KernelInfoCollector::ContainsString(const std::string &filePath, const std::string &targetString) const
     121              : {
     122       138791 :     Path jsonFilePath(filePath);
     123       138955 :     if (!jsonFilePath.RealPath()) {
     124            0 :         IDE_LOGD("The json file path[%s] is invalid, please check the path.", jsonFilePath.GetCString());
     125            0 :         return false;
     126              :     }
     127              : 
     128       138654 :     std::ifstream file(jsonFilePath.GetString());
     129       138956 :     if (!file.is_open()) {
     130        10663 :         IDE_LOGD("The json file path[%s] open failed, please check the permission.", jsonFilePath.GetCString());
     131        10663 :         return false;
     132              :     }
     133       128078 :     std::string line;
     134    110005752 :     while (std::getline(file, line)) {
     135    110184784 :         if (line.find(targetString) == std::string::npos) {
     136    110205585 :             continue;
     137              :         }
     138            0 :         if (IsTargetLine(line, "kernelName", targetString)) {
     139           10 :             file.close();
     140           10 :             return true;
     141              :         }
     142              :     }
     143       122126 :     file.close();
     144       128227 :     return false;
     145       138900 : }
     146              : 
     147           16 : bool KernelInfoCollector::IsTargetLine(const std::string &currentLine, const std::string &key,
     148              :                                        const std::string &value) const
     149              : {
     150           16 :     size_t curPlace = 0;
     151           16 :     if (GetFirstItem(currentLine, curPlace) != key) {
     152            6 :         return false;
     153              :     }
     154           10 :     if (GetFirstItem(currentLine, curPlace) != value) {
     155            0 :         return false;
     156              :     }
     157           10 :     return true;
     158              : }
     159              : 
     160           26 : std::string KernelInfoCollector::GetFirstItem(const std::string &curLine, size_t &curPlace) const
     161              : {
     162           26 :     size_t head = curLine.find_first_of('"', curPlace);
     163           26 :     if (head == std::string::npos) {
     164            0 :         return "";
     165              :     }
     166           26 :     size_t end = curLine.find_first_of('"', head + 1);
     167           26 :     if (end == std::string::npos) {
     168            0 :         return "";
     169              :     }
     170           26 :     curPlace = end + 1;
     171           26 :     return StrUtils::Trim(curLine.substr(head + 1, end - head - 1));
     172              : }
     173              : 
     174           85 : std::string KernelInfoCollector::SearchJsonFiles(const std::string &rootPath, const std::string &targetString) const
     175              : {
     176           85 :     std::stack<std::string> directories;
     177           85 :     directories.push(rootPath);
     178              : 
     179        47109 :     while (!directories.empty()) {
     180        46952 :         std::string currentDir = directories.top();
     181        46971 :         directories.pop();
     182              : 
     183        46931 :         DIR *dir = opendir(currentDir.c_str());
     184        47036 :         if (!dir) {
     185           28 :             IDE_LOGW("Unable to open directory[%s].", currentDir.c_str());
     186           28 :             continue;
     187              :         }
     188              : 
     189              :         struct dirent *entry;
     190       619857 :         while ((entry = readdir(dir)) != nullptr) {
     191       572252 :             std::string name = entry->d_name;
     192      1046046 :             if (name == "." || name == ".." ||
     193       476275 :                 ((currentDir == "./") && (name.find("kernel_meta") == std::string::npos))) {
     194        93741 :                 continue;
     195              :             }
     196              : 
     197       474517 :             std::string fullPath = currentDir + "/" + name;
     198              :             struct stat fileStat;
     199       477835 :             if (stat(fullPath.c_str(), &fileStat) == -1) {
     200            0 :                 IDE_LOGD("Unable to stat file[%s].", fullPath.c_str());
     201            0 :                 continue;
     202              :             }
     203              : 
     204       479180 :             if (S_ISDIR(fileStat.st_mode)) {
     205        46992 :                 directories.push(fullPath);
     206        46979 :                 continue;
     207              :             }
     208       863122 :             if (!S_ISREG(fileStat.st_mode) || name.size() <= JSON_SUFFIX_LEN ||
     209       863181 :                 name.substr(name.size() - JSON_SUFFIX_LEN) != ".json") {
     210       293121 :                 continue;
     211              :             }
     212              : 
     213              :             // If the file is a JSON file, check whether the file contains the target character string.
     214       138995 :             if (ContainsString(fullPath, targetString)) {
     215           10 :                 (void)closedir(dir);
     216           10 :                 return fullPath;
     217              :             }
     218      1051367 :         }
     219              : 
     220        47617 :         (void)closedir(dir);
     221        47441 :     }
     222              : 
     223           77 :     IDE_LOGI("Can not find kernel file in %s, targetString: %s", rootPath.c_str(), targetString.c_str());
     224          150 :     return "";
     225           90 : }
     226              : 
     227              : /**
     228              :  * @brief       : dump the kernel bin file runtime load from host
     229              :  * @param [in]  : dumpPath      path of the exception dump file
     230              :  * @return      : ADUMP_SUCCESS succeed; ADUMP_FAILED failed
     231              :  * @note        : 从 StartCollectKernel 拆出,供调用方在慢搜索/修正 PC 之前单独同步落 _host.o。
     232              :  */
     233           88 : int32_t KernelInfoCollector::DumpHostKernelBin(const std::string &dumpPath) const
     234              : {
     235              :     // 与拆分前 StartCollectKernel 入口保持一致的前置校验:kernelName 为空会退化成非唯一的 "_host.o",
     236              :     // 不同异常/算子会互相覆盖并污染幂等判断,故此处直接跳过。
     237           88 :     if (kernelName_.empty() || kernelBinHandle_ == nullptr || kernelBinSize_ == 0) {
     238           52 :         IDE_LOGI("Kernel name or kernel bin is empty, skip dump host kernel bin.");
     239           52 :         return ADUMP_SUCCESS;
     240              :     }
     241           36 :     if (kernelBinData_.empty()) {
     242            2 :         IDE_LOGE("Kernel bin data is empty.");
     243            2 :         return ADUMP_FAILED;
     244              :     }
     245           34 :     std::string kernelName = GetProcessedKernelName();
     246           34 :     Path hostKernelBinPath(dumpPath);
     247           34 :     std::string hostKernelBinName = StrUtils::Replace(kernelName, INVALID_FILE_NAME_CHAR, '_') + "_host.o";
     248           34 :     hostKernelBinPath.Concat(hostKernelBinName);
     249              :     // 幂等:_host.o 可能已由调用方在符号化前提前同步落盘。仅当磁盘上文件大小与待写入一致时才跳过,
     250              :     // 避免上次部分写(如磁盘满)残留的截断/空文件被 Exist() 误判为已完成而永久跳过重写。
     251              :     struct stat hostBinStat;
     252           50 :     if (hostKernelBinPath.Exist() && stat(hostKernelBinPath.GetCString(), &hostBinStat) == 0 &&
     253           16 :         static_cast<uint64_t>(hostBinStat.st_size) == static_cast<uint64_t>(kernelBinSize_)) {
     254           15 :         IDE_LOGI("[Dump][Exception] host kernel bin already dumped, skip. file: %s", hostKernelBinPath.GetCString());
     255           15 :         return ADUMP_SUCCESS;
     256              :     }
     257           19 :     File hostKernelBinFile(hostKernelBinPath.GetString(), M_WRONLY | M_CREAT | M_TRUNC);
     258           19 :     int32_t ret = hostKernelBinFile.IsFileOpen();
     259           19 :     if (ret != ADUMP_SUCCESS) {
     260            0 :         IDE_LOGE("Open host kernel bin file[%s] failed.", hostKernelBinPath.GetCString());
     261            0 :         return ADUMP_FAILED;
     262              :     }
     263              :     static std::mutex KernelBinLock;
     264           19 :     std::lock_guard<std::mutex> lock(KernelBinLock);
     265           19 :     auto writeSize = hostKernelBinFile.Write(kernelBinData_.c_str(), static_cast<int64_t>(kernelBinSize_));
     266           19 :     if (writeSize < 0) {
     267              :         // 不清理残留文件(与拆分前行为一致):M_TRUNC 已把文件截为 0,失败后盘上会留下空/截断的 _host.o。
     268              :         // 下次调用因大小不匹配会重写,不会误判为完整;但符号化阶段若在此期间读取会拿到残缺文件,故打印告警提醒。
     269            1 :         IDE_LOGE("Write host kernel bin file[%s] failed, an incomplete _host.o may remain on disk.",
     270              :             hostKernelBinPath.GetCString());
     271            1 :         return ADUMP_FAILED;
     272              :     }
     273              :     // File::Write 内部循环处理短写、返回的是最后一次 mmWrite 的长度而非累计值,不能用它判完整性;
     274              :     // 改为写后 stat 校验最终文件大小,截断/短写(如磁盘满)时文件大小不足即判失败,避免残留截断文件被幂等误判为完整。
     275           34 :     if (stat(hostKernelBinPath.GetCString(), &hostBinStat) != 0 ||
     276           16 :         static_cast<uint64_t>(hostBinStat.st_size) != static_cast<uint64_t>(kernelBinSize_)) {
     277              :         // 同上:残留的截断文件不主动删除,靠下次大小校验重写;此处打印告警提醒盘上存在不完整的 _host.o。
     278            3 :         IDE_LOGE("Write host kernel bin file[%s] incomplete, fileSize=%lld, expect=%u, "
     279              :             "an incomplete _host.o may remain on disk.",
     280              :             hostKernelBinPath.GetCString(), static_cast<long long>(hostBinStat.st_size), kernelBinSize_);
     281            3 :         return ADUMP_FAILED;
     282              :     }
     283           15 :     IDE_LOGE("[Dump][Exception] dump host kernel to file, file: %s", hostKernelBinPath.GetCString());
     284           15 :     (void)mmChmod(hostKernelBinPath.GetCString(), M_IRUSR);  // 安全要求,落盘文件置为最小权限:用户只读, 400
     285              : 
     286           15 :     return ADUMP_SUCCESS;
     287           34 : }
     288              : 
     289           13 : std::vector<std::string> KernelInfoCollector::SplitString(const std::string &str, char delimiter) const
     290              : {
     291           13 :     std::vector<std::string> result;
     292           13 :     std::stringstream ss(str);
     293           13 :     std::string item;
     294              : 
     295           39 :     while (std::getline(ss, item, delimiter)) {
     296           26 :         result.push_back(item);
     297              :     }
     298              : 
     299           13 :     return result;
     300           13 : }
     301              : 
     302              : /*
     303              :     按照指定的路径优先级来进行.o和.json文件搜索
     304              : */
     305           31 : std::vector<std::string> KernelInfoCollector::GetSearchPath() const
     306              : {
     307           31 :     std::string envStr;
     308           31 :     std::vector<std::string> searchPath;
     309              : 
     310           31 :     ADX_GET_ENV(MM_ENV_ASCEND_CACHE_PATH, envStr);
     311           31 :     if (!envStr.empty()) {
     312           13 :         searchPath.emplace_back(envStr);
     313           13 :         IDE_LOGI("Add ASCEND_CACHE_PATH[%s] to search path.", envStr.c_str());
     314              :     }
     315              : 
     316           31 :     ADX_GET_ENV(MM_ENV_HOME, envStr);
     317           31 :     if (!envStr.empty()) {
     318           26 :         envStr += "/atc_data";
     319           26 :         searchPath.emplace_back(envStr);
     320           26 :         IDE_LOGI("Add HOME[%s] to search path.", envStr.c_str());
     321              :     }
     322              :     
     323              :     // custom install path
     324           31 :     ADX_GET_ENV(MM_ENV_ASCEND_CUSTOM_OPP_PATH, envStr);
     325           31 :     if (!envStr.empty()) {
     326           13 :         std::vector<std::string> customPaths = SplitString(envStr, ':');
     327           39 :         for (const auto &customPath : customPaths) {
     328           26 :             searchPath.emplace_back(customPath);
     329           26 :             IDE_LOGI("Add ASCEND_CUSTOM_OPP_PATH[%s] to search path.", customPath.c_str());
     330              :         }
     331           13 :     }
     332              : 
     333           31 :     ADX_GET_ENV(MM_ENV_ASCEND_OPP_PATH, envStr);
     334           31 :     if (!envStr.empty()) {
     335           26 :         std::string vendorsPath = envStr + "/vendors";
     336           26 :         searchPath.emplace_back(vendorsPath);
     337           26 :         IDE_LOGI("Add ASCEND_OPP_PATH[%s] to search path.", vendorsPath.c_str());
     338              : 
     339           26 :         std::string buildInPath = envStr + "/built-in";
     340           26 :         searchPath.emplace_back(buildInPath);
     341           26 :         IDE_LOGI("Add ASCEND_OPP_PATH[%s] to search path.", buildInPath.c_str());
     342           26 :     }
     343              : 
     344              :     // kernel meta generate in current path
     345           31 :     searchPath.emplace_back("./");
     346           31 :     IDE_LOGI("Add current path[./] to search path.");
     347              : 
     348           31 :     ADX_GET_ENV(MM_ENV_ASCEND_WORK_PATH, envStr);
     349           31 :     if (!envStr.empty()) {
     350           11 :         envStr += "/kernel_meta";
     351           11 :         searchPath.emplace_back(envStr);
     352              :     }
     353           31 :     IDE_LOGI("Add current path[%s] to search path.", envStr.c_str());
     354              : 
     355           31 :     return searchPath;
     356           31 : }
     357              : 
     358           23 : int32_t KernelInfoCollector::CollectKernelFile(const std::string &kernelName, const std::string &dumpPath) const
     359              : {
     360           23 :     std::vector<std::string> searchPath = GetSearchPath();
     361              : 
     362           23 :     bool failFlag = false;
     363           98 :     for (const auto &path : searchPath) {
     364           82 :         std::string jsonFilePath = SearchJsonFiles(path, kernelName);
     365           82 :         if (jsonFilePath.empty()) {
     366           75 :             continue;
     367              :         }
     368              : 
     369            7 :         Path jsonPath(jsonFilePath);
     370            7 :         Path dumpJsonPath(dumpPath);
     371            7 :         dumpJsonPath.Concat(jsonPath.GetFileName());
     372              :         static std::mutex KernelfileLock;
     373            7 :         std::lock_guard<std::mutex> lock(KernelfileLock);
     374            7 :         if (File::Copy(jsonFilePath, dumpJsonPath.GetString()) != ADUMP_SUCCESS) {
     375            0 :             IDE_LOGE("Copy kernel file[%s] to dump path[%s] failed.", jsonFilePath.c_str(), dumpJsonPath.GetCString());
     376            0 :             failFlag = true;
     377              :         } else {
     378            7 :             IDE_LOGE("[Dump][Exception] dump kernel json to file, file: %s", dumpJsonPath.GetCString());
     379            7 :             (void)mmChmod(dumpJsonPath.GetCString(), M_IRUSR);  // 安全要求,落盘文件置为最小权限:用户只读, 400
     380              :         }
     381              : 
     382            7 :         std::string kernelBinPath = jsonFilePath.substr(0, jsonFilePath.size() - JSON_SUFFIX_LEN) + ".o";
     383            7 :         Path kernelPath(kernelBinPath);
     384            7 :         Path dumpKernelPath(dumpPath);
     385            7 :         dumpKernelPath.Concat(kernelPath.GetFileName());
     386            7 :         if (File::Copy(kernelBinPath, dumpKernelPath.GetString()) != ADUMP_SUCCESS) {
     387            6 :             IDE_LOGE("Copy kernel file[%s] to dump path[%s] failed.", kernelBinPath.c_str(),
     388              :                      dumpKernelPath.GetCString());
     389            6 :             failFlag = true;
     390              :         } else {
     391            1 :             IDE_LOGE("[Dump][Exception] dump kernel to file, file: %s", dumpKernelPath.GetCString());
     392            1 :             (void)mmChmod(dumpKernelPath.GetCString(), M_IRUSR);  // 安全要求,落盘文件置为最小权限:用户只读, 400
     393              :         }
     394            7 :         break;
     395           89 :     }
     396           23 :     if (failFlag) {
     397            6 :         return ADUMP_FAILED;
     398              :     }
     399              : 
     400           17 :     return ADUMP_SUCCESS;
     401           23 : }
     402              : 
     403              : }  // namespace Adx
        

Generated by: LCOV version 2.0-1