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