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 #ifdef LOG_DOMAIN
45 #undef LOG_DOMAIN
46 #define LOG_DOMAIN 0xD002D11
47 #endif
48
49 #ifdef LOG_TAG
50 #undef LOG_TAG
51 #define LOG_TAG "DfxDumpCatcher"
52 #endif
53 static const int DUMP_CATCHE_WORK_TIME_S = 60;
54 static const int BACK_TRACE_DUMP_MIX_TIMEOUT_MS = 2000;
55 static const int BACK_TRACE_DUMP_CPP_TIMEOUT_MS = 10000;
56 static const std::string DFXDUMPCATCHER_TAG = "DfxDumpCatcher";
57
58 enum DfxDumpPollRes : int32_t {
59 DUMP_POLL_INIT = -1,
60 DUMP_POLL_OK,
61 DUMP_POLL_FD,
62 DUMP_POLL_FAILED,
63 DUMP_POLL_TIMEOUT,
64 DUMP_POLL_RETURN,
65 };
66 }
67
DoDumpCurrTid(const size_t skipFrameNum,std::string & msg,size_t maxFrameNums,bool isJson)68 bool DfxDumpCatcher::DoDumpCurrTid(const size_t skipFrameNum, std::string& msg, size_t maxFrameNums, bool isJson)
69 {
70 bool ret = false;
71
72 ret = GetBacktrace(msg, skipFrameNum + 1, false, maxFrameNums, isJson);
73 if (!ret) {
74 int currTid = syscall(SYS_gettid);
75 msg.append("Failed to dump curr thread:" + std::to_string(currTid) + ".\n");
76 }
77 DFXLOG_DEBUG("%s :: DoDumpCurrTid :: return %d.", DFXDUMPCATCHER_TAG.c_str(), ret);
78 return ret;
79 }
80
DoDumpLocalTid(const int tid,std::string & msg,size_t maxFrameNums,bool isJson)81 bool DfxDumpCatcher::DoDumpLocalTid(const int tid, std::string& msg, size_t maxFrameNums, bool isJson)
82 {
83 bool ret = false;
84 if (tid <= 0) {
85 DFXLOG_ERROR("%s :: DoDumpLocalTid :: return false as param error.", DFXDUMPCATCHER_TAG.c_str());
86 return ret;
87 }
88 if (isJson) {
89 #ifndef is_ohos_lite
90 ret = GetBacktraceJsonByTid(msg, tid, 0, false, maxFrameNums);
91 #endif
92 } else {
93 ret = GetBacktraceStringByTid(msg, tid, 0, false, maxFrameNums);
94 }
95
96 if (!ret) {
97 msg.append("Failed to dump thread:" + std::to_string(tid) + ".\n");
98 }
99 DFXLOG_DEBUG("%s :: DoDumpLocalTid :: return %d.", DFXDUMPCATCHER_TAG.c_str(), ret);
100 return ret;
101 }
102
DoDumpLocalPid(int pid,std::string & msg,size_t maxFrameNums,bool isJson)103 bool DfxDumpCatcher::DoDumpLocalPid(int pid, std::string& msg, size_t maxFrameNums, bool isJson)
104 {
105 bool ret = false;
106 if (pid <= 0) {
107 DFXLOG_ERROR("%s :: DoDumpLocalPid :: return false as param error.", DFXDUMPCATCHER_TAG.c_str());
108 return ret;
109 }
110 size_t skipFramNum = 3; // 3: skip 3 frame
111
112 std::function<bool(int)> func = [&](int tid) {
113 if (tid <= 0) {
114 return false;
115 }
116
117 if (tid == gettid()) {
118 return DoDumpCurrTid(skipFramNum, msg, maxFrameNums, isJson);
119 }
120 return DoDumpLocalTid(tid, msg, maxFrameNums, isJson);
121 };
122 std::vector<int> tids;
123 ret = GetTidsByPidWithFunc(getpid(), tids, func);
124
125 DFXLOG_DEBUG("%s :: DoDumpLocalPid :: return %d.", DFXDUMPCATCHER_TAG.c_str(), ret);
126 return ret;
127 }
128
DoDumpRemoteLocked(int pid,int tid,std::string & msg,bool isJson)129 bool DfxDumpCatcher::DoDumpRemoteLocked(int pid, int tid, std::string& msg, bool isJson)
130 {
131 int type = DUMP_TYPE_NATIVE;
132 return DoDumpCatchRemote(type, pid, tid, msg, isJson);
133 }
134
DoDumpLocalLocked(int pid,int tid,std::string & msg,size_t maxFrameNums,bool isJson)135 bool DfxDumpCatcher::DoDumpLocalLocked(int pid, int tid, std::string& msg, size_t maxFrameNums, bool isJson)
136 {
137 bool ret = false;
138 if (tid == syscall(SYS_gettid)) {
139 size_t skipFramNum = 2; // 2: skip 2 frame
140 ret = DoDumpCurrTid(skipFramNum, msg, maxFrameNums, isJson);
141 } else if (tid == 0) {
142 ret = DoDumpLocalPid(pid, msg, maxFrameNums, isJson);
143 } else {
144 if (!IsThreadInPid(pid, tid)) {
145 msg.append("tid(" + std::to_string(tid) + ") is not in pid(" + std::to_string(pid) + ").\n");
146 } else {
147 ret = DoDumpLocalTid(tid, msg, maxFrameNums, isJson);
148 }
149 }
150
151 DFXLOG_DEBUG("%s :: DoDumpLocal :: ret(%d).", DFXDUMPCATCHER_TAG.c_str(), ret);
152 return ret;
153 }
154
DumpCatchMix(int pid,int tid,std::string & msg)155 bool DfxDumpCatcher::DumpCatchMix(int pid, int tid, std::string& msg)
156 {
157 int type = DUMP_TYPE_MIX;
158 return DoDumpCatchRemote(type, pid, tid, msg);
159 }
160
DumpCatch(int pid,int tid,std::string & msg,size_t maxFrameNums,bool isJson)161 bool DfxDumpCatcher::DumpCatch(int pid, int tid, std::string& msg, size_t maxFrameNums, bool isJson)
162 {
163 bool ret = false;
164 if (pid <= 0 || tid < 0) {
165 DFXLOG_ERROR("%s :: dump_catch :: param error.", DFXDUMPCATCHER_TAG.c_str());
166 return ret;
167 }
168
169 std::unique_lock<std::mutex> lck(mutex_);
170 int currentPid = getpid();
171 DFXLOG_DEBUG("%s :: dump_catch :: cPid(%d), pid(%d), tid(%d).",
172 DFXDUMPCATCHER_TAG.c_str(), currentPid, pid, tid);
173
174 if (pid == currentPid) {
175 ret = DoDumpLocalLocked(pid, tid, msg, maxFrameNums, isJson);
176 } else {
177 if (maxFrameNums != DEFAULT_MAX_FRAME_NUM) {
178 DFXLOG_INFO("%s :: dump_catch :: maxFrameNums does not support setting when pid is not equal to caller pid",
179 DFXDUMPCATCHER_TAG.c_str());
180 }
181 ret = DoDumpRemoteLocked(pid, tid, msg, isJson);
182 }
183 if (ret && isJson && !IsValidJson(msg)) {
184 DFXLOG_WARN("%s :: dump_catch :: json stack info is invalid, try to dump stack again.",
185 DFXDUMPCATCHER_TAG.c_str());
186 msg.clear();
187 if (pid == currentPid) {
188 ret = DoDumpLocalLocked(pid, tid, msg, maxFrameNums, false);
189 } else {
190 ret = DoDumpRemoteLocked(pid, tid, msg, false);
191 }
192 }
193 DFXLOG_DEBUG("%s :: dump_catch :: ret: %d, msg: %s", DFXDUMPCATCHER_TAG.c_str(), ret, msg.c_str());
194 return ret;
195 }
196
DumpCatchFd(int pid,int tid,std::string & msg,int fd,size_t maxFrameNums)197 bool DfxDumpCatcher::DumpCatchFd(int pid, int tid, std::string& msg, int fd, size_t maxFrameNums)
198 {
199 bool ret = false;
200 ret = DumpCatch(pid, tid, msg, maxFrameNums);
201 if (fd > 0) {
202 ret = write(fd, msg.c_str(), msg.length());
203 }
204 return ret;
205 }
206
DoDumpCatchRemote(const int type,int pid,int tid,std::string & msg,bool isJson)207 bool DfxDumpCatcher::DoDumpCatchRemote(const int type, int pid, int tid, std::string& msg, bool isJson)
208 {
209 bool ret = false;
210 if (pid <= 0 || tid < 0) {
211 msg.append("Result: pid(" + std::to_string(pid) + ") param error.\n");
212 DFXLOG_WARN("%s :: %s :: %s", DFXDUMPCATCHER_TAG.c_str(), __func__, msg.c_str());
213 return ret;
214 }
215 int sdkdumpRet = RequestSdkDumpJson(type, pid, tid, isJson);
216 if (sdkdumpRet != static_cast<int>(FaultLoggerSdkDumpResp::SDK_DUMP_PASS)) {
217 if (sdkdumpRet == static_cast<int>(FaultLoggerSdkDumpResp::SDK_DUMP_REPEAT)) {
218 msg.append("Result: pid(" + std::to_string(pid) + ") is dumping.\n");
219 } else if (sdkdumpRet == static_cast<int>(FaultLoggerSdkDumpResp::SDK_DUMP_REJECT)) {
220 msg.append("Result: pid(" + std::to_string(pid) + ") check permission error.\n");
221 } else if (sdkdumpRet == static_cast<int>(FaultLoggerSdkDumpResp::SDK_DUMP_NOPROC)) {
222 msg.append("Result: pid(" + std::to_string(pid) + ") syscall SIGDUMP error.\n");
223 RequestDelPipeFd(pid);
224 }
225 DFXLOG_WARN("%s :: %s :: %s", DFXDUMPCATCHER_TAG.c_str(), __func__, msg.c_str());
226 return ret;
227 }
228
229 int pollRet = DoDumpRemotePid(type, pid, msg, isJson);
230 switch (pollRet) {
231 case DUMP_POLL_OK:
232 ret = true;
233 break;
234 case DUMP_POLL_TIMEOUT:
235 if (type == DUMP_TYPE_MIX) {
236 msg.append("Result: pid(" + std::to_string(pid) + ") dump mix timeout, try dump native frame.\n");
237 int type = DUMP_TYPE_NATIVE;
238 return DoDumpCatchRemote(type, pid, tid, msg, isJson);
239 } else if (type == DUMP_TYPE_NATIVE) {
240 ReadProcessStatus(msg, pid);
241 ReadProcessWchan(msg, pid, false, true);
242 }
243 break;
244 default:
245 break;
246 }
247 DFXLOG_INFO("%s :: %s :: pid(%d) ret: %d", DFXDUMPCATCHER_TAG.c_str(), __func__, pid, ret);
248 return ret;
249 }
250
DoDumpRemotePid(const int type,int pid,std::string & msg,bool isJson)251 int DfxDumpCatcher::DoDumpRemotePid(const int type, int pid, std::string& msg, bool isJson)
252 {
253 int readBufFd = -1;
254 int readResFd = -1;
255 if (isJson) {
256 readBufFd = RequestPipeFd(pid, FaultLoggerPipeType::PIPE_FD_JSON_READ_BUF);
257 readResFd = RequestPipeFd(pid, FaultLoggerPipeType::PIPE_FD_JSON_READ_RES);
258 } else {
259 readBufFd = RequestPipeFd(pid, FaultLoggerPipeType::PIPE_FD_READ_BUF);
260 readResFd = RequestPipeFd(pid, FaultLoggerPipeType::PIPE_FD_READ_RES);
261 }
262 DFXLOG_DEBUG("read res fd: %d", readResFd);
263
264 int timeout = BACK_TRACE_DUMP_CPP_TIMEOUT_MS;
265 if (type == DUMP_TYPE_MIX) {
266 timeout = BACK_TRACE_DUMP_MIX_TIMEOUT_MS;
267 }
268 int ret = DoDumpRemotePoll(readBufFd, readResFd, timeout, msg, isJson);
269 // request close fds in faultloggerd
270 RequestDelPipeFd(pid);
271 if (readBufFd >= 0) {
272 close(readBufFd);
273 readBufFd = -1;
274 }
275 if (readResFd >= 0) {
276 close(readResFd);
277 readResFd = -1;
278 }
279 DFXLOG_INFO("%s :: %s :: pid(%d) poll ret: %d", DFXDUMPCATCHER_TAG.c_str(), __func__, pid, ret);
280 return ret;
281 }
282
DoDumpRemotePoll(int bufFd,int resFd,int timeout,std::string & msg,bool isJson)283 int DfxDumpCatcher::DoDumpRemotePoll(int bufFd, int resFd, int timeout, std::string& msg, bool isJson)
284 {
285 int ret = DUMP_POLL_INIT;
286 bool res = false;
287 std::string bufMsg;
288 std::string resMsg;
289 struct pollfd readfds[2];
290 (void)memset_s(readfds, sizeof(readfds), 0, sizeof(readfds));
291 readfds[0].fd = bufFd;
292 readfds[0].events = POLLIN;
293 readfds[1].fd = resFd;
294 readfds[1].events = POLLIN;
295 int fdsSize = sizeof(readfds) / sizeof(readfds[0]);
296 bool bPipeConnect = false;
297 while (true) {
298 if (bufFd < 0 || resFd < 0) {
299 ret = DUMP_POLL_FD;
300 resMsg.append("Result: bufFd or resFd < 0.\n");
301 break;
302 }
303 int pollRet = poll(readfds, fdsSize, timeout);
304 if (pollRet < 0) {
305 if (errno == EINTR) {
306 DFXLOG_INFO("%s :: %s :: errno == EINTR", DFXDUMPCATCHER_TAG.c_str(), __func__);
307 continue;
308 }
309 ret = DUMP_POLL_FAILED;
310 resMsg.append("Result: poll error, errno(" + std::to_string(errno) + ")\n");
311 break;
312 } else if (pollRet == 0) {
313 ret = DUMP_POLL_TIMEOUT;
314 resMsg.append("Result: poll timeout.\n");
315 break;
316 }
317
318 bool bufRet = true;
319 bool resRet = false;
320 bool eventRet = true;
321 for (int i = 0; i < fdsSize; ++i) {
322 if (!bPipeConnect && ((uint32_t)readfds[i].revents & POLLIN)) {
323 bPipeConnect = true;
324 }
325 if (bPipeConnect &&
326 (((uint32_t)readfds[i].revents & POLLERR) || ((uint32_t)readfds[i].revents & POLLHUP))) {
327 eventRet = false;
328 resMsg.append("Result: poll events error.\n");
329 break;
330 }
331
332 if (((uint32_t)readfds[i].revents & POLLIN) != POLLIN) {
333 continue;
334 }
335
336 if (readfds[i].fd == bufFd) {
337 bufRet = DoReadBuf(bufFd, bufMsg);
338 }
339
340 if (readfds[i].fd == resFd) {
341 resRet = DoReadRes(resFd, res, resMsg);
342 }
343 }
344
345 if ((eventRet == false) || (bufRet == false) || (resRet == true)) {
346 ret = DUMP_POLL_RETURN;
347 break;
348 }
349 }
350
351 DFXLOG_INFO("%s :: %s :: %s", DFXDUMPCATCHER_TAG.c_str(), __func__, resMsg.c_str());
352 if (isJson) {
353 msg = bufMsg;
354 } else {
355 msg = resMsg + bufMsg;
356 }
357
358 if (res) {
359 ret = DUMP_POLL_OK;
360 }
361 return ret;
362 }
363
DoReadBuf(int fd,std::string & msg)364 bool DfxDumpCatcher::DoReadBuf(int fd, std::string& msg)
365 {
366 bool ret = false;
367 char *buffer = new char[MAX_PIPE_SIZE];
368 do {
369 ssize_t nread = read(fd, buffer, MAX_PIPE_SIZE);
370 if (nread <= 0) {
371 DFXLOG_WARN("%s :: %s :: read error", DFXDUMPCATCHER_TAG.c_str(), __func__);
372 break;
373 }
374 DFXLOG_INFO("%s :: %s :: nread: %zu", DFXDUMPCATCHER_TAG.c_str(), __func__, nread);
375 ret = true;
376 msg.append(buffer);
377 } while (false);
378 delete []buffer;
379 return ret;
380 }
381
DoReadRes(int fd,bool & ret,std::string & msg)382 bool DfxDumpCatcher::DoReadRes(int fd, bool &ret, std::string& msg)
383 {
384 int32_t res = DumpErrorCode::DUMP_ESUCCESS;
385 ssize_t nread = read(fd, &res, sizeof(res));
386 if (nread != sizeof(res)) {
387 DFXLOG_WARN("%s :: %s :: read error", DFXDUMPCATCHER_TAG.c_str(), __func__);
388 return false;
389 }
390
391 if (res == DumpErrorCode::DUMP_ESUCCESS) {
392 ret = true;
393 }
394 msg.append("Result: " + DfxDumpRes::ToString(res) + "\n");
395 return true;
396 }
397
DumpCatchMultiPid(const std::vector<int> pidV,std::string & msg)398 bool DfxDumpCatcher::DumpCatchMultiPid(const std::vector<int> pidV, std::string& msg)
399 {
400 bool ret = false;
401 int pidSize = (int)pidV.size();
402 if (pidSize <= 0) {
403 DFXLOG_ERROR("%s :: %s :: param error, pidSize(%d).", DFXDUMPCATCHER_TAG.c_str(), __func__, pidSize);
404 return ret;
405 }
406
407 std::unique_lock<std::mutex> lck(mutex_);
408 int currentPid = getpid();
409 int currentTid = syscall(SYS_gettid);
410 DFXLOG_DEBUG("%s :: %s :: cPid(%d), cTid(%d), pidSize(%d).", DFXDUMPCATCHER_TAG.c_str(), \
411 __func__, currentPid, currentTid, pidSize);
412
413 time_t startTime = time(nullptr);
414 if (startTime > 0) {
415 DFXLOG_DEBUG("%s :: %s :: startTime(%" PRId64 ").", DFXDUMPCATCHER_TAG.c_str(), __func__, startTime);
416 }
417
418 for (int i = 0; i < pidSize; i++) {
419 int pid = pidV[i];
420 std::string pidStr;
421 if (DoDumpRemoteLocked(pid, 0, pidStr)) {
422 msg.append(pidStr + "\n");
423 } else {
424 msg.append("Failed to dump process:" + std::to_string(pid));
425 }
426
427 time_t currentTime = time(nullptr);
428 if (currentTime > 0) {
429 DFXLOG_DEBUG("%s :: %s :: startTime(%" PRId64 "), currentTime(%" PRId64 ").", DFXDUMPCATCHER_TAG.c_str(), \
430 __func__, startTime, currentTime);
431 if (currentTime > startTime + DUMP_CATCHE_WORK_TIME_S) {
432 break;
433 }
434 }
435 }
436
437 DFXLOG_DEBUG("%s :: %s :: msg(%s).", DFXDUMPCATCHER_TAG.c_str(), __func__, msg.c_str());
438 if (msg.find("Tid:") != std::string::npos) {
439 ret = true;
440 }
441 return ret;
442 }
443
IsValidJson(const std::string & json)444 bool DfxDumpCatcher::IsValidJson(const std::string& json)
445 {
446 int squareBrackets = 0;
447 int braces = 0;
448 for (const auto& ch : json) {
449 switch (ch) {
450 case '[':
451 squareBrackets++;
452 break;
453 case ']':
454 squareBrackets--;
455 break;
456 case '{':
457 braces++;
458 break;
459 case '}':
460 braces--;
461 break;
462 default:
463 break;
464 }
465 }
466 return squareBrackets == 0 && braces == 0;
467 }
468 } // namespace HiviewDFX
469 } // namespace OHOS
470