LCOV - code coverage report
Current view: top level - atrace/utrace/stacktrace - stacktrace_signal.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 90.3 % 103 93
Test Date: 2026-08-31 10:07:06 Functions: 100.0 % 11 11

            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              :  * @brief       : set timestamp
      95              :  * @return      : NA
      96              :  */
      97           88 : STATIC void TraceSignalSetTime(void)
      98              : {
      99           88 :     uint64_t signalTime = GetRealTime();
     100           88 :     if (g_sigMgr.timeStamp == 0) {
     101           24 :         g_sigMgr.timeStamp = signalTime;
     102              :     }
     103           88 : }
     104              : 
     105              : /**
     106              :  * @brief       : check function is enabled or not
     107              :  * @return      : true  enable; false  disable
     108              :  */
     109         1013 : STATIC bool TraceSignalCheckEnv(void)
     110              : {
     111         1013 :     char envSignal[TRACE_STACK_SIGNAL_ENV_LENGTH] = {0};
     112         1013 :     const char* env = NULL;
     113         1013 :     MM_SYS_GET_ENV(MM_ENV_ASCEND_COREDUMP_SIGNAL, (env));
     114         1013 :     TraStatus ret = TraceHandleEnvString(env, envSignal, TRACE_STACK_SIGNAL_ENV_LENGTH);
     115         1013 :     if (ret != TRACE_SUCCESS) {
     116         1013 :         return true;
     117              :     }
     118            0 :     if (strcmp(envSignal, TRACE_STACK_SIGNAL_NONE) == 0) {
     119            0 :         ADIAG_RUN_INF(
     120              :             "get env %s = %s, close the signal capture function", TRACE_STACK_SIGNAL_ENV, TRACE_STACK_SIGNAL_NONE);
     121            0 :         return false;
     122              :     } else {
     123            0 :         ADIAG_WAR("env %s is invalid, use default signal capture function.", TRACE_STACK_SIGNAL_ENV);
     124              :     }
     125            0 :     return true;
     126              : }
     127              : 
     128              : /**
     129              :  * @brief       signal handler function to register
     130              :  * @param [in]  signo:      signo, set by system
     131              :  * @param [in]  siginfo:    signo info, set by system
     132              :  * @param [in]  ucontext:      no use
     133              :  * @return      NA
     134              :  */
     135           88 : STATIC void TraceSignalHandler(int32_t signo, siginfo_t* siginfo, void* ucontext)
     136              : {
     137           88 :     STACKTRACE_LOG_RUN("receive signal(%d).", signo);
     138           88 :     TraceSignalSetTime();
     139           88 :     StacktraceMonitorStartUpdate();
     140           88 :     TraceSignalInfo info = {signo, siginfo, ucontext, g_sigMgr.timeStamp};
     141         1162 :     for (uint32_t i = 0; i < REGISTER_SIGNAL_NUM; i++) {
     142         1097 :         if ((signo != g_sigMgr.sigAct[i].signo) || (g_sigMgr.sigAct[i].registerFlag != true)) {
     143         1009 :             continue;
     144              :         }
     145           88 :         LOGR("start to callback signal %d", signo);
     146           88 :         if (g_sigMgr.sigAct[i].callbackFunc != NULL) {
     147           88 :             g_sigMgr.sigAct[i].callbackFunc(&info);
     148              :         }
     149           83 :         if (signo == SIG_ATRACE) {
     150           18 :             if (ScdSignalIsBinDump(info.signo, info.siginfo)) {
     151              :                 // StackcoreLogSave is safe here: g_stackLogMgr is initialized in
     152              :                 // TraceDumperInit() which runs before TraceSignalInit() registers
     153              :                 // signal handlers. StackcoreLogSave() also has NULL check protection.
     154           18 :                 StackcoreLogSave();
     155              :             }
     156           18 :             return;
     157              :         }
     158              :         // Save crash signals here (SIG_ATRACE bin dump is saved above).
     159              :         // StackcoreLogSave is safe: g_stackLogMgr is initialized before signal
     160              :         // handlers are registered, and StackcoreLogSave() has NULL check protection.
     161           65 :         StackcoreLogSave();
     162              :         // recover the signal handler
     163           65 :         if (sigaction(g_sigMgr.sigAct[i].signo, &g_sigMgr.sigAct[i].oldSigAct, NULL) < 0) {
     164            0 :             g_sigMgr.sigAct[i].registerFlag = false;
     165            0 :             return;
     166              :         }
     167           65 :         (void)TraceRaise(signo);
     168              :     }
     169              : }
     170              : 
     171              : /**
     172              :  * @brief       add func to signal list
     173              :  * @param [in]  signo:      signo array
     174              :  * @param [in]  size:       size of array
     175              :  * @param [in]  func:       func pointer
     176              :  * @return      TraStatus
     177              :  */
     178          973 : TraStatus TraceSignalAddFunc(const int32_t* signo, uint32_t size, TraceSignalHandle func)
     179              : {
     180        13622 :     for (uint32_t i = 0; i < size; i++) {
     181       177086 :         for (uint32_t j = 0; j < REGISTER_SIGNAL_NUM; j++) {
     182       164437 :             if (signo[i] != g_sigMgr.sigAct[j].signo) {
     183       151788 :                 continue;
     184              :             }
     185              : 
     186        12649 :             g_sigMgr.sigAct[j].callbackFunc = func;
     187              :         }
     188              :     }
     189          973 :     return TRACE_SUCCESS;
     190              : }
     191              : 
     192              : /**
     193              :  * @brief       register signal callback to system
     194              :  * @return      TraStatus
     195              :  */
     196         1013 : STATIC TraStatus TraceSignalRegister(void)
     197              : {
     198        13922 :     for (uint32_t i = 0; i < REGISTER_SIGNAL_NUM; i++) {
     199        12929 :         if (sigaction(g_sigMgr.sigAct[i].signo, &g_sigMgr.sigAct[i].sigAct, &g_sigMgr.sigAct[i].oldSigAct) < 0) {
     200           20 :             ADIAG_ERR(
     201              :                 "register signal handler for signal %d failed, info: %s", g_sigMgr.sigAct[i].signo,
     202              :                 strerror(AdiagGetErrorCode()));
     203           20 :             return TRACE_FAILURE;
     204              :         }
     205        12909 :         ADIAG_INF("register signal handler for signal %d succeed.", g_sigMgr.sigAct[i].signo);
     206        12909 :         g_sigMgr.sigAct[i].registerFlag = true;
     207              :     }
     208          993 :     ADIAG_INF("trace signal init succeed.");
     209          993 :     return TRACE_SUCCESS;
     210              : }
     211              : 
     212              : /**
     213              :  * @brief       recover old signal callback
     214              :  * @return      TraStatus
     215              :  */
     216         1069 : STATIC void TraceSignalUnregister(void)
     217              : {
     218        14966 :     for (uint32_t i = 0; i < REGISTER_SIGNAL_NUM; i++) {
     219        13897 :         if (!g_sigMgr.sigAct[i].registerFlag) {
     220         1326 :             continue;
     221              :         }
     222        12571 :         if (sigaction(g_sigMgr.sigAct[i].signo, &g_sigMgr.sigAct[i].oldSigAct, NULL) < 0) {
     223          260 :             ADIAG_ERR(
     224              :                 "recover signal handler for signal %d failed, info: %s", g_sigMgr.sigAct[i].signo,
     225              :                 strerror(AdiagGetErrorCode()));
     226              :         }
     227        12571 :         g_sigMgr.sigAct[i].registerFlag = false;
     228              :     }
     229         1069 :     ADIAG_RUN_INF("unregister all signal handlers, can not capture signal.");
     230         1069 : }
     231              : 
     232          154 : bool TraceSignalCheckExit(void) { return atomic_load(&g_sigMgr.exitFlag); }
     233              : 
     234         1360 : void TraceSignalSetHandleFlag(bool value)
     235              : {
     236         1360 :     g_sigMgr.handlePid = getpid();
     237         1360 :     atomic_store(&g_sigMgr.handleFlag, value);
     238         1360 : }
     239              : 
     240              : /**
     241              :  * @brief       install a dedicated alternate signal stack so the handler can
     242              :  *              still run when the thread stack is exhausted (stack-overflow
     243              :  *              SIGSEGV). Failure is not fatal: fall back to the normal stack.
     244              :  *              Runs before any signal handler is registered, so strerror is
     245              :  *              used here without reentrancy concerns.
     246              :  * @return      NA
     247              :  */
     248         1013 : STATIC void TraceSignalSetupAltStack(void)
     249              : {
     250              :     stack_t altStack;
     251         1013 :     (void)memset_s(&altStack, sizeof(altStack), 0, sizeof(altStack));
     252         1013 :     altStack.ss_sp = g_traceAltStack;
     253         1013 :     altStack.ss_size = sizeof(g_traceAltStack);
     254         1013 :     altStack.ss_flags = 0;
     255         1013 :     if (sigaltstack(&altStack, NULL) != 0) {
     256            0 :         ADIAG_WAR(
     257              :             "set alternate signal stack failed, info: %s, stack overflow may not be captured.",
     258              :             strerror(AdiagGetErrorCode()));
     259              :     }
     260         1013 : }
     261              : 
     262         1013 : TraStatus TraceSignalInit(void)
     263              : {
     264         1013 :     TraceSignalSetupAltStack();
     265        14182 :     for (uint32_t i = 0; i < REGISTER_SIGNAL_NUM; i++) {
     266        13169 :         struct TraceSigAction* sigAct = &g_sigMgr.sigAct[i];
     267        13169 :         (void)memset_s(&sigAct->sigAct, sizeof(struct sigaction), 0, sizeof(struct sigaction));
     268              :         // sa_mask is per-thread: while this handler runs, block every managed
     269              :         // fatal signal on the *handling* thread so a second fatal signal cannot
     270              :         // re-enter and corrupt the handler on the same thread. (It does not
     271              :         // block signals on other threads — see the g_traceAltStack limitation
     272              :         // note above.) sigaddset is idempotent.
     273        13169 :         (void)sigemptyset(&(sigAct->sigAct.sa_mask));
     274       184366 :         for (uint32_t j = 0; j < REGISTER_SIGNAL_NUM; j++) {
     275       171197 :             (void)sigaddset(&(sigAct->sigAct.sa_mask), g_sigMgr.sigAct[j].signo);
     276              :         }
     277        13169 :         sigAct->sigAct.sa_sigaction = TraceSignalHandler;
     278              :         // SA_ONSTACK runs the handler on the alternate stack installed above.
     279        13169 :         sigAct->sigAct.sa_flags = SA_SIGINFO | SA_ONSTACK;
     280              :     }
     281              : 
     282         1013 :     if (!TraceSignalCheckEnv()) {
     283            0 :         return TRACE_SUCCESS;
     284              :     }
     285         1013 :     StacktraceMonitorInit();
     286         1013 :     atomic_store(&g_sigMgr.exitFlag, false);
     287         1013 :     TraceSignalSetHandleFlag(false);
     288         1013 :     TraStatus ret = TraceSignalRegister();
     289         1013 :     ADIAG_CHK_EXPR_ACTION(ret != TRACE_SUCCESS, return ret, "trace register signal failed, ret=%d.", ret);
     290          993 :     return TRACE_SUCCESS;
     291              : }
     292              : 
     293         1069 : void TraceSignalExit(void)
     294              : {
     295         1069 :     atomic_store(&g_sigMgr.exitFlag, true);
     296         1069 :     TraceSignalUnregister();
     297         1069 :     StacktraceMonitorExit();
     298              :     // handle func is running, and is in handle process
     299         1069 :     while (atomic_load(&g_sigMgr.handleFlag) && (g_sigMgr.handlePid == getpid())) {
     300            0 :         (void)usleep(TRACE_STACK_SIGNAL_SLEEP);
     301              :     }
     302              :     // Disable the alternate signal stack, symmetric with TraceSignalInit's
     303              :     // TraceSignalSetupAltStack(). Avoids leaving a stale alt-stack setting on
     304              :     // repeated Init/Exit cycles (e.g. tests, dynamic load). sigaltstack is
     305              :     // per-thread; this clears it on the calling (init) thread. SS_DISABLE on a
     306              :     // thread that never installed an alt stack is a no-op, so this is safe even
     307              :     // if Init never ran.
     308              :     stack_t disableStack;
     309         1069 :     (void)memset_s(&disableStack, sizeof(disableStack), 0, sizeof(disableStack));
     310         1069 :     disableStack.ss_flags = SS_DISABLE;
     311         1069 :     (void)sigaltstack(&disableStack, NULL);
     312         1069 : }
        

Generated by: LCOV version 2.0-1