• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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