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-08-31 10:07:06 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         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 : }
        

Generated by: LCOV version 2.0-1