• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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