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