1 /*
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "process_dumper.h"
17
18 #include <cerrno>
19 #include <chrono>
20 #include <cinttypes>
21 #include <csignal>
22 #include <cstdio>
23 #include <cstdlib>
24 #include <cstring>
25 #include <fcntl.h>
26 #include <iostream>
27 #include <memory>
28 #include <pthread.h>
29 #include <securec.h>
30 #include <string>
31 #include <syscall.h>
32 #include <ucontext.h>
33 #include <unistd.h>
34
35 #include "cppcrash_reporter.h"
36 #include "dfx_config.h"
37 #include "dfx_define.h"
38 #include "dfx_dump_request.h"
39 #include "dfx_dump_res.h"
40 #include "dfx_logger.h"
41 #include "dfx_process.h"
42 #include "dfx_regs.h"
43 #include "dfx_ring_buffer_wrapper.h"
44 #include "dfx_stack_info_formatter.h"
45 #include "dfx_thread.h"
46 #include "dfx_util.h"
47 #include "faultloggerd_client.h"
48 #include "procinfo.h"
49
50 #if defined(__x86_64__)
51 #include "dfx_unwind_remote_emulator.h"
52 #include "printer_emulator.h"
53 #else
54 #include "dfx_unwind_remote.h"
55 #include "printer.h"
56 #include "unwinder_config.h"
57 #endif
58
59 namespace OHOS {
60 namespace HiviewDFX {
61 namespace {
WriteData(int fd,const std::string & data,size_t blockSize)62 void WriteData(int fd, const std::string& data, size_t blockSize)
63 {
64 size_t dataSize = data.length();
65 size_t index = 0;
66 while (index < dataSize) {
67 size_t writeLength = (index + blockSize) <= dataSize ? blockSize : (dataSize - index);
68 ssize_t nwrite = OHOS_TEMP_FAILURE_RETRY(write(fd, data.substr(index, writeLength).c_str(), writeLength));
69 if (nwrite != static_cast<ssize_t>(writeLength)) {
70 DFXLOG_INFO("%s :: nwrite: %zd, writeLength: %zu", __func__, nwrite, writeLength);
71 }
72 index += writeLength;
73 }
74 DFXLOG_INFO("%s :: needWriteDataSize: %zu, writeDataSize: %zu", __func__, dataSize, index);
75 }
76 }
77
GetInstance()78 ProcessDumper &ProcessDumper::GetInstance()
79 {
80 static ProcessDumper ins;
81 return ins;
82 }
83
Dump()84 void ProcessDumper::Dump()
85 {
86 std::shared_ptr<ProcessDumpRequest> request = std::make_shared<ProcessDumpRequest>();
87 resDump_ = DumpProcess(request);
88 if (process_ == nullptr) {
89 DFXLOG_ERROR("Dump process failed, please check permission and whether pid is valid.");
90 } else {
91 if (isCrash_) {
92 if (process_->vmThread_ != nullptr) {
93 process_->vmThread_->Detach();
94 }
95 process_->Detach();
96 }
97 if (process_->keyThread_ != nullptr) {
98 process_->keyThread_->Detach();
99 }
100 }
101
102 std::string jsonInfo;
103 if (isJsonDump_ || isCrash_) {
104 DfxStackInfoFormatter formatter(process_, request);
105 formatter.GetStackInfo(isJsonDump_, jsonInfo);
106 DFXLOG_INFO("Finish GetStackInfo len %" PRIuPTR "", jsonInfo.length());
107 if (isJsonDump_) {
108 WriteData(jsonFd_, jsonInfo, MAX_PIPE_SIZE);
109 }
110 }
111
112 WriteDumpRes(resDump_);
113 DfxRingBufferWrapper::GetInstance().StopThread();
114 DFXLOG_INFO("Finish dump stacktrace for %s(%d:%d).", request->processName, request->pid, request->tid);
115 CloseDebugLog();
116 // check dump result
117 if (reporter_ != nullptr) {
118 reporter_->SetCppCrashInfo(jsonInfo);
119 reporter_->ReportToHiview();
120 reporter_->ReportToAbilityManagerService();
121 }
122 }
123
DumpProcess(std::shared_ptr<ProcessDumpRequest> request)124 int ProcessDumper::DumpProcess(std::shared_ptr<ProcessDumpRequest> request)
125 {
126 int dumpRes = DumpErrorCode::DUMP_ESUCCESS;
127 do {
128 ssize_t readCount = read(STDIN_FILENO, request.get(), sizeof(ProcessDumpRequest));
129 if (readCount != static_cast<long>(sizeof(ProcessDumpRequest))) {
130 DFXLOG_ERROR("Fail to read DumpRequest(%d).", errno);
131 dumpRes = DumpErrorCode::DUMP_EREADREQUEST;
132 break;
133 }
134
135 isCrash_ = request->siginfo.si_signo != SIGDUMP;
136 bool isLeakDump = request->siginfo.si_signo == SIGLEAK_STACK;
137 // We need check pid is same with getppid().
138 // As in signal handler, current process is a child process, and target pid is our parent process.
139 // If pid namespace is enabled, both ppid and pid are equal one.
140 // In this case, we have to parse /proc/self/status
141 if (((!isCrash_) && (syscall(SYS_getppid) != request->nsPid)) ||
142 ((isCrash_ || isLeakDump) && (syscall(SYS_getppid) != request->vmNsPid))) {
143 DFXLOG_ERROR("Target process(%s:%d) is not parent pid(%ld), exit processdump for signal(%d).",
144 request->processName, request->nsPid, syscall(SYS_getppid), request->siginfo.si_signo);
145 dumpRes = DumpErrorCode::DUMP_EGETPPID;
146 break;
147 }
148 DFXLOG_INFO("Processdump SigVal(%d), TargetPid(%d:%d), TargetTid(%d), threadname(%s).",
149 request->siginfo.si_value.sival_int, request->pid, request->nsPid, request->tid, request->threadName);
150
151 if (InitProcessInfo(request) < 0) {
152 DFXLOG_ERROR("Failed to init crash process info.");
153 dumpRes = DumpErrorCode::DUMP_EATTACH;
154 break;
155 }
156
157 if (InitPrintThread(request) < 0) {
158 DFXLOG_ERROR("Failed to init print thread.");
159 dumpRes = DumpErrorCode::DUMP_EGETFD;
160 }
161
162 if (isCrash_ && !isLeakDump) {
163 reporter_ = std::make_shared<CppCrashReporter>(request->timeStamp, process_);
164 }
165
166 #if defined(__x86_64__)
167 if (!DfxUnwindRemote::GetInstance().UnwindProcess(request, process_)) {
168 Printer::PrintDumpHeader(request, process_);
169 #else
170 if (!DfxUnwindRemote::GetInstance().UnwindProcess(request, process_, unwinder_)) {
171 #endif
172 DFXLOG_ERROR("Failed to unwind process.");
173 dumpRes = DumpErrorCode::DUMP_ESTOPUNWIND;
174 }
175
176 if ((!isCrash_ && (syscall(SYS_getppid) != request->nsPid)) ||
177 (isCrash_ && (syscall(SYS_getppid) != request->vmNsPid))) {
178 DfxRingBufferWrapper::GetInstance().AppendMsg(
179 "Target process has been killed, the crash log may not be fully generated.");
180 dumpRes = DumpErrorCode::DUMP_EGETPPID;
181 break;
182 }
183 } while (false);
184 return dumpRes;
185 }
186
187 int ProcessDumper::InitProcessInfo(std::shared_ptr<ProcessDumpRequest> request)
188 {
189 if (request->pid <= 0) {
190 return -1;
191 }
192 process_ = DfxProcess::Create(request->pid, request->nsPid);
193 if (process_ == nullptr) {
194 return -1;
195 }
196 if (process_->processInfo_.processName.empty()) {
197 process_->processInfo_.processName = std::string(request->processName);
198 }
199 process_->processInfo_.uid = request->uid;
200 process_->recycleTid_ = request->recycleTid;
201 process_->SetFatalMessage(request->lastFatalMessage);
202
203 if (isCrash_ && request->vmPid != 0) {
204 if (getppid() != request->vmNsPid) {
205 DFXLOG_ERROR("VM process(%d) should be parent pid.", request->vmNsPid);
206 return -1;
207 }
208 process_->vmThread_ = DfxThread::Create(request->vmPid, request->vmPid, request->vmNsPid);
209 if ((process_->vmThread_ == nullptr) || (!process_->vmThread_->Attach())) {
210 DFXLOG_ERROR("Failed to attach vm thread(%d).", request->vmNsPid);
211 return -1;
212 }
213
214 #if defined(__x86_64__)
215 process_->vmThread_->SetThreadRegs(DfxRegs::CreateFromContext(request->context));
216 #else
217 process_->vmThread_->SetThreadRegs(DfxRegs::CreateFromUcontext(request->context));
218 #endif
219 process_->vmThread_->threadInfo_.threadName = std::string(request->threadName);
220 }
221
222 pid_t dumpTid = request->siginfo.si_value.sival_int;
223 pid_t nsTid = request->tid;
224 pid_t tid = process_->ChangeTid(nsTid, true);
225 if (!isCrash_) {
226 if (dumpTid != 0 && dumpTid != tid && (IsThreadInPid(process_->processInfo_.pid, dumpTid))) {
227 tid = dumpTid;
228 nsTid = process_->ChangeTid(tid, false);
229 }
230 }
231 process_->keyThread_ = DfxThread::Create(process_->processInfo_.pid, tid, nsTid);
232 if ((process_->keyThread_ == nullptr) || (!process_->keyThread_->Attach())) {
233 DFXLOG_ERROR("Failed to attach key thread(%d).", nsTid);
234 if (!isCrash_) {
235 return -1;
236 }
237 }
238 #if !defined(__x86_64__)
239 if (!isCrash_) {
240 process_->keyThread_->SetThreadRegs(DfxRegs::CreateFromUcontext(request->context));
241 }
242 #endif
243 if ((process_->keyThread_ != nullptr) && process_->keyThread_->threadInfo_.threadName.empty()) {
244 process_->keyThread_->threadInfo_.threadName = std::string(request->threadName);
245 }
246
247 if (isCrash_) {
248 process_->InitOtherThreads();
249 process_->Attach();
250 } else {
251 if (dumpTid == 0) {
252 process_->InitOtherThreads();
253 }
254 }
255 #if !defined(__x86_64__)
256 if (isCrash_) {
257 unwinder_ = std::make_shared<Unwinder>(process_->vmThread_->threadInfo_.pid);
258 } else {
259 unwinder_ = std::make_shared<Unwinder>(process_->processInfo_.pid);
260 }
261 #if defined(PROCESSDUMP_MINIDEBUGINFO)
262 UnwinderConfig::SetEnableMiniDebugInfo(true);
263 UnwinderConfig::SetEnableLoadSymbolLazily(true);
264 #endif
265 #endif
266 return 0;
267 }
268
269 int ProcessDumper::GetLogTypeBySignal(int sig)
270 {
271 switch (sig) {
272 case SIGLEAK_STACK:
273 return FaultLoggerType::LEAK_STACKTRACE;
274 case SIGDUMP:
275 return FaultLoggerType::CPP_STACKTRACE;
276 default:
277 return FaultLoggerType::CPP_CRASH;
278 }
279 }
280
281 int ProcessDumper::InitPrintThread(std::shared_ptr<ProcessDumpRequest> request)
282 {
283 int fd = -1;
284 struct FaultLoggerdRequest faultloggerdRequest;
285 (void)memset_s(&faultloggerdRequest, sizeof(faultloggerdRequest), 0, sizeof(struct FaultLoggerdRequest));
286 faultloggerdRequest.type = ProcessDumper::GetLogTypeBySignal(request->siginfo.si_signo);
287 faultloggerdRequest.pid = request->pid;
288 faultloggerdRequest.tid = request->tid;
289 faultloggerdRequest.uid = request->uid;
290 faultloggerdRequest.time = request->timeStamp;
291 isJsonDump_ = false;
292 jsonFd_ = -1;
293 if (isCrash_ || faultloggerdRequest.type == FaultLoggerType::LEAK_STACKTRACE) {
294 if (DfxConfig::GetConfig().logPersist) {
295 InitDebugLog((int)faultloggerdRequest.type, request->pid, request->tid, request->uid);
296 }
297 fd = RequestFileDescriptorEx(&faultloggerdRequest);
298 DfxRingBufferWrapper::GetInstance().SetWriteFunc(ProcessDumper::WriteDumpBuf);
299 } else {
300 fd = RequestPipeFd(request->pid, FaultLoggerPipeType::PIPE_FD_WRITE_BUF);
301 DFXLOG_DEBUG("write buf fd: %d", fd);
302 if (fd < 0) {
303 // If fd returns -1, we try to obtain the fd that needs to return JSON style
304 jsonFd_ = RequestPipeFd(request->pid, FaultLoggerPipeType::PIPE_FD_JSON_WRITE_BUF);
305 resFd_ = RequestPipeFd(request->pid, FaultLoggerPipeType::PIPE_FD_JSON_WRITE_RES);
306 DFXLOG_DEBUG("write json fd: %d, res fd: %d", jsonFd_, resFd_);
307 } else {
308 resFd_ = RequestPipeFd(request->pid, FaultLoggerPipeType::PIPE_FD_WRITE_RES);
309 DFXLOG_DEBUG("write res fd: %d", resFd_);
310 }
311 }
312 if (jsonFd_ > 0) {
313 isJsonDump_ = true;
314 }
315 if ((fd < 0) && (jsonFd_ < 0)) {
316 DFXLOG_WARN("Failed to request fd from faultloggerd.");
317 }
318
319 if (!isJsonDump_) {
320 DfxRingBufferWrapper::GetInstance().SetWriteBufFd(fd);
321 }
322 DfxRingBufferWrapper::GetInstance().StartThread();
323 if (isJsonDump_) {
324 return jsonFd_;
325 } else {
326 return fd;
327 }
328 }
329
330 int ProcessDumper::WriteDumpBuf(int fd, const char* buf, const int len)
331 {
332 if (buf == nullptr) {
333 return -1;
334 }
335 return WriteLog(fd, "%s", buf);
336 }
337
338 void ProcessDumper::WriteDumpRes(int32_t res)
339 {
340 DFXLOG_DEBUG("%s :: res: %d", __func__, res);
341 if (resFd_ > 0) {
342 write(resFd_, &res, sizeof(res));
343 close(resFd_);
344 resFd_ = -1;
345 } else {
346 if (res != DUMP_ESUCCESS) {
347 DfxRingBufferWrapper::GetInstance().AppendMsg("Result:\n");
348 DfxRingBufferWrapper::GetInstance().AppendMsg(DfxDumpRes::ToString(res) + "\n");
349 }
350 }
351 }
352
353 bool ProcessDumper::IsCrash() const
354 {
355 return isCrash_;
356 }
357 } // namespace HiviewDFX
358 } // namespace OHOS
359