1 /*
2 * Copyright (c) 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_unwind_remote_emulator.h"
17
18 #include <algorithm>
19 #include <cstdio>
20 #include <cstring>
21 #include <elf.h>
22 #include <link.h>
23 #include <securec.h>
24 #include <sys/ptrace.h>
25
26 #include "dfx_config.h"
27 #include "dfx_define.h"
28 #include "dfx_logger.h"
29 #include "dfx_maps.h"
30 #include "dfx_process.h"
31 #include "dfx_regs.h"
32 #include "dfx_ring_buffer_wrapper.h"
33 #include "dfx_symbols.h"
34 #include "dfx_thread.h"
35 #include "dfx_util.h"
36 #include "libunwind.h"
37 #include "libunwind_i-ohos.h"
38 #include "process_dumper.h"
39 #include "printer_emulator.h"
40
41 namespace OHOS {
42 namespace HiviewDFX {
43 namespace {
44 // we should have at least 2 frames, is pc and lr
45 // if pc and lr are both invalid, just try fp
46 static constexpr int MIN_VALID_FRAME_COUNT = 3;
47 static constexpr int ARM_EXEC_STEP_NORMAL = 4;
48 static constexpr size_t MAX_BUILD_ID_LENGTH = 32;
49 static constexpr int ARK_JS_HEAD_LEN = 256;
50 }
51
GetInstance()52 DfxUnwindRemote &DfxUnwindRemote::GetInstance()
53 {
54 static DfxUnwindRemote ins;
55 return ins;
56 }
57
DfxUnwindRemote()58 DfxUnwindRemote::DfxUnwindRemote() : as_(nullptr)
59 {
60 std::unique_ptr<DfxSymbols> symbols(new DfxSymbols());
61 symbols_ = std::move(symbols);
62 }
63
UnwindProcess(std::shared_ptr<ProcessDumpRequest> request,std::shared_ptr<DfxProcess> process)64 bool DfxUnwindRemote::UnwindProcess(std::shared_ptr<ProcessDumpRequest> request, std::shared_ptr<DfxProcess> process)
65 {
66 bool ret = false;
67 if (process == nullptr) {
68 DFXLOG_WARN("%s::can not unwind null process.", __func__);
69 return ret;
70 }
71
72 as_ = unw_create_addr_space(&_UPT_accessors, 0);
73 if (as_ == nullptr) {
74 DFXLOG_WARN("%s::failed to create address space.", __func__);
75 return ret;
76 }
77 unw_set_caching_policy(as_, UNW_CACHE_GLOBAL);
78
79 std::shared_ptr<DfxThread> unwThread = process->keyThread_;
80 if (ProcessDumper::GetInstance().IsCrash() && (process->vmThread_ != nullptr)) {
81 unw_set_target_pid(as_, process->vmThread_->threadInfo_.tid);
82 unwThread = process->vmThread_;
83 } else {
84 unw_set_target_pid(as_, process->processInfo_.pid);
85 }
86
87 do {
88 if (unwThread == nullptr) {
89 break;
90 }
91
92 ret = UnwindThread(process, unwThread);
93 if (!ret) {
94 UnwindThreadFallback(process, unwThread);
95 }
96 DfxUnwindRemote::UnwindThreadByParseStackIfNeed(process, unwThread);
97 Printer::PrintDumpHeader(request, process);
98 Printer::PrintThreadHeaderByConfig(process->keyThread_);
99 Printer::PrintThreadBacktraceByConfig(unwThread);
100 if (ProcessDumper::GetInstance().IsCrash()) {
101 Printer::PrintThreadRegsByConfig(unwThread);
102 if (!DfxConfig::GetConfig().dumpOtherThreads) {
103 break;
104 }
105 }
106
107 auto threads = process->GetOtherThreads();
108 if (threads.empty()) {
109 break;
110 }
111 unw_set_target_pid(as_, process->processInfo_.pid);
112
113 size_t index = 0;
114 for (auto thread : threads) {
115 if ((index == 0) && ProcessDumper::GetInstance().IsCrash()) {
116 Printer::PrintOtherThreadHeaderByConfig();
117 }
118
119 if (thread->Attach()) {
120 Printer::PrintThreadHeaderByConfig(thread);
121 UnwindThread(process, thread);
122 Printer::PrintThreadBacktraceByConfig(thread);
123 thread->Detach();
124 }
125 index++;
126 }
127 ret = true;
128 } while (false);
129
130 if (ProcessDumper::GetInstance().IsCrash()) {
131 Printer::PrintThreadFaultStackByConfig(process, unwThread);
132 Printer::PrintProcessMapsByConfig(process);
133 }
134
135 unw_destroy_addr_space(as_);
136 as_ = nullptr;
137 return ret;
138 }
139
UnwindThreadByParseStackIfNeed(std::shared_ptr<DfxProcess> & process,std::shared_ptr<DfxThread> & thread)140 void DfxUnwindRemote::UnwindThreadByParseStackIfNeed(std::shared_ptr<DfxProcess> &process,
141 std::shared_ptr<DfxThread> &thread)
142 {
143 if (process == nullptr || thread == nullptr) {
144 return;
145 }
146 auto frames = thread->GetFrames();
147 constexpr int minFramesNum = 3;
148 size_t initSize = frames.size();
149 if (initSize < minFramesNum ||
150 frames[minFramesNum - 1].mapName.find("Not mapped") != std::string::npos) {
151 bool needParseStack = true;
152 thread->InitFaultStack(needParseStack);
153 process->InitProcessMaps();
154 auto faultStack = thread->GetFaultStack();
155 if (faultStack == nullptr || !faultStack->ParseUnwindStack(process->GetMaps(), frames)) {
156 DFXLOG_ERROR("%s : Failed to parse unwind stack.", __func__);
157 return;
158 }
159 thread->SetFrames(frames);
160 std::string tip = StringPrintf(
161 " Failed to unwind stack, try to get unreliable call stack from #%02zu by reparsing thread stack",
162 initSize);
163 std::string msg = process->GetFatalMessage() + tip;
164 process->SetFatalMessage(msg);
165 }
166 }
167
DoAdjustPc(unw_cursor_t & cursor,uint64_t pc)168 uint64_t DfxUnwindRemote::DoAdjustPc(unw_cursor_t & cursor, uint64_t pc)
169 {
170 DFXLOG_DEBUG("%s :: pc(0x%lx).", __func__, pc);
171 uint64_t ret = 0;
172
173 if (pc <= ARM_EXEC_STEP_NORMAL) {
174 ret = pc; // pc zero is abnormal case, so we don't adjust pc.
175 } else {
176 #if defined(__arm__)
177 ret = pc - unw_get_previous_instr_sz(&cursor);
178 #elif defined(__aarch64__)
179 ret = pc - ARM_EXEC_STEP_NORMAL;
180 #elif defined(__x86_64__)
181 ret = pc - 1;
182 #endif
183 }
184 DFXLOG_DEBUG("%s :: ret(0x%lx).", __func__, ret);
185 return ret;
186 }
187
GetReadableBuildId(uint8_t * buildId,size_t length)188 std::string DfxUnwindRemote::GetReadableBuildId(uint8_t* buildId, size_t length)
189 {
190 if (length > MAX_BUILD_ID_LENGTH) {
191 std::string ret = "Wrong Build-Id length:" + std::to_string(length);
192 return ret;
193 }
194
195 static const char HEXTABLE[] = "0123456789abcdef";
196 uint8_t* buildIdPtr = buildId;
197 std::string buildIdStr;
198 for (size_t i = 0; i < length; i++) {
199 buildIdStr.push_back(HEXTABLE[*buildIdPtr >> 4]); // 4 : higher 4 bit of uint8
200 buildIdStr.push_back(HEXTABLE[*buildIdPtr & 0xf]);
201 buildIdPtr++;
202 }
203 return buildIdStr;
204 }
205
DoUnwindStep(size_t const & index,std::shared_ptr<DfxThread> & thread,unw_cursor_t & cursor,std::shared_ptr<DfxProcess> process)206 bool DfxUnwindRemote::DoUnwindStep(size_t const & index,
207 std::shared_ptr<DfxThread>& thread, unw_cursor_t& cursor, std::shared_ptr<DfxProcess> process)
208 {
209 bool ret = false;
210 uint64_t framePc;
211 static unw_word_t prevFramePc = 0;
212 static unw_word_t prevFrameSp = 0;
213 if (unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t*)(&framePc))) {
214 DFXLOG_WARN("Fail to get current pc.");
215 return ret;
216 }
217
218 uint64_t frameSp;
219 if (unw_get_reg(&cursor, UNW_REG_SP, (unw_word_t*)(&frameSp))) {
220 DFXLOG_WARN("Fail to get stack pointer.");
221 return ret;
222 }
223
224 // use lr as pc in the second frame may not change sp
225 if (prevFrameSp == frameSp && prevFramePc == framePc) {
226 return ret;
227 }
228 prevFrameSp = frameSp;
229 prevFramePc = framePc;
230
231 uint64_t relPc = unw_get_rel_pc(&cursor);
232 if (index != 0) {
233 relPc = DoAdjustPc(cursor, relPc);
234 framePc = DoAdjustPc(cursor, framePc);
235 #if defined(__arm__)
236 unw_set_adjust_pc(&cursor, framePc);
237 #endif
238 }
239
240 if (relPc == 0) {
241 relPc = framePc;
242 }
243
244 DfxFrame frame;
245 frame.relPc = relPc;
246 frame.pc = framePc;
247 frame.sp = frameSp;
248 frame.index = index;
249 ret = UpdateAndFillFrame(cursor, frame, process, thread, ProcessDumper::GetInstance().IsCrash());
250 if (ret) {
251 thread->AddFrame(frame);
252 }
253
254 DFXLOG_DEBUG("%s :: index(%zu), framePc(0x%lx), frameSp(0x%lx).", __func__, index, framePc, frameSp);
255 return ret;
256 }
257
UpdateAndFillFrame(unw_cursor_t & cursor,DfxFrame & frame,std::shared_ptr<DfxProcess> process,std::shared_ptr<DfxThread> thread,bool enableBuildId)258 bool DfxUnwindRemote::UpdateAndFillFrame(unw_cursor_t& cursor, DfxFrame& frame,
259 std::shared_ptr<DfxProcess> process, std::shared_ptr<DfxThread> thread, bool enableBuildId)
260 {
261 struct map_info* mapInfo = unw_get_map(&cursor);
262 bool isValidFrame = true;
263 if (mapInfo != nullptr) {
264 std::string mapPath = std::string(mapInfo->path);
265 std::string funcName;
266 bool isGetFuncName = false;
267 #if defined(__aarch64__)
268 if ((frame.index == 0) && ((mapPath.find("ArkTS Code") != std::string::npos))) {
269 isGetFuncName = GetArkJsHeapFuncName(funcName, thread);
270 if (isGetFuncName) {
271 frame.funcName = funcName;
272 }
273 }
274 #endif
275 uint64_t funcOffset;
276 if (!isGetFuncName && symbols_->GetNameAndOffsetByPc(as_, frame.pc, funcName, funcOffset)) {
277 frame.funcName = funcName;
278 frame.funcOffset = funcOffset;
279 }
280
281 if (mapPath.find(".hap") != std::string::npos) {
282 char libraryName[PATH_LEN] = { 0 };
283 if (unw_get_library_name_by_map(mapInfo, libraryName, PATH_LEN - 1) == 0) {
284 mapPath = mapPath + "!" + std::string(libraryName);
285 }
286 }
287 frame.mapName = mapPath;
288
289 if (enableBuildId && buildIds_.find(mapPath) != buildIds_.end()) {
290 frame.buildId = buildIds_[mapPath];
291 } else if (enableBuildId && buildIds_.find(mapPath) == buildIds_.end()) {
292 uint8_t* buildId = nullptr;
293 size_t length = 0;
294 std::string buildIdStr = "";
295 if (unw_get_build_id(mapInfo, &buildId, &length)) {
296 buildIdStr = GetReadableBuildId(buildId, length);
297 }
298 if (!buildIdStr.empty()) {
299 buildIds_.insert(std::pair<std::string, std::string>(std::string(mapPath), buildIdStr));
300 frame.buildId = buildIdStr;
301 }
302 }
303 } else {
304 process->InitProcessMaps();
305 std::shared_ptr<DfxElfMaps> maps = process->GetMaps();
306 std::shared_ptr<DfxElfMap> map;
307 if (maps != nullptr && maps->FindMapByAddr(frame.pc, map)) {
308 frame.relPc = map->GetRelPc(frame.pc);
309 frame.mapName = map->path;
310 } else {
311 std::string tips = "Not mapped ";
312 std::shared_ptr<DfxRegs> regs = thread->GetThreadRegs();
313 if (regs != nullptr) {
314 tips.append(regs->GetSpecialRegisterName(frame.pc));
315 }
316 frame.mapName = tips;
317 isValidFrame = false;
318 }
319 }
320
321 if (isValidFrame && (frame.pc == frame.relPc) &&
322 (frame.mapName.find("Ark") == std::string::npos)) {
323 isValidFrame = false;
324 }
325
326 bool ret = frame.index < MIN_VALID_FRAME_COUNT || isValidFrame;
327 return ret;
328 }
329
GetArkJsHeapFuncName(std::string & funcName,std::shared_ptr<DfxThread> thread)330 bool DfxUnwindRemote::GetArkJsHeapFuncName(std::string& funcName, std::shared_ptr<DfxThread> thread)
331 {
332 bool ret = false;
333 #if defined(__aarch64__)
334 char buf[ARK_JS_HEAD_LEN] = {0};
335 do {
336 std::shared_ptr<DfxRegs> regs = thread->GetThreadRegs();
337 if (regs == nullptr) {
338 break;
339 }
340 std::vector<uintptr_t> regsData = regs->GetRegsData();
341 if (regsData.size() < (UNW_AARCH64_X29 + 1)) {
342 break;
343 }
344 uintptr_t x20 = regsData[UNW_AARCH64_X20];
345 uintptr_t fp = regsData[UNW_AARCH64_X29];
346
347 int result = unw_get_ark_js_heap_crash_info(thread->threadInfo_.tid,
348 (uintptr_t*)&x20, (uintptr_t*)&fp, false, buf, ARK_JS_HEAD_LEN);
349 if (result < 0) {
350 DFXLOG_WARN("Fail to unw_get_ark_js_heap_crash_info.");
351 break;
352 }
353 ret = true;
354
355 size_t len = std::min(strlen(buf), static_cast<size_t>(ARK_JS_HEAD_LEN - 1));
356 funcName = std::string(buf, len);
357 } while (false);
358 #endif
359 return ret;
360 }
361
UnwindThread(std::shared_ptr<DfxProcess> process,std::shared_ptr<DfxThread> thread)362 bool DfxUnwindRemote::UnwindThread(std::shared_ptr<DfxProcess> process, std::shared_ptr<DfxThread> thread)
363 {
364 if (thread == nullptr) {
365 DFXLOG_ERROR("NULL thread needs unwind.");
366 return false;
367 }
368
369 pid_t nsTid = thread->threadInfo_.nsTid;
370 void *context = _UPT_create(nsTid);
371 if (context == nullptr) {
372 DfxRingBufferWrapper::GetInstance().AppendBuf("Failed to create unwind context for %d.\n", nsTid);
373 return false;
374 }
375
376 if (as_ == nullptr) {
377 as_ = unw_create_addr_space(&_UPT_accessors, 0);
378 if (as_ == nullptr) {
379 DfxRingBufferWrapper::GetInstance().AppendBuf("Unwind address space is not exist for %d.\n", nsTid);
380 _UPT_destroy(context);
381 return false;
382 }
383 unw_set_caching_policy(as_, UNW_CACHE_GLOBAL);
384 }
385
386 unw_cursor_t cursor;
387 int unwRet = unw_init_remote(&cursor, as_, context);
388 if (unwRet != 0) {
389 DfxRingBufferWrapper::GetInstance().AppendBuf("Failed to init cursor for thread:%d ret:%d.\n", nsTid, unwRet);
390 _UPT_destroy(context);
391 return false;
392 }
393
394 std::shared_ptr<DfxRegs> regs = thread->GetThreadRegs();
395 if (regs != nullptr) {
396 std::vector<uintptr_t> regsData = regs->GetRegsData();
397 unw_set_context(&cursor, regsData.data(), regsData.size());
398 }
399
400 size_t index = 0;
401 do {
402 // store current frame
403 if (!DoUnwindStep(index, thread, cursor, process)) {
404 break;
405 }
406 index++;
407
408 // find next frame
409 unwRet = unw_step(&cursor);
410 } while ((unwRet > 0) && (index < DfxConfig::GetConfig().maxFrameNums));
411
412 _UPT_destroy(context);
413 return true;
414 }
415
UnwindThreadFallback(std::shared_ptr<DfxProcess> process,std::shared_ptr<DfxThread> thread)416 void DfxUnwindRemote::UnwindThreadFallback(std::shared_ptr<DfxProcess> process, std::shared_ptr<DfxThread> thread)
417 {
418 // As we failed to init libunwind, just print pc and lr for first two frames
419 std::shared_ptr<DfxRegs> regs = thread->GetThreadRegs();
420 if (regs == nullptr) {
421 DfxRingBufferWrapper::GetInstance().AppendMsg("RegisterInfo is not existed for crash process");
422 return;
423 }
424
425 process->InitProcessMaps();
426 std::shared_ptr<DfxElfMaps> maps = process->GetMaps();
427 if (maps == nullptr) {
428 DfxRingBufferWrapper::GetInstance().AppendMsg("MapsInfo is not existed for crash process");
429 return;
430 }
431
432 auto createFrame = [maps, thread] (size_t index, uintptr_t pc) {
433 std::shared_ptr<DfxElfMap> map;
434 DfxFrame frame;
435 frame.pc = pc;
436 frame.index = index;
437 if (maps->FindMapByAddr(pc, map)) {
438 frame.relPc = map->GetRelPc(pc);
439 frame.mapName = map->path;
440 } else {
441 frame.relPc = pc;
442 frame.mapName = (index == 0 ? "Not mapped pc" : "Not mapped lr");
443 }
444 thread->AddFrame(frame);
445 };
446
447 createFrame(0, regs->pc_);
448 createFrame(1, regs->lr_);
449 }
450 } // namespace HiviewDFX
451 } // namespace OHOS
452