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