• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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 /* This files contains faultlog sdk interface functions. */
17 
18 #include "dfx_dump_catcher.h"
19 
20 #include <cerrno>
21 #include <climits>
22 #include <csignal>
23 #include <cstdlib>
24 #include <cstring>
25 #include <ctime>
26 #include <memory>
27 #include <mutex>
28 #include <string>
29 #include <vector>
30 
31 #include <dirent.h>
32 #include <poll.h>
33 #include <unistd.h>
34 
35 #include <sys/syscall.h>
36 #include <sys/types.h>
37 
38 #include "dfx_util.h"
39 #include "dfx_define.h"
40 #include "dfx_dump_res.h"
41 #include "dfx_frame.h"
42 #include "dfx_log.h"
43 #include "dfx_unwind_local.h"
44 #include "faultloggerd_client.h"
45 #include "iosfwd"
46 #include "securec.h"
47 #include "strings.h"
48 #include "file_ex.h"
49 
50 static const int NUMBER_TEN = 10;
51 static const int MAX_TEMP_FILE_LENGTH = 256;
52 static const int DUMP_CATCHE_WORK_TIME_S = 60;
53 static const int NUMBER_TWO_KB = 2048;
54 static const int BACK_TRACE_DUMP_MIX_TIMEOUT_MS = 2000;
55 static const int BACK_TRACE_DUMP_CPP_TIMEOUT_MS = 10000;
56 
57 namespace OHOS {
58 namespace HiviewDFX {
59 namespace {
60 static const std::string DFXDUMPCATCHER_TAG = "DfxDumpCatcher";
IsThreadInCurPid(int32_t tid)61 static bool IsThreadInCurPid(int32_t tid)
62 {
63     std::string path = "/proc/self/task/" + std::to_string(tid);
64     return access(path.c_str(), F_OK) == 0;
65 }
66 enum DfxDumpPollRes : int32_t {
67     DUMP_POLL_INIT = -1,
68     DUMP_POLL_OK,
69     DUMP_POLL_FD,
70     DUMP_POLL_FAILED,
71     DUMP_POLL_TIMEOUT,
72     DUMP_POLL_RETURN,
73 };
74 }
75 
DfxDumpCatcher()76 DfxDumpCatcher::DfxDumpCatcher()
77 {
78     frameCatcherPid_ = 0;
79     (void)GetProcStatus(procInfo_);
80 }
81 
DfxDumpCatcher(int32_t pid)82 DfxDumpCatcher::DfxDumpCatcher(int32_t pid) : frameCatcherPid_(pid)
83 {
84     (void)GetProcStatus(procInfo_);
85 }
86 
~DfxDumpCatcher()87 DfxDumpCatcher::~DfxDumpCatcher()
88 {
89 }
90 
DoDumpCurrTid(const size_t skipFramNum,std::string & msg)91 bool DfxDumpCatcher::DoDumpCurrTid(const size_t skipFramNum, std::string& msg)
92 {
93     bool ret = false;
94     int currTid = syscall(SYS_gettid);
95     size_t tmpSkipFramNum = skipFramNum + 1;
96     ret = DfxUnwindLocal::GetInstance().ExecLocalDumpUnwind(tmpSkipFramNum);
97     if (ret) {
98         msg.append(DfxUnwindLocal::GetInstance().CollectUnwindResult(currTid));
99     } else {
100         msg.append("Failed to dump curr thread:" + std::to_string(currTid) + ".\n");
101     }
102     DfxLogDebug("%s :: DoDumpCurrTid :: return %d.", DFXDUMPCATCHER_TAG.c_str(), ret);
103     return ret;
104 }
105 
DoDumpLocalTid(const int tid,std::string & msg)106 bool DfxDumpCatcher::DoDumpLocalTid(const int tid, std::string& msg)
107 {
108     bool ret = false;
109     if (tid <= 0) {
110         DfxLogError("%s :: DoDumpLocalTid :: return false as param error.", DFXDUMPCATCHER_TAG.c_str());
111         return ret;
112     }
113 
114     if (DfxUnwindLocal::GetInstance().SendAndWaitRequest(tid)) {
115         ret = DfxUnwindLocal::GetInstance().ExecLocalDumpUnwindByWait();
116     }
117 
118     if (ret) {
119         msg.append(DfxUnwindLocal::GetInstance().CollectUnwindResult(tid));
120     } else {
121         msg.append("Failed to dump thread:" + std::to_string(tid) + ".\n");
122     }
123     DfxLogDebug("%s :: DoDumpLocalTid :: return %d.", DFXDUMPCATCHER_TAG.c_str(), ret);
124     return ret;
125 }
126 
DoDumpLocalPid(int pid,std::string & msg)127 bool DfxDumpCatcher::DoDumpLocalPid(int pid, std::string& msg)
128 {
129     bool ret = false;
130     if (pid <= 0) {
131         DfxLogError("%s :: DoDumpLocalPid :: return false as param error.", DFXDUMPCATCHER_TAG.c_str());
132         return ret;
133     }
134     size_t skipFramNum = DUMP_CATCHER_NUMBER_THREE;
135 
136     char realPath[PATH_MAX] = {'\0'};
137     if (realpath("/proc/self/task", realPath) == nullptr) {
138         DfxLogError("%s :: DoDumpLocalPid :: return false as realpath failed.", DFXDUMPCATCHER_TAG.c_str());
139         return ret;
140     }
141 
142     DIR *dir = opendir(realPath);
143     if (dir == nullptr) {
144         DfxLogError("%s :: DoDumpLocalPid :: return false as opendir failed.", DFXDUMPCATCHER_TAG.c_str());
145         return ret;
146     }
147 
148     struct dirent *ent;
149     while ((ent = readdir(dir)) != nullptr) {
150         if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
151             continue;
152         }
153 
154         pid_t tid = atoi(ent->d_name);
155         if (tid == 0) {
156             continue;
157         }
158 
159         int currentTid = syscall(SYS_gettid);
160         if (tid == currentTid) {
161             ret = DoDumpCurrTid(skipFramNum, msg);
162         } else {
163             ret = DoDumpLocalTid(tid, msg);
164         }
165     }
166 
167     if (closedir(dir) == -1) {
168         DfxLogError("closedir failed.");
169     }
170 
171     DfxLogDebug("%s :: DoDumpLocalPid :: return %d.", DFXDUMPCATCHER_TAG.c_str(), ret);
172     return ret;
173 }
174 
DoDumpRemoteLocked(int pid,int tid,std::string & msg)175 bool DfxDumpCatcher::DoDumpRemoteLocked(int pid, int tid, std::string& msg)
176 {
177     int type = static_cast<int>(DUMP_TYPE_NATIVE);
178     return DoDumpCatchRemote(type, pid, tid, msg);
179 }
180 
DoDumpLocalLocked(int pid,int tid,std::string & msg)181 bool DfxDumpCatcher::DoDumpLocalLocked(int pid, int tid, std::string& msg)
182 {
183     bool ret = DfxUnwindLocal::GetInstance().Init();
184     if (!ret) {
185         DfxLogError("%s :: DoDumpLocal :: Init error.", DFXDUMPCATCHER_TAG.c_str());
186         DfxUnwindLocal::GetInstance().Destroy();
187         return ret;
188     }
189 
190     size_t skipFramNum = DUMP_CATCHER_NUMBER_TWO;
191     if (tid == syscall(SYS_gettid)) {
192         ret = DoDumpCurrTid(skipFramNum, msg);
193     } else if (tid == 0) {
194         ret = DoDumpLocalPid(pid, msg);
195     } else {
196         if (!IsThreadInCurPid(tid)) {
197             msg.append("tid(" + std::to_string(tid) + ") is not in pid(" + std::to_string(pid) + ").\n");
198         } else {
199             ret = DoDumpLocalTid(tid, msg);
200         }
201     }
202     DfxUnwindLocal::GetInstance().Destroy();
203     DfxLogDebug("%s :: DoDumpLocal :: ret(%d).", DFXDUMPCATCHER_TAG.c_str(), ret);
204     return ret;
205 }
206 
DumpCatchMix(int pid,int tid,std::string & msg)207 bool DfxDumpCatcher::DumpCatchMix(int pid, int tid, std::string& msg)
208 {
209     int type = static_cast<int>(DUMP_TYPE_MIX);
210     return DoDumpCatchRemote(type, pid, tid, msg);
211 }
212 
DumpCatch(int pid,int tid,std::string & msg)213 bool DfxDumpCatcher::DumpCatch(int pid, int tid, std::string& msg)
214 {
215     bool ret = false;
216     if (pid <= 0 || tid < 0) {
217         DfxLogError("%s :: dump_catch :: param error.", DFXDUMPCATCHER_TAG.c_str());
218         return ret;
219     }
220 
221     std::unique_lock<std::mutex> lck(dumpCatcherMutex_);
222     int currentPid = getpid();
223     DfxLogDebug("%s :: dump_catch :: cPid(%d), pid(%d), tid(%d).",
224         DFXDUMPCATCHER_TAG.c_str(), currentPid, pid, tid);
225 
226     if (pid == currentPid) {
227         ret = DoDumpLocalLocked(pid, tid, msg);
228     } else {
229         ret = DoDumpRemoteLocked(pid, tid, msg);
230     }
231     DfxLogDebug("%s :: dump_catch :: ret: %d, msg: %s", DFXDUMPCATCHER_TAG.c_str(), ret, msg.c_str());
232     return ret;
233 }
234 
DumpCatchFd(int pid,int tid,std::string & msg,int fd)235 bool DfxDumpCatcher::DumpCatchFd(int pid, int tid, std::string& msg, int fd)
236 {
237     bool ret = false;
238     ret = DumpCatch(pid, tid, msg);
239     if (fd > 0) {
240         ret = write(fd, msg.c_str(), msg.length());
241     }
242     return ret;
243 }
244 
SignalTargetProcess(const int type,int pid,int tid)245 static int SignalTargetProcess(const int type, int pid, int tid)
246 {
247     DfxLogDebug("%s :: SignalTargetProcess :: type: %d", DFXDUMPCATCHER_TAG.c_str(), type);
248 #pragma clang diagnostic push
249 #pragma clang diagnostic ignored "-Winitializer-overrides"
250     siginfo_t si = {
251         .si_signo = SIGDUMP,
252         .si_errno = 0,
253         .si_code = type,
254         .si_value.sival_int = tid,
255         .si_pid = getpid(),
256         .si_uid = static_cast<uid_t>(syscall(SYS_gettid))
257     };
258 #pragma clang diagnostic pop
259     if (tid == 0) {
260         if (syscall(SYS_rt_sigqueueinfo, pid, si.si_signo, &si) != 0) {
261             return errno;
262         }
263     } else {
264         if (syscall(SYS_rt_tgsigqueueinfo, pid, tid, si.si_signo, &si) != 0) {
265             return errno;
266         }
267     }
268     return 0;
269 }
270 
LoadPathContent(const std::string & desc,const std::string & path,std::string & result)271 static void LoadPathContent(const std::string& desc, const std::string& path, std::string& result)
272 {
273     if (access(path.c_str(), F_OK) != 0) {
274         result.append("Target path(");
275         result.append(path);
276         result.append(") is not exist. errno(");
277         result.append(std::to_string(errno));
278         result.append(").\n");
279         return;
280     }
281 
282     std::string content;
283     LoadStringFromFile(path, content);
284     if (!content.empty()) {
285         std::string str = desc + ":\n" + content + "\n";
286         result.append(str);
287     }
288     return;
289 }
290 
LoadPidStat(const int pid,std::string & msg)291 static void LoadPidStat(const int pid, std::string& msg)
292 {
293     std::string statPath = "/proc/" + std::to_string(pid) + "/stat";
294     std::string wchanPath = "/proc/" + std::to_string(pid) + "/wchan";
295     LoadPathContent("stat", statPath, msg);
296     LoadPathContent("wchan", wchanPath, msg);
297 }
298 
DoDumpCatchRemote(const int type,int pid,int tid,std::string & msg)299 bool DfxDumpCatcher::DoDumpCatchRemote(const int type, int pid, int tid, std::string& msg)
300 {
301     bool ret = false;
302     if (pid <= 0 || tid < 0) {
303         msg.append("Result: pid(" + std::to_string(pid) + ") param error.\n");
304         DfxLogWarn("%s :: %s :: %s", DFXDUMPCATCHER_TAG.c_str(), __func__, msg.c_str());
305         return ret;
306     }
307 
308     int sdkdumpRet = RequestSdkDump(type, pid, tid);
309     if (sdkdumpRet != (int)FaultLoggerSdkDumpResp::SDK_DUMP_PASS) {
310         if (sdkdumpRet == (int)FaultLoggerSdkDumpResp::SDK_DUMP_REPEAT) {
311             msg.append("Result: pid(" + std::to_string(pid) + ") is dumping.\n");
312             DfxLogWarn("%s :: %s :: %s", DFXDUMPCATCHER_TAG.c_str(), __func__, msg.c_str());
313             return ret;
314         } else {
315             if (sdkdumpRet == (int)FaultLoggerSdkDumpResp::SDK_DUMP_REJECT) {
316                 msg.append("Result: pid(" + std::to_string(pid) + ") check permission error.\n");
317             }
318             int err = SignalTargetProcess(type, pid, tid);
319             if (err != 0) {
320                 msg.append("Result: pid(" + std::to_string(pid) + ") syscall SIGDUMP error, \
321                     errno(" + std::to_string(err) + ").\n");
322                 DfxLogWarn("%s :: %s :: %s", DFXDUMPCATCHER_TAG.c_str(), __func__, msg.c_str());
323                 RequestDelPipeFd(pid);
324                 return ret;
325             }
326         }
327     }
328 
329     int pollRet = DoDumpRemotePid(type, pid, msg);
330     switch (pollRet) {
331         case DUMP_POLL_OK:
332             ret = true;
333             break;
334         case DUMP_POLL_TIMEOUT:
335             if (type == DUMP_TYPE_MIX) {
336                 msg.append("Result: pid(" + std::to_string(pid) + ") dump mix timeout, try dump native frame.\n");
337                 int type = static_cast<int>(DUMP_TYPE_NATIVE);
338                 DoDumpCatchRemote(type, pid, tid, msg);
339             } else if (type == DUMP_TYPE_NATIVE) {
340                 LoadPidStat(pid, msg);
341             }
342             break;
343         default:
344             break;
345     }
346     DfxLogInfo("%s :: %s :: ret: %d", DFXDUMPCATCHER_TAG.c_str(), __func__, ret);
347     return ret;
348 }
349 
DoDumpRemotePid(const int type,int pid,std::string & msg)350 int DfxDumpCatcher::DoDumpRemotePid(const int type, int pid, std::string& msg)
351 {
352     int readBufFd = RequestPipeFd(pid, FaultLoggerPipeType::PIPE_FD_READ_BUF);
353     DfxLogDebug("read buf fd: %d", readBufFd);
354 
355     int readResFd = RequestPipeFd(pid, FaultLoggerPipeType::PIPE_FD_READ_RES);
356     DfxLogDebug("read res fd: %d", readResFd);
357 
358     int timeout = BACK_TRACE_DUMP_CPP_TIMEOUT_MS;
359     if (type == DUMP_TYPE_MIX) {
360         timeout = BACK_TRACE_DUMP_MIX_TIMEOUT_MS;
361     }
362 
363     int ret = DoDumpRemotePoll(readBufFd, readResFd, timeout, msg);
364 
365     // request close fds in faultloggerd
366     RequestDelPipeFd(pid);
367     if (readBufFd >= 0) {
368         close(readBufFd);
369         readBufFd = -1;
370     }
371     if (readResFd >= 0) {
372         close(readResFd);
373         readResFd = -1;
374     }
375     DfxLogInfo("%s :: %s :: ret: %d", DFXDUMPCATCHER_TAG.c_str(), __func__, ret);
376     return ret;
377 }
378 
DoDumpRemotePoll(int bufFd,int resFd,int timeout,std::string & msg)379 int DfxDumpCatcher::DoDumpRemotePoll(int bufFd, int resFd, int timeout, std::string& msg)
380 {
381     int ret = DUMP_POLL_INIT;
382     bool res = false;
383     std::string bufMsg, resMsg;
384     struct pollfd readfds[2];
385     (void)memset_s(readfds, sizeof(readfds), 0, sizeof(readfds));
386     readfds[0].fd = bufFd;
387     readfds[0].events = POLLIN;
388     readfds[1].fd = resFd;
389     readfds[1].events = POLLIN;
390     int fdsSize = sizeof(readfds) / sizeof(readfds[0]);
391     bool bPipeConnect = false;
392     while (true) {
393         if (bufFd < 0 || resFd < 0) {
394             ret = DUMP_POLL_FD;
395             resMsg.append("Result: bufFd or resFd  < 0.\n");
396             break;
397         }
398 
399         int pollRet = poll(readfds, fdsSize, timeout);
400         if (pollRet < 0) {
401             ret = DUMP_POLL_FAILED;
402             resMsg.append("Result: poll error, errno(" + std::to_string(errno) + ")\n");
403             break;
404         } else if (pollRet == 0) {
405             ret = DUMP_POLL_TIMEOUT;
406             resMsg.append("Result: poll timeout.\n");
407             break;
408         }
409 
410         bool bufRet = true, resRet = false, eventRet = true;
411         for (int i = 0; i < fdsSize; ++i) {
412             if (!bPipeConnect && ((uint32_t)readfds[i].revents & POLLIN)) {
413                 bPipeConnect = true;
414             }
415             if (bPipeConnect &&
416                 (((uint32_t)readfds[i].revents & POLLERR) || ((uint32_t)readfds[i].revents & POLLHUP))) {
417                 eventRet = false;
418                 resMsg.append("Result: poll events error.\n");
419                 break;
420             }
421 
422             if (((uint32_t)readfds[i].revents & POLLIN) != POLLIN) {
423                 continue;
424             }
425 
426             if (readfds[i].fd == bufFd) {
427                 bufRet = DoReadBuf(bufFd, bufMsg);
428             }
429 
430             if (readfds[i].fd == resFd) {
431                 resRet = DoReadRes(resFd, res, resMsg);
432             }
433         }
434 
435         if ((eventRet == false) || (bufRet == false) || (resRet == true)) {
436             ret = DUMP_POLL_RETURN;
437             break;
438         }
439     }
440 
441     DfxLogInfo("%s :: %s :: %s", DFXDUMPCATCHER_TAG.c_str(), __func__, resMsg.c_str());
442     msg = resMsg + bufMsg;
443     if (res) {
444         ret = DUMP_POLL_OK;
445     }
446     return ret;
447 }
448 
DoReadBuf(int fd,std::string & msg)449 bool DfxDumpCatcher::DoReadBuf(int fd, std::string& msg)
450 {
451     char buffer[LOG_BUF_LEN];
452     bzero(buffer, sizeof(buffer));
453     ssize_t nread = read(fd, buffer, sizeof(buffer) - 1);
454     if (nread <= 0) {
455         DfxLogWarn("%s :: %s :: read error", DFXDUMPCATCHER_TAG.c_str(), __func__);
456         return false;
457     }
458     msg.append(buffer);
459     return true;
460 }
461 
DoReadRes(int fd,bool & ret,std::string & msg)462 bool DfxDumpCatcher::DoReadRes(int fd, bool &ret, std::string& msg)
463 {
464     DumpResMsg dumpRes;
465     ssize_t nread = read(fd, &dumpRes, sizeof(struct DumpResMsg));
466     if (nread != sizeof(struct DumpResMsg)) {
467         DfxLogWarn("%s :: %s :: read error", DFXDUMPCATCHER_TAG.c_str(), __func__);
468         return false;
469     }
470 
471     if (dumpRes.res == ProcessDumpRes::DUMP_ESUCCESS) {
472         ret = true;
473     }
474     DfxDumpRes::GetInstance().SetRes(dumpRes.res);
475     msg.append("Result: " + DfxDumpRes::GetInstance().ToString() + "\n");
476     return true;
477 }
478 
DumpCatchMultiPid(const std::vector<int> pidV,std::string & msg)479 bool DfxDumpCatcher::DumpCatchMultiPid(const std::vector<int> pidV, std::string& msg)
480 {
481     bool ret = false;
482     int pidSize = (int)pidV.size();
483     if (pidSize <= 0) {
484         DfxLogError("%s :: %s :: param error, pidSize(%d).", DFXDUMPCATCHER_TAG.c_str(), __func__, pidSize);
485         return ret;
486     }
487 
488     std::unique_lock<std::mutex> lck(dumpCatcherMutex_);
489     int currentPid = getpid();
490     int currentTid = syscall(SYS_gettid);
491     DfxLogDebug("%s :: %s :: cPid(%d), cTid(%d), pidSize(%d).", DFXDUMPCATCHER_TAG.c_str(), \
492         __func__, currentPid, currentTid, pidSize);
493 
494     time_t startTime = time(nullptr);
495     if (startTime > 0) {
496         DfxLogDebug("%s :: %s :: startTime(%lld).", DFXDUMPCATCHER_TAG.c_str(), __func__, startTime);
497     }
498 
499     for (int i = 0; i < pidSize; i++) {
500         int pid = pidV[i];
501         std::string pidStr;
502         if (DoDumpRemoteLocked(pid, 0, pidStr)) {
503             msg.append(pidStr + "\n");
504         } else {
505             msg.append("Failed to dump process:" + std::to_string(pid));
506         }
507 
508         time_t currentTime = time(nullptr);
509         if (currentTime > 0) {
510             DfxLogDebug("%s :: %s :: startTime(%lld), currentTime(%lld).", DFXDUMPCATCHER_TAG.c_str(), \
511                 __func__, startTime, currentTime);
512             if (currentTime > startTime + DUMP_CATCHE_WORK_TIME_S) {
513                 break;
514             }
515         }
516     }
517 
518     DfxLogDebug("%s :: %s :: msg(%s).", DFXDUMPCATCHER_TAG.c_str(), __func__, msg.c_str());
519     if (msg.find("Tid:") != std::string::npos) {
520         ret = true;
521     }
522     return ret;
523 }
524 
InitFrameCatcher()525 bool DfxDumpCatcher::InitFrameCatcher()
526 {
527     std::unique_lock<std::mutex> lck(dumpCatcherMutex_);
528     bool ret = DfxUnwindLocal::GetInstance().Init();
529     if (!ret) {
530         DfxUnwindLocal::GetInstance().Destroy();
531     }
532     return ret;
533 }
534 
DestroyFrameCatcher()535 void DfxDumpCatcher::DestroyFrameCatcher()
536 {
537     std::unique_lock<std::mutex> lck(dumpCatcherMutex_);
538     DfxUnwindLocal::GetInstance().Destroy();
539 }
540 
RequestCatchFrame(int tid)541 bool DfxDumpCatcher::RequestCatchFrame(int tid)
542 {
543     if (tid == procInfo_.tid) {
544         return true;
545     }
546     return DfxUnwindLocal::GetInstance().SendAndWaitRequest(tid);
547 }
548 
CatchFrame(int tid,std::vector<std::shared_ptr<DfxFrame>> & frames)549 bool DfxDumpCatcher::CatchFrame(int tid, std::vector<std::shared_ptr<DfxFrame>>& frames)
550 {
551     if (tid <= 0 || frameCatcherPid_ != procInfo_.pid) {
552         DfxLogError("DfxDumpCatchFrame :: only support localDump.");
553         return false;
554     }
555 
556     if (!IsThreadInCurPid(tid) && !procInfo_.ns) {
557         DfxLogError("DfxDumpCatchFrame :: target tid is not in our task.");
558         return false;
559     }
560     size_t skipFramNum = DUMP_CATCHER_NUMBER_ONE;
561 
562     std::unique_lock<std::mutex> lck(dumpCatcherMutex_);
563     if (tid == procInfo_.tid) {
564         if (!DfxUnwindLocal::GetInstance().ExecLocalDumpUnwind(skipFramNum)) {
565             DfxLogError("DfxDumpCatchFrame :: failed to unwind for current thread(%d).", tid);
566             return false;
567         }
568     } else if (!DfxUnwindLocal::GetInstance().ExecLocalDumpUnwindByWait()) {
569         DfxLogError("DfxDumpCatchFrame :: failed to unwind for thread(%d).", tid);
570         return false;
571     }
572 
573     DfxUnwindLocal::GetInstance().CollectUnwindFrames(frames);
574     return true;
575 }
576 } // namespace HiviewDFX
577 } // namespace OHOS
578