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