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

Generated by: LCOV version 2.0-1