• 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 "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