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, ®s, 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 : }
|