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