LCOV - code coverage report
Current view: top level - adump/exception - kernel_info_collector.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 94.1 % 238 224
Test Date: 2026-08-17 09:38:02 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           32 :     int32_t ret = collector->LoadKernelBinBuffer();
      42              :     // _host.o 已由编排层(DumpHostKernelBinBeforeSymbolize)在符号化前无条件同步落盘,
      43              :     // 这里不再重复落盘,只把慢的 kernel_meta 搜索拷贝丢到异步线程。
      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          134 : void KernelInfoCollector::LoadKernelInfo(const rtExceptionArgsInfo &argsInfo)
      56              : {
      57          134 :     kernelBinHandle_ = nullptr;
      58          134 :     kernelBinData_.clear();
      59          134 :     kernelBinSize_ = 0;
      60          134 :     kernelName_.clear();
      61              : 
      62          134 :     kernelBinHandle_ = argsInfo.exceptionKernelInfo.bin;  // binHandle
      63          134 :     kernelBinSize_ = argsInfo.exceptionKernelInfo.binSize;
      64          134 :     if ((argsInfo.exceptionKernelInfo.kernelName != nullptr) && (argsInfo.exceptionKernelInfo.kernelNameSize != 0)) {
      65           74 :         kernelName_ = std::string(argsInfo.exceptionKernelInfo.kernelName, argsInfo.exceptionKernelInfo.kernelNameSize);
      66              :     }
      67          134 :     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          134 : }
      71              : 
      72            8 : int32_t KernelInfoCollector::InitFromBinHandle(rtBinHandle BinHandle, const std::string &kernelName)
      73              : {
      74            8 :     kernelBinHandle_ = BinHandle;
      75            8 :     kernelName_ = kernelName;
      76              : 
      77            8 :     return LoadKernelBinBuffer();
      78              : }
      79              : 
      80           91 : int32_t KernelInfoCollector::LoadKernelBinBuffer()
      81              : {
      82           91 :     if (kernelBinHandle_ == nullptr) {
      83           50 :         return ADUMP_SUCCESS;
      84              :     }
      85           41 :     std::string binData;
      86           41 :     uint32_t binSize = 0;
      87           41 :     int32_t ret = ExceptionInfoCommon::GetBinDataFromHandle(kernelBinHandle_, binData, binSize);
      88           41 :     if (ret != ADUMP_SUCCESS) {
      89            2 :         kernelBinData_ = "";
      90            2 :         return ADUMP_FAILED;
      91              :     }
      92           39 :     kernelBinSize_ = binSize;
      93           39 :     kernelBinData_ = binData;
      94           39 :     return ADUMP_SUCCESS;
      95           41 : }
      96              : 
      97           50 : std::string KernelInfoCollector::GetProcessedKernelName() const
      98              : {
      99           50 :     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       138926 : bool KernelInfoCollector::ContainsString(const std::string &filePath, const std::string &targetString) const
     121              : {
     122       138926 :     Path jsonFilePath(filePath);
     123       138962 :     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       138956 :     std::ifstream file(jsonFilePath.GetString());
     129       138983 :     if (!file.is_open()) {
     130        10679 :         IDE_LOGD("The json file path[%s] open failed, please check the permission.", jsonFilePath.GetCString());
     131        10679 :         return false;
     132              :     }
     133       128293 :     std::string line;
     134    113999602 :     while (std::getline(file, line)) {
     135    113946497 :         if (line.find(targetString) == std::string::npos) {
     136    114075903 :             continue;
     137              :         }
     138            0 :         if (IsTargetLine(line, "kernelName", targetString)) {
     139           10 :             file.close();
     140           10 :             return true;
     141              :         }
     142              :     }
     143        94292 :     file.close();
     144       128292 :     return false;
     145       138981 : }
     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        47657 :     while (!directories.empty()) {
     180        47579 :         std::string currentDir = directories.top();
     181        47572 :         directories.pop();
     182              : 
     183        47566 :         DIR *dir = opendir(currentDir.c_str());
     184        47583 :         if (!dir) {
     185           28 :             IDE_LOGW("Unable to open directory[%s].", currentDir.c_str());
     186           28 :             continue;
     187              :         }
     188              : 
     189              :         struct dirent *entry;
     190       625176 :         while ((entry = readdir(dir)) != nullptr) {
     191       577436 :             std::string name = entry->d_name;
     192      1058036 :             if (name == "." || name == ".." ||
     193       481493 :                 ((currentDir == "./") && (name.find("kernel_meta") == std::string::npos))) {
     194        95298 :                 continue;
     195              :             }
     196              : 
     197       480720 :             std::string fullPath = currentDir + "/" + name;
     198              :             struct stat fileStat;
     199       482105 :             if (stat(fullPath.c_str(), &fileStat) == -1) {
     200            1 :                 IDE_LOGD("Unable to stat file[%s].", fullPath.c_str());
     201            1 :                 continue;
     202              :             }
     203              : 
     204       482600 :             if (S_ISDIR(fileStat.st_mode)) {
     205        47528 :                 directories.push(fullPath);
     206        47512 :                 continue;
     207              :             }
     208       869243 :             if (!S_ISREG(fileStat.st_mode) || name.size() <= JSON_SUFFIX_LEN ||
     209       869240 :                 name.substr(name.size() - JSON_SUFFIX_LEN) != ".json") {
     210       296021 :                 continue;
     211              :             }
     212              : 
     213              :             // If the file is a JSON file, check whether the file contains the target character string.
     214       138959 :             if (ContainsString(fullPath, targetString)) {
     215           10 :                 (void)closedir(dir);
     216           10 :                 return fullPath;
     217              :             }
     218      1060097 :         }
     219              : 
     220        47772 :         (void)closedir(dir);
     221        47625 :     }
     222              : 
     223           75 :     IDE_LOGI("Can not find kernel file in %s, targetString: %s", rootPath.c_str(), targetString.c_str());
     224          150 :     return "";
     225           85 : }
     226              : 
     227           19 : std::string KernelInfoCollector::GetHostOFilePath(const std::string &dumpPath) const
     228              : {
     229           19 :     std::string kernelName = GetProcessedKernelName();
     230           19 :     Path hostKernelBinPath(dumpPath);
     231           19 :     std::string hostKernelBinName = StrUtils::Replace(kernelName, INVALID_FILE_NAME_CHAR, '_') + "_host.o";
     232           19 :     hostKernelBinPath.Concat(hostKernelBinName);
     233           38 :     return hostKernelBinPath.GetString();
     234           19 : }
     235              : 
     236              : /**
     237              :  * @brief       : dump the kernel bin file runtime load from host
     238              :  * @param [in]  : dumpPath      path of the exception dump file
     239              :  * @param [out] : outHostOPath  绝对/拼接后的 _host.o 落盘路径,供后续 symbolize 使用
     240              :  * @return      : ADUMP_SUCCESS succeed; ADUMP_FAILED failed
     241              :  * @note        : 从 StartCollectKernel 拆出,供调用方在慢搜索/修正 PC 之前单独同步落 _host.o。
     242              :  */
     243           57 : int32_t KernelInfoCollector::DumpHostKernelBin(const std::string &dumpPath, std::string &outHostOPath) const
     244              : {
     245           57 :     outHostOPath.clear();
     246              :     // 与拆分前 StartCollectKernel 入口保持一致的前置校验:kernelName 为空会退化成非唯一的 "_host.o",
     247              :     // 不同异常/算子会互相覆盖并污染幂等判断,故此处直接跳过。
     248           57 :     if (kernelName_.empty() || kernelBinHandle_ == nullptr || kernelBinSize_ == 0) {
     249           42 :         IDE_LOGI("Kernel name or kernel bin is empty, skip dump host kernel bin.");
     250           42 :         return ADUMP_SUCCESS;
     251              :     }
     252           15 :     if (kernelBinData_.empty()) {
     253            0 :         IDE_LOGE("Kernel bin data is empty.");
     254            0 :         return ADUMP_FAILED;
     255              :     }
     256           15 :     std::string hostKernelBinPath = GetHostOFilePath(dumpPath);
     257              :     // 幂等:_host.o 可能已由调用方在符号化前提前同步落盘。仅当磁盘上文件大小与待写入一致时才跳过,
     258              :     // 避免上次部分写(如磁盘满)残留的截断/空文件被 Exist() 误判为已完成而永久跳过重写。
     259              :     struct stat hostBinStat;
     260           20 :     if (stat(hostKernelBinPath.c_str(), &hostBinStat) == 0 &&
     261            5 :         static_cast<uint64_t>(hostBinStat.st_size) == static_cast<uint64_t>(kernelBinSize_)) {
     262            4 :         IDE_LOGI("[Dump][Exception] host kernel bin already dumped, skip. file: %s", hostKernelBinPath.c_str());
     263            4 :         outHostOPath = hostKernelBinPath;
     264            4 :         return ADUMP_SUCCESS;
     265              :     }
     266           11 :     File hostKernelBinFile(hostKernelBinPath, M_WRONLY | M_CREAT | M_TRUNC);
     267           11 :     int32_t ret = hostKernelBinFile.IsFileOpen();
     268           11 :     if (ret != ADUMP_SUCCESS) {
     269            0 :         IDE_LOGE("Open host kernel bin file[%s] failed.", hostKernelBinPath.c_str());
     270            0 :         return ADUMP_FAILED;
     271              :     }
     272              :     static std::mutex KernelBinLock;
     273           11 :     std::lock_guard<std::mutex> lock(KernelBinLock);
     274           11 :     auto writeSize = hostKernelBinFile.Write(kernelBinData_.c_str(), static_cast<int64_t>(kernelBinSize_));
     275           11 :     if (writeSize < 0) {
     276              :         // 不清理残留文件(与拆分前行为一致):M_TRUNC 已把文件截为 0,失败后盘上会留下空/截断的 _host.o。
     277              :         // 下次调用因大小不匹配会重写,不会误判为完整;但符号化阶段若在此期间读取会拿到残缺文件,故打印告警提醒。
     278            0 :         IDE_LOGE("Write host kernel bin file[%s] failed, an incomplete _host.o may remain on disk.",
     279              :             hostKernelBinPath.c_str());
     280            0 :         return ADUMP_FAILED;
     281              :     }
     282              :     // File::Write 内部循环处理短写、返回的是最后一次 mmWrite 的长度而非累计值,不能用它判完整性;
     283              :     // 改为写后 stat 校验最终文件大小,截断/短写(如磁盘满)时文件大小不足即判失败,避免残留截断文件被幂等误判为完整。
     284           22 :     if (stat(hostKernelBinPath.c_str(), &hostBinStat) != 0 ||
     285           11 :         static_cast<uint64_t>(hostBinStat.st_size) != static_cast<uint64_t>(kernelBinSize_)) {
     286              :         // 同上:残留的截断文件不主动删除,靠下次大小校验重写;此处打印告警提醒盘上存在不完整的 _host.o。
     287            1 :         IDE_LOGE("Write host kernel bin file[%s] incomplete, fileSize=%lld, expect=%u, "
     288              :             "an incomplete _host.o may remain on disk.",
     289              :             hostKernelBinPath.c_str(), static_cast<long long>(hostBinStat.st_size), kernelBinSize_);
     290            1 :         return ADUMP_FAILED;
     291              :     }
     292           10 :     IDE_LOGE("[Dump][Exception] dump host kernel to file, file: %s", hostKernelBinPath.c_str());
     293           10 :     (void)mmChmod(hostKernelBinPath.c_str(), M_IRUSR);  // 安全要求,落盘文件置为最小权限:用户只读, 400
     294           10 :     outHostOPath = hostKernelBinPath;
     295              : 
     296           10 :     return ADUMP_SUCCESS;
     297           15 : }
     298              : 
     299           13 : std::vector<std::string> KernelInfoCollector::SplitString(const std::string &str, char delimiter) const
     300              : {
     301           13 :     std::vector<std::string> result;
     302           13 :     std::stringstream ss(str);
     303           13 :     std::string item;
     304              : 
     305           39 :     while (std::getline(ss, item, delimiter)) {
     306           26 :         result.push_back(item);
     307              :     }
     308              : 
     309           13 :     return result;
     310           13 : }
     311              : 
     312              : /*
     313              :     按照指定的路径优先级来进行.o和.json文件搜索
     314              : */
     315           31 : std::vector<std::string> KernelInfoCollector::GetSearchPath() const
     316              : {
     317           31 :     std::string envStr;
     318           31 :     std::vector<std::string> searchPath;
     319              : 
     320           31 :     ADX_GET_ENV(MM_ENV_ASCEND_CACHE_PATH, envStr);
     321           31 :     if (!envStr.empty()) {
     322           13 :         searchPath.emplace_back(envStr);
     323           13 :         IDE_LOGI("Add ASCEND_CACHE_PATH[%s] to search path.", envStr.c_str());
     324              :     }
     325              : 
     326           31 :     ADX_GET_ENV(MM_ENV_HOME, envStr);
     327           31 :     if (!envStr.empty()) {
     328           26 :         envStr += "/atc_data";
     329           26 :         searchPath.emplace_back(envStr);
     330           26 :         IDE_LOGI("Add HOME[%s] to search path.", envStr.c_str());
     331              :     }
     332              :     
     333              :     // custom install path
     334           31 :     ADX_GET_ENV(MM_ENV_ASCEND_CUSTOM_OPP_PATH, envStr);
     335           31 :     if (!envStr.empty()) {
     336           13 :         std::vector<std::string> customPaths = SplitString(envStr, ':');
     337           39 :         for (const auto &customPath : customPaths) {
     338           26 :             searchPath.emplace_back(customPath);
     339           26 :             IDE_LOGI("Add ASCEND_CUSTOM_OPP_PATH[%s] to search path.", customPath.c_str());
     340              :         }
     341           13 :     }
     342              : 
     343           31 :     ADX_GET_ENV(MM_ENV_ASCEND_OPP_PATH, envStr);
     344           31 :     if (!envStr.empty()) {
     345           26 :         std::string vendorsPath = envStr + "/vendors";
     346           26 :         searchPath.emplace_back(vendorsPath);
     347           26 :         IDE_LOGI("Add ASCEND_OPP_PATH[%s] to search path.", vendorsPath.c_str());
     348              : 
     349           26 :         std::string buildInPath = envStr + "/built-in";
     350           26 :         searchPath.emplace_back(buildInPath);
     351           26 :         IDE_LOGI("Add ASCEND_OPP_PATH[%s] to search path.", buildInPath.c_str());
     352           26 :     }
     353              : 
     354              :     // kernel meta generate in current path
     355           31 :     searchPath.emplace_back("./");
     356           31 :     IDE_LOGI("Add current path[./] to search path.");
     357              : 
     358           31 :     ADX_GET_ENV(MM_ENV_ASCEND_WORK_PATH, envStr);
     359           31 :     if (!envStr.empty()) {
     360           11 :         envStr += "/kernel_meta";
     361           11 :         searchPath.emplace_back(envStr);
     362              :     }
     363           31 :     IDE_LOGI("Add current path[%s] to search path.", envStr.c_str());
     364              : 
     365           31 :     return searchPath;
     366           31 : }
     367              : 
     368           23 : int32_t KernelInfoCollector::CollectKernelFile(const std::string &kernelName, const std::string &dumpPath) const
     369              : {
     370           23 :     std::vector<std::string> searchPath = GetSearchPath();
     371              : 
     372           23 :     bool failFlag = false;
     373           98 :     for (const auto &path : searchPath) {
     374           82 :         std::string jsonFilePath = SearchJsonFiles(path, kernelName);
     375           82 :         if (jsonFilePath.empty()) {
     376           75 :             continue;
     377              :         }
     378              : 
     379            7 :         Path jsonPath(jsonFilePath);
     380            7 :         Path dumpJsonPath(dumpPath);
     381            7 :         dumpJsonPath.Concat(jsonPath.GetFileName());
     382              :         static std::mutex KernelfileLock;
     383            7 :         std::lock_guard<std::mutex> lock(KernelfileLock);
     384            7 :         if (File::Copy(jsonFilePath, dumpJsonPath.GetString()) != ADUMP_SUCCESS) {
     385            0 :             IDE_LOGE("Copy kernel file[%s] to dump path[%s] failed.", jsonFilePath.c_str(), dumpJsonPath.GetCString());
     386            0 :             failFlag = true;
     387              :         } else {
     388            7 :             IDE_LOGE("[Dump][Exception] dump kernel json to file, file: %s", dumpJsonPath.GetCString());
     389            7 :             (void)mmChmod(dumpJsonPath.GetCString(), M_IRUSR);  // 安全要求,落盘文件置为最小权限:用户只读, 400
     390              :         }
     391              : 
     392            7 :         std::string kernelBinPath = jsonFilePath.substr(0, jsonFilePath.size() - JSON_SUFFIX_LEN) + ".o";
     393            7 :         Path kernelPath(kernelBinPath);
     394            7 :         Path dumpKernelPath(dumpPath);
     395            7 :         dumpKernelPath.Concat(kernelPath.GetFileName());
     396            7 :         if (File::Copy(kernelBinPath, dumpKernelPath.GetString()) != ADUMP_SUCCESS) {
     397            6 :             IDE_LOGE("Copy kernel file[%s] to dump path[%s] failed.", kernelBinPath.c_str(),
     398              :                      dumpKernelPath.GetCString());
     399            6 :             failFlag = true;
     400              :         } else {
     401            1 :             IDE_LOGE("[Dump][Exception] dump kernel to file, file: %s", dumpKernelPath.GetCString());
     402            1 :             (void)mmChmod(dumpKernelPath.GetCString(), M_IRUSR);  // 安全要求,落盘文件置为最小权限:用户只读, 400
     403              :         }
     404            7 :         break;
     405           89 :     }
     406           23 :     if (failFlag) {
     407            6 :         return ADUMP_FAILED;
     408              :     }
     409              : 
     410           17 :     return ADUMP_SUCCESS;
     411           23 : }
     412              : 
     413              : }  // namespace Adx
        

Generated by: LCOV version 2.0-1