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