• 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(), 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