• 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;
384     std::string resMsg;
385     struct pollfd readfds[2];
386     (void)memset_s(readfds, sizeof(readfds), 0, sizeof(readfds));
387     readfds[0].fd = bufFd;
388     readfds[0].events = POLLIN;
389     readfds[1].fd = resFd;
390     readfds[1].events = POLLIN;
391     int fdsSize = sizeof(readfds) / sizeof(readfds[0]);
392     bool bPipeConnect = false;
393     while (true) {
394         if (bufFd < 0 || resFd < 0) {
395             ret = DUMP_POLL_FD;
396             resMsg.append("Result: bufFd or resFd  < 0.\n");
397             break;
398         }
399 
400         int pollRet = poll(readfds, fdsSize, timeout);
401         if (pollRet < 0) {
402             ret = DUMP_POLL_FAILED;
403             resMsg.append("Result: poll error, errno(" + std::to_string(errno) + ")\n");
404             break;
405         } else if (pollRet == 0) {
406             ret = DUMP_POLL_TIMEOUT;
407             resMsg.append("Result: poll timeout.\n");
408             break;
409         }
410 
411         bool bufRet = true;
412         bool resRet = false;
413         bool eventRet = true;
414         for (int i = 0; i < fdsSize; ++i) {
415             if (!bPipeConnect && ((uint32_t)readfds[i].revents & POLLIN)) {
416                 bPipeConnect = true;
417             }
418             if (bPipeConnect &&
419                 (((uint32_t)readfds[i].revents & POLLERR) || ((uint32_t)readfds[i].revents & POLLHUP))) {
420                 eventRet = false;
421                 resMsg.append("Result: poll events error.\n");
422                 break;
423             }
424 
425             if (((uint32_t)readfds[i].revents & POLLIN) != POLLIN) {
426                 continue;
427             }
428 
429             if (readfds[i].fd == bufFd) {
430                 bufRet = DoReadBuf(bufFd, bufMsg);
431             }
432 
433             if (readfds[i].fd == resFd) {
434                 resRet = DoReadRes(resFd, res, resMsg);
435             }
436         }
437 
438         if ((eventRet == false) || (bufRet == false) || (resRet == true)) {
439             ret = DUMP_POLL_RETURN;
440             break;
441         }
442     }
443 
444     DfxLogInfo("%s :: %s :: %s", DFXDUMPCATCHER_TAG.c_str(), __func__, resMsg.c_str());
445     msg = resMsg + bufMsg;
446     if (res) {
447         ret = DUMP_POLL_OK;
448     }
449     return ret;
450 }
451 
DoReadBuf(int fd,std::string & msg)452 bool DfxDumpCatcher::DoReadBuf(int fd, std::string& msg)
453 {
454     char buffer[LOG_BUF_LEN];
455     bzero(buffer, sizeof(buffer));
456     ssize_t nread = read(fd, buffer, sizeof(buffer) - 1);
457     if (nread <= 0) {
458         DfxLogWarn("%s :: %s :: read error", DFXDUMPCATCHER_TAG.c_str(), __func__);
459         return false;
460     }
461     msg.append(buffer);
462     return true;
463 }
464 
DoReadRes(int fd,bool & ret,std::string & msg)465 bool DfxDumpCatcher::DoReadRes(int fd, bool &ret, std::string& msg)
466 {
467     DumpResMsg dumpRes;
468     ssize_t nread = read(fd, &dumpRes, sizeof(struct DumpResMsg));
469     if (nread != sizeof(struct DumpResMsg)) {
470         DfxLogWarn("%s :: %s :: read error", DFXDUMPCATCHER_TAG.c_str(), __func__);
471         return false;
472     }
473 
474     if (dumpRes.res == ProcessDumpRes::DUMP_ESUCCESS) {
475         ret = true;
476     }
477     DfxDumpRes::GetInstance().SetRes(dumpRes.res);
478     msg.append("Result: " + DfxDumpRes::GetInstance().ToString() + "\n");
479     return true;
480 }
481 
DumpCatchMultiPid(const std::vector<int> pidV,std::string & msg)482 bool DfxDumpCatcher::DumpCatchMultiPid(const std::vector<int> pidV, std::string& msg)
483 {
484     bool ret = false;
485     int pidSize = (int)pidV.size();
486     if (pidSize <= 0) {
487         DfxLogError("%s :: %s :: param error, pidSize(%d).", DFXDUMPCATCHER_TAG.c_str(), __func__, pidSize);
488         return ret;
489     }
490 
491     std::unique_lock<std::mutex> lck(dumpCatcherMutex_);
492     int currentPid = getpid();
493     int currentTid = syscall(SYS_gettid);
494     DfxLogDebug("%s :: %s :: cPid(%d), cTid(%d), pidSize(%d).", DFXDUMPCATCHER_TAG.c_str(), \
495         __func__, currentPid, currentTid, pidSize);
496 
497     time_t startTime = time(nullptr);
498     if (startTime > 0) {
499         DfxLogDebug("%s :: %s :: startTime(%lld).", DFXDUMPCATCHER_TAG.c_str(), __func__, startTime);
500     }
501 
502     for (int i = 0; i < pidSize; i++) {
503         int pid = pidV[i];
504         std::string pidStr;
505         if (DoDumpRemoteLocked(pid, 0, pidStr)) {
506             msg.append(pidStr + "\n");
507         } else {
508             msg.append("Failed to dump process:" + std::to_string(pid));
509         }
510 
511         time_t currentTime = time(nullptr);
512         if (currentTime > 0) {
513             DfxLogDebug("%s :: %s :: startTime(%lld), currentTime(%lld).", DFXDUMPCATCHER_TAG.c_str(), \
514                 __func__, startTime, currentTime);
515             if (currentTime > startTime + DUMP_CATCHE_WORK_TIME_S) {
516                 break;
517             }
518         }
519     }
520 
521     DfxLogDebug("%s :: %s :: msg(%s).", DFXDUMPCATCHER_TAG.c_str(), __func__, msg.c_str());
522     if (msg.find("Tid:") != std::string::npos) {
523         ret = true;
524     }
525     return ret;
526 }
527 
InitFrameCatcher()528 bool DfxDumpCatcher::InitFrameCatcher()
529 {
530     std::unique_lock<std::mutex> lck(dumpCatcherMutex_);
531     bool ret = DfxUnwindLocal::GetInstance().Init();
532     if (!ret) {
533         DfxUnwindLocal::GetInstance().Destroy();
534     }
535     return ret;
536 }
537 
DestroyFrameCatcher()538 void DfxDumpCatcher::DestroyFrameCatcher()
539 {
540     std::unique_lock<std::mutex> lck(dumpCatcherMutex_);
541     DfxUnwindLocal::GetInstance().Destroy();
542 }
543 
RequestCatchFrame(int tid)544 bool DfxDumpCatcher::RequestCatchFrame(int tid)
545 {
546     if (tid == procInfo_.tid) {
547         return true;
548     }
549     return DfxUnwindLocal::GetInstance().SendAndWaitRequest(tid);
550 }
551 
CatchFrame(int tid,std::vector<std::shared_ptr<DfxFrame>> & frames)552 bool DfxDumpCatcher::CatchFrame(int tid, std::vector<std::shared_ptr<DfxFrame>>& frames)
553 {
554     if (tid <= 0 || frameCatcherPid_ != procInfo_.pid) {
555         DfxLogError("DfxDumpCatchFrame :: only support localDump.");
556         return false;
557     }
558 
559     if (!IsThreadInCurPid(tid) && !procInfo_.ns) {
560         DfxLogError("DfxDumpCatchFrame :: target tid is not in our task.");
561         return false;
562     }
563     size_t skipFramNum = DUMP_CATCHER_NUMBER_ONE;
564 
565     std::unique_lock<std::mutex> lck(dumpCatcherMutex_);
566     if (tid == procInfo_.tid) {
567         if (!DfxUnwindLocal::GetInstance().ExecLocalDumpUnwind(skipFramNum)) {
568             DfxLogError("DfxDumpCatchFrame :: failed to unwind for current thread(%d).", tid);
569             return false;
570         }
571     } else if (!DfxUnwindLocal::GetInstance().ExecLocalDumpUnwindByWait()) {
572         DfxLogError("DfxDumpCatchFrame :: failed to unwind for thread(%d).", tid);
573         return false;
574     }
575 
576     DfxUnwindLocal::GetInstance().CollectUnwindFrames(frames);
577     return true;
578 }
579 } // namespace HiviewDFX
580 } // namespace OHOS
581