1 /*
2 * Copyright (c) 2021-2022 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 /* This files contains process dump main module. */
17
18 #include "process_dumper.h"
19
20 #include <cerrno>
21 #include <cinttypes>
22 #include <csignal>
23 #include <cstdio>
24 #include <cstdlib>
25 #include <cstring>
26 #include <iostream>
27 #include <memory>
28 #include <string>
29
30 #include <fcntl.h>
31 #include <pthread.h>
32 #include <syscall.h>
33 #include <ucontext.h>
34 #include <unistd.h>
35
36 #include <faultloggerd_client.h>
37 #include <securec.h>
38
39 #include "cppcrash_reporter.h"
40 #include "dfx_config.h"
41 #include "dfx_define.h"
42 #include "dfx_dump_res.h"
43 #include "dfx_logger.h"
44 #include "dfx_process.h"
45 #include "dfx_ring_buffer_wrapper.h"
46 #include "dfx_signal.h"
47 #include "dfx_thread.h"
48 #include "dfx_unwind_remote.h"
49 #include "dfx_util.h"
50
51 namespace OHOS {
52 namespace HiviewDFX {
GetInstance()53 ProcessDumper &ProcessDumper::GetInstance()
54 {
55 static ProcessDumper ins;
56 return ins;
57 }
58
PrintDumpProcessWithSignalContextHeader(std::shared_ptr<ProcessDumpRequest> request)59 void ProcessDumper::PrintDumpProcessWithSignalContextHeader(std::shared_ptr<ProcessDumpRequest> request)
60 {
61 auto info = request->GetSiginfo();
62 auto msg = request->GetLastFatalMessage();
63 if (info.si_signo != SIGDUMP) {
64 DfxRingBufferWrapper::GetInstance().AppendMsg("Timestamp:" + GetCurrentTimeStr(request->GetTimeStamp()));
65 } else {
66 DfxRingBufferWrapper::GetInstance().AppendMsg("Timestamp:" + GetCurrentTimeStr());
67 }
68 DfxRingBufferWrapper::GetInstance().AppendBuf("Pid:%d\n", targetProcess_->GetPid());
69 DfxRingBufferWrapper::GetInstance().AppendBuf("Uid:%d\n", targetProcess_->GetUid());
70 DfxRingBufferWrapper::GetInstance().AppendBuf("Process name:%s\n", targetProcess_->GetProcessName().c_str());
71
72 if (info.si_signo != SIGDUMP) {
73 DfxRingBufferWrapper::GetInstance().AppendBuf("Reason:");
74 DfxRingBufferWrapper::GetInstance().AppendMsg(PrintSignal(info));
75 if (info.si_signo == SIGABRT && !msg.empty()) {
76 DfxRingBufferWrapper::GetInstance().AppendBuf("LastFatalMessage:%s\n", msg.c_str());
77 }
78
79 auto traceId = request->GetTraceInfo();
80 if (traceId.chainId != 0) {
81 DfxRingBufferWrapper::GetInstance().AppendBuf("TraceId:%llx\n",
82 static_cast<unsigned long long>(traceId.chainId));
83 }
84
85 if (targetProcess_->GetThreads().size() != 0) {
86 DfxRingBufferWrapper::GetInstance().AppendBuf("Fault thread Info:\n");
87 }
88 }
89 }
90
InitPrintThread(bool fromSignalHandler,std::shared_ptr<ProcessDumpRequest> request)91 int ProcessDumper::InitPrintThread(bool fromSignalHandler, std::shared_ptr<ProcessDumpRequest> request)
92 {
93 int fd = -1;
94 if (!fromSignalHandler) {
95 fd = STDOUT_FILENO;
96 DfxRingBufferWrapper::GetInstance().SetWriteFunc(ProcessDumper::WriteDumpBuf);
97 } else {
98 int32_t pid = request->GetPid();
99 int32_t signo = request->GetSiginfo().si_signo;
100 bool isCrash = (signo != SIGDUMP);
101 FaultLoggerType type = isCrash ? FaultLoggerType::CPP_CRASH : FaultLoggerType::CPP_STACKTRACE;
102
103 struct FaultLoggerdRequest faultloggerdRequest;
104 if (memset_s(&faultloggerdRequest, sizeof(faultloggerdRequest), 0, sizeof(struct FaultLoggerdRequest)) != 0) {
105 DfxLogError("memset_s error.");
106 return fd;
107 }
108
109 if (isCrash) {
110 if (DfxConfig::GetInstance().GetLogPersist()) {
111 InitDebugLog((int)type, targetPid_, request->GetTid(), request->GetUid());
112 }
113
114 faultloggerdRequest.type = (int32_t)type;
115 faultloggerdRequest.pid = request->GetPid();
116 faultloggerdRequest.tid = request->GetTid();
117 faultloggerdRequest.uid = request->GetUid();
118 faultloggerdRequest.time = request->GetTimeStamp();
119 if (strncpy_s(faultloggerdRequest.module, sizeof(faultloggerdRequest.module),
120 targetProcess_->GetProcessName().c_str(), sizeof(faultloggerdRequest.module) - 1) != 0) {
121 DfxLogWarn("Failed to set process name.");
122 return fd;
123 }
124 fd = RequestFileDescriptorEx(&faultloggerdRequest);
125
126 DfxRingBufferWrapper::GetInstance().SetWriteFunc(ProcessDumper::WriteDumpBuf);
127 reporter_ = std::make_shared<CppCrashReporter>(request->GetTimeStamp(), request->GetSiginfo(),
128 targetProcess_);
129 } else {
130 fd = RequestPipeFd(pid, FaultLoggerPipeType::PIPE_FD_WRITE_BUF);
131 DfxLogDebug("write buf fd: %d", fd);
132
133 resFd_ = RequestPipeFd(pid, FaultLoggerPipeType::PIPE_FD_WRITE_RES);
134 DfxLogDebug("write res fd: %d", resFd_);
135 }
136
137 if (fd < 0) {
138 DfxLogWarn("Failed to request fd from faultloggerd.");
139 }
140 }
141
142 DfxRingBufferWrapper::GetInstance().SetWriteBufFd(fd);
143 DfxRingBufferWrapper::GetInstance().StartThread();
144 return fd;
145 }
146
CreateVmProcessIfNeed(std::shared_ptr<ProcessDumpRequest> request,bool enableNs)147 void ProcessDumper::CreateVmProcessIfNeed(std::shared_ptr<ProcessDumpRequest> request, bool enableNs)
148 {
149 if (request == nullptr) {
150 return;
151 }
152
153 int vmPid = request->GetVmPid();
154 if (vmPid <= 0) {
155 return;
156 }
157
158 if (getppid() != vmPid) {
159 DfxLogError("VM Process should be our parent.");
160 return;
161 }
162
163 if (!enableNs) {
164 targetVmPid_ = vmPid;
165 targetVmNsPid_ = vmPid;
166 }
167
168 std::shared_ptr<DfxThread> vmThread = std::make_shared<DfxThread>(targetVmPid_, targetVmPid_, vmPid);
169 if (!vmThread->Attach()) {
170 DfxLogError("Fail to attach vm thread.");
171 return;
172 }
173
174 vmProcess_ = DfxProcess::CreateProcessWithKeyThread(targetVmPid_, vmThread);
175 vmProcess_->SetNsPid(targetVmNsPid_);
176 }
177
DumpProcessWithSignalContext(std::shared_ptr<ProcessDumpRequest> request)178 int ProcessDumper::DumpProcessWithSignalContext(std::shared_ptr<ProcessDumpRequest> request)
179 {
180 int dumpRes = ProcessDumpRes::DUMP_ESUCCESS;
181 do {
182 ssize_t readCount = read(STDIN_FILENO, request.get(), sizeof(ProcessDumpRequest));
183 if (readCount != static_cast<long>(sizeof(ProcessDumpRequest))) {
184 DfxLogError("Fail to read DumpRequest(%d).", errno);
185 dumpRes = ProcessDumpRes::DUMP_EREADREQUEST;
186 break;
187 }
188
189 bool isCrash = (request->GetSiginfo().si_signo != SIGDUMP);
190 std::string storeProcessName = request->GetProcessNameString();
191 // We need check pid is same with getppid().
192 // As in signal handler, current process is a child process, and target pid is our parent process.
193 // If pid namespace is enalbed, both ppid and pid are equal one.
194 // In this case, we have to parse /proc/self/stat
195 if ((!isCrash && (syscall(SYS_getppid) != request->GetPid())) ||
196 (isCrash && (syscall(SYS_getppid) != request->GetVmPid()))) {
197 DfxLogError("Target process(%s:%d) is not parent pid(%d), exit processdump for signal(%d).",
198 storeProcessName.c_str(), request->GetPid(), syscall(SYS_getppid), request->GetSiginfo().si_signo);
199 dumpRes = ProcessDumpRes::DUMP_EGETPPID;
200 break;
201 }
202
203 targetPid_ = request->GetPid();
204 bool isPidNsEnabled = (targetPid_ == 1);
205 if (isPidNsEnabled) {
206 isPidNsEnabled = InitProcessNsInfo(request, isCrash);
207 }
208
209 CreateVmProcessIfNeed(request, isPidNsEnabled);
210 DfxLogInfo("Processdump SigVal:%d, TargetPid:%d, TargetTid:%d.",
211 request->GetSiginfo().si_value.sival_int, targetPid_, request->GetTid());
212 if (InitProcessInfo(request, isCrash, isPidNsEnabled) < 0) {
213 DfxLogError("Failed to init crash process info.");
214 dumpRes = ProcessDumpRes::DUMP_EATTACH;
215 break;
216 }
217
218 if (InitPrintThread(true, request) < 0) {
219 DfxLogError("Failed to init print thread.");
220 dumpRes = ProcessDumpRes::DUMP_EGETFD;
221 }
222
223 PrintDumpProcessWithSignalContextHeader(request);
224 if (DfxUnwindRemote::GetInstance().UnwindProcess(targetProcess_) == false) {
225 DfxLogError("Failed to unwind process.");
226 dumpRes = ProcessDumpRes::DUMP_ESTOPUNWIND;
227 }
228
229 if (!isPidNsEnabled && (syscall(SYS_getppid) != request->GetPid()) &&
230 (syscall(SYS_getppid) != request->GetVmPid())) {
231 DfxRingBufferWrapper::GetInstance().AppendBuf(
232 "Target process has been killed, the crash log may not be fully generated.");
233 dumpRes = ProcessDumpRes::DUMP_EGETPPID;
234 break;
235 }
236 } while (false);
237
238 return dumpRes;
239 }
240
InitProcessNsInfo(std::shared_ptr<ProcessDumpRequest> request,bool isCrash)241 bool ProcessDumper::InitProcessNsInfo(std::shared_ptr<ProcessDumpRequest> request, bool isCrash)
242 {
243 ProcInfo procInfo;
244 bool ret = false;
245 (void)memset_s(&procInfo, sizeof(procInfo), 0, sizeof(struct ProcInfo));
246 if (GetProcStatus(procInfo) == -1) {
247 return ret;
248 }
249
250 if (!isCrash) {
251 if (procInfo.ppid == 0) { // real init case
252 targetPid_ = 1;
253 targetNsPid_ = 1;
254 ret = false;
255 } else {
256 targetPid_ = procInfo.ppid;
257 targetNsPid_ = getppid();
258 ret = true;
259 }
260 DfxLogInfo("Dump in targetPid:%d targetNsPid:%d.", targetPid_, targetNsPid_);
261 } else {
262 ret = true;
263 targetVmPid_ = procInfo.ppid;
264 targetVmNsPid_ = getppid();
265 DfxLogInfo("Crash in vmPid:%d nsVmPid:%d.", targetVmPid_, targetVmNsPid_);
266 (void)memset_s(&procInfo, sizeof(procInfo), 0, sizeof(struct ProcInfo));
267 if (GetProcStatusByPid(targetVmPid_, procInfo) == -1) {
268 DfxLogError("Failed to read real target pid.");
269 targetPid_ = targetVmPid_;
270 targetNsPid_ = targetVmNsPid_;
271 } else {
272 targetPid_ = procInfo.ppid;
273 targetNsPid_ = request->GetPid();
274 DfxLogInfo("Crash in targetPid:%d targetNsPid:%d.", targetPid_, targetNsPid_);
275 }
276 }
277
278 request->SetPid(targetPid_);
279 return ret;
280 }
281
InitProcessInfo(std::shared_ptr<ProcessDumpRequest> request,bool isCrash,bool enableNs)282 int ProcessDumper::InitProcessInfo(std::shared_ptr<ProcessDumpRequest> request, bool isCrash, bool enableNs)
283 {
284 // if Nspid is enabled, target tid and real tid should be paresed from /proc/pid/task
285 int tid = request->GetSiginfo().si_value.sival_int;
286 int targetTid = request->GetTid();
287 if (!enableNs) {
288 targetNsPid_ = targetPid_;
289 }
290
291 std::shared_ptr<DfxThread> keyThread = isCrash ?
292 std::make_shared<DfxThread>(targetPid_, targetPid_, targetTid, request->GetContext()) :
293 std::make_shared<DfxThread>(targetPid_, tid == 0 ? targetPid_ : tid, targetTid);
294 if (!keyThread->Attach()) {
295 DfxLogError("Fail to attach key thread(%d).", targetTid);
296 if (!isCrash || vmProcess_ == nullptr) {
297 return -1;
298 }
299 }
300
301 keyThread->SetIsCrashThread(true);
302 if ((keyThread->GetThreadName()).empty()) {
303 keyThread->SetThreadName(request->GetThreadNameString());
304 }
305
306 targetProcess_ = DfxProcess::CreateProcessWithKeyThread(targetPid_, keyThread);
307 if (!targetProcess_) {
308 return -1;
309 }
310
311 if ((targetProcess_->GetProcessName()).empty()) {
312 targetProcess_->SetProcessName(request->GetProcessNameString());
313 }
314
315 targetProcess_->SetNsPid(targetNsPid_);
316 targetProcess_->SetIsSignalDump(!isCrash);
317 targetProcess_->SetUid(request->GetUid());
318 targetProcess_->SetRecycleTid(request->GetRecycleTid());
319 targetProcess_->SetFatalMessage(request->GetLastFatalMessage());
320 if (tid == 0) {
321 targetProcess_->InitOtherThreads(isCrash);
322 }
323
324 return 0;
325 }
326
GetTargetPid()327 int32_t ProcessDumper::GetTargetPid()
328 {
329 if (vmProcess_ != nullptr) {
330 return vmProcess_->GetPid();
331 }
332
333 if (targetProcess_ != nullptr) {
334 return targetProcess_->GetPid();
335 }
336
337 return -1;
338 }
339
GetTargetNsPid()340 int32_t ProcessDumper::GetTargetNsPid()
341 {
342 if (vmProcess_ != nullptr) {
343 return vmProcess_->GetNsPid();
344 }
345
346 if (targetProcess_ != nullptr) {
347 return targetProcess_->GetNsPid();
348 }
349
350 return -1;
351 }
352
Dump()353 void ProcessDumper::Dump()
354 {
355 std::shared_ptr<ProcessDumpRequest> request = std::make_shared<ProcessDumpRequest>();
356 resDump_ = DumpProcessWithSignalContext(request);
357 if (targetProcess_ == nullptr) {
358 DfxLogError("Dump process failed, please check permission and whether pid is valid.");
359 } else {
360 if (targetProcess_->GetIsSignalDump()) {
361 targetProcess_->Detach();
362 }
363
364 if (vmProcess_ != nullptr) {
365 vmProcess_->Detach();
366 }
367 }
368
369 WriteDumpRes(resDump_);
370 DfxRingBufferWrapper::GetInstance().StopThread();
371 DfxLogInfo("Finish dump stacktrace for %s(%d:%d).",
372 request->GetProcessNameString().c_str(), request->GetPid(), request->GetTid());
373 CloseDebugLog();
374
375 // check dump result ?
376 if (reporter_ != nullptr) {
377 reporter_->ReportToHiview();
378 }
379
380 _exit(0);
381 }
382
WriteDumpBuf(int fd,const char * buf,const int len)383 int ProcessDumper::WriteDumpBuf(int fd, const char* buf, const int len)
384 {
385 if (buf == nullptr) {
386 return -1;
387 }
388 return WriteLog(fd, "%s", buf);
389 }
390
WriteDumpRes(int32_t res)391 void ProcessDumper::WriteDumpRes(int32_t res)
392 {
393 DfxLogDebug("%s :: res: %d", __func__, res);
394 DumpResMsg dumpResMsg;
395 dumpResMsg.res = res;
396 const char* strRes = DfxDumpRes::GetInstance().GetResStr(res);
397 if (strncpy_s(dumpResMsg.strRes, sizeof(dumpResMsg.strRes), strRes, strlen(strRes)) != 0) {
398 DfxLogError("%s :: strncpy failed.", __func__);
399 }
400 if (resFd_ > 0) {
401 write(resFd_, &dumpResMsg, sizeof(struct DumpResMsg));
402 } else {
403 if (res != DUMP_ESUCCESS) {
404 DfxRingBufferWrapper::GetInstance().AppendMsg("Result:\n");
405 DfxRingBufferWrapper::GetInstance().AppendMsg(DfxDumpRes::GetInstance().ToString() + "\n");
406 }
407 }
408 }
409 } // namespace HiviewDFX
410 } // namespace OHOS
411