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