LCOV - code coverage report
Current view: top level - atrace/utrace/stacktrace - stacktrace_dumper.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 87.8 % 147 129
Test Date: 2026-07-28 10:53:44 Functions: 100.0 % 9 9

            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 : }
        

Generated by: LCOV version 2.0-1