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_signal.h"
12 : #include <stdatomic.h>
13 : #include "adiag_list.h"
14 : #include "adiag_print.h"
15 : #include "adiag_utils.h"
16 : #include "trace_system_api.h"
17 : #include "stacktrace_monitor.h"
18 :
19 : #define TRACE_STACK_SIGNAL_ENV "ASCEND_COREDUMP_SIGNAL"
20 : #define TRACE_STACK_SIGNAL_ENV_LENGTH 256U
21 : #define TRACE_STACK_SIGNAL_NONE "none"
22 : #define TRACE_STACK_SIGNAL_ALL "all"
23 : #define TRACE_STACK_SIGNAL_SLEEP 100000U // 100ms
24 :
25 : // Dedicated alternate signal stack. A stack-overflow SIGSEGV exhausts the
26 : // thread stack, so the handler must run on a separate stack (sigaltstack +
27 : // SA_ONSTACK) to be able to capture the crash at all.
28 : //
29 : // Size basis: the crash handler here only does a short prologue (copy siginfo/
30 : // ucontext into the global args, pipe/clone the asc_dumper sub-process, waitpid)
31 : // — the heavy work (ptrace stack unwind, file write) runs in the asc_dumper
32 : // child process, NOT on this stack. 64KB therefore comfortably exceeds
33 : // MINSIGSTKSZ plus the handler's own frame needs. A fixed compile-time constant
34 : // is used because on newer glibc SIGSTKSZ is a runtime sysconf value and cannot
35 : // size a static array.
36 : //
37 : // Limitation (single shared stack): sigaltstack is per-thread, but a newly
38 : // created thread inherits the parent's alt-stack *pointer*, so every thread in
39 : // the host process points at this one buffer. Two threads crashing
40 : // simultaneously would run their handlers on the same memory and corrupt each
41 : // other. This is mitigated, not fully eliminated, by:
42 : // - g_dumperMgr.mutex serialising the dump path so only one handler does the
43 : // clone/waitpid heavy work at a time;
44 : // - the crash ucontext/siginfo being copied into the global g_dumperMgr.args
45 : // (not kept on the alt stack) before the child reads them;
46 : // - sa_mask blocking all managed fatal signals on the *handling* thread so it
47 : // is not re-interrupted mid-handler.
48 : // A true per-thread alt stack would need to intercept host thread creation,
49 : // which a passively-loaded library cannot do; tracked as a follow-up.
50 : #define TRACE_ALT_STACK_SIZE (64U * 1024U)
51 : STATIC char g_traceAltStack[TRACE_ALT_STACK_SIZE];
52 :
53 : typedef struct {
54 : TraceSignalHandle func;
55 : const void *data;
56 : } TraceSigFuncNode;
57 : struct TraceSigAction {
58 : bool registerFlag; // has registered to system or not
59 : int32_t signo; // signal number
60 : TraceSignalHandle callbackFunc;
61 : struct sigaction sigAct; // the new action for signo
62 : struct sigaction oldSigAct; // the previous action for signo
63 : };
64 :
65 : struct TraceSigMgr {
66 : uint64_t timeStamp; // timestamp of signal handle callback
67 : atomic_bool exitFlag; // exit is running or not
68 : atomic_bool handleFlag; // signal_handle is running or not
69 : int32_t handlePid; // signal_handle is running in which process
70 : struct TraceSigAction sigAct[REGISTER_SIGNAL_NUM];
71 : };
72 :
73 : STATIC struct TraceSigMgr g_sigMgr = {
74 : .timeStamp = 0,
75 : .exitFlag = false,
76 : .handleFlag = false,
77 : .sigAct = {
78 : {.registerFlag = false, .signo = SIGINT }, // 2
79 : {.registerFlag = false, .signo = SIGTERM }, // 15
80 : {.registerFlag = false, .signo = SIGQUIT }, // 3
81 : {.registerFlag = false, .signo = SIGILL }, // 4
82 : {.registerFlag = false, .signo = SIGTRAP }, // 5
83 : {.registerFlag = false, .signo = SIGABRT }, // 6
84 : {.registerFlag = false, .signo = SIGBUS }, // 7
85 : {.registerFlag = false, .signo = SIGFPE }, // 8
86 : {.registerFlag = false, .signo = SIGSEGV }, // 11
87 : {.registerFlag = false, .signo = SIGXCPU }, // 24
88 : {.registerFlag = false, .signo = SIGXFSZ }, // 25
89 : {.registerFlag = false, .signo = SIGSYS }, // 31
90 : {.registerFlag = false, .signo = SIG_ATRACE } // 35
91 : }
92 : };
93 :
94 : /**
95 : * @brief : set timestamp
96 : * @return : NA
97 : */
98 80 : STATIC void TraceSignalSetTime(void)
99 : {
100 80 : uint64_t signalTime = GetRealTime();
101 80 : if (g_sigMgr.timeStamp == 0) {
102 22 : g_sigMgr.timeStamp = signalTime;
103 : }
104 80 : }
105 :
106 : /**
107 : * @brief : check function is enabled or not
108 : * @return : true enable; false disable
109 : */
110 906 : STATIC bool TraceSignalCheckEnv(void)
111 : {
112 906 : char envSignal[TRACE_STACK_SIGNAL_ENV_LENGTH] = { 0 };
113 906 : const char *env = NULL;
114 906 : MM_SYS_GET_ENV(MM_ENV_ASCEND_COREDUMP_SIGNAL, (env));
115 906 : TraStatus ret = TraceHandleEnvString(env, envSignal, TRACE_STACK_SIGNAL_ENV_LENGTH);
116 906 : if (ret != TRACE_SUCCESS) {
117 906 : return true;
118 : }
119 0 : if (strcmp(envSignal, TRACE_STACK_SIGNAL_NONE) == 0) {
120 0 : ADIAG_RUN_INF("get env %s = %s, close the signal capture function",
121 : TRACE_STACK_SIGNAL_ENV, TRACE_STACK_SIGNAL_NONE);
122 0 : return false;
123 : } else {
124 0 : ADIAG_WAR("env %s is invalid, use default signal capture function.", TRACE_STACK_SIGNAL_ENV);
125 : }
126 0 : return true;
127 : }
128 :
129 : /**
130 : * @brief signal handler function to register
131 : * @param [in] signo: signo, set by system
132 : * @param [in] siginfo: signo info, set by system
133 : * @param [in] ucontext: no use
134 : * @return NA
135 : */
136 80 : STATIC void TraceSignalHandler(int32_t signo, siginfo_t *siginfo, void *ucontext)
137 : {
138 80 : STACKTRACE_LOG_RUN("receive signal(%d).", signo);
139 80 : TraceSignalSetTime();
140 80 : StacktraceMonitorStartUpdate();
141 80 : TraceSignalInfo info = { signo, siginfo, ucontext, g_sigMgr.timeStamp };
142 1052 : for (uint32_t i = 0; i < REGISTER_SIGNAL_NUM; i++) {
143 993 : if ((signo != g_sigMgr.sigAct[i].signo) || (g_sigMgr.sigAct[i].registerFlag != true)) {
144 913 : continue;
145 : }
146 80 : LOGR("start to callback signal %d", signo);
147 80 : if (g_sigMgr.sigAct[i].callbackFunc != NULL) {
148 80 : g_sigMgr.sigAct[i].callbackFunc(&info);
149 : }
150 75 : if (signo == SIG_ATRACE) {
151 16 : if (ScdSignalIsBinDump(info.signo, info.siginfo)) {
152 : // StackcoreLogSave is safe here: g_stackLogMgr is initialized in
153 : // TraceDumperInit() which runs before TraceSignalInit() registers
154 : // signal handlers. StackcoreLogSave() also has NULL check protection.
155 16 : StackcoreLogSave();
156 : }
157 16 : return;
158 : }
159 : // Save crash signals here (SIG_ATRACE bin dump is saved above).
160 : // StackcoreLogSave is safe: g_stackLogMgr is initialized before signal
161 : // handlers are registered, and StackcoreLogSave() has NULL check protection.
162 59 : StackcoreLogSave();
163 : // recover the signal handler
164 59 : if (sigaction(g_sigMgr.sigAct[i].signo, &g_sigMgr.sigAct[i].oldSigAct, NULL) < 0) {
165 0 : g_sigMgr.sigAct[i].registerFlag = false;
166 0 : return;
167 : }
168 59 : (void)TraceRaise(signo);
169 : }
170 : }
171 :
172 : /**
173 : * @brief add func to signal list
174 : * @param [in] signo: signo array
175 : * @param [in] size: size of array
176 : * @param [in] func: func pointer
177 : * @return TraStatus
178 : */
179 861 : TraStatus TraceSignalAddFunc(const int32_t *signo, uint32_t size, TraceSignalHandle func)
180 : {
181 12054 : for (uint32_t i = 0 ; i < size; i++) {
182 156702 : for (uint32_t j = 0; j < REGISTER_SIGNAL_NUM; j++) {
183 145509 : if (signo[i] != g_sigMgr.sigAct[j].signo) {
184 134316 : continue;
185 : }
186 :
187 11193 : g_sigMgr.sigAct[j].callbackFunc = func;
188 : }
189 : }
190 861 : return TRACE_SUCCESS;
191 : }
192 :
193 : #ifdef ATRACE_API
194 : /**
195 : * @brief register signal callback to system
196 : * @return TraStatus
197 : */
198 897 : STATIC TraStatus TraceSignalRegister(void)
199 : {
200 12324 : for (uint32_t i = 0; i < REGISTER_SIGNAL_NUM; i++) {
201 11445 : if (sigaction(g_sigMgr.sigAct[i].signo, &g_sigMgr.sigAct[i].sigAct, &g_sigMgr.sigAct[i].oldSigAct) < 0) {
202 18 : ADIAG_ERR("register signal handler for signal %d failed, info: %s",
203 : g_sigMgr.sigAct[i].signo, strerror(AdiagGetErrorCode()));
204 18 : return TRACE_FAILURE;
205 : }
206 11427 : ADIAG_INF("register signal handler for signal %d succeed.", g_sigMgr.sigAct[i].signo);
207 11427 : g_sigMgr.sigAct[i].registerFlag = true;
208 : }
209 879 : ADIAG_INF("trace signal init succeed.");
210 879 : return TRACE_SUCCESS;
211 : }
212 :
213 : /**
214 : * @brief recover old signal callback
215 : * @return TraStatus
216 : */
217 947 : STATIC void TraceSignalUnregister(void)
218 : {
219 13258 : for (uint32_t i = 0; i < REGISTER_SIGNAL_NUM; i++) {
220 12311 : if (!g_sigMgr.sigAct[i].registerFlag) {
221 1196 : continue;
222 : }
223 11115 : if (sigaction(g_sigMgr.sigAct[i].signo, &g_sigMgr.sigAct[i].oldSigAct, NULL) < 0) {
224 234 : ADIAG_ERR("recover signal handler for signal %d failed, info: %s",
225 : g_sigMgr.sigAct[i].signo, strerror(AdiagGetErrorCode()));
226 : }
227 11115 : g_sigMgr.sigAct[i].registerFlag = false;
228 : }
229 947 : ADIAG_RUN_INF("unregister all signal handlers, can not capture signal.");
230 947 : }
231 : #else
232 9 : STATIC TraStatus TraceSignalRegister(void)
233 : {
234 9 : return TRACE_SUCCESS;
235 : }
236 :
237 9 : STATIC void TraceSignalUnregister(void)
238 : {
239 9 : return;
240 : }
241 : #endif
242 :
243 140 : bool TraceSignalCheckExit(void)
244 : {
245 140 : return atomic_load(&g_sigMgr.exitFlag);
246 : }
247 :
248 1221 : void TraceSignalSetHandleFlag(bool value)
249 : {
250 1221 : g_sigMgr.handlePid = getpid();
251 1221 : atomic_store(&g_sigMgr.handleFlag, value);
252 1221 : }
253 :
254 : /**
255 : * @brief install a dedicated alternate signal stack so the handler can
256 : * still run when the thread stack is exhausted (stack-overflow
257 : * SIGSEGV). Failure is not fatal: fall back to the normal stack.
258 : * Runs before any signal handler is registered, so strerror is
259 : * used here without reentrancy concerns.
260 : * @return NA
261 : */
262 906 : STATIC void TraceSignalSetupAltStack(void)
263 : {
264 : stack_t altStack;
265 906 : (void)memset_s(&altStack, sizeof(altStack), 0, sizeof(altStack));
266 906 : altStack.ss_sp = g_traceAltStack;
267 906 : altStack.ss_size = sizeof(g_traceAltStack);
268 906 : altStack.ss_flags = 0;
269 906 : if (sigaltstack(&altStack, NULL) != 0) {
270 0 : ADIAG_WAR("set alternate signal stack failed, info: %s, stack overflow may not be captured.",
271 : strerror(AdiagGetErrorCode()));
272 : }
273 906 : }
274 :
275 906 : TraStatus TraceSignalInit(void)
276 : {
277 906 : TraceSignalSetupAltStack();
278 12684 : for (uint32_t i = 0; i < REGISTER_SIGNAL_NUM; i++) {
279 11778 : struct TraceSigAction *sigAct = &g_sigMgr.sigAct[i];
280 11778 : (void)memset_s(&sigAct->sigAct, sizeof(struct sigaction), 0, sizeof(struct sigaction));
281 : // sa_mask is per-thread: while this handler runs, block every managed
282 : // fatal signal on the *handling* thread so a second fatal signal cannot
283 : // re-enter and corrupt the handler on the same thread. (It does not
284 : // block signals on other threads — see the g_traceAltStack limitation
285 : // note above.) sigaddset is idempotent.
286 11778 : (void)sigemptyset(&(sigAct->sigAct.sa_mask));
287 164892 : for (uint32_t j = 0; j < REGISTER_SIGNAL_NUM; j++) {
288 153114 : (void)sigaddset(&(sigAct->sigAct.sa_mask), g_sigMgr.sigAct[j].signo);
289 : }
290 11778 : sigAct->sigAct.sa_sigaction = TraceSignalHandler;
291 : // SA_ONSTACK runs the handler on the alternate stack installed above.
292 11778 : sigAct->sigAct.sa_flags = SA_SIGINFO | SA_ONSTACK;
293 : }
294 :
295 906 : if (!TraceSignalCheckEnv()) {
296 0 : return TRACE_SUCCESS;
297 : }
298 906 : StacktraceMonitorInit();
299 906 : atomic_store(&g_sigMgr.exitFlag, false);
300 906 : TraceSignalSetHandleFlag(false);
301 906 : TraStatus ret = TraceSignalRegister();
302 906 : ADIAG_CHK_EXPR_ACTION(ret != TRACE_SUCCESS, return ret, "trace register signal failed, ret=%d.", ret);
303 888 : return TRACE_SUCCESS;
304 : }
305 :
306 956 : void TraceSignalExit(void)
307 : {
308 956 : atomic_store(&g_sigMgr.exitFlag, true);
309 956 : TraceSignalUnregister();
310 956 : StacktraceMonitorExit();
311 : // handle func is running, and is in handle process
312 956 : while (atomic_load(&g_sigMgr.handleFlag) && (g_sigMgr.handlePid == getpid())) {
313 0 : (void)usleep(TRACE_STACK_SIGNAL_SLEEP);
314 : }
315 : // Disable the alternate signal stack, symmetric with TraceSignalInit's
316 : // TraceSignalSetupAltStack(). Avoids leaving a stale alt-stack setting on
317 : // repeated Init/Exit cycles (e.g. tests, dynamic load). sigaltstack is
318 : // per-thread; this clears it on the calling (init) thread. SS_DISABLE on a
319 : // thread that never installed an alt stack is a no-op, so this is safe even
320 : // if Init never ran.
321 : stack_t disableStack;
322 956 : (void)memset_s(&disableStack, sizeof(disableStack), 0, sizeof(disableStack));
323 956 : disableStack.ss_flags = SS_DISABLE;
324 956 : (void)sigaltstack(&disableStack, NULL);
325 956 : }
|