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