• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-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 "mix_stack_dumper.h"
17 
18 #include <ctime>
19 #include <mutex>
20 
21 #include <dirent.h>
22 #include <securec.h>
23 #include <unistd.h>
24 #include <sigchain.h>
25 
26 #include "dfx_dump_res.h"
27 #include "dfx_frame_format.h"
28 #include "dfx_util.h"
29 #include "faultloggerd_client.h"
30 #include "file_ex.h"
31 #include "hilog_wrapper.h"
32 #include "js_runtime.h"
33 #include "procinfo.h"
34 
35 using namespace OHOS::HiviewDFX;
36 
37 namespace OHOS {
38 namespace AppExecFwk {
39 std::weak_ptr<EventHandler> MixStackDumper::signalHandler_;
40 std::weak_ptr<OHOSApplication> MixStackDumper::application_;
41 static std::mutex g_mutex;
42 
43 namespace {
44 static const std::string PROC_SELF_CMDLINE_PATH = "/proc/self/cmdline";
45 static constexpr int HEADER_BUF_LEN = 512;
46 static bool hasInstalled = false;
47 static pid_t g_targetDumpTid = -1;
48 static struct ProcInfo g_procInfo = {0};
49 
PrintJsFrame(const JsFrames & jsFrame)50 static std::string PrintJsFrame(const JsFrames& jsFrame)
51 {
52     return "  at " + jsFrame.functionName + " (" + jsFrame.fileName + ":" + jsFrame.pos + ")\n";
53 }
54 
GetPid()55 static pid_t GetPid()
56 {
57     return g_procInfo.pid;
58 }
59 
HasNameSpace()60 static bool HasNameSpace()
61 {
62     return g_procInfo.ns;
63 }
64 }
65 
Dump_SignalHandler(int sig,siginfo_t * si,void * context)66 bool MixStackDumper::Dump_SignalHandler(int sig, siginfo_t *si, void *context)
67 {
68     HILOG_INFO("Dump_SignalHandler.");
69     bool ret = false;
70     if (si->si_code != DUMP_TYPE_MIX) {
71         return ret;
72     }
73 
74     HILOG_INFO("Received mix stack dump request.");
75     if (signalHandler_.expired()) {
76         HILOG_WARN("signalHandler is expired.");
77         return ret;
78     }
79     auto handler = signalHandler_.lock();
80     if (handler == nullptr) {
81         return ret;
82     }
83     g_targetDumpTid = si->si_value.sival_int;
84     handler->PostTask(&MixStackDumper::HandleMixDumpRequest);
85     return ret;
86 }
87 
IsInstalled()88 bool MixStackDumper::IsInstalled()
89 {
90     return hasInstalled;
91 }
92 
InstallDumpHandler(std::shared_ptr<OHOSApplication> application,std::shared_ptr<EventHandler> handler)93 void MixStackDumper::InstallDumpHandler(std::shared_ptr<OHOSApplication> application,
94     std::shared_ptr<EventHandler> handler)
95 {
96     if (!hasInstalled) {
97         signalHandler_ = handler;
98         application_ = application;
99 
100         struct signal_chain_action sigchain = {
101             .sca_sigaction = MixStackDumper::Dump_SignalHandler,
102             .sca_mask = {},
103             .sca_flags = 0,
104         };
105         add_special_signal_handler(SIGDUMP, &sigchain);
106         hasInstalled = true;
107     }
108 }
109 
IsJsNativePcEqual(uintptr_t * jsNativePointer,uint64_t nativePc,uint64_t nativeOffset)110 bool MixStackDumper::IsJsNativePcEqual(uintptr_t *jsNativePointer, uint64_t nativePc, uint64_t nativeOffset)
111 {
112     uint64_t jsPc_ = (uint64_t)jsNativePointer;
113     if (nativePc - nativeOffset == jsPc_) {
114         return true;
115     }
116     return false;
117 }
118 
BuildJsNativeMixStack(int fd,std::vector<JsFrames> & jsFrames,std::vector<DfxFrame> & nativeFrames)119 void MixStackDumper::BuildJsNativeMixStack(int fd, std::vector<JsFrames>& jsFrames,
120     std::vector<DfxFrame>& nativeFrames)
121 {
122     uint32_t jsIdx = 0;
123     uint32_t nativeIdx = 0;
124     std::string mixStackStr = "";
125     bool matchJsFrame = false;
126     while (jsIdx < jsFrames.size() && jsFrames[jsIdx].nativePointer == nullptr) {
127         jsIdx++;
128     }
129 
130     while (jsIdx < jsFrames.size() && nativeIdx < nativeFrames.size()) {
131         if (jsFrames[jsIdx].nativePointer == nullptr) {
132             mixStackStr += PrintJsFrame(jsFrames[jsIdx]);
133             jsIdx++;
134             continue;
135         }
136 
137         if (IsJsNativePcEqual(jsFrames[jsIdx].nativePointer, nativeFrames[nativeIdx].pc,
138             nativeFrames[nativeIdx].funcOffset)) {
139             HILOG_DEBUG("DfxMixStackDumper::BuildJsNativeMixStack pc register values matched.");
140             mixStackStr += HiviewDFX::DfxFrameFormat::GetFrameStr(nativeFrames[nativeIdx]);
141             mixStackStr += PrintJsFrame(jsFrames[jsIdx]);
142             nativeIdx++;
143             jsIdx++;
144             matchJsFrame = true;
145         } else {
146             mixStackStr += HiviewDFX::DfxFrameFormat::GetFrameStr(nativeFrames[nativeIdx]);
147             nativeIdx++;
148         }
149     }
150 
151     std::string jsStack;
152     if (!matchJsFrame && jsFrames.size() > 0) {
153         jsIdx = 0;
154         while (jsIdx < jsFrames.size()) {
155             jsStack += PrintJsFrame(jsFrames[jsIdx]);
156             jsIdx++;
157         }
158     }
159 
160     while (nativeIdx < nativeFrames.size()) {
161         mixStackStr += HiviewDFX::DfxFrameFormat::GetFrameStr(nativeFrames[nativeIdx]);
162         nativeIdx++;
163     }
164 
165     jsStack += mixStackStr;
166     Write(fd, jsStack);
167     Write(fd, "\n");
168 }
169 
GetThreadStackTraceLabel(pid_t tid)170 std::string MixStackDumper::GetThreadStackTraceLabel(pid_t tid)
171 {
172     std::ostringstream ss;
173     std::string threadName = "";
174     ReadThreadName(tid, threadName);
175     ss << "Tid:" << tid << ", Name:" << threadName << std::endl;
176     return ss.str();
177 }
178 
PrintProcessHeader(int fd,pid_t pid,uid_t uid)179 void MixStackDumper::PrintProcessHeader(int fd, pid_t pid, uid_t uid)
180 {
181     char headerBuf[HEADER_BUF_LEN] = { 0 };
182     std::string processName = "";
183     int ret = 1;
184     ReadProcessName(getpid(), processName);
185     if (!processName.empty()) {
186         ret = snprintf_s(headerBuf, HEADER_BUF_LEN, HEADER_BUF_LEN - 1,
187                          "Timestamp:%sPid:%d\nUid:%d\nProcess name:%s\n",
188                          GetCurrentTimeStr().c_str(), pid, uid, processName.c_str());
189     } else {
190         ret = snprintf_s(headerBuf, HEADER_BUF_LEN, HEADER_BUF_LEN - 1,
191                          "Timestamp:%sPid:%d\nUid:%d\nProcess name:unknown\n",
192                          GetCurrentTimeStr().c_str(), pid, uid);
193     }
194     if (ret <= 0) {
195         HILOG_ERROR("snprintf_s process mix stack header failed.");
196         return;
197     }
198     Write(fd, std::string(headerBuf));
199 }
200 
DumpMixFrame(int fd,pid_t nstid,pid_t tid)201 bool MixStackDumper::DumpMixFrame(int fd, pid_t nstid, pid_t tid)
202 {
203     if (catcher_ == nullptr) {
204         HILOG_ERROR("No FrameCatcher? call init first.");
205         return false;
206     }
207 
208     std::vector<DfxFrame> nativeFrames;
209     bool hasNativeFrame = true;
210     bool isVmSuspended = false;
211     auto application = application_.lock();
212     if (application != nullptr && application->GetRuntime() != nullptr) {
213         isVmSuspended = application->GetRuntime()->SuspendVM(nstid);
214     }
215     if (!catcher_->CatchFrame(nstid, nativeFrames)) {
216         hasNativeFrame = false;
217     }
218 
219     bool hasJsFrame = true;
220     std::vector<JsFrames> jsFrames;
221     // if we failed to get native frame, target thread may not be seized
222     if (application != nullptr && application->GetRuntime() != nullptr && isVmSuspended) {
223         hasJsFrame = application->GetRuntime()->BuildJsStackInfoList(nstid, jsFrames);
224     }
225     catcher_->ReleaseThread(nstid);
226     if (application != nullptr && application->GetRuntime() != nullptr && isVmSuspended) {
227         application->GetRuntime()->ResumeVM(nstid);
228     }
229 
230     if (jsFrames.size() == 0) {
231         hasJsFrame = false;
232     }
233 
234     Write(fd, GetThreadStackTraceLabel(tid));
235     if (!hasNativeFrame && !hasJsFrame) {
236         HILOG_ERROR("Failed to unwind frames for %{public}d.", nstid);
237         std::string wchan;
238         ReadThreadWchan(wchan, tid, true);
239         Write(fd, wchan);
240         Write(fd, "\n");
241         return false;
242     }
243 
244     if (hasNativeFrame && !hasJsFrame) {
245         Write(fd, HiviewDFX::DfxFrameFormat::GetFramesStr(nativeFrames));
246         Write(fd, "\n");
247         return true;
248     }
249     BuildJsNativeMixStack(fd, jsFrames, nativeFrames);
250     return true;
251 }
252 
Init(pid_t pid)253 void MixStackDumper::Init(pid_t pid)
254 {
255     catcher_ = std::make_unique<OHOS::HiviewDFX::DfxCatchFrameLocal>(pid);
256     if (!catcher_->InitFrameCatcher()) {
257         HILOG_ERROR("Init DumpCatcher Failed.");
258     }
259 }
260 
Destroy()261 void MixStackDumper::Destroy()
262 {
263     if (catcher_ != nullptr) {
264         catcher_->DestroyFrameCatcher();
265         catcher_ = nullptr;
266     }
267 }
268 
HandleMixDumpRequest()269 void MixStackDumper::HandleMixDumpRequest()
270 {
271     int fd = -1;
272     int resFd = -1;
273     int dumpRes = OHOS::HiviewDFX::DumpErrorCode::DUMP_ESUCCESS;
274     std::unique_lock<std::mutex> lock(g_mutex);
275     HILOG_INFO("Current process is ready to dump stack trace.");
276     (void)GetProcStatus(g_procInfo);
277     do {
278         fd = RequestPipeFd(GetPid(), FaultLoggerPipeType::PIPE_FD_WRITE_BUF);
279         resFd = RequestPipeFd(GetPid(), FaultLoggerPipeType::PIPE_FD_WRITE_RES);
280         if (fd < 0 || resFd < 0) {
281             HILOG_ERROR("request pid(%{public}d) pipe fd failed", GetPid());
282             dumpRes = OHOS::HiviewDFX::DumpErrorCode::DUMP_EGETFD;
283             break;
284         }
285         MixStackDumper mixDumper;
286         mixDumper.DumpMixStackLocked(fd, g_targetDumpTid);
287         g_targetDumpTid = -1;
288     } while (false);
289 
290     if (resFd >= 0) {
291         write(resFd, &dumpRes, sizeof(dumpRes));
292         close(resFd);
293     }
294     if (fd >= 0) {
295         close(fd);
296     }
297     HILOG_INFO("Finish dumping stack trace.");
298 }
299 
Write(int fd,const std::string & outStr)300 void MixStackDumper::Write(int fd, const std::string& outStr)
301 {
302     if (fd < 0) {
303         outputStr_.append(outStr);
304     } else {
305         write(fd, outStr.c_str(), outStr.size());
306     }
307 }
308 
DumpMixStackLocked(int fd,pid_t requestTid)309 std::string MixStackDumper::DumpMixStackLocked(int fd, pid_t requestTid)
310 {
311     if (fd < 0) {
312         outputStr_.clear();
313     }
314 
315     Init(GetPid());
316     PrintProcessHeader(fd, GetPid(), getuid());
317     if (requestTid > 0) {
318         pid_t targetNsTid = requestTid;
319         pid_t targetTid = requestTid;
320         if (HasNameSpace()) {
321             TidToNstid(GetPid(), targetTid, targetNsTid);
322         }
323         DumpMixFrame(fd, targetNsTid, targetTid);
324     } else {
325         std::vector<pid_t> tids;
326         std::function<bool(int)> func = [&](int tid) {
327             pid_t nstid = tid;
328             if (HasNameSpace()) {
329                 TidToNstid(GetPid(), tid, nstid);
330             }
331             if (nstid != gettid()) {
332                 DumpMixFrame(fd, nstid, tid);
333             }
334             return true;
335         };
336         GetTidsByPidWithFunc(GetPid(), tids, func);
337     }
338     Destroy();
339     return outputStr_;
340 }
341 
GetMixStack(bool onlyMainThread)342 std::string MixStackDumper::GetMixStack(bool onlyMainThread)
343 {
344     std::unique_lock<std::mutex> lock(g_mutex);
345     (void)GetProcStatus(g_procInfo);
346     MixStackDumper mixDumper;
347     return mixDumper.DumpMixStackLocked(-1, onlyMainThread ? GetPid() : -1);
348 }
349 } // namespace AppExecFwk
350 } // namespace OHOS
351