• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 #include "mix_stack_dumper.h"
17 
18 #include <ctime>
19 #include <dirent.h>
20 #include <securec.h>
21 #include <unistd.h>
22 
23 #include "dfx_dump_catcher.h"
24 #include "dfx_dump_res.h"
25 #include "faultloggerd_client.h"
26 #include "file_ex.h"
27 #include "hilog_wrapper.h"
28 #include "js_runtime.h"
29 
30 namespace OHOS {
31 namespace AppExecFwk {
32 std::weak_ptr<EventHandler> MixStackDumper::signalHandler_;
33 std::weak_ptr<OHOSApplication> MixStackDumper::application_;
34 namespace {
35 static const char PID_STR_NAME[] = "Pid:";
36 static const char PPID_STR_NAME[] = "PPid:";
37 static const char NSPID_STR_NAME[] = "NSpid:";
38 static const char PROC_SELF_STATUS_PATH[] = "/proc/self/status";
39 static const std::string PROC_SELF_CMDLINE_PATH = "/proc/self/cmdline";
40 static constexpr int STATUS_LINE_SIZE = 1024;
41 static constexpr int FRAME_BUF_LEN = 1024;
42 static constexpr int HEADER_BUF_LEN = 512;
43 static constexpr int NATIVE_DUMP = -1;
44 static constexpr int MIX_DUMP = -2;
45 static constexpr int NAMESPACE_MATCH_NUM = 2;
46 typedef struct ProcInfo {
47     int tid;
48     int pid;
49     int ppid;
50     bool ns;
51 } ProcInfo;
52 }
53 
54 typedef void (*DumpSignalHandlerFunc) (int sig, siginfo_t *si, void *context);
55 static DumpSignalHandlerFunc g_dumpSignalHandlerFunc = nullptr;
56 static pid_t g_targetDumpTid = -1;
57 static struct ProcInfo g_procInfo;
58 
PrintJsFrame(const JsFrames & jsFrame)59 static std::string PrintJsFrame(const JsFrames& jsFrame)
60 {
61     return "  at " + jsFrame.functionName + " (" + jsFrame.fileName + ":" + jsFrame.pos + ")\n";
62 }
63 
PrintNativeFrame(std::shared_ptr<OHOS::HiviewDFX::DfxFrame> frame)64 static std::string PrintNativeFrame(std::shared_ptr<OHOS::HiviewDFX::DfxFrame> frame)
65 {
66     if (frame == nullptr) {
67         return "";
68     }
69     char buf[FRAME_BUF_LEN] = {0};
70     std::string mapName = frame->GetFrameMapName();
71     if (mapName.empty()) {
72         mapName = "Unknown";
73     }
74 
75 #ifdef __LP64__
76     char frameFormatWithMapName[] = "#%02zu pc %016" PRIx64 " %s\n";
77     char frameFormatWithFuncName[] = "#%02zu pc %016" PRIx64 " %s(%s+%" PRIu64 ")\n";
78 #else
79     char frameFormatWithMapName[] = "#%02zu pc %08" PRIx64 " %s\n";
80     char frameFormatWithFuncName[] = "#%02zu pc %08" PRIx64 " %s(%s+%" PRIu64 ")\n";
81 #endif
82 
83     if (frame->GetFrameFuncName().empty()) {
84         int ret = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, frameFormatWithMapName, \
85             frame->GetFrameIndex(), frame->GetFrameRelativePc(), mapName.c_str());
86         if (ret <= 0) {
87             HILOG_ERROR("DfxMixStackDumper::PrintNativeFrame snprintf_s failed.");
88         }
89         return std::string(buf);
90     }
91 
92     int ret = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, frameFormatWithFuncName, \
93         frame->GetFrameIndex(), frame->GetFrameRelativePc(), mapName.c_str(),\
94         frame->GetFrameFuncName().c_str(), frame->GetFrameFuncOffset());
95     if (ret <= 0) {
96         HILOG_ERROR("DfxMixStackDumper::PrintNativeFrame snprintf_s failed.");
97     }
98     return std::string(buf);
99 }
100 
GetPid()101 static pid_t GetPid()
102 {
103     return g_procInfo.pid;
104 }
105 
HasNameSpace()106 static bool HasNameSpace()
107 {
108     return g_procInfo.ns;
109 }
110 
GetProcStatus(struct ProcInfo * procInfo)111 static int GetProcStatus(struct ProcInfo* procInfo)
112 {
113     procInfo->pid = getpid();
114     procInfo->tid = gettid();
115     procInfo->ppid = getppid();
116     procInfo->ns = false;
117     char buf[STATUS_LINE_SIZE];
118     FILE *fp = fopen(PROC_SELF_STATUS_PATH, "r");
119     if (fp == NULL) {
120         return -1;
121     }
122     int p = 0, pp = 0, t = 0;
123     while (!feof(fp)) {
124         if (fgets(buf, STATUS_LINE_SIZE, fp) == NULL) {
125             fclose(fp);
126             return -1;
127         }
128         if (strncmp(buf, PID_STR_NAME, strlen(PID_STR_NAME)) == 0) {
129             // Pid:    1892
130             if (sscanf_s(buf, "%*[^0-9]%d", &p) != 1) {
131                 perror("sscanf_s failed.");
132             }
133             procInfo->pid = p;
134             if (procInfo->pid == getpid()) {
135                 procInfo->ns = false;
136                 break;
137             }
138             procInfo->ns = true;
139             continue;
140         }
141         if (strncmp(buf, PPID_STR_NAME, strlen(PPID_STR_NAME)) == 0) {
142             // PPid:   240
143             if (sscanf_s(buf, "%*[^0-9]%d", &pp) != 1) {
144                 perror("sscanf_s failed.");
145             }
146             procInfo->ppid = pp;
147             continue;
148         }
149         // NSpid:  1892    1
150         if (strncmp(buf, NSPID_STR_NAME, strlen(NSPID_STR_NAME)) == 0) {
151             if (sscanf_s(buf, "%*[^0-9]%d%*[^0-9]%d", &p, &t) != NAMESPACE_MATCH_NUM) {
152                 perror("sscanf_s failed.");
153             }
154             procInfo->tid = t;
155             break;
156         }
157     }
158     (void)fclose(fp);
159     return 0;
160 }
161 
TidToNstid(const int tid,int & nstid)162 static void TidToNstid(const int tid, int& nstid)
163 {
164     char path[NAME_LEN];
165     (void)memset_s(path, sizeof(path), '\0', sizeof(path));
166     if (snprintf_s(path, sizeof(path), sizeof(path) - 1, "/proc/%d/task/%d/status", GetPid(), tid) <= 0) {
167         HILOG_WARN("snprintf_s error.");
168         return;
169     }
170 
171     char buf[STATUS_LINE_SIZE];
172     FILE *fp = fopen(path, "r");
173     if (fp == NULL) {
174         return;
175     }
176 
177     int p = 0, t = 0;
178     while (!feof(fp)) {
179         if (fgets(buf, STATUS_LINE_SIZE, fp) == NULL) {
180             fclose(fp);
181             return;
182         }
183 
184         // NSpid:  1892    1
185         if (strncmp(buf, NSPID_STR_NAME, strlen(NSPID_STR_NAME)) == 0) {
186             if (sscanf_s(buf, "%*[^0-9]%d%*[^0-9]%d", &p, &t) != NAMESPACE_MATCH_NUM) {
187                 HILOG_ERROR("sscanf_s failed.");
188             }
189             nstid = t;
190             break;
191         }
192     }
193     (void)fclose(fp);
194 }
195 
GetCurrentTimeStr(uint64_t current=0)196 static std::string GetCurrentTimeStr(uint64_t current = 0)
197 {
198     time_t now = time(nullptr);
199     uint64_t millisecond = 0;
200     const uint64_t ratio = 1000;
201     if (current > static_cast<uint64_t>(now)) {
202         millisecond = current % ratio;
203         now = static_cast<time_t>(current / ratio);
204     }
205 
206     auto tm = std::localtime(&now);
207     char seconds[128] = { 0 }; // 128 : time buffer size
208     if (tm == nullptr || strftime(seconds, sizeof(seconds) - 1, "%Y-%m-%d %H:%M:%S", tm) == 0) {
209         return "invalid timestamp\n";
210     }
211 
212     char formatTimeBuf[256] = { 0 }; // 256 : buffer size
213     int ret = snprintf_s(formatTimeBuf, sizeof(formatTimeBuf), sizeof(formatTimeBuf) - 1,
214         "%s.%03u\n", seconds, millisecond);
215     if (ret <= 0) {
216         return "invalid timestamp\n";
217     }
218     return std::string(formatTimeBuf, strlen(formatTimeBuf));
219 }
220 
Dump_SignalHandler(int sig,siginfo_t * si,void * context)221 void MixStackDumper::Dump_SignalHandler(int sig, siginfo_t *si, void *context)
222 {
223     switch (si->si_code) {
224         case NATIVE_DUMP: {
225             if (g_dumpSignalHandlerFunc != nullptr) {
226                 g_dumpSignalHandlerFunc(sig, si, context);
227             }
228             break;
229         }
230         case MIX_DUMP: {
231             HILOG_INFO("Received mix stack dump request.");
232             auto handler = signalHandler_.lock();
233             if (handler == nullptr) {
234                 return;
235             }
236             g_targetDumpTid = si->si_value.sival_int;
237             handler->PostTask(&MixStackDumper::HandleMixDumpRequest);
238             break;
239         }
240         default:
241             break;
242     }
243 }
244 
InstallDumpHandler(std::shared_ptr<OHOSApplication> application,std::shared_ptr<EventHandler> handler)245 void MixStackDumper::InstallDumpHandler(std::shared_ptr<OHOSApplication> application,
246     std::shared_ptr<EventHandler> handler)
247 {
248     MixStackDumper::signalHandler_ = handler;
249     MixStackDumper::application_ = application;
250     struct sigaction newDumpAction;
251     struct sigaction oldDumpAction;
252     (void)memset_s(&newDumpAction, sizeof(newDumpAction), 0, sizeof(newDumpAction));
253     (void)memset_s(&oldDumpAction, sizeof(oldDumpAction), 0, sizeof(oldDumpAction));
254     sigfillset(&newDumpAction.sa_mask);
255     newDumpAction.sa_sigaction = Dump_SignalHandler;
256     newDumpAction.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
257     sigaction(SIGDUMP, &newDumpAction, &oldDumpAction);
258     if (oldDumpAction.sa_sigaction != nullptr) {
259         g_dumpSignalHandlerFunc = oldDumpAction.sa_sigaction;
260     }
261 }
262 
IsJsNativePcEqual(uintptr_t * jsNativePointer,uint64_t nativePc,uint64_t nativeOffset)263 bool MixStackDumper::IsJsNativePcEqual(uintptr_t *jsNativePointer, uint64_t nativePc, uint64_t nativeOffset)
264 {
265     uint64_t jsPc_ = (uint64_t)jsNativePointer;
266     if (nativePc - nativeOffset == jsPc_) {
267         return true;
268     }
269     return false;
270 }
271 
BuildJsNativeMixStack(int fd,std::vector<JsFrames> & jsFrames,std::vector<std::shared_ptr<OHOS::HiviewDFX::DfxFrame>> & nativeFrames)272 void MixStackDumper::BuildJsNativeMixStack(int fd, std::vector<JsFrames>& jsFrames,
273     std::vector<std::shared_ptr<OHOS::HiviewDFX::DfxFrame>>& nativeFrames)
274 {
275     uint32_t jsIdx = 0;
276     uint32_t nativeIdx = 0;
277     std::string mixStackStr = "";
278     while (jsIdx < jsFrames.size() && jsFrames[jsIdx].nativePointer == nullptr) {
279         jsIdx++;
280     }
281     while (jsIdx < jsFrames.size() && nativeIdx < nativeFrames.size()) {
282         if (jsFrames[jsIdx].nativePointer == nullptr) {
283             mixStackStr += PrintJsFrame(jsFrames[jsIdx]);
284             jsIdx++;
285             continue;
286         }
287         if (IsJsNativePcEqual(jsFrames[jsIdx].nativePointer, nativeFrames[nativeIdx]->GetFramePc(),
288             nativeFrames[nativeIdx]->GetFrameFuncOffset())) {
289             HILOG_DEBUG("DfxMixStackDumper::BuildJsNativeMixStack pc register values matched.");
290             mixStackStr += PrintNativeFrame(nativeFrames[nativeIdx]);
291             mixStackStr += PrintJsFrame(jsFrames[jsIdx]);
292             nativeIdx++;
293             jsIdx++;
294         } else {
295             mixStackStr += PrintNativeFrame(nativeFrames[nativeIdx]);
296             nativeIdx++;
297         }
298     }
299     while (nativeIdx < nativeFrames.size()) {
300         mixStackStr +=  PrintNativeFrame(nativeFrames[nativeIdx]);
301         nativeIdx++;
302     }
303     write(fd, mixStackStr.c_str(), mixStackStr.size());
304     write(fd, "\n", 1);
305 }
306 
GetThreadStackTraceLabel(pid_t tid)307 std::string MixStackDumper::GetThreadStackTraceLabel(pid_t tid)
308 {
309     std::ostringstream result;
310     result << "Tid:" << tid;
311     std::string path = "/proc/self/task/" + std::to_string(tid) + "/comm";
312     std::string threadComm;
313     if (LoadStringFromFile(path, threadComm)) {
314         result << " comm:" << threadComm;
315     } else {
316         result << std::endl;
317     }
318     return result.str();
319 }
320 
PrintNativeFrames(int fd,std::vector<std::shared_ptr<OHOS::HiviewDFX::DfxFrame>> & nativeFrames)321 void MixStackDumper::PrintNativeFrames(int fd, std::vector<std::shared_ptr<OHOS::HiviewDFX::DfxFrame>>& nativeFrames)
322 {
323     for (const auto& frame : nativeFrames) {
324         std::string nativeFrameStr = PrintNativeFrame(frame);
325         write(fd, nativeFrameStr.c_str(), nativeFrameStr.size());
326     }
327     write(fd, "\n", 1);
328 }
329 
PrintProcessHeader(int fd,pid_t pid,uid_t uid)330 void MixStackDumper::PrintProcessHeader(int fd, pid_t pid, uid_t uid)
331 {
332     char headerBuf[HEADER_BUF_LEN] = { 0 };
333     std::string processName = "";
334     int ret = 1;
335     if (LoadStringFromFile(PROC_SELF_CMDLINE_PATH, processName)) {
336         ret = snprintf_s(headerBuf, HEADER_BUF_LEN, HEADER_BUF_LEN - 1,
337                          "Timestamp:%sPid:%d\nUid:%d\nProcess name:%s\n",
338                          GetCurrentTimeStr().c_str(), pid, uid, processName.c_str());
339     } else {
340         ret = snprintf_s(headerBuf, HEADER_BUF_LEN, HEADER_BUF_LEN - 1,
341                          "Timestamp:%sPid:%d\nUid:%d\nProcess name:unknown\n",
342                          GetCurrentTimeStr().c_str(), pid, uid);
343     }
344     if (ret <= 0) {
345         HILOG_ERROR("snprintf_s process mix stack header failed.");
346         return;
347     }
348     write(fd, headerBuf, strlen(headerBuf));
349 }
350 
DumpMixFrame(int fd,pid_t nstid,pid_t tid)351 bool MixStackDumper::DumpMixFrame(int fd, pid_t nstid, pid_t tid)
352 {
353     if (catcher_ == nullptr) {
354         HILOG_ERROR("No FrameCatcher? call init first.");
355         return false;
356     }
357 
358     std::string threadComm = GetThreadStackTraceLabel(tid);
359     write(fd, threadComm.c_str(), threadComm.size());
360     if (!catcher_->RequestCatchFrame(nstid)) {
361         std::string result = "Failed to suspend thread(" + std::to_string(nstid) + ").\n";
362         HILOG_ERROR("%{public}s", result.c_str());
363         write(fd, result.c_str(), result.size());
364         return false;
365     }
366 
367     bool onlyDumpNative = false;
368     std::vector<JsFrames> jsFrames;
369     auto application = application_.lock();
370     if (application != nullptr && application->GetRuntime() != nullptr) {
371         bool ret = application->GetRuntime()->BuildJsStackInfoList(nstid, jsFrames);
372         if (!ret || jsFrames.size() == 0) {
373             onlyDumpNative = true;
374         }
375     }
376 
377     std::vector<std::shared_ptr<OHOS::HiviewDFX::DfxFrame>> nativeFrames;
378     if (catcher_->CatchFrame(nstid, nativeFrames) == false) {
379         HILOG_ERROR("DfxMixStackDumper::DumpMixFrame Capture thread(%{public}d) native frames failed.", nstid);
380     }
381 
382     if (onlyDumpNative) {
383         PrintNativeFrames(fd, nativeFrames);
384         return true;
385     }
386 
387     BuildJsNativeMixStack(fd, jsFrames, nativeFrames);
388     return true;
389 }
390 
GetThreadList(std::vector<pid_t> & threadList)391 void MixStackDumper::GetThreadList(std::vector<pid_t>& threadList)
392 {
393     char realPath[PATH_MAX] = {'\0'};
394     if (realpath("/proc/self/task", realPath) == nullptr) {
395         HILOG_ERROR("DfxMixStackDumper::GetThreadList return false as realpath failed.");
396         return;
397     }
398     DIR *dir = opendir(realPath);
399     if (dir == nullptr) {
400         HILOG_ERROR("DfxMixStackDumper::GetThreadList return false as opendir failed.");
401         return;
402     }
403     struct dirent *ent;
404     while ((ent = readdir(dir)) != nullptr) {
405         if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
406             continue;
407         }
408         pid_t tid = atoi(ent->d_name);
409         if (tid == 0) {
410             continue;
411         }
412         threadList.emplace_back(tid);
413     }
414     if (closedir(dir) == -1) {
415         HILOG_ERROR("MixStackDumper::GetThreadList closedir failed.");
416     }
417 }
418 
Init(pid_t pid)419 void MixStackDumper::Init(pid_t pid)
420 {
421     catcher_ = std::make_unique<OHOS::HiviewDFX::DfxDumpCatcher>(pid);
422     if (!catcher_->InitFrameCatcher()) {
423         HILOG_ERROR("Init DumpCatcher Failed.");
424     }
425 }
426 
Destroy()427 void MixStackDumper::Destroy()
428 {
429     if (catcher_ != nullptr) {
430         catcher_->DestroyFrameCatcher();
431         catcher_ = nullptr;
432     }
433 }
434 
HandleMixDumpRequest()435 void MixStackDumper::HandleMixDumpRequest()
436 {
437     int fd = -1;
438     int resFd = -1;
439     int dumpRes = OHOS::HiviewDFX::ProcessDumpRes::DUMP_ESUCCESS;
440     (void)memset_s(&g_procInfo, sizeof(g_procInfo), 0, sizeof(g_procInfo));
441     (void)GetProcStatus(&g_procInfo);
442     HILOG_INFO("Current process is ready to dump stack trace.");
443     do {
444         fd = RequestPipeFd(GetPid(), FaultLoggerPipeType::PIPE_FD_WRITE_BUF);
445         resFd = RequestPipeFd(GetPid(), FaultLoggerPipeType::PIPE_FD_WRITE_RES);
446         if (fd < 0 || resFd < 0) {
447             HILOG_ERROR("request pid(%{public}d) pipe fd failed", GetPid());
448             dumpRes = OHOS::HiviewDFX::ProcessDumpRes::DUMP_EGETFD;
449             break;
450         }
451         MixStackDumper mixDumper;
452         mixDumper.Init(GetPid());
453         mixDumper.PrintProcessHeader(fd, GetPid(), getuid());
454         if (g_targetDumpTid > 0) {
455             pid_t targetNsTid = g_targetDumpTid;
456             if (HasNameSpace()) {
457                 TidToNstid(g_targetDumpTid, targetNsTid);
458             }
459             mixDumper.DumpMixFrame(fd, targetNsTid, g_targetDumpTid);
460             g_targetDumpTid = -1;
461             mixDumper.Destroy();
462             break;
463         }
464         std::vector<pid_t> threads;
465         mixDumper.GetThreadList(threads);
466         for (auto& tid : threads) {
467             pid_t nstid = tid;
468             if (HasNameSpace()) {
469                 TidToNstid(tid, nstid);
470             }
471             if (nstid == gettid()) {
472                 continue;
473             }
474             mixDumper.DumpMixFrame(fd, nstid, tid);
475         }
476         mixDumper.Destroy();
477     } while (false);
478 
479     OHOS::HiviewDFX::DumpResMsg dumpResMsg;
480     dumpResMsg.res = dumpRes;
481     const char* strRes = OHOS::HiviewDFX::DfxDumpRes::GetInstance().GetResStr(dumpRes);
482     if (strncpy_s(dumpResMsg.strRes, sizeof(dumpResMsg.strRes), strRes, sizeof(dumpResMsg.strRes) - 1) != 0) {
483         HILOG_ERROR("DfxMixStackDumper::HandleProcessMixDumpRequest strncpy_s failed.");
484     }
485     if (resFd != -1) {
486         write(resFd, &dumpResMsg, sizeof(struct OHOS::HiviewDFX::DumpResMsg));
487         close(resFd);
488     }
489     if (fd != -1) {
490         close(fd);
491     }
492     HILOG_INFO("Finish dumping stack trace.");
493 }
494 } // AppExecFwk
495 } // OHOS
496