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_exec.h"
12 : #include <signal.h>
13 : #include "adiag_print.h"
14 : #include "stacktrace_safe_recorder.h"
15 : #include "stacktrace_err_code.h"
16 : #include "scd_process.h"
17 :
18 : #define STACKTRACE_DUMP_EXE "asc_dumper"
19 : #define TRACE_UTIL_TEMP_FAILURE_RETRY(exp) ({ \
20 : __typeof__(exp) rc; \
21 : do { \
22 : errno = 0; \
23 : rc = (exp); \
24 : } while (rc == -1 && errno == EINTR); \
25 : rc; })
26 :
27 213 : STATIC TraStatus ScExecUnblockDumpSignals(void)
28 : {
29 213 : const int32_t dumpSignals[] = {
30 : SIGINT, SIGTERM, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGSEGV,
31 : SIGXCPU, SIGXFSZ, SIGSYS, SIG_ATRACE
32 : };
33 : sigset_t set;
34 213 : if (sigemptyset(&set) != 0) {
35 16 : STACKTRACE_LOG_ERR("empty signal mask failed, errno=%d.", errno);
36 16 : return TRACE_FAILURE;
37 : }
38 2550 : for (size_t i = 0; i < sizeof(dumpSignals) / sizeof(dumpSignals[0]); i++) {
39 2369 : if (sigaddset(&set, dumpSignals[i]) != 0) {
40 16 : STACKTRACE_LOG_ERR("add signal %d to mask failed, errno=%d.", dumpSignals[i], errno);
41 16 : return TRACE_FAILURE;
42 : }
43 : }
44 181 : if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
45 16 : STACKTRACE_LOG_ERR("unblock stacktrace signals failed, errno=%d.", errno);
46 16 : return TRACE_FAILURE;
47 : }
48 165 : return TRACE_SUCCESS;
49 : }
50 :
51 277 : STATIC TraStatus ScExecSetArgs(ScdProcessArgs *args, const ThreadArgument *info)
52 : {
53 277 : errno_t ret = memcpy_s(&args->si, sizeof(siginfo_t), &info->siginfo, sizeof(siginfo_t));
54 277 : if (ret != EOK) {
55 16 : LOGE("copy siginfo failed, err=%d.", ret);
56 16 : return TRACE_FAILURE;
57 : }
58 261 : ret = memcpy_s(&args->uc, sizeof(ucontext_t), &info->ucontext, sizeof(ucontext_t));
59 261 : if (ret != EOK) {
60 16 : LOGE("copy context failed, err=%d.", ret);
61 16 : return TRACE_FAILURE;
62 : }
63 245 : ret = strcpy_s(args->filePath, SCD_MAX_FILEPATH_LEN + 1U, info->filePath);
64 245 : if (ret != EOK) {
65 16 : LOGE("copy file path failed, err=%d.", ret);
66 16 : return TRACE_FAILURE;
67 : }
68 229 : ret = strcpy_s(args->fileName, SCD_MAX_FILENAME_LEN + 1U, info->fileName);
69 229 : if (ret != EOK) {
70 16 : LOGE("copy file name failed, err=%d.", ret);
71 16 : return TRACE_FAILURE;
72 : }
73 213 : args->pid = info->pid;
74 213 : args->crashTid = info->tid;
75 213 : args->signo = info->signo;
76 213 : args->crashTime = info->crashTime;
77 213 : args->stackBaseAddr = info->stackBaseAddr;
78 :
79 : // set mode
80 213 : uint32_t mode = (uint32_t)info->siginfo.si_value.sival_int;
81 213 : if ((info->signo == SIG_ATRACE) && (mode == STACKTRACE_DUMP_BIN_MODE)) {
82 17 : args->handleType = SCD_DUMP_THREADS_BIN;
83 17 : LOGR("core path=%s/%s.bin", args->filePath, args->fileName);
84 : } else {
85 196 : args->handleType = SCD_DUMP_THREADS_TXT;
86 196 : LOGR("core path=%s/%s.txt", args->filePath, args->fileName);
87 : }
88 :
89 213 : return TRACE_SUCCESS;
90 : }
91 :
92 293 : STATIC int32_t ScExecEntry(void *args)
93 : {
94 293 : if (args == NULL) {
95 16 : LOGE("args is null.");
96 16 : return 1;
97 : }
98 277 : const ThreadArgument *info = (const ThreadArgument *)args;
99 : ScdProcessArgs scdArgs;
100 277 : if (ScExecSetArgs(&scdArgs, info) != TRACE_SUCCESS) {
101 64 : LOGE("set args failed.");
102 64 : return SCD_ERR_CODE_SET_ARG;
103 : }
104 213 : if (ScExecUnblockDumpSignals() != TRACE_SUCCESS) {
105 48 : return SCD_ERR_CODE_SIGMASK;
106 : }
107 :
108 : //create args pipe
109 : int32_t pipeFd[2];
110 165 : errno = 0;
111 165 : if(pipe2(pipeFd, O_CLOEXEC) != 0) {
112 48 : STACKTRACE_LOG_ERR("create args pipe failed, errno=%d.", errno);
113 48 : return SCD_ERR_CODE_PIPE2;
114 : }
115 117 : int32_t writeLen = (int32_t)sizeof(ScdProcessArgs);
116 117 : if(fcntl(pipeFd[1], F_SETPIPE_SZ, writeLen) < writeLen) {
117 32 : STACKTRACE_LOG_ERR("set args pipe size failed, errno=%d", errno);
118 32 : return SCD_ERR_CODE_FCNTL;
119 : }
120 :
121 : //write args to pipe
122 85 : struct iovec iovs[1] = {
123 : {.iov_base = &scdArgs, .iov_len = sizeof(ScdProcessArgs)}
124 : };
125 85 : int32_t iovsCnt = 1;
126 85 : ssize_t ret = TRACE_UTIL_TEMP_FAILURE_RETRY(writev(pipeFd[1], iovs, iovsCnt));
127 85 : if (ret != writeLen) {
128 48 : STACKTRACE_LOG_ERR("write args to pipe failed, ret=%ld, errno=%d.", ret, errno);
129 48 : return SCD_ERR_CODE_WRITEV;
130 : }
131 37 : if (TRACE_UTIL_TEMP_FAILURE_RETRY(dup2(pipeFd[0], STDIN_FILENO)) == -1) {
132 32 : STACKTRACE_LOG_ERR("dup2 stdin failed, errno=%d.", errno);
133 32 : return SCD_ERR_CODE_DUP2;
134 : }
135 5 : (void)syscall(SYS_close, pipeFd[0]);
136 5 : (void)syscall(SYS_close, pipeFd[1]);
137 :
138 0 : if (execlp(info->exePath, STACKTRACE_DUMP_EXE, NULL) < 0) {
139 0 : STACKTRACE_LOG_ERR("execlp stacktrace dumper process failed, path=%s, errno=%d.", info->exePath, errno);
140 0 : _exit(SCD_ERR_CODE_EXECLP);
141 : }
142 0 : return SCD_ERR_CODE_SUCCESS;
143 : }
144 :
145 187 : STATIC void ScExecLogExitError(int32_t pid, int32_t exitStatus)
146 : {
147 187 : switch (exitStatus) {
148 16 : case SCD_ERR_CODE_PIPE2:
149 16 : STACKTRACE_LOG_ERR("create args pipe failed.");
150 16 : break;
151 16 : case SCD_ERR_CODE_FCNTL:
152 16 : STACKTRACE_LOG_ERR("set args pipe size failed.");
153 16 : break;
154 16 : case SCD_ERR_CODE_WRITEV:
155 16 : STACKTRACE_LOG_ERR("write args to pipe failed.");
156 16 : break;
157 16 : case SCD_ERR_CODE_DUP2:
158 16 : STACKTRACE_LOG_ERR("dup2 stdin failed.");
159 16 : break;
160 91 : case SCD_ERR_CODE_EXECLP:
161 91 : STACKTRACE_LOG_ERR("execlp stacktrace dumper process failed.");
162 91 : break;
163 16 : case SCD_ERR_CODE_SIGMASK:
164 16 : STACKTRACE_LOG_ERR("unblock stacktrace signals failed.");
165 16 : break;
166 16 : default:
167 16 : STACKTRACE_LOG_ERR("child %d exited normally with non-zero exit status(%d)", pid, exitStatus);
168 16 : break;
169 : }
170 187 : }
171 :
172 224 : TraStatus ScExecStart(void *stack, ThreadArgument *args, int32_t *pid)
173 : {
174 224 : if (stack == NULL) {
175 16 : LOGE("stack for clone is null");
176 16 : return TRACE_FAILURE;
177 : }
178 208 : int32_t err = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
179 208 : if (err != 0) {
180 32 : STACKTRACE_LOG_ERR("set dumpable failed, errno=%d", errno);
181 32 : return TRACE_FAILURE;
182 : }
183 176 : const int32_t child = clone(ScExecEntry, stack, CLONE_VFORK | CLONE_FS | CLONE_UNTRACED, args, NULL, NULL, NULL);
184 171 : if (child == -1) {
185 32 : STACKTRACE_LOG_ERR("clone failed, errno=%d", errno);
186 32 : return TRACE_FAILURE;
187 : }
188 139 : *pid = child;
189 139 : STACKTRACE_LOG_RUN("create sub process, pid=%d", child);
190 139 : err = prctl(PR_SET_PTRACER, child, 0, 0, 0);
191 139 : if (err != 0) {
192 32 : STACKTRACE_LOG_ERR("set ptracer failed, child=%d, errno=%d", child, errno);
193 : } else {
194 107 : LOGI("set ptracer successfully, child=%d", child);
195 : }
196 139 : if ((err == 0) || (args->signo == SIG_ATRACE)) {
197 123 : TraceStackRecorderInfo recordInfo = {args->crashTime, args->signo, args->pid, args->tid};
198 123 : TraStatus ret = TraceSafeMkdirPath(&recordInfo);
199 123 : if (ret != TRACE_SUCCESS) {
200 32 : STACKTRACE_LOG_ERR("mkdir path failed, ret=%d.", ret);
201 : }
202 : }
203 139 : return TRACE_SUCCESS;
204 : }
205 :
206 251 : TraStatus ScExecEnd(int32_t pid)
207 : {
208 251 : int32_t status = 0;
209 251 : int32_t ret = TRACE_UTIL_TEMP_FAILURE_RETRY(waitpid(pid, &status, __WALL));
210 251 : if (ret == -1) {
211 16 : STACKTRACE_LOG_ERR("waitpid failed, child=%d, errno=%d", pid, errno);
212 16 : return TRACE_FAILURE;
213 : }
214 :
215 : //check child process state
216 235 : if (WIFEXITED(status)) {
217 203 : int32_t exitStatus = WEXITSTATUS(status);
218 203 : STACKTRACE_LOG_RUN("get sub process result(%d).", exitStatus);
219 203 : if (exitStatus == 0) {
220 16 : LOGR("child %d exited normally with zero", pid);
221 16 : return TRACE_SUCCESS;
222 : } else {
223 187 : ScExecLogExitError(pid, exitStatus);
224 : }
225 32 : } else if (WIFSIGNALED(status)) {
226 16 : int32_t signalNum = WTERMSIG(status);
227 16 : STACKTRACE_LOG_ERR("sub process exited by signal, signal(%d), child=%d.", signalNum, pid);
228 : } else {
229 16 : STACKTRACE_LOG_ERR("child %d did not exit normally", pid);
230 : }
231 :
232 219 : return TRACE_FAILURE;
233 : }
|