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 "dfx_dump_catcher.h"
17
18 #include <cerrno>
19 #include <memory>
20 #include <vector>
21
22 #include <poll.h>
23 #include <sys/syscall.h>
24 #include <sys/types.h>
25 #include <securec.h>
26 #include <strings.h>
27
28 #include "dfx_define.h"
29 #include "dfx_dump_res.h"
30 #include "dfx_log.h"
31 #include "dfx_util.h"
32 #include "faultloggerd_client.h"
33 #include "file_util.h"
34
35 #include "libunwind.h"
36 #include "libunwind_i-ohos.h"
37
38 #include "backtrace_local.h"
39 #include "procinfo.h"
40
41 namespace OHOS {
42 namespace HiviewDFX {
43 namespace {
44 static const int DUMP_CATCHE_WORK_TIME_S = 60;
45 static const int BACK_TRACE_DUMP_MIX_TIMEOUT_MS = 2000;
46 static const int BACK_TRACE_DUMP_CPP_TIMEOUT_MS = 10000;
47 static const std::string DFXDUMPCATCHER_TAG = "DfxDumpCatcher";
48
49 enum DfxDumpPollRes : int32_t {
50 DUMP_POLL_INIT = -1,
51 DUMP_POLL_OK,
52 DUMP_POLL_FD,
53 DUMP_POLL_FAILED,
54 DUMP_POLL_TIMEOUT,
55 DUMP_POLL_RETURN,
56 };
57 }
58
DoDumpCurrTid(const size_t skipFrameNum,std::string & msg)59 bool DfxDumpCatcher::DoDumpCurrTid(const size_t skipFrameNum, std::string& msg)
60 {
61 bool ret = false;
62 ret = GetBacktrace(msg, skipFrameNum + 1, false);
63 if (!ret) {
64 int currTid = syscall(SYS_gettid);
65 msg.append("Failed to dump curr thread:" + std::to_string(currTid) + ".\n");
66 }
67 DFXLOG_DEBUG("%s :: DoDumpCurrTid :: return %d.", DFXDUMPCATCHER_TAG.c_str(), ret);
68 return ret;
69 }
70
DoDumpLocalTid(const int tid,std::string & msg)71 bool DfxDumpCatcher::DoDumpLocalTid(const int tid, std::string& msg)
72 {
73 bool ret = false;
74 if (tid <= 0) {
75 DFXLOG_ERROR("%s :: DoDumpLocalTid :: return false as param error.", DFXDUMPCATCHER_TAG.c_str());
76 return ret;
77 }
78
79 ret = GetBacktraceStringByTid(msg, tid, 0, false);
80 if (!ret) {
81 msg.append("Failed to dump thread:" + std::to_string(tid) + ".\n");
82 }
83 DFXLOG_DEBUG("%s :: DoDumpLocalTid :: return %d.", DFXDUMPCATCHER_TAG.c_str(), ret);
84 return ret;
85 }
86
DoDumpLocalPid(int pid,std::string & msg)87 bool DfxDumpCatcher::DoDumpLocalPid(int pid, std::string& msg)
88 {
89 bool ret = false;
90 if (pid <= 0) {
91 DFXLOG_ERROR("%s :: DoDumpLocalPid :: return false as param error.", DFXDUMPCATCHER_TAG.c_str());
92 return ret;
93 }
94 size_t skipFramNum = DUMP_CATCHER_NUMBER_THREE;
95
96 std::function<bool(int)> func = [&](int tid) {
97 if (tid <= 0) {
98 return false;
99 }
100
101 if (tid == gettid()) {
102 return DoDumpCurrTid(skipFramNum, msg);
103 }
104 return DoDumpLocalTid(tid, msg);
105 };
106 std::vector<int> tids;
107 ret = GetTidsByPidWithFunc(getpid(), tids, func);
108
109 DFXLOG_DEBUG("%s :: DoDumpLocalPid :: return %d.", DFXDUMPCATCHER_TAG.c_str(), ret);
110 return ret;
111 }
112
DoDumpRemoteLocked(int pid,int tid,std::string & msg)113 bool DfxDumpCatcher::DoDumpRemoteLocked(int pid, int tid, std::string& msg)
114 {
115 int type = DUMP_TYPE_NATIVE;
116 return DoDumpCatchRemote(type, pid, tid, msg);
117 }
118
DoDumpLocalLocked(int pid,int tid,std::string & msg)119 bool DfxDumpCatcher::DoDumpLocalLocked(int pid, int tid, std::string& msg)
120 {
121 bool ret = false;
122 size_t skipFramNum = DUMP_CATCHER_NUMBER_TWO;
123 if (tid == syscall(SYS_gettid)) {
124 ret = DoDumpCurrTid(skipFramNum, msg);
125 } else if (tid == 0) {
126 ret = DoDumpLocalPid(pid, msg);
127 } else {
128 if (!IsThreadInPid(pid, tid)) {
129 msg.append("tid(" + std::to_string(tid) + ") is not in pid(" + std::to_string(pid) + ").\n");
130 } else {
131 ret = DoDumpLocalTid(tid, msg);
132 }
133 }
134
135 DFXLOG_DEBUG("%s :: DoDumpLocal :: ret(%d).", DFXDUMPCATCHER_TAG.c_str(), ret);
136 return ret;
137 }
138
DumpCatchMix(int pid,int tid,std::string & msg)139 bool DfxDumpCatcher::DumpCatchMix(int pid, int tid, std::string& msg)
140 {
141 int type = DUMP_TYPE_MIX;
142 return DoDumpCatchRemote(type, pid, tid, msg);
143 }
144
DumpCatch(int pid,int tid,std::string & msg)145 bool DfxDumpCatcher::DumpCatch(int pid, int tid, std::string& msg)
146 {
147 bool ret = false;
148 if (pid <= 0 || tid < 0) {
149 DFXLOG_ERROR("%s :: dump_catch :: param error.", DFXDUMPCATCHER_TAG.c_str());
150 return ret;
151 }
152
153 std::unique_lock<std::mutex> lck(mutex_);
154 int currentPid = getpid();
155 DFXLOG_DEBUG("%s :: dump_catch :: cPid(%d), pid(%d), tid(%d).",
156 DFXDUMPCATCHER_TAG.c_str(), currentPid, pid, tid);
157
158 if (pid == currentPid) {
159 ret = DoDumpLocalLocked(pid, tid, msg);
160 } else {
161 ret = DoDumpRemoteLocked(pid, tid, msg);
162 }
163 DFXLOG_DEBUG("%s :: dump_catch :: ret: %d, msg: %s", DFXDUMPCATCHER_TAG.c_str(), ret, msg.c_str());
164 return ret;
165 }
166
DumpCatchFd(int pid,int tid,std::string & msg,int fd)167 bool DfxDumpCatcher::DumpCatchFd(int pid, int tid, std::string& msg, int fd)
168 {
169 bool ret = false;
170 ret = DumpCatch(pid, tid, msg);
171 if (fd > 0) {
172 ret = write(fd, msg.c_str(), msg.length());
173 }
174 return ret;
175 }
176
DoDumpCatchRemote(const int type,int pid,int tid,std::string & msg)177 bool DfxDumpCatcher::DoDumpCatchRemote(const int type, int pid, int tid, std::string& msg)
178 {
179 bool ret = false;
180 if (pid <= 0 || tid < 0) {
181 msg.append("Result: pid(" + std::to_string(pid) + ") param error.\n");
182 DFXLOG_WARN("%s :: %s :: %s", DFXDUMPCATCHER_TAG.c_str(), __func__, msg.c_str());
183 return ret;
184 }
185
186 int sdkdumpRet = RequestSdkDump(type, pid, tid);
187 if (sdkdumpRet != static_cast<int>(FaultLoggerSdkDumpResp::SDK_DUMP_PASS)) {
188 if (sdkdumpRet == static_cast<int>(FaultLoggerSdkDumpResp::SDK_DUMP_REPEAT)) {
189 msg.append("Result: pid(" + std::to_string(pid) + ") is dumping.\n");
190 } else if (sdkdumpRet == static_cast<int>(FaultLoggerSdkDumpResp::SDK_DUMP_REJECT)) {
191 msg.append("Result: pid(" + std::to_string(pid) + ") check permission error.\n");
192 } else if (sdkdumpRet == static_cast<int>(FaultLoggerSdkDumpResp::SDK_DUMP_NOPROC)) {
193 msg.append("Result: pid(" + std::to_string(pid) + ") syscall SIGDUMP error.\n");
194 RequestDelPipeFd(pid);
195 }
196 DFXLOG_WARN("%s :: %s :: %s", DFXDUMPCATCHER_TAG.c_str(), __func__, msg.c_str());
197 return ret;
198 }
199
200 int pollRet = DoDumpRemotePid(type, pid, msg);
201 switch (pollRet) {
202 case DUMP_POLL_OK:
203 ret = true;
204 break;
205 case DUMP_POLL_TIMEOUT:
206 if (type == DUMP_TYPE_MIX) {
207 msg.append("Result: pid(" + std::to_string(pid) + ") dump mix timeout, try dump native frame.\n");
208 int type = DUMP_TYPE_NATIVE;
209 return DoDumpCatchRemote(type, pid, tid, msg);
210 } else if (type == DUMP_TYPE_NATIVE) {
211 ReadProcessStatus(msg, pid);
212 ReadProcessWchan(msg, pid, false, true);
213 }
214 break;
215 default:
216 break;
217 }
218 DFXLOG_INFO("%s :: %s :: pid(%d) ret: %d", DFXDUMPCATCHER_TAG.c_str(), __func__, pid, ret);
219 return ret;
220 }
221
DoDumpRemotePid(const int type,int pid,std::string & msg)222 int DfxDumpCatcher::DoDumpRemotePid(const int type, int pid, std::string& msg)
223 {
224 int readBufFd = RequestPipeFd(pid, FaultLoggerPipeType::PIPE_FD_READ_BUF);
225 DFXLOG_DEBUG("read buf fd: %d", readBufFd);
226
227 int readResFd = RequestPipeFd(pid, FaultLoggerPipeType::PIPE_FD_READ_RES);
228 DFXLOG_DEBUG("read res fd: %d", readResFd);
229
230 int timeout = BACK_TRACE_DUMP_CPP_TIMEOUT_MS;
231 if (type == DUMP_TYPE_MIX) {
232 timeout = BACK_TRACE_DUMP_MIX_TIMEOUT_MS;
233 }
234
235 int ret = DoDumpRemotePoll(readBufFd, readResFd, timeout, msg);
236
237 // request close fds in faultloggerd
238 RequestDelPipeFd(pid);
239 if (readBufFd >= 0) {
240 close(readBufFd);
241 readBufFd = -1;
242 }
243 if (readResFd >= 0) {
244 close(readResFd);
245 readResFd = -1;
246 }
247 DFXLOG_INFO("%s :: %s :: pid(%d) poll ret: %d", DFXDUMPCATCHER_TAG.c_str(), __func__, pid, ret);
248 return ret;
249 }
250
DoDumpRemotePoll(int bufFd,int resFd,int timeout,std::string & msg)251 int DfxDumpCatcher::DoDumpRemotePoll(int bufFd, int resFd, int timeout, std::string& msg)
252 {
253 int ret = DUMP_POLL_INIT;
254 bool res = false;
255 std::string bufMsg;
256 std::string resMsg;
257 struct pollfd readfds[2];
258 (void)memset_s(readfds, sizeof(readfds), 0, sizeof(readfds));
259 readfds[0].fd = bufFd;
260 readfds[0].events = POLLIN;
261 readfds[1].fd = resFd;
262 readfds[1].events = POLLIN;
263 int fdsSize = sizeof(readfds) / sizeof(readfds[0]);
264 bool bPipeConnect = false;
265 while (true) {
266 if (bufFd < 0 || resFd < 0) {
267 ret = DUMP_POLL_FD;
268 resMsg.append("Result: bufFd or resFd < 0.\n");
269 break;
270 }
271
272 int pollRet = poll(readfds, fdsSize, timeout);
273 if (pollRet < 0) {
274 ret = DUMP_POLL_FAILED;
275 resMsg.append("Result: poll error, errno(" + std::to_string(errno) + ")\n");
276 break;
277 } else if (pollRet == 0) {
278 ret = DUMP_POLL_TIMEOUT;
279 resMsg.append("Result: poll timeout.\n");
280 break;
281 }
282
283 bool bufRet = true;
284 bool resRet = false;
285 bool eventRet = true;
286 for (int i = 0; i < fdsSize; ++i) {
287 if (!bPipeConnect && ((uint32_t)readfds[i].revents & POLLIN)) {
288 bPipeConnect = true;
289 }
290 if (bPipeConnect &&
291 (((uint32_t)readfds[i].revents & POLLERR) || ((uint32_t)readfds[i].revents & POLLHUP))) {
292 eventRet = false;
293 resMsg.append("Result: poll events error.\n");
294 break;
295 }
296
297 if (((uint32_t)readfds[i].revents & POLLIN) != POLLIN) {
298 continue;
299 }
300
301 if (readfds[i].fd == bufFd) {
302 bufRet = DoReadBuf(bufFd, bufMsg);
303 }
304
305 if (readfds[i].fd == resFd) {
306 resRet = DoReadRes(resFd, res, resMsg);
307 }
308 }
309
310 if ((eventRet == false) || (bufRet == false) || (resRet == true)) {
311 ret = DUMP_POLL_RETURN;
312 break;
313 }
314 }
315
316 DFXLOG_INFO("%s :: %s :: %s", DFXDUMPCATCHER_TAG.c_str(), __func__, resMsg.c_str());
317 msg = resMsg + bufMsg;
318 if (res) {
319 ret = DUMP_POLL_OK;
320 }
321 return ret;
322 }
323
DoReadBuf(int fd,std::string & msg)324 bool DfxDumpCatcher::DoReadBuf(int fd, std::string& msg)
325 {
326 char buffer[LOG_BUF_LEN] = {0};
327 ssize_t nread = read(fd, buffer, sizeof(buffer) - 1);
328 if (nread <= 0) {
329 DFXLOG_WARN("%s :: %s :: read error", DFXDUMPCATCHER_TAG.c_str(), __func__);
330 return false;
331 }
332 msg.append(buffer);
333 return true;
334 }
335
DoReadRes(int fd,bool & ret,std::string & msg)336 bool DfxDumpCatcher::DoReadRes(int fd, bool &ret, std::string& msg)
337 {
338 int32_t res = DumpErrorCode::DUMP_ESUCCESS;
339 ssize_t nread = read(fd, &res, sizeof(res));
340 if (nread != sizeof(res)) {
341 DFXLOG_WARN("%s :: %s :: read error", DFXDUMPCATCHER_TAG.c_str(), __func__);
342 return false;
343 }
344
345 if (res == DumpErrorCode::DUMP_ESUCCESS) {
346 ret = true;
347 }
348 msg.append("Result: " + DfxDumpRes::ToString(res) + "\n");
349 return true;
350 }
351
DumpCatchMultiPid(const std::vector<int> pidV,std::string & msg)352 bool DfxDumpCatcher::DumpCatchMultiPid(const std::vector<int> pidV, std::string& msg)
353 {
354 bool ret = false;
355 int pidSize = (int)pidV.size();
356 if (pidSize <= 0) {
357 DFXLOG_ERROR("%s :: %s :: param error, pidSize(%d).", DFXDUMPCATCHER_TAG.c_str(), __func__, pidSize);
358 return ret;
359 }
360
361 std::unique_lock<std::mutex> lck(mutex_);
362 int currentPid = getpid();
363 int currentTid = syscall(SYS_gettid);
364 DFXLOG_DEBUG("%s :: %s :: cPid(%d), cTid(%d), pidSize(%d).", DFXDUMPCATCHER_TAG.c_str(), \
365 __func__, currentPid, currentTid, pidSize);
366
367 time_t startTime = time(nullptr);
368 if (startTime > 0) {
369 DFXLOG_DEBUG("%s :: %s :: startTime(%lld).", DFXDUMPCATCHER_TAG.c_str(), __func__, startTime);
370 }
371
372 for (int i = 0; i < pidSize; i++) {
373 int pid = pidV[i];
374 std::string pidStr;
375 if (DoDumpRemoteLocked(pid, 0, pidStr)) {
376 msg.append(pidStr + "\n");
377 } else {
378 msg.append("Failed to dump process:" + std::to_string(pid));
379 }
380
381 time_t currentTime = time(nullptr);
382 if (currentTime > 0) {
383 DFXLOG_DEBUG("%s :: %s :: startTime(%lld), currentTime(%lld).", DFXDUMPCATCHER_TAG.c_str(), \
384 __func__, startTime, currentTime);
385 if (currentTime > startTime + DUMP_CATCHE_WORK_TIME_S) {
386 break;
387 }
388 }
389 }
390
391 DFXLOG_DEBUG("%s :: %s :: msg(%s).", DFXDUMPCATCHER_TAG.c_str(), __func__, msg.c_str());
392 if (msg.find("Tid:") != std::string::npos) {
393 ret = true;
394 }
395 return ret;
396 }
397 } // namespace HiviewDFX
398 } // namespace OHOS
399