LCOV - code coverage report
Current view: top level - atrace/utrace/stacktrace - stacktrace_signal.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 90.7 % 108 98
Test Date: 2026-07-28 10:53:44 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              : /**
      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 : }
        

Generated by: LCOV version 2.0-1