LCOV - code coverage report
Current view: top level - atrace/utrace/stacktrace - stacktrace_unwind.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 65.9 % 132 87
Test Date: 2026-08-31 10:07:06 Functions: 77.8 % 9 7

            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              : 
      11              : #include "stacktrace_unwind.h"
      12              : #include <execinfo.h>
      13              : #include <stdlib.h>
      14              : #include <pthread.h>
      15              : #include <fcntl.h>
      16              : #include <errno.h>
      17              : #include <string.h>
      18              : #include <link.h>
      19              : #include "stacktrace_unwind_inner.h"
      20              : #include "stacktrace_unwind_instr.h"
      21              : #include "stacktrace_fp.h"
      22              : #include "stacktrace_signal.h"
      23              : #include "stacktrace_unwind_dfx.h"
      24              : #include "stddef.h"
      25              : #include "securec.h"
      26              : #include "trace_system_api.h"
      27              : #include "adiag_utils.h"
      28              : #include "adiag_print.h"
      29              : #include "scd_memory.h"
      30              : #include "scd_maps.h"
      31              : #include "scd_dwarf.h"
      32              : 
      33              : #define FDE_ENTRY_SIZE 8U
      34              : #define MAX_SHARE_LIB_NUM 500U
      35              : #define UNWIND_MAP_NUM 2U
      36              : 
      37              : // 记录所有动态库信息
      38              : typedef struct ShareLibInfo {
      39              :     uint32_t shareLibNum;
      40              :     TraceUnwindMapInfo unwindMapInfo[MAX_SHARE_LIB_NUM];
      41              : } ShareLibInfo;
      42              : 
      43              : static ShareLibInfo g_shareLibInfo[UNWIND_MAP_NUM]; // ping pong manager
      44              : static uint32_t g_shareLibIdx = 0;
      45              : 
      46          567 : static void TraceGetElfUnwindInfo(struct dl_phdr_info* pstInfo, TraceUnwindMapInfo* unwindMapInfo)
      47              : {
      48          567 :     const ElfW(Phdr)* psthdr = pstInfo->dlpi_phdr;
      49          567 :     unwindMapInfo->loadBase = pstInfo->dlpi_addr;
      50          567 :     unwindMapInfo->codeListNum = 0;
      51          567 :     unwindMapInfo->size = psthdr->p_memsz;
      52              :     errno_t ret;
      53         6678 :     for (int i = pstInfo->dlpi_phnum; i > 0; i--) {
      54         6111 :         if (psthdr->p_type == PT_LOAD) {
      55         2079 :             unwindMapInfo->codeList[unwindMapInfo->codeListNum].loadProgSegAddr = psthdr->p_vaddr + pstInfo->dlpi_addr;
      56         2079 :             unwindMapInfo->codeList[unwindMapInfo->codeListNum].segSize = (uint32_t)psthdr->p_memsz;
      57         2079 :             if (pstInfo->dlpi_name[0] == '\0') {
      58          252 :                 ret = strcpy_s(unwindMapInfo->objName, sizeof(unwindMapInfo->objName), program_invocation_short_name);
      59              :             } else {
      60         1827 :                 ret = strcpy_s(unwindMapInfo->objName, sizeof(unwindMapInfo->objName), pstInfo->dlpi_name);
      61              :             }
      62         2079 :             if (ret != EOK) {
      63            0 :                 LOGW("strcpy_s failed, err = %d, strerr = %s.", (int32_t)ret, strerror(AdiagGetErrorCode()));
      64              :             }
      65         2079 :             unwindMapInfo->codeListNum++;
      66         4032 :         } else if (psthdr->p_type == PT_GNU_EH_FRAME) {
      67          567 :             unwindMapInfo->unwindSegStart = psthdr->p_vaddr + pstInfo->dlpi_addr;
      68          567 :             unwindMapInfo->count = psthdr->p_memsz / FDE_ENTRY_SIZE;
      69          567 :             unwindMapInfo->size = psthdr->p_memsz;
      70          567 :             LOGI(
      71              :                 "[LLT] unwindSegStart : %lx, unwindMapInfo->count : %zu", unwindMapInfo->unwindSegStart,
      72              :                 unwindMapInfo->count);
      73              :         } else {
      74              :             ;
      75              :         }
      76         6111 :         psthdr++;
      77              :     }
      78         2646 :     for (uint32_t j = 0; j < unwindMapInfo->codeListNum; j++) {
      79         2079 :         TraceLoadProgSegInfo* segInfo = &unwindMapInfo->codeList[j];
      80         2079 :         LOGI(
      81              :             "[%u] [%lx, %lx] %s", j, segInfo->loadProgSegAddr, segInfo->loadProgSegAddr + segInfo->segSize,
      82              :             unwindMapInfo->objName);
      83              :     }
      84          567 : }
      85              : 
      86              : /**
      87              :  * @brief           callback function called by dl_iterate_phdr, record dynamic library load info
      88              :  * @param [in]      pstInfo:            a pointer to a structure containing information about the shared object
      89              :  * @param [in]      size:               the size of the structure pointed to by info
      90              :  * @param [in]      ptr:                a copy of whatever value was passed by the calling program as the
      91              :  *                                      second argument (also named data) in the call to dl_iterate_phdr().
      92              :  * @return          0 for success, others for failed
      93              :  */
      94          567 : STATIC int32_t TraceFindunwindSegCallback(struct dl_phdr_info* pstInfo, size_t size, void* ptr)
      95              : {
      96          567 :     if (size < sizeof(struct dl_phdr_info)) {
      97            0 :         return -1;
      98              :     }
      99          567 :     ShareLibInfo* shareLibInfo = (ShareLibInfo*)ptr;
     100          567 :     if (shareLibInfo->shareLibNum == MAX_SHARE_LIB_NUM) {
     101            0 :         LOGE("share lib num exceeding the size of array %u", MAX_SHARE_LIB_NUM);
     102            0 :         return -1;
     103              :     }
     104          567 :     TraceUnwindMapInfo* unwindMapInfo = &shareLibInfo->unwindMapInfo[shareLibInfo->shareLibNum];
     105          567 :     TraceGetElfUnwindInfo(pstInfo, unwindMapInfo);
     106          567 :     shareLibInfo->shareLibNum++;
     107          567 :     return 0;
     108              : }
     109              : 
     110           63 : static void TraceGetAllModuleBaseInfo(void)
     111              : {
     112           63 :     ShareLibInfo* shareLibInfo = &g_shareLibInfo[1U - g_shareLibIdx];
     113           63 :     shareLibInfo->shareLibNum = 0;
     114           63 :     (void)dl_iterate_phdr(TraceFindunwindSegCallback, shareLibInfo);
     115           63 :     g_shareLibIdx = 1U - g_shareLibIdx;
     116           63 : }
     117              : 
     118            0 : static void DumpStack(uint32_t idx, uintptr_t pc, char* data, size_t len)
     119              : {
     120            0 :     for (uint32_t i = 0; i < g_shareLibInfo[g_shareLibIdx].shareLibNum; i++) {
     121            0 :         TraceUnwindMapInfo* unwindMapInfo = &g_shareLibInfo[g_shareLibIdx].unwindMapInfo[i];
     122            0 :         for (uint32_t j = 0; j < unwindMapInfo->codeListNum; j++) {
     123            0 :             TraceLoadProgSegInfo* segInfo = &unwindMapInfo->codeList[j];
     124            0 :             if (pc < segInfo->loadProgSegAddr || pc > segInfo->loadProgSegAddr + segInfo->segSize) {
     125            0 :                 continue;
     126              :             }
     127            0 :             int ret = snprintf_s(
     128              :                 data, len, len - 1U, "#%02u 0x%016lx 0x%016lx %s\n", idx, pc,
     129            0 :                 unwindMapInfo->codeList[0].loadProgSegAddr, unwindMapInfo->objName);
     130            0 :             LOGR(
     131              :                 "#%02u 0x%016lx 0x%016lx %s", idx, pc, unwindMapInfo->codeList[0].loadProgSegAddr,
     132              :                 unwindMapInfo->objName);
     133            0 :             if (ret == -1) {
     134            0 :                 LOGE("snprintf_s stack info failed, strerr=%s.", strerror(AdiagGetErrorCode()));
     135              :             }
     136            0 :             return;
     137              :         }
     138              :     }
     139              : }
     140              : 
     141            0 : static void TraceDumpResult(uintptr_t* callstack, uint32_t maxDepth, TraceStackInfo* stackInfo)
     142              : {
     143            0 :     stackInfo->layer = 0;
     144            0 :     for (uint32_t i = 0; i < maxDepth; i++) {
     145            0 :         if (callstack[i] != 0) {
     146            0 :             DumpStack(i, callstack[i], stackInfo->frame[i].info, sizeof(stackInfo->frame[i].info));
     147            0 :             stackInfo->layer++;
     148              :         }
     149              :     }
     150            0 :     stackInfo->layer--;
     151            0 : }
     152              : 
     153              : /**
     154              :  * 获取eh_frame_hdr地址
     155              :  * @param [in] pc 程序计数器
     156              :  * @param [out] count 反向解码计数
     157              :  * @param [out] dwarf pc所在的dwarf信息
     158              :  * @return 如果找到匹配的地址,返回地址;否则返回0
     159              :  */
     160           18 : TraStatus TraceGetEhFrameHdrAddr(uintptr_t pc, ScdDwarf* dwarf)
     161              : {
     162          180 :     for (uint32_t i = 0; i < g_shareLibInfo[g_shareLibIdx].shareLibNum; i++) {
     163          162 :         TraceUnwindMapInfo* unwindMapInfo = &g_shareLibInfo[g_shareLibIdx].unwindMapInfo[i];
     164          162 :         TraceLoadProgSegInfo* baseSegInfo = &unwindMapInfo->codeList[0]; // use first codeList for base
     165          756 :         for (uint32_t j = 0; j < unwindMapInfo->codeListNum; j++) {
     166              :             // 获取当前代码段的信息
     167          594 :             TraceLoadProgSegInfo* segInfo = &unwindMapInfo->codeList[j];
     168          594 :             if (pc >= segInfo->loadProgSegAddr && pc < segInfo->loadProgSegAddr + segInfo->segSize) {
     169            0 :                 dwarf->fdeCount = unwindMapInfo->count;
     170            0 :                 LOGI(
     171              :                     "use codeList [0x%lx, 0x%lx], unwindSegStart %lx", baseSegInfo->loadProgSegAddr,
     172              :                     baseSegInfo->loadProgSegAddr + baseSegInfo->segSize, unwindMapInfo->unwindSegStart);
     173            0 :                 LOGI("unwindMapInfo->unwindSegStart %lx ", unwindMapInfo->unwindSegStart);
     174            0 :                 LOGI(
     175              :                     "[LLT] PC offset %lx, unwindSegStart offset %lx", pc - baseSegInfo->loadProgSegAddr,
     176              :                     unwindMapInfo->unwindSegStart - baseSegInfo->loadProgSegAddr);
     177            0 :                 dwarf->loadBias = 0;
     178            0 :                 dwarf->memory->data = unwindMapInfo->unwindSegStart;
     179            0 :                 dwarf->memory->size =
     180              :                     UINT32_MAX; // unable to determine size of elf, set to maximum value to avoid check
     181            0 :                 return TRACE_SUCCESS;
     182              :             }
     183              :         }
     184              :     }
     185              :     // 如果没有找到匹配的地址,返回0
     186           18 :     return TRACE_FAILURE;
     187              : }
     188              : typedef struct TraceStackAddrLimit {
     189              :     uintptr_t stackMaxAddr;
     190              :     uintptr_t stackMinAddr;
     191              : } TraceStackAddrLimit;
     192              : 
     193              : /**
     194              :  * 通过异常处理程序的栈帧获取调用栈。
     195              :  *
     196              :  * @param [in]  stackAddr 堆栈地址限制信息。
     197              :  * @param [in]  regs 核心寄存器数组。
     198              :  * @param [out] callstack 调用栈缓冲区,用于存储获取到的调用栈信息。
     199              :  * @param [in]  maxDepth 最大调用栈深度。
     200              :  *
     201              :  * @return      : !=0 failure; ==0 success
     202              :  */
     203           18 : static TraStatus TraceCallStackGetByUnwind(
     204              :     const TraceStackAddrLimit* stackAddr, ScdRegs* regs, uintptr_t* callstack, uint32_t maxDepth)
     205              : {
     206           18 :     uint32_t depth = 0;
     207           18 :     uintptr_t nextPc = 0;
     208           18 :     uintptr_t pc = regs->r[VOS_R_IP];
     209           18 :     regs->r[VOS_R_IP] = 0;
     210           18 :     callstack[depth] = pc;
     211           18 :     depth++;
     212              :     ScdDwarfStepArgs args;
     213              :     ScdMemory memory;
     214           18 :     ScdMemoryInitLocal(&memory);
     215           18 :     args.stackMinAddr = stackAddr->stackMinAddr;
     216           18 :     args.stackMaxAddr = stackAddr->stackMaxAddr;
     217           18 :     args.isFirstStack = true;
     218           18 :     for (; depth < maxDepth; depth++) {
     219           18 :         ScdDwarf dwarf = {0};
     220           18 :         dwarf.memory = &memory;
     221           18 :         dwarf.ehFrameHdrOffset = 0;
     222           18 :         if ((TraceGetEhFrameHdrAddr(pc, &dwarf) != TRACE_SUCCESS) || (dwarf.memory->data == 0)) {
     223           18 :             LOGE("cannot find pc 0x%lx in so", pc);
     224           18 :             return TRACE_FAILURE;
     225              :         }
     226            0 :         int ret = ScdDwarfStep(&dwarf, regs, &args, pc, &nextPc);
     227            0 :         if (ret != TRACE_SUCCESS) {
     228            0 :             LOGE("get next pc by unwind failed");
     229            0 :             return ret;
     230              :         }
     231            0 :         if (nextPc == 0) {
     232            0 :             break;
     233              :         }
     234            0 :         args.isFirstStack = false;
     235            0 :         pc = nextPc;
     236            0 :         callstack[depth] = nextPc;
     237              :     }
     238            0 :     return TRACE_SUCCESS;
     239              : }
     240              : 
     241           63 : void TraceStackUnwindInit(void) { TraceGetAllModuleBaseInfo(); }
     242              : 
     243              : /**
     244              :  * @brief       get backtrace by unwind info
     245              :  * @param [in]  arg:        thread argument
     246              :  * @param [in]  regsAddr:   register info
     247              :  * @param [in]  regNum:     number of register
     248              :  * @param [out] stackInfo:  stack info get by unwind info
     249              :  * @return      TraStatus
     250              :  */
     251           90 : TraStatus TraceStackUnwind(const ThreadArgument* arg, uintptr_t* regsAddr, uint32_t regNum, TraceStackInfo* stackInfo)
     252              : {
     253           90 :     if ((arg == NULL) || (stackInfo == NULL) || (regsAddr == NULL) || (regNum < MAX_USE_REG_NUM)) {
     254           54 :         LOGE("invalid argument arg or stackInfo");
     255           54 :         return TRACE_FAILURE;
     256              :     }
     257              : 
     258           36 :     uintptr_t callstack[MAX_STACK_LAYER] = {0};
     259           36 :     ScdRegs regs = {0};
     260              :     errno_t err =
     261           36 :         memcpy_s(regs.r, sizeof(uintptr_t) * TRACE_CORE_REG_NUM, regsAddr, sizeof(uintptr_t) * TRACE_CORE_REG_NUM);
     262           36 :     if (err != EOK) {
     263           18 :         LOGE("memcpy failed, err = %d, strerr = %s.", err, strerror(AdiagGetErrorCode()));
     264           18 :         return TRACE_FAILURE;
     265              :     }
     266              : 
     267           18 :     uintptr_t nfp = regs.r[VOS_R_BP]; // RBP register
     268           18 :     uintptr_t sp = regs.r[VOS_R_SP];  // RSP register
     269           18 :     uintptr_t pc = regs.r[VOS_R_IP];  // RIP register
     270           18 :     LOGI("pc : %lx, sp : %lx, nfp : %lx", pc, sp, nfp);
     271           18 :     TraceStackAddrLimit stackAddr = {0};
     272           18 :     stackAddr.stackMaxAddr = arg->stackBaseAddr;
     273           18 :     stackAddr.stackMinAddr = sp;
     274           18 :     LOGI("stack addr : [%lx, %lx]", stackAddr.stackMinAddr, stackAddr.stackMaxAddr);
     275           18 :     DumpStackToFile(stackAddr.stackMinAddr, stackAddr.stackMaxAddr - stackAddr.stackMinAddr);
     276              : 
     277           18 :     TraStatus ret = TraceCallStackGetByUnwind(&stackAddr, &regs, callstack, MAX_STACK_LAYER);
     278           18 :     if (ret != TRACE_SUCCESS) {
     279           18 :         return ret;
     280              :     }
     281            0 :     TraceDumpResult(callstack, MAX_STACK_LAYER, stackInfo);
     282            0 :     return TRACE_SUCCESS;
     283              : }
        

Generated by: LCOV version 2.0-1