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 <cinttypes>
20 #include <csignal>
21 #include <cstdio>
22 #include <cstdlib>
23 #include <cstring>
24 #include <iostream>
25 #include <memory>
26 #include <string>
27 #include <fcntl.h>
28 #include <pthread.h>
29 #include <syscall.h>
30 #include <ucontext.h>
31 #include <unistd.h>
32 #include <securec.h>
33
34 #include "faultloggerd_client.h"
35 #include "cppcrash_reporter.h"
36 #include "printer.h"
37 #include "procinfo.h"
38 #include "dfx_config.h"
39 #include "dfx_define.h"
40 #include "dfx_dump_request.h"
41 #include "dfx_dump_res.h"
42 #include "dfx_logger.h"
43 #include "dfx_process.h"
44 #include "dfx_regs.h"
45 #include "dfx_ring_buffer_wrapper.h"
46 #include "dfx_thread.h"
47 #include "dfx_unwind_remote.h"
48 #include "dfx_util.h"
49
50 namespace OHOS {
51 namespace HiviewDFX {
GetInstance()52 ProcessDumper &ProcessDumper::GetInstance()
53 {
54 static ProcessDumper ins;
55 return ins;
56 }
57
Dump()58 void ProcessDumper::Dump()
59 {
60 std::shared_ptr<ProcessDumpRequest> request = std::make_shared<ProcessDumpRequest>();
61 resDump_ = DumpProcess(request);
62 if (process_ == nullptr) {
63 DFXLOG_ERROR("Dump process failed, please check permission and whether pid is valid.");
64 } else {
65 if (isCrash_) {
66 process_->vmThread_->Detach();
67 process_->Detach();
68 }
69 process_->keyThread_->Detach();
70 }
71
72 WriteDumpRes(resDump_);
73 DfxRingBufferWrapper::GetInstance().StopThread();
74 DFXLOG_INFO("Finish dump stacktrace for %s(%d:%d).", request->processName, request->pid, request->tid);
75 CloseDebugLog();
76
77 // check dump result ?
78 if (reporter_ != nullptr) {
79 reporter_->ReportToHiview();
80 reporter_->ReportToAbilityManagerService();
81 }
82 }
83
DumpProcess(std::shared_ptr<ProcessDumpRequest> request)84 int ProcessDumper::DumpProcess(std::shared_ptr<ProcessDumpRequest> request)
85 {
86 int dumpRes = DumpErrorCode::DUMP_ESUCCESS;
87 do {
88 ssize_t readCount = read(STDIN_FILENO, request.get(), sizeof(ProcessDumpRequest));
89 if (readCount != static_cast<long>(sizeof(ProcessDumpRequest))) {
90 DFXLOG_ERROR("Fail to read DumpRequest(%d).", errno);
91 dumpRes = DumpErrorCode::DUMP_EREADREQUEST;
92 break;
93 }
94
95 isCrash_ = (request->siginfo.si_signo != SIGDUMP);
96 // We need check pid is same with getppid().
97 // As in signal handler, current process is a child process, and target pid is our parent process.
98 // If pid namespace is enalbed, both ppid and pid are equal one.
99 // In this case, we have to parse /proc/self/status
100 if ((!isCrash_ && (syscall(SYS_getppid) != request->nsPid)) ||
101 (isCrash_ && (syscall(SYS_getppid) != request->vmNsPid))) {
102 DFXLOG_ERROR("Target process(%s:%d) is not parent pid(%d), exit processdump for signal(%d).",
103 request->processName, request->nsPid, syscall(SYS_getppid), request->siginfo.si_signo);
104 dumpRes = DumpErrorCode::DUMP_EGETPPID;
105 break;
106 }
107 DFXLOG_INFO("Processdump SigVal(%d), TargetPid(%d:%d), TargetTid(%d), threadname(%s).",
108 request->siginfo.si_value.sival_int, request->pid, request->nsPid, request->tid, request->threadName);
109
110 if (InitProcessInfo(request) < 0) {
111 DFXLOG_ERROR("Failed to init crash process info.");
112 dumpRes = DumpErrorCode::DUMP_EATTACH;
113 break;
114 }
115
116 if (InitPrintThread(request) < 0) {
117 DFXLOG_ERROR("Failed to init print thread.");
118 dumpRes = DumpErrorCode::DUMP_EGETFD;
119 }
120
121 if (isCrash_) {
122 reporter_ = std::make_shared<CppCrashReporter>(request->timeStamp, request->siginfo, process_);
123 }
124
125 Printer::PrintDumpHeader(request, process_);
126 if (DfxUnwindRemote::GetInstance().UnwindProcess(process_) == false) {
127 DFXLOG_ERROR("Failed to unwind process.");
128 dumpRes = DumpErrorCode::DUMP_ESTOPUNWIND;
129 }
130
131 if ((!isCrash_ && (syscall(SYS_getppid) != request->nsPid)) ||
132 (isCrash_ && (syscall(SYS_getppid) != request->vmNsPid))) {
133 DfxRingBufferWrapper::GetInstance().AppendMsg(
134 "Target process has been killed, the crash log may not be fully generated.");
135 dumpRes = DumpErrorCode::DUMP_EGETPPID;
136 break;
137 }
138 } while (false);
139 return dumpRes;
140 }
141
InitProcessInfo(std::shared_ptr<ProcessDumpRequest> request)142 int ProcessDumper::InitProcessInfo(std::shared_ptr<ProcessDumpRequest> request)
143 {
144 if (request->pid <= 0) {
145 return -1;
146 }
147 process_ = DfxProcess::Create(request->pid, request->nsPid);
148 if (process_ == nullptr) {
149 return -1;
150 }
151 if (process_->processInfo_.processName.empty()) {
152 process_->processInfo_.processName = std::string(request->processName);
153 }
154 process_->processInfo_.uid = request->uid;
155 process_->recycleTid_ = request->recycleTid;
156 process_->SetFatalMessage(request->lastFatalMessage);
157
158 if (isCrash_ && request->vmPid != 0) {
159 if (getppid() != request->vmNsPid) {
160 DFXLOG_ERROR("VM process(%d) should be parent pid.", request->vmNsPid);
161 return -1;
162 }
163 process_->vmThread_ = DfxThread::Create(request->vmPid, request->vmPid, request->vmNsPid);
164 if (!process_->vmThread_->Attach()) {
165 DFXLOG_ERROR("Fail to attach vm thread(%d).", request->vmNsPid);
166 return -1;
167 }
168
169 process_->vmThread_->SetThreadRegs(DfxRegs::CreateFromContext(request->context));
170 process_->vmThread_->threadInfo_.threadName = std::string(request->threadName);
171 }
172
173 pid_t dumpTid = request->siginfo.si_value.sival_int;
174 pid_t nsTid = request->tid;
175 pid_t tid = process_->ChangeTid(nsTid, true);
176 if (!isCrash_) {
177 if (dumpTid != 0 && dumpTid != tid && (IsThreadInPid(process_->processInfo_.pid, dumpTid))) {
178 tid = dumpTid;
179 nsTid = process_->ChangeTid(tid, false);
180 }
181 }
182 process_->keyThread_ = DfxThread::Create(process_->processInfo_.pid, tid, nsTid);
183 if (!process_->keyThread_->Attach()) {
184 DFXLOG_ERROR("Fail to attach key thread(%d).", nsTid);
185 if (!isCrash_) {
186 return -1;
187 }
188 }
189 if (process_->keyThread_->threadInfo_.threadName.empty()) {
190 process_->keyThread_->threadInfo_.threadName = std::string(request->threadName);
191 }
192
193 if (isCrash_) {
194 process_->InitOtherThreads();
195 process_->Attach();
196 } else {
197 if (dumpTid == 0) {
198 process_->InitOtherThreads();
199 }
200 }
201 return 0;
202 }
203
InitPrintThread(std::shared_ptr<ProcessDumpRequest> request)204 int ProcessDumper::InitPrintThread(std::shared_ptr<ProcessDumpRequest> request)
205 {
206 int fd = -1;
207 FaultLoggerType type = isCrash_ ? FaultLoggerType::CPP_CRASH : FaultLoggerType::CPP_STACKTRACE;
208
209 struct FaultLoggerdRequest faultloggerdRequest;
210 (void)memset_s(&faultloggerdRequest, sizeof(faultloggerdRequest), 0, sizeof(struct FaultLoggerdRequest));
211
212 if (isCrash_) {
213 if (DfxConfig::GetConfig().logPersist) {
214 InitDebugLog((int)type, request->pid, request->tid, request->uid);
215 }
216
217 faultloggerdRequest.type = (int32_t)type;
218 faultloggerdRequest.pid = request->pid;
219 faultloggerdRequest.tid = request->tid;
220 faultloggerdRequest.uid = request->uid;
221 faultloggerdRequest.time = request->timeStamp;
222 fd = RequestFileDescriptorEx(&faultloggerdRequest);
223
224 DfxRingBufferWrapper::GetInstance().SetWriteFunc(ProcessDumper::WriteDumpBuf);
225 } else {
226 fd = RequestPipeFd(request->pid, FaultLoggerPipeType::PIPE_FD_WRITE_BUF);
227 DFXLOG_DEBUG("write buf fd: %d", fd);
228
229 resFd_ = RequestPipeFd(request->pid, FaultLoggerPipeType::PIPE_FD_WRITE_RES);
230 DFXLOG_DEBUG("write res fd: %d", resFd_);
231 }
232
233 if (fd < 0) {
234 DFXLOG_WARN("Failed to request fd from faultloggerd.");
235 }
236
237 DfxRingBufferWrapper::GetInstance().SetWriteBufFd(fd);
238 DfxRingBufferWrapper::GetInstance().StartThread();
239 return fd;
240 }
241
WriteDumpBuf(int fd,const char * buf,const int len)242 int ProcessDumper::WriteDumpBuf(int fd, const char* buf, const int len)
243 {
244 if (buf == nullptr) {
245 return -1;
246 }
247 return WriteLog(fd, "%s", buf);
248 }
249
WriteDumpRes(int32_t res)250 void ProcessDumper::WriteDumpRes(int32_t res)
251 {
252 DFXLOG_DEBUG("%s :: res: %d", __func__, res);
253 if (resFd_ > 0) {
254 write(resFd_, &res, sizeof(res));
255 close(resFd_);
256 } else {
257 if (res != DUMP_ESUCCESS) {
258 DfxRingBufferWrapper::GetInstance().AppendMsg("Result:\n");
259 DfxRingBufferWrapper::GetInstance().AppendMsg(DfxDumpRes::ToString(res) + "\n");
260 }
261 }
262 }
263
IsCrash() const264 bool ProcessDumper::IsCrash() const
265 {
266 return isCrash_;
267 }
268 } // namespace HiviewDFX
269 } // namespace OHOS
270