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_parse.h"
12 : #include <stdio.h>
13 : #include <stdlib.h>
14 : #include <string.h>
15 : #include <fcntl.h>
16 : #include <sys/stat.h>
17 : #include <sys/utsname.h>
18 : #include "securec.h"
19 : #include "adiag_print.h"
20 : #include "adiag_utils.h"
21 : #include "trace_system_api.h"
22 : #include "stacktrace_logger.h"
23 : #include "stacktrace_file_struct.h"
24 :
25 : #define MAX_BUFFER_LENGTH 1024U
26 : #define TXT_FILE_MODE 0640U
27 : #define PROCESS_SECTION "[process]\n"
28 : #define STACK_SECTION "[stack]\n"
29 :
30 : /**
31 : * @brief safe write data to file
32 : * @param [in] fd: file descriptor
33 : * @param [in] data: write data
34 : * @param [in] len: data buffer length
35 : * @return TraStatus
36 : */
37 90 : static TraStatus TraceWrite(int32_t fd, const char* data, size_t len)
38 : {
39 90 : if ((fd < 0) || (data == NULL) || (len == 0)) {
40 18 : return TRACE_INVALID_PARAM;
41 : }
42 :
43 72 : size_t reserved = len;
44 : do {
45 72 : ssize_t wrLen = write(fd, data + (len - reserved), reserved);
46 72 : if (wrLen < 0 && errno == EINTR) {
47 0 : wrLen = 0;
48 72 : } else if (wrLen < 0) {
49 0 : return TRACE_FAILURE;
50 : } else {
51 : ;
52 : }
53 72 : reserved -= (size_t)wrLen;
54 72 : } while (reserved != 0);
55 72 : return TRACE_SUCCESS;
56 : }
57 :
58 126 : static TraStatus TraceCheckBinPath(const char* filePath, uint32_t len)
59 : {
60 126 : if (len >= TRACE_MAX_PATH) {
61 18 : LOGE("path length [%u] bytes exceeds [%d] bytes", len, TRACE_MAX_PATH);
62 18 : return TRACE_INVALID_PARAM;
63 : }
64 108 : if (len == 0) {
65 18 : LOGE("path length is 0 bytes");
66 18 : return TRACE_INVALID_PARAM;
67 : }
68 90 : if (access(filePath, F_OK) != 0) {
69 18 : LOGE("input path [%s] does not exist", filePath);
70 18 : return TRACE_INVALID_PARAM;
71 : }
72 72 : const char* suffix = strrchr(filePath, '.');
73 72 : if ((suffix != NULL) && strcmp(suffix, ".bin") == 0) {
74 54 : return TRACE_SUCCESS;
75 : } else {
76 18 : LOGE("invalid path [%s]", filePath);
77 18 : return TRACE_INVALID_PARAM;
78 : }
79 : }
80 :
81 36 : static TraStatus TraceCheckBinHead(const struct ScHead* head)
82 : {
83 36 : if ((head->magic == STACK_HEAD_MAGIC) && (head->version == STACK_HEAD_VERSION)) {
84 18 : LOGR("check head successfully, magic=%x, version=%x.", head->magic, head->version);
85 18 : return TRACE_SUCCESS;
86 : }
87 18 : LOGE("check head failed, magic=%x, version=%x.", head->magic, head->version);
88 18 : return TRACE_FAILURE;
89 : }
90 :
91 18 : STATIC int32_t TraceGetTxtFd(const char* filePath, uint32_t len)
92 : {
93 : (void)len;
94 18 : char* fullPath = strdup(filePath);
95 18 : char* pos = strstr(fullPath, ".bin");
96 18 : if (pos == NULL) {
97 0 : LOGE("find string failed, path = %s.", filePath);
98 0 : ADIAG_SAFE_FREE(fullPath);
99 0 : return -1;
100 : }
101 18 : errno_t err = strncpy_s(pos, strlen(pos) + 1U, ".txt", strlen(".txt"));
102 18 : if (err != EOK) {
103 0 : LOGE("strncpy_s failed, path = %s.", filePath);
104 0 : ADIAG_SAFE_FREE(fullPath);
105 0 : return -1;
106 : }
107 :
108 18 : int32_t fd = open(fullPath, (uint32_t)O_CREAT | (uint32_t)O_WRONLY | (uint32_t)O_APPEND, TXT_FILE_MODE);
109 18 : if (fd >= 0) {
110 18 : (void)fchmod(fd, TXT_FILE_MODE);
111 : }
112 18 : ADIAG_SAFE_FREE(fullPath);
113 18 : return fd;
114 : }
115 :
116 18 : static void TraceWriteProcessReg(int32_t fd, const ScProcessInfo* info)
117 : {
118 18 : char tmpBuf[MAX_BUFFER_LENGTH] = {0};
119 18 : int32_t ret = -1;
120 : #if defined(__aarch64__)
121 : ret = snprintf_s(
122 : tmpBuf, MAX_BUFFER_LENGTH, MAX_BUFFER_LENGTH - 1U,
123 : "crash registers:\n"
124 : " x0 0x%016lx x1 0x%016lx x2 0x%016lx x3 0x%016lx\n"
125 : " x4 0x%016lx x5 0x%016lx x6 0x%016lx x7 0x%016lx\n"
126 : " x8 0x%016lx x9 0x%016lx x10 0x%016lx x11 0x%016lx\n"
127 : " x12 0x%016lx x13 0x%016lx x14 0x%016lx x15 0x%016lx\n"
128 : " x16 0x%016lx x17 0x%016lx x18 0x%016lx x19 0x%016lx\n"
129 : " x20 0x%016lx x21 0x%016lx x22 0x%016lx x23 0x%016lx\n"
130 : " x24 0x%016lx x25 0x%016lx x26 0x%016lx x27 0x%016lx\n"
131 : " x28 0x%016lx x29 0x%016lx\n"
132 : " sp 0x%016lx lr 0x%016lx pc 0x%016lx\n\n",
133 : info->regs[SCD_REGS_X0], info->regs[SCD_REGS_X1], info->regs[SCD_REGS_X2], info->regs[SCD_REGS_X3],
134 : info->regs[SCD_REGS_X4], info->regs[SCD_REGS_X5], info->regs[SCD_REGS_X6], info->regs[SCD_REGS_X7],
135 : info->regs[SCD_REGS_X8], info->regs[SCD_REGS_X9], info->regs[SCD_REGS_X10], info->regs[SCD_REGS_X11],
136 : info->regs[SCD_REGS_X12], info->regs[SCD_REGS_X13], info->regs[SCD_REGS_X14], info->regs[SCD_REGS_X15],
137 : info->regs[SCD_REGS_X16], info->regs[SCD_REGS_X17], info->regs[SCD_REGS_X18], info->regs[SCD_REGS_X19],
138 : info->regs[SCD_REGS_X20], info->regs[SCD_REGS_X21], info->regs[SCD_REGS_X22], info->regs[SCD_REGS_X23],
139 : info->regs[SCD_REGS_X24], info->regs[SCD_REGS_X25], info->regs[SCD_REGS_X26], info->regs[SCD_REGS_X27],
140 : info->regs[SCD_REGS_X28], info->regs[SCD_REGS_X29], info->regs[SCD_REGS_SP], info->regs[SCD_REGS_LR],
141 : info->regs[SCD_REGS_PC]);
142 : #elif defined(__x86_64__)
143 18 : ret = snprintf_s(
144 : tmpBuf, MAX_BUFFER_LENGTH, MAX_BUFFER_LENGTH - 1U,
145 : "crash registers:\n"
146 : " rax 0x%016lx rbx 0x%016lx rcx 0x%016lx rdx 0x%016lx\n"
147 : " r8 0x%016lx r9 0x%016lx r10 0x%016lx r11 0x%016lx\n"
148 : " r12 0x%016lx r13 0x%016lx r14 0x%016lx r15 0x%016lx\n"
149 : " rdi 0x%016lx rsi 0x%016lx\n"
150 : " rbp 0x%016lx rsp 0x%016lx rip 0x%016lx\n\n",
151 18 : info->regs[SCD_REGS_RAX], info->regs[SCD_REGS_RBX], info->regs[SCD_REGS_RCX], info->regs[SCD_REGS_RDX],
152 18 : info->regs[SCD_REGS_R8], info->regs[SCD_REGS_R9], info->regs[SCD_REGS_R10], info->regs[SCD_REGS_R11],
153 18 : info->regs[SCD_REGS_R12], info->regs[SCD_REGS_R13], info->regs[SCD_REGS_R14], info->regs[SCD_REGS_R15],
154 18 : info->regs[SCD_REGS_RDI], info->regs[SCD_REGS_RSI], info->regs[SCD_REGS_RBP], info->regs[SCD_REGS_RSP],
155 18 : info->regs[SCD_REGS_RIP]);
156 : #else
157 : (void)info;
158 : #endif
159 :
160 18 : if (ret == -1) {
161 0 : LOGE("snprintf_s failed, ret = %d, errno = %d.", ret, errno);
162 0 : return;
163 : }
164 18 : (void)TraceWrite(fd, tmpBuf, strlen(tmpBuf));
165 : }
166 :
167 18 : STATIC void TraceWriteKernelVersion(int32_t fd)
168 : {
169 18 : char tmpBuf[MAX_BUFFER_LENGTH] = {0};
170 18 : int32_t ret = snprintf_s(tmpBuf, MAX_BUFFER_LENGTH, MAX_BUFFER_LENGTH - 1U, "kernel version: unknown\n");
171 18 : if (ret == -1) {
172 0 : LOGE("snprintf_s failed, ret = %d, errno = %d.", ret, errno);
173 0 : return;
174 : }
175 :
176 18 : FILE* fp = popen("uname -a", "r");
177 18 : if (fp == NULL) {
178 0 : LOGE("get kernel version failed, errno=%d.", errno);
179 0 : (void)TraceWrite(fd, tmpBuf, strlen(tmpBuf));
180 0 : return;
181 : }
182 :
183 18 : char kernelVersion[MAX_BUFFER_LENGTH] = {0};
184 18 : if (fgets(kernelVersion, (int32_t)sizeof(kernelVersion), fp) != NULL) {
185 18 : ret = snprintf_s(tmpBuf, MAX_BUFFER_LENGTH, MAX_BUFFER_LENGTH - 1U, "kernel version: %s", kernelVersion);
186 18 : if (ret == -1) {
187 0 : LOGE("snprintf_s failed, ret = %d, errno = %d.", ret, errno);
188 : }
189 : }
190 18 : (void)pclose(fp);
191 18 : (void)TraceWrite(fd, tmpBuf, strlen(tmpBuf));
192 : }
193 :
194 18 : static void TraceWriteProcess(int32_t fd, const ScProcessInfo* info)
195 : {
196 18 : char tmpBuf[MAX_BUFFER_LENGTH] = {0};
197 18 : int32_t ret = snprintf_s(
198 : tmpBuf, MAX_BUFFER_LENGTH, MAX_BUFFER_LENGTH - 1U,
199 : PROCESS_SECTION "crash task:%s\n"
200 : "crash pid:%d\n"
201 : "crash tid:%d\n"
202 : "crash stack base:0x%016lx\n"
203 : "crash stack top:0x%016lx\n"
204 : "crash reason: signal %d\n",
205 18 : info->task, info->pid, info->crashTid, info->baseAddr, info->topAddr, info->signo);
206 18 : if (ret == -1) {
207 0 : LOGE("snprintf_s failed, ret = %d, errno = %d.", ret, errno);
208 0 : return;
209 : }
210 18 : (void)TraceWrite(fd, tmpBuf, strlen(tmpBuf));
211 18 : TraceWriteKernelVersion(fd);
212 18 : TraceWriteProcessReg(fd, info);
213 : }
214 :
215 18 : static void TraceWriteStack(int32_t fd, const struct ScThreadInfo* data)
216 : {
217 18 : (void)TraceWrite(fd, STACK_SECTION, strlen(STACK_SECTION));
218 18 : char tmpBuf[MAX_BUFFER_LENGTH] = {0};
219 18 : for (uint32_t i = 0; i < MAX_THREAD_NUM; i++) {
220 18 : if (data[i].tid == 0) {
221 18 : break;
222 : }
223 0 : int32_t ret = snprintf_s(
224 0 : tmpBuf, MAX_BUFFER_LENGTH, MAX_BUFFER_LENGTH - 1U, "Thread %u (%d, %s)\n", i + 1U, data[i].tid,
225 0 : data[i].name);
226 0 : if (ret == -1) {
227 0 : LOGE("snprintf_s failed, ret = %d, errno = %d.", ret, errno);
228 0 : return;
229 : }
230 0 : (void)TraceWrite(fd, tmpBuf, strlen(tmpBuf));
231 0 : for (int32_t j = 0; j <= data[i].layer; j++) {
232 0 : (void)TraceWrite(fd, data[i].frames[j].frame, strlen(data[i].frames[j].frame));
233 : }
234 0 : (void)TraceWrite(fd, "\n", strlen("\n"));
235 : }
236 : }
237 :
238 18 : static void TraceWriteTxt(int32_t fd, const struct StackcoreBuffer* data)
239 : {
240 18 : TraceWriteProcess(fd, &data->process);
241 18 : TraceWriteStack(fd, data->thread);
242 18 : return;
243 : }
244 :
245 54 : STATIC TraStatus TraceParseBinFile(const char* filePath, uint32_t len)
246 : {
247 54 : int32_t binFile = open(filePath, O_RDONLY);
248 54 : if (binFile == -1) {
249 0 : LOGE("could not open file [%s]", filePath);
250 0 : return TRACE_INVALID_PARAM;
251 : }
252 :
253 54 : struct StackcoreBuffer* data = (struct StackcoreBuffer*)AdiagMalloc(sizeof(struct StackcoreBuffer));
254 54 : if (data == NULL) {
255 0 : TraceClose(&binFile);
256 0 : LOGE("malloc failed.");
257 0 : return TRACE_FAILURE;
258 : }
259 :
260 54 : ssize_t readBytes = read(binFile, data, sizeof(struct StackcoreBuffer));
261 54 : if ((size_t)readBytes != sizeof(struct StackcoreBuffer)) {
262 18 : TraceClose(&binFile);
263 18 : ADIAG_SAFE_FREE(data);
264 18 : LOGE("can not read data from file [%s]", filePath);
265 18 : return TRACE_INVALID_PARAM;
266 : }
267 :
268 36 : TraStatus ret = TraceCheckBinHead(&data->head);
269 36 : if (ret != TRACE_SUCCESS) {
270 18 : TraceClose(&binFile);
271 18 : ADIAG_SAFE_FREE(data);
272 18 : return ret;
273 : }
274 :
275 18 : int32_t txtFile = TraceGetTxtFd(filePath, len);
276 18 : if (txtFile == -1) {
277 0 : LOGE("could not open txt file[%s]", filePath);
278 0 : TraceClose(&binFile);
279 0 : ADIAG_SAFE_FREE(data);
280 0 : return TRACE_FAILURE;
281 : }
282 :
283 18 : TraceWriteTxt(txtFile, data);
284 :
285 18 : char tmpBuf[MAX_BUFFER_LENGTH] = {0};
286 : do {
287 18 : readBytes = read(binFile, tmpBuf, MAX_BUFFER_LENGTH);
288 18 : (void)TraceWrite(txtFile, tmpBuf, (size_t)readBytes);
289 18 : } while (readBytes > 0);
290 :
291 18 : ADIAG_SAFE_FREE(data);
292 18 : TraceClose(&binFile);
293 18 : TraceClose(&txtFile);
294 18 : return TRACE_SUCCESS;
295 : }
296 :
297 126 : TraStatus TraceStackParse(const char* filePath, uint32_t len)
298 : {
299 126 : LOGR("start to parse bin file[%s]", filePath);
300 126 : TraStatus ret = TraceCheckBinPath(filePath, len);
301 126 : if (ret != TRACE_SUCCESS) {
302 72 : return ret;
303 : }
304 54 : char realPath[TRACE_MAX_PATH] = {0};
305 54 : if ((TraceRealPath(filePath, realPath, TRACE_MAX_PATH) != EN_OK) && (AdiagGetErrorCode() != ENOENT)) {
306 0 : LOGE("can not get realpath, path=%s, strerr=%s.", filePath, strerror(AdiagGetErrorCode()));
307 0 : return TRACE_FAILURE;
308 : }
309 :
310 54 : ret = TraceParseBinFile(realPath, (uint32_t)strlen(realPath));
311 54 : if (ret != TRACE_SUCCESS) {
312 36 : return ret;
313 : }
314 :
315 18 : if (remove(realPath) != 0) {
316 0 : LOGW("can not remove file [%s]", realPath);
317 0 : return TRACE_SUCCESS;
318 : }
319 :
320 18 : return TRACE_SUCCESS;
321 : }
|