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(), signo, targetProcess_);
128 } else {
129 fd = RequestPipeFd(pid, FaultLoggerPipeType::PIPE_FD_WRITE_BUF);
130 DfxLogDebug("write buf fd: %d", fd);
131
132 resFd_ = RequestPipeFd(pid, FaultLoggerPipeType::PIPE_FD_WRITE_RES);
133 DfxLogDebug("write res fd: %d", resFd_);
134 }
135
136 if (fd < 0) {
137 DfxLogWarn("Failed to request fd from faultloggerd.");
138 }
139 }
140
141 DfxRingBufferWrapper::GetInstance().SetWriteBufFd(fd);
142 DfxRingBufferWrapper::GetInstance().StartThread();
143 return fd;
144 }
145
CreateVmProcessIfNeed(std::shared_ptr<ProcessDumpRequest> request,bool enableNs)146 void ProcessDumper::CreateVmProcessIfNeed(std::shared_ptr<ProcessDumpRequest> request, bool enableNs)
147 {
148 if (request == nullptr) {
149 return;
150 }
151
152 int vmPid = request->GetVmPid();
153 if (vmPid <= 0) {
154 return;
155 }
156
157 if (getppid() != vmPid) {
158 DfxLogError("VM Process should be our parent.");
159 return;
160 }
161
162 if (!enableNs) {
163 targetVmPid_ = vmPid;
164 targetVmNsPid_ = vmPid;
165 }
166
167 std::shared_ptr<DfxThread> vmThread = std::make_shared<DfxThread>(targetVmPid_, targetVmPid_, vmPid);
168 if (!vmThread->Attach()) {
169 DfxLogError("Fail to attach vm thread.");
170 return;
171 }
172
173 vmProcess_ = DfxProcess::CreateProcessWithKeyThread(targetVmPid_, vmThread);
174 vmProcess_->SetNsPid(targetVmNsPid_);
175 }
176
DumpProcessWithSignalContext(std::shared_ptr<ProcessDumpRequest> request)177 int ProcessDumper::DumpProcessWithSignalContext(std::shared_ptr<ProcessDumpRequest> request)
178 {
179 int dumpRes = ProcessDumpRes::DUMP_ESUCCESS;
180 do {
181 ssize_t readCount = read(STDIN_FILENO, request.get(), sizeof(ProcessDumpRequest));
182 if (readCount != static_cast<long>(sizeof(ProcessDumpRequest))) {
183 DfxLogError("Fail to read DumpRequest(%d).", errno);
184 dumpRes = ProcessDumpRes::DUMP_EREADREQUEST;
185 break;
186 }
187
188 bool isCrash = (request->GetSiginfo().si_signo != SIGDUMP);
189 std::string storeProcessName = request->GetProcessNameString();
190 // We need check pid is same with getppid().
191 // As in signal handler, current process is a child process, and target pid is our parent process.
192 // If pid namespace is enalbed, both ppid and pid are equal one.
193 // In this case, we have to parse /proc/self/stat
194 if ((!isCrash && (syscall(SYS_getppid) != request->GetPid())) ||
195 (isCrash && (syscall(SYS_getppid) != request->GetVmPid()))) {
196 DfxLogError("Target process(%s:%d) is not parent pid(%d), exit processdump for signal(%d).",
197 storeProcessName.c_str(), request->GetPid(), syscall(SYS_getppid), request->GetSiginfo().si_signo);
198 dumpRes = ProcessDumpRes::DUMP_EGETPPID;
199 break;
200 }
201
202 targetPid_ = request->GetPid();
203 bool isPidNsEnabled = (targetPid_ == 1);
204 if (isPidNsEnabled) {
205 isPidNsEnabled = InitProcessNsInfo(request, isCrash);
206 }
207
208 CreateVmProcessIfNeed(request, isPidNsEnabled);
209 DfxLogInfo("Processdump SigVal:%d, TargetPid:%d, TargetTid:%d.",
210 request->GetSiginfo().si_value.sival_int, targetPid_, request->GetTid());
211 if (InitProcessInfo(request, isCrash, isPidNsEnabled) < 0) {
212 DfxLogError("Failed to init crash process info.");
213 dumpRes = ProcessDumpRes::DUMP_EATTACH;
214 break;
215 }
216
217 if (InitPrintThread(true, request) < 0) {
218 DfxLogError("Failed to init print thread.");
219 dumpRes = ProcessDumpRes::DUMP_EGETFD;
220 }
221
222 PrintDumpProcessWithSignalContextHeader(request);
223 if (DfxUnwindRemote::GetInstance().UnwindProcess(targetProcess_) == false) {
224 DfxLogError("Failed to unwind process.");
225 dumpRes = ProcessDumpRes::DUMP_ESTOPUNWIND;
226 }
227
228 if (!isPidNsEnabled && (syscall(SYS_getppid) != request->GetPid()) &&
229 (syscall(SYS_getppid) != request->GetVmPid())) {
230 DfxRingBufferWrapper::GetInstance().AppendBuf(
231 "Target process has been killed, the crash log may not be fully generated.");
232 dumpRes = ProcessDumpRes::DUMP_EGETPPID;
233 break;
234 }
235 } while (false);
236
237 return dumpRes;
238 }
239
InitProcessNsInfo(std::shared_ptr<ProcessDumpRequest> request,bool isCrash)240 bool ProcessDumper::InitProcessNsInfo(std::shared_ptr<ProcessDumpRequest> request, bool isCrash)
241 {
242 ProcInfo procInfo;
243 bool ret = false;
244 (void)memset_s(&procInfo, sizeof(procInfo), 0, sizeof(struct ProcInfo));
245 if (GetProcStatus(procInfo) == -1) {
246 return ret;
247 }
248
249 if (!isCrash) {
250 if (procInfo.ppid == 0) { // real init case
251 targetPid_ = 1;
252 targetNsPid_ = 1;
253 ret = false;
254 } else {
255 targetPid_ = procInfo.ppid;
256 targetNsPid_ = getppid();
257 ret = true;
258 }
259 DfxLogInfo("Dump in targetPid:%d targetNsPid:%d.", targetPid_, targetNsPid_);
260 } else {
261 ret = true;
262 targetVmPid_ = procInfo.ppid;
263 targetVmNsPid_ = getppid();
264 DfxLogInfo("Crash in vmPid:%d nsVmPid:%d.", targetVmPid_, targetVmNsPid_);
265 (void)memset_s(&procInfo, sizeof(procInfo), 0, sizeof(struct ProcInfo));
266 if (GetProcStatusByPid(targetVmPid_, procInfo) == -1) {
267 DfxLogError("Failed to read real target pid.");
268 targetPid_ = targetVmPid_;
269 targetNsPid_ = targetVmNsPid_;
270 } else {
271 targetPid_ = procInfo.ppid;
272 targetNsPid_ = request->GetPid();
273 DfxLogInfo("Crash in targetPid:%d targetNsPid:%d.", targetPid_, targetNsPid_);
274 }
275 }
276
277 request->SetPid(targetPid_);
278 return ret;
279 }
280
InitProcessInfo(std::shared_ptr<ProcessDumpRequest> request,bool isCrash,bool enableNs)281 int ProcessDumper::InitProcessInfo(std::shared_ptr<ProcessDumpRequest> request, bool isCrash, bool enableNs)
282 {
283 // if Nspid is enabled, target tid and real tid should be paresed from /proc/pid/task
284 int tid = request->GetSiginfo().si_value.sival_int;
285 int targetTid = request->GetTid();
286 if (!enableNs) {
287 targetNsPid_ = targetPid_;
288 }
289
290 std::shared_ptr<DfxThread> keyThread = isCrash ?
291 std::make_shared<DfxThread>(targetPid_, targetPid_, targetTid, request->GetContext()) :
292 std::make_shared<DfxThread>(targetPid_, tid == 0 ? targetPid_ : tid, targetTid);
293 if (!keyThread->Attach()) {
294 DfxLogError("Fail to attach key thread(%d).", targetTid);
295 if (!isCrash || vmProcess_ == nullptr) {
296 return -1;
297 }
298 }
299
300 keyThread->SetIsCrashThread(true);
301 if ((keyThread->GetThreadName()).empty()) {
302 keyThread->SetThreadName(request->GetThreadNameString());
303 }
304
305 targetProcess_ = DfxProcess::CreateProcessWithKeyThread(targetPid_, keyThread);
306 if (!targetProcess_) {
307 return -1;
308 }
309
310 if ((targetProcess_->GetProcessName()).empty()) {
311 targetProcess_->SetProcessName(request->GetProcessNameString());
312 }
313
314 targetProcess_->SetNsPid(targetNsPid_);
315 targetProcess_->SetIsSignalDump(!isCrash);
316 targetProcess_->SetUid(request->GetUid());
317 targetProcess_->SetRecycleTid(request->GetRecycleTid());
318 if (tid == 0) {
319 targetProcess_->InitOtherThreads(isCrash);
320 }
321
322 return 0;
323 }
324
GetTargetPid()325 int32_t ProcessDumper::GetTargetPid()
326 {
327 if (vmProcess_ != nullptr) {
328 return vmProcess_->GetPid();
329 }
330
331 if (targetProcess_ != nullptr) {
332 return targetProcess_->GetPid();
333 }
334
335 return -1;
336 }
337
GetTargetNsPid()338 int32_t ProcessDumper::GetTargetNsPid()
339 {
340 if (vmProcess_ != nullptr) {
341 return vmProcess_->GetNsPid();
342 }
343
344 if (targetProcess_ != nullptr) {
345 return targetProcess_->GetNsPid();
346 }
347
348 return -1;
349 }
350
Dump()351 void ProcessDumper::Dump()
352 {
353 std::shared_ptr<ProcessDumpRequest> request = std::make_shared<ProcessDumpRequest>();
354 if (!request) {
355 DfxLogError("Failed to create dump request.");
356 return;
357 }
358
359 resDump_ = DumpProcessWithSignalContext(request);
360 if (targetProcess_ == nullptr) {
361 DfxLogError("Dump process failed, please check permission and whether pid is valid.");
362 } else {
363 if (targetProcess_->GetIsSignalDump()) {
364 targetProcess_->Detach();
365 }
366
367 if (vmProcess_ != nullptr) {
368 vmProcess_->Detach();
369 }
370 }
371
372 WriteDumpRes(resDump_);
373 DfxRingBufferWrapper::GetInstance().StopThread();
374 DfxLogInfo("Finish dump stacktrace for %s(%d:%d).",
375 request->GetProcessNameString().c_str(), request->GetPid(), request->GetTid());
376 CloseDebugLog();
377
378 // check dump result ?
379 if (reporter_ != nullptr) {
380 reporter_->ReportToHiview();
381 }
382
383 _exit(0);
384 }
385
WriteDumpBuf(int fd,const char * buf,const int len)386 int ProcessDumper::WriteDumpBuf(int fd, const char* buf, const int len)
387 {
388 if (buf == nullptr) {
389 return -1;
390 }
391 return WriteLog(fd, "%s", buf);
392 }
393
WriteDumpRes(int32_t res)394 void ProcessDumper::WriteDumpRes(int32_t res)
395 {
396 DfxLogDebug("%s :: res: %d", __func__, res);
397 DumpResMsg dumpResMsg;
398 dumpResMsg.res = res;
399 const char* strRes = DfxDumpRes::GetInstance().GetResStr(res);
400 if (strncpy_s(dumpResMsg.strRes, sizeof(dumpResMsg.strRes), strRes, strlen(strRes)) != 0) {
401 DfxLogError("%s :: strncpy failed.", __func__);
402 }
403 if (resFd_ > 0) {
404 write(resFd_, &dumpResMsg, sizeof(struct DumpResMsg));
405 } else {
406 if (res != DUMP_ESUCCESS) {
407 DfxRingBufferWrapper::GetInstance().AppendMsg("Result:\n");
408 DfxRingBufferWrapper::GetInstance().AppendMsg(DfxDumpRes::GetInstance().ToString() + "\n");
409 }
410 }
411 }
412 } // namespace HiviewDFX
413 } // namespace OHOS
414