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_dumper.h"
12 : #include <stdatomic.h>
13 : #include <pthread.h>
14 : #include <sys/ptrace.h>
15 : #include "securec.h"
16 : #include "stacktrace_signal.h"
17 : #include "stacktrace_fp.h"
18 : #include "stacktrace_logger.h"
19 : #include "trace_system_api.h"
20 : #include "trace_recorder.h"
21 : #include "adiag_print.h"
22 : #include "adiag_utils.h"
23 : #include "stacktrace_unwind_reg.h"
24 : #include "stacktrace_safe_recorder.h"
25 : #include "stacktrace_exec.h"
26 :
27 : #define SCD_CRASH_CHILD_STACK_LEN (16U * 1024U)
28 : #ifndef SCD_EXE_RELATIVE_PATH
29 : #define SCD_EXE_RELATIVE_PATH "/../bin/asc_dumper"
30 : #endif
31 :
32 : typedef struct {
33 : DumperCallback func; // func registered by tracer
34 : void *arg; // argument for func
35 : } TracerCallback;
36 :
37 : typedef struct {
38 : uint8_t *stack; // dynamically applied address for the sub-process
39 : uint32_t stackSize; // size of stack
40 : atomic_flag done; // func has been executed or not
41 : TracerCallback tracer; // info of tracer callback
42 : ThreadArgument args; // argument for the sub-process
43 : pthread_mutex_t mutex;
44 : } StackTraceDumperMgr;
45 : STATIC StackTraceDumperMgr g_dumperMgr = { 0 };
46 :
47 890 : TraStatus TraceDumperSetCallback(DumperCallback func, void *arg)
48 : {
49 890 : if ((func == NULL) || (arg == NULL)) {
50 20 : ADIAG_ERR("set callback to dumper failed.");
51 20 : return TRACE_INVALID_PARAM;
52 : }
53 870 : g_dumperMgr.tracer.func = func;
54 870 : g_dumperMgr.tracer.arg = arg;
55 870 : return TRACE_SUCCESS;
56 : }
57 :
58 119 : STATIC TraStatus DumperExecuteCallback(uint64_t timeStamp)
59 : {
60 119 : if ((g_dumperMgr.tracer.func == NULL) || (g_dumperMgr.tracer.arg == NULL)) {
61 0 : return TRACE_INVALID_PARAM;
62 : }
63 :
64 119 : TraStatus ret = g_dumperMgr.tracer.func(g_dumperMgr.tracer.arg, timeStamp);
65 119 : return ret;
66 : }
67 :
68 240 : STATIC TraStatus DumperSignalSetArgs(const TraceSignalInfo* info)
69 : {
70 240 : errno_t err = memcpy_s(&(g_dumperMgr.args.siginfo), sizeof(siginfo_t), info->siginfo, sizeof(siginfo_t));
71 240 : if (err != EOK) {
72 20 : LOGE("memcpy signal info failed, ret=%d.", (int32_t)err);
73 20 : return TRACE_FAILURE;
74 : }
75 220 : err = memcpy_s(&(g_dumperMgr.args.ucontext), sizeof(ucontext_t), info->ucontext, sizeof(ucontext_t));
76 220 : if (err != EOK) {
77 20 : LOGE("memcpy context failed, ret=%d.", (int32_t)err);
78 20 : return TRACE_FAILURE;
79 : }
80 : // set file path
81 200 : int32_t pid = getpid();
82 200 : int32_t tid = (int32_t)syscall(__NR_gettid);
83 200 : TraceStackRecorderInfo recordInfo = {info->timeStamp, info->signo, pid, tid};
84 200 : TraStatus ret = TraceSafeGetDirPath(&recordInfo, g_dumperMgr.args.filePath, SCD_MAX_FILEPATH_LEN + 1U);
85 200 : if (ret != TRACE_SUCCESS) {
86 20 : LOGE("get dir path failed, ret=%d.", ret);
87 20 : return TRACE_FAILURE;
88 : }
89 180 : ret = TraceSafeGetFileName(&recordInfo, g_dumperMgr.args.fileName, SCD_MAX_FILENAME_LEN + 1U);
90 180 : if (ret != TRACE_SUCCESS) {
91 20 : LOGE("get file name failed, ret=%d.", ret);
92 20 : return TRACE_FAILURE;
93 : }
94 160 : StacktraceLogSetPathSuffix(g_dumperMgr.args.filePath, g_dumperMgr.args.fileName,
95 160 : ScdSignalIsBinDump(info->signo, info->siginfo) ? ".log" : ".txt");
96 160 : g_dumperMgr.args.crashTime = info->timeStamp;
97 160 : g_dumperMgr.args.pid = pid;
98 160 : g_dumperMgr.args.tid = tid;
99 160 : g_dumperMgr.args.signo = info->signo;
100 :
101 160 : return TRACE_SUCCESS;
102 : }
103 :
104 : // run dumper sub process, execute tracer callback once, and fall back to fp
105 : // unwind when the sub process cannot handle the signal.
106 140 : STATIC TraStatus DumperRunAndFallback(const TraceSignalInfo *info)
107 : {
108 140 : pid_t child = -1;
109 140 : TraStatus ret = ScExecStart((void *)(g_dumperMgr.stack + g_dumperMgr.stackSize), &g_dumperMgr.args, &child);
110 135 : if (ret == TRACE_SUCCESS) {
111 75 : LOGI("dumper start successfully, pc=0x%lx, sp=0x%lx, fp=0x%lx.",
112 : GET_PCREG_FROM_CONTEXT(&(g_dumperMgr.args.ucontext.uc_mcontext)),
113 : GET_SPREG_FROM_CONTEXT(&(g_dumperMgr.args.ucontext.uc_mcontext)),
114 : GET_FPREG_FROM_CONTEXT(&(g_dumperMgr.args.ucontext.uc_mcontext)));
115 75 : ret = ScExecEnd(child);
116 : }
117 :
118 : // tracer process, only once
119 135 : if (atomic_flag_test_and_set(&g_dumperMgr.done)) {
120 16 : LOGI("signal handler has been executed.");
121 : } else {
122 119 : (void)DumperExecuteCallback(info->timeStamp);
123 : }
124 : // fp process
125 135 : if (ret != TRACE_SUCCESS) {
126 135 : LOGW("dumper process can not handle signal, directly derivation stack.");
127 135 : ret = TraceStackSigHandler(&g_dumperMgr.args);
128 : }
129 135 : return ret;
130 : }
131 :
132 260 : STATIC TraStatus DumperSignalHandler(const TraceSignalInfo *arg)
133 : {
134 260 : LOGR("pid[%d] tid[%ld] start to handle signal.", getpid(), syscall(__NR_gettid));
135 : #if !defined (__aarch64__) && !defined (__x86_64__)
136 : return TRACE_FAILURE;
137 : #endif
138 260 : if (g_dumperMgr.stack == NULL) {
139 0 : return TRACE_FAILURE;
140 : }
141 260 : if (arg == NULL) {
142 0 : return TRACE_FAILURE;
143 : }
144 260 : const TraceSignalInfo* info = (const TraceSignalInfo *)arg;
145 260 : if ((info->ucontext == NULL) || (info->siginfo == NULL)) {
146 0 : return TRACE_FAILURE;
147 : }
148 :
149 : // if sub process receive signal, return and continue to handle signal
150 260 : if ((info->signo != SIG_ATRACE) && (g_dumperMgr.args.pid != 0) && (g_dumperMgr.args.pid != getpid())) {
151 20 : (void)ptrace(PTRACE_DETACH, g_dumperMgr.args.pid, NULL, NULL); // if sub process receive signal, detach crash pid
152 20 : LOGW("current_pid[%d], crash_pid[%d].", getpid(), g_dumperMgr.args.pid);
153 20 : return TRACE_FAILURE;
154 : }
155 :
156 : // if crash thread receive signal(B) again when handler signal(A), return and continue to handle signal(A)
157 240 : if ((g_dumperMgr.args.tid != 0) && (g_dumperMgr.args.tid == syscall(__NR_gettid))) {
158 0 : LOGW("current_tid[%ld], crash_tid[%d].", syscall(__NR_gettid), g_dumperMgr.args.tid);
159 0 : return TRACE_FAILURE;
160 : }
161 :
162 240 : (void)pthread_mutex_lock(&g_dumperMgr.mutex);
163 : // dumper process
164 240 : if (DumperSignalSetArgs(info) != TRACE_SUCCESS) {
165 80 : (void)pthread_mutex_unlock(&g_dumperMgr.mutex);
166 80 : return TRACE_FAILURE;
167 : }
168 :
169 160 : TraceSignalSetHandleFlag(true);
170 160 : if (TraceSignalCheckExit()) {
171 20 : LOGW("pid[%d] tid[%ld] exit is running, can not handle signal.", g_dumperMgr.args.pid, g_dumperMgr.args.tid);
172 20 : TraceSignalSetHandleFlag(false);
173 20 : (void)pthread_mutex_unlock(&g_dumperMgr.mutex);
174 20 : return TRACE_FAILURE;
175 : }
176 :
177 140 : TraStatus ret = DumperRunAndFallback(info);
178 135 : TraceSignalSetHandleFlag(false);
179 135 : (void)pthread_mutex_unlock(&g_dumperMgr.mutex);
180 135 : g_dumperMgr.args.tid = 0;
181 135 : return ret;
182 : }
183 :
184 : /**
185 : * @brief : get stack start addr
186 : */
187 901 : STATIC uintptr_t TraceGetStackBaseAddr(void)
188 : {
189 : pthread_attr_t attr;
190 901 : void *stackAddr = NULL;
191 901 : size_t stackSize = 0;
192 :
193 901 : (void)memset_s(&attr, sizeof(pthread_attr_t), 0, sizeof(pthread_attr_t));
194 901 : int32_t ret = pthread_getattr_np(pthread_self(), &attr);
195 901 : if (ret != 0) {
196 40 : return 0;
197 : }
198 861 : (void)pthread_attr_getstack(&attr, &stackAddr, &stackSize);
199 861 : (void)pthread_attr_destroy(&attr);
200 861 : return (uintptr_t)stackAddr + (uintptr_t)stackSize;
201 : }
202 :
203 : /**
204 : * @brief : get bin path by library path
205 : */
206 861 : STATIC TraStatus TraceGetExePath(char *path, uint32_t len)
207 : {
208 861 : char realPath[TRACE_MAX_PATH] = { 0 };
209 861 : char fullPath[TRACE_MAX_PATH] = { 0 };
210 861 : mmDlInfo dlInfo = { 0 };
211 861 : if (mmDladdr((void *)(&TraceGetExePath), &dlInfo) != EN_OK) {
212 0 : ADIAG_ERR("dladdr library failed, error: %s", mmDlerror());
213 0 : return TRACE_FAILURE;
214 : }
215 861 : if ((TraceRealPath(dlInfo.dli_fname, fullPath, TRACE_MAX_PATH) != EN_OK) && (AdiagGetErrorCode() != ENOENT)) {
216 0 : ADIAG_ERR("can not get realpath, path=%s, strerr=%s.", dlInfo.dli_fname, strerror(AdiagGetErrorCode()));
217 0 : return TRACE_FAILURE;
218 : }
219 :
220 861 : char *pos = strrchr(fullPath, '/');
221 861 : if (pos == NULL) {
222 0 : ADIAG_ERR("check path failed, path=%s.", fullPath);
223 0 : return TRACE_FAILURE;
224 : }
225 :
226 861 : errno_t err = strncpy_s(pos, TRACE_MAX_PATH - (pos - fullPath), SCD_EXE_RELATIVE_PATH, strlen(SCD_EXE_RELATIVE_PATH));
227 861 : if (err != EOK) {
228 0 : ADIAG_ERR("strncpy_s failed.");
229 0 : return TRACE_FAILURE;
230 : }
231 861 : if ((TraceRealPath(fullPath, realPath, TRACE_MAX_PATH) != EN_OK) && (AdiagGetErrorCode() != ENOENT)) {
232 0 : ADIAG_ERR("can not get realpath, path=%s, strerr=%s.", fullPath, strerror(AdiagGetErrorCode()));
233 0 : return TRACE_FAILURE;
234 : }
235 861 : err = strncpy_s(path, len, realPath, strlen(realPath));
236 861 : if (err != EOK) {
237 0 : ADIAG_ERR("strncpy_s failed, path = %s.", realPath);
238 0 : return TRACE_FAILURE;
239 : }
240 :
241 861 : ADIAG_INF("path = %s", path);
242 861 : LOGR("path = %s", path);
243 861 : return TRACE_SUCCESS;
244 : }
245 :
246 901 : TraStatus TraceDumperInit(void)
247 : {
248 901 : if (g_dumperMgr.stack != NULL) {
249 20 : ADIAG_INF("stacktrace dumper has been initialized.");
250 20 : return TRACE_SUCCESS;
251 : }
252 881 : uint8_t *stack = AdiagMalloc(SCD_CRASH_CHILD_STACK_LEN);
253 881 : if (stack == NULL) {
254 20 : ADIAG_ERR("stacktrace dumper malloc for stack failed, size=%u bytes.", SCD_CRASH_CHILD_STACK_LEN);
255 20 : return TRACE_FAILURE;
256 : }
257 861 : g_dumperMgr.stack = stack;
258 861 : g_dumperMgr.stackSize = SCD_CRASH_CHILD_STACK_LEN;
259 861 : g_dumperMgr.args.stackBaseAddr = TraceGetStackBaseAddr();
260 861 : TraStatus ret = TraceGetExePath(g_dumperMgr.args.exePath, MAX_BIN_PATH_LEN);
261 861 : ADIAG_CHK_EXPR_ACTION(ret != TRACE_SUCCESS, return ret, "get bin path failed, ret=%d.", ret);
262 861 : atomic_flag_clear(&g_dumperMgr.done);
263 :
264 861 : ret = StacktraceLogInit(STACKTRACE_LOG_TITLE_MAIN);
265 861 : ADIAG_CHK_EXPR_ACTION(ret != TRACE_SUCCESS, return ret, "stacktrace log init failed, ret=%d.", ret);
266 :
267 861 : int32_t signo[REGISTER_SIGNAL_NUM] = { SIGINT, SIGTERM,
268 : SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGSEGV, SIGXCPU, SIGXFSZ, SIGSYS, SIG_ATRACE };
269 861 : ret = TraceSignalAddFunc(signo, REGISTER_SIGNAL_NUM, DumperSignalHandler);
270 861 : ADIAG_CHK_EXPR_ACTION(ret != TRACE_SUCCESS, return ret, "stacktrace dumper add func failed, ret=%d.", ret);
271 861 : return TRACE_SUCCESS;
272 : }
273 :
274 931 : void TraceDumperExit(void)
275 : {
276 931 : StackcoreLogExit();
277 931 : if (g_dumperMgr.stack != NULL) {
278 855 : ADIAG_SAFE_FREE(g_dumperMgr.stack);
279 : }
280 931 : (void)memset_s(&g_dumperMgr.args, sizeof(ThreadArgument), 0, sizeof(ThreadArgument));
281 931 : }
|