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