1/* 2 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. All rights reserved. 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#define HILOG_TAG "CallStack" 16 17#include "call_stack.h" 18#include "dfx_ark.h" 19 20#include <string> 21#if defined(HAVE_LIBUNWIND) && HAVE_LIBUNWIND 22#include <libunwind.h> 23extern "C" { 24#include <libunwind_i.h> 25} 26#endif 27 28#include "register.h" 29#ifdef target_cpu_arm 30// reg size is int (unw_word_t) 31#define UNW_WORD_PFLAG "x" 32#else 33// reg size is long (unw_word_t) 34#define UNW_WORD_PFLAG "zx" 35#endif 36namespace OHOS { 37namespace Developtools { 38namespace NativeDaemon { 39bool CallStack::ReadVirtualThreadMemory(UnwindInfo &unwindInfoPtr, ADDR_TYPE addr, 40 ADDR_TYPE *data) 41{ 42 auto process = unwindInfoPtr.callStack.porcessMemoryMap_.find(unwindInfoPtr.thread.pid_); 43 if (process != unwindInfoPtr.callStack.porcessMemoryMap_.end()) { 44 auto memory = process->second.find(addr); 45 if (memory != process->second.end()) { 46 *data = memory->second; 47 return true; 48 } 49 } 50 51 if (unwindInfoPtr.thread.ReadRoMemory(addr, reinterpret_cast<uint8_t *>(data), sizeof(ADDR_TYPE))) { 52 unwindInfoPtr.callStack.porcessMemoryMap_[unwindInfoPtr.thread.pid_][addr] = *data; 53 return true; 54 } else { 55 return false; 56 } 57} 58 59#if defined(HAVE_LIBUNWIND) && HAVE_LIBUNWIND 60const std::map<unw_error_t, const std::string> UNW_ERROR_MAP = { 61 {UNW_ESUCCESS, std::to_string(UNW_ESUCCESS)}, 62 {UNW_EUNSPEC, std::to_string(UNW_EUNSPEC)}, 63 {UNW_ENOMEM, std::to_string(UNW_ENOMEM)}, 64 {UNW_EBADREG, std::to_string(UNW_EBADREG)}, 65 {UNW_EREADONLYREG, std::to_string(UNW_EREADONLYREG)}, 66 {UNW_ESTOPUNWIND, std::to_string(UNW_ESTOPUNWIND)}, 67 {UNW_EINVALIDIP, std::to_string(UNW_EINVALIDIP)}, 68 {UNW_EBADFRAME, std::to_string(UNW_EBADFRAME)}, 69 {UNW_EINVAL, std::to_string(UNW_EINVAL)}, 70 {UNW_EBADVERSION, std::to_string(UNW_EBADVERSION)}, 71 {UNW_ENOINFO, std::to_string(UNW_ENOINFO)}, 72}; 73const std::string CallStack::GetUnwErrorName(int error) 74{ 75 if (UNW_ERROR_MAP.count(static_cast<unw_error_t>(-error)) > 0) { 76 return UNW_ERROR_MAP.at(static_cast<unw_error_t>(-error)); 77 } else { 78 return "UNKNOW_UNW_ERROR"; 79 } 80} 81 82void CallStack::dumpUDI(unw_dyn_info_t &di) 83{ 84 HLOGV("unwind_table info: "); 85 HLOGV(" di.start_ip: 0x%016" UNW_WORD_PFLAG "", di.start_ip); 86 HLOGV(" di.end_ip: 0x%016" UNW_WORD_PFLAG "", di.end_ip); 87 HLOGV(" di.u.rti.segbase: 0x%016" UNW_WORD_PFLAG "", di.u.rti.segbase); 88 HLOGV(" di.u.rti.table_data: 0x%016" UNW_WORD_PFLAG "", di.u.rti.table_data); 89 HLOGV(" di.u.rti.table_len: 0x%016" UNW_WORD_PFLAG "", di.u.rti.table_len); 90} 91 92bool CallStack::fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, std::shared_ptr<DfxMap> map, 93 const VirtualThread &thread) 94{ 95 uint64_t fdeTableElfOffset = 0; 96 uint64_t fdeTableSize = 0; 97 uint64_t ehFrameHdrElfOffset = 0; 98 uint64_t SectionVaddr = 0; 99 uint64_t SectionSize = 0; 100 uint64_t SectionFileOffset = 0; 101 di.start_ip = map.begin; 102 di.end_ip = map.end; 103#ifndef target_cpu_arm 104 if ((UNW_INFO_FORMAT_REMOTE_TABLE == di.format) && 105 symbolsFile.GetHDRSectionInfo(ehFrameHdrElfOffset, fdeTableElfOffset, fdeTableSize)) { 106 /* 107 unw_word_t name_ptr; // addr. of table name (e.g., library name) 108 unw_word_t segbase; // segment base 109 unw_word_t table_len; // must be a multiple of sizeof(unw_word_t)! 110 unw_word_t table_data; 111 */ 112 /* 113 all the rti addr is offset of the elf file 114 begin - page offset = elf file base addr in vaddr user space 115 begin - page offset + elf offset = vaddr in real word.(for this thread) 116 */ 117 118 // segbase is file offset . 119 /* 120 00200000-00344000 r--p 00000000 08:02 46404365 121 00344000-005c4000 r-xp 00143000 08:02 46404365 122 123 LOAD 0x00000000001439c0 0x00000000003449c0 0x00000000003449c0 124 0x000000000027f3c0 0x000000000027f3c0 R E 0x1000 125 126 GNU_EH_FRAME 0x00000000000f3248 0x00000000002f3248 0x00000000002f3248 127 0x000000000000bb04 0x000000000000bb04 R 0x4 128 129 */ 130 auto ehFrameMap = thread.FindMapByFileInfo(map->name, ehFrameHdrElfOffset); 131 UNWIND_CHECK_NOTNULL(ehFrameMap, false, "no ehframe mmap found."); 132 di.u.rti.segbase = ehFrameMap->begin + ehFrameHdrElfOffset - ehFrameMap->offset; 133 di.u.rti.table_data = ehFrameMap->begin + fdeTableElfOffset - ehFrameMap->offset; 134 di.u.rti.table_len = fdeTableSize / sizeof(uintptr_t); 135 136 HLOGV(" map pageoffset: 0x%016" PRIx64 "", map.offset); 137 HLOGV(" ehFrameHdrElfOffset: 0x%016" PRIx64 "", ehFrameHdrElfOffset); 138 HLOGV(" fdeTableElfOffset: 0x%016" PRIx64 "", fdeTableElfOffset); 139 HLOGV(" fdeTableSize: 0x%016" PRIx64 "", fdeTableSize); 140 return true; 141 } 142#else 143 if ((UNW_INFO_FORMAT_ARM_EXIDX == di.format) && 144 symbolsFile.GetSectionInfo(ARM_EXIDX, SectionVaddr, SectionSize, SectionFileOffset)) { 145 const MemMapItem *targetMmap = thread.FindMapByFileInfo(mmap.name_, SectionFileOffset); 146 UNWIND_CHECK_NOTNULL(targetMmap, false, "no debug mmap found."); 147 HLOGV(" begin: %" PRIx64 " offset:%" PRIx64 "", targetMmap->begin_, 148 targetMmap->pageoffset_); 149 150 di.u.rti.table_data = targetMap->begin + SectionFileOffset - targetMap->offset; 151 di.u.rti.table_len = SectionSize; 152 HLOGV(" SectionName: %s", std::string(ARM_EXIDX).c_str()); 153 HLOGV(" SectionVaddrt: 0x%016" PRIx64 "", SectionVaddr); 154 HLOGV(" SectionFileOffset 0x%016" PRIx64 "", SectionFileOffset); 155 HLOGV(" SectionSize: 0x%016" PRIx64 "", SectionSize); 156 157 // GetSectionInfo return true, but SectionVaddr || SectionSize is 0 ??? 158 HLOG_ASSERT(SectionVaddr != 0 && SectionSize != 0); 159 return true; 160 } 161#endif 162 return false; 163} 164 165/* 166 https://www.nongnu.org/libunwind/man/libunwind-dynamic(3).html 167*/ 168int CallStack::FindUnwindTable(SymbolsFile *symbolsFile, std::shared_ptr<DfxMap> map, 169 UnwindInfo *unwindInfoPtr, unw_addr_space_t as, unw_word_t ip, 170 unw_proc_info_t *pi, int need_unwind_info, void *arg) 171{ 172 HLOGV("try seach debug info at %s", symbolsFile->filePath_.c_str()); 173 auto &dynInfoProcessMap = unwindInfoPtr->callStack.unwindTableInfoMap_; 174 // all the thread in same process have same map and symbols 175 if (dynInfoProcessMap.find(unwindInfoPtr->thread.pid_) == dynInfoProcessMap.end()) { 176 dynInfoProcessMap.emplace(unwindInfoPtr->thread.pid_, dsoUnwDynInfoMap {}); 177 } 178 dsoUnwDynInfoMap &dynFileMap = dynInfoProcessMap[unwindInfoPtr->thread.pid_]; 179 // find use dso name as key 180 if (dynFileMap.find(symbolsFile->filePath_) == dynFileMap.end()) { 181 // we make a option empty value first 182 std::optional<unw_dyn_info_t> &odi = dynFileMap[symbolsFile->filePath_]; 183 184 unw_dyn_info_t newdi; 185 if (memset_s(&newdi, sizeof(unw_dyn_info_t), 0, sizeof(unw_dyn_info_t)) != EOK) { 186 HLOGE("memset_s failed"); 187 } 188#ifdef target_cpu_arm 189 // arm use .ARM.exidx , not use ehframe 190 newdi.format = UNW_INFO_FORMAT_ARM_EXIDX; 191#else 192 // otherwise we use EH FRAME 193 newdi.format = UNW_INFO_FORMAT_REMOTE_TABLE; 194#endif 195 if (fillUDI(newdi, *symbolsFile, map, unwindInfoPtr->thread)) { 196 dumpUDI(newdi); 197 odi = newdi; 198 } 199 } 200 201 HLOG_ASSERT(dynInfoProcessMap.find(unwindInfoPtr->thread.pid_) != dynInfoProcessMap.end()); 202 HLOG_ASSERT_MESSAGE(dynFileMap.find(symbolsFile->filePath_) != dynFileMap.end(), "%s", 203 symbolsFile->filePath_.c_str()); 204 std::optional<unw_dyn_info_t> &odi = 205 dynInfoProcessMap.at(unwindInfoPtr->thread.pid_).at(symbolsFile->filePath_); 206 207 if (odi.has_value()) { 208 unw_dyn_info_t &di = odi.value(); 209 /* 210 we dont use dwarf_search_unwind_table 211 because in arm it will search two function: 212 1 arm_search_unwind_table first 213 2 dwarf_search_unwind_table 214 215 see libunwind_i.h for arm 216 define tdep_search_unwind_table UNW_OBJ(search_unwind_table) 217 218 */ 219 int ret = static_cast<unw_error_t>( 220 tdep_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg)); 221 222 HLOGM("search_unwind_table ret %d:%s", ret, GetUnwErrorName(ret).c_str()); 223 224 if (UNW_ESUCCESS != ret) { 225 if (UNW_ENOINFO != ret) { 226 HLOGW("search_unwind_table ret error %d:%s", ret, GetUnwErrorName(ret).c_str()); 227 } 228 return -UNW_EUNSPEC; 229 } else { 230 return UNW_ESUCCESS; 231 } 232 } else { 233 HLOGW("no debug info found for thread %d:%s", unwindInfoPtr->thread.tid_, 234 unwindInfoPtr->thread.name_.c_str()); 235 return -UNW_EUNSPEC; 236 } 237} 238 239int CallStack::FindProcInfo(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, 240 int need_unwind_info, void *arg) 241{ 242 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg); 243 244 HLOGM("need_unwind_info ret %d ip %" UNW_WORD_PFLAG "", need_unwind_info, ip); 245 auto map = unwindInfoPtr->thread.FindMapByAddr(ip); 246 if (map != nullptr) { 247 SymbolsFile *symbolsFile = unwindInfoPtr->thread.FindSymbolsFileByMap(*map); 248 if (symbolsFile != nullptr) { 249 return FindUnwindTable(symbolsFile, map, unwindInfoPtr, as, ip, pi, need_unwind_info, 250 arg); 251 } else { 252 HLOGW("no symbols file found for thread %d:%s", unwindInfoPtr->thread.tid_, 253 unwindInfoPtr->thread.name_.c_str()); 254 } 255 } else { 256 HLOGE("ip 0x%016" UNW_WORD_PFLAG " not found in thread %d:%s", ip, 257 unwindInfoPtr->thread.tid_, unwindInfoPtr->thread.name_.c_str()); 258 } 259 260 return -UNW_EUNSPEC; 261} 262 263int CallStack::AccessMem([[maybe_unused]] unw_addr_space_t as, unw_word_t addr, 264 unw_word_t *valuePoint, int writeOperation, void *arg) 265{ 266 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg); 267 size_t stackOffset = 0; 268 *valuePoint = 0; 269 HLOGDUMMY("try access addr 0x%" UNW_WORD_PFLAG " ", addr); 270 271 HLOG_ASSERT(writeOperation == 0); 272 273 /* Check overflow. */ 274 UNWIND_CHECK_TRUE(addr + sizeof(unw_word_t) >= addr, -UNW_EUNSPEC, 275 "address overfolw at 0x%" UNW_WORD_PFLAG " increase 0x%zu", 276 addr, sizeof(unw_word_t)); 277 278 if (addr < unwindInfoPtr->callStack.stackPoint_ or 279 addr + sizeof(unw_word_t) >= unwindInfoPtr->callStack.stackEnd_) { 280 if (ReadVirtualThreadMemory(*unwindInfoPtr, addr, valuePoint)) { 281 HLOGM("access_mem addr get val 0x%" UNW_WORD_PFLAG ", from mmap", *valuePoint); 282 } else { 283 HLOGW("access_mem addr failed, from mmap, "); 284 HLOGW("stack range 0x%" PRIx64 " - 0x%" PRIx64 "(0x%" PRIx64 ")", 285 unwindInfoPtr->callStack.stackPoint_, unwindInfoPtr->callStack.stackEnd_, 286 unwindInfoPtr->callStack.stackEnd_ - unwindInfoPtr->callStack.stackPoint_); 287 return -UNW_EUNSPEC; 288 } 289 } else { 290 stackOffset = addr - unwindInfoPtr->callStack.stackPoint_; 291 *valuePoint = *(unw_word_t *)&unwindInfoPtr->callStack.stack_[stackOffset]; 292 HLOGM("access_mem addr val %" UNW_WORD_PFLAG ", from stack offset %zu", *valuePoint, stackOffset); 293 } 294 295 return UNW_ESUCCESS; 296} 297 298int CallStack::AccessReg([[maybe_unused]] unw_addr_space_t as, unw_regnum_t regnum, 299 unw_word_t *valuePoint, int writeOperation, void *arg) 300{ 301 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg); 302 uint64_t val; 303 size_t perfRegIndex = LibunwindRegIdToPerfReg(regnum); 304 if (perfRegIndex < PERF_REG_ARM64_X29) { 305 // libunwind not access other regs 306 HLOGE("access_reg not expected %d", regnum); 307 } 308 /* Don't support write, I suspect we don't need it. */ 309 UNWIND_CHECK_TRUE(!writeOperation, -UNW_EINVAL, "access_reg %d", regnum); 310 311 if (unwindInfoPtr->callStack.regsNum_ == 0) { 312 return -UNW_EUNSPEC; 313 } 314 UNWIND_CHECK_TRUE( 315 RegisterGetValue(val, unwindInfoPtr->callStack.regs_, perfRegIndex, unwindInfoPtr->callStack.regsNum_), 316 -UNW_EUNSPEC, "can't read reg %zu", perfRegIndex); 317 *valuePoint = (unw_word_t)val; 318 HLOGV("reg %d:%s, val 0x%" UNW_WORD_PFLAG "", regnum, RegisterGetName(perfRegIndex).c_str(), *valuePoint); 319 return UNW_ESUCCESS; 320} 321 322void CallStack::PutUnwindInfo([[maybe_unused]] unw_addr_space_t as, 323 [[maybe_unused]] unw_proc_info_t *pi, [[maybe_unused]] void *arg) 324{ 325} 326 327int CallStack::AccessFpreg([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_regnum_t num, 328 [[maybe_unused]] unw_fpreg_t *val, [[maybe_unused]] int writeOperation, 329 [[maybe_unused]] void *arg) 330{ 331 return -UNW_EINVAL; 332} 333 334int CallStack::GetDynInfoListAaddr([[maybe_unused]] unw_addr_space_t as, 335 [[maybe_unused]] unw_word_t *dil_vaddr, 336 [[maybe_unused]] void *arg) 337{ 338 return -UNW_ENOINFO; 339} 340 341int CallStack::Resume([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_cursor_t *cu, 342 [[maybe_unused]] void *arg) 343{ 344 return -UNW_EINVAL; 345} 346 347int CallStack::getProcName([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_word_t addr, 348 [[maybe_unused]] char *bufp, [[maybe_unused]] size_t buf_len, 349 [[maybe_unused]] unw_word_t *offp, [[maybe_unused]] void *arg) 350{ 351 return -UNW_EINVAL; 352} 353 354void CallStack::UnwindStep(unw_cursor_t &c, std::vector<CallFrame> &callStack, size_t maxStackLevel) 355{ 356 while (callStack.size() < maxStackLevel) { 357 int ret = unw_step(&c); 358 if (ret > 0) { 359 unw_word_t ip = 0; 360 unw_word_t sp = 0; 361 unw_get_reg(&c, UNW_REG_IP, &ip); 362 unw_get_reg(&c, UNW_REG_SP, &sp); 363 364 if (ip == 0) { 365 HLOGD("ip == 0 something is wrong. break"); 366 break; 367 } 368 369 /* 370 * Decrement the IP for any non-activation frames. 371 * this is required to properly find the srcline 372 * for caller frames. 373 * See also the documentation for dwfl_frame_pc(), 374 * which this code tries to replicate. 375 */ 376 if (unw_is_signal_frame(&c) <= 0) { 377 --ip; 378 } 379 HLOGV("unwind:%zu: ip 0x%" UNW_WORD_PFLAG " sp 0x%" UNW_WORD_PFLAG "", callStack.size(), 380 ip, sp); 381 if (callStack.back().ip_ == ip && callStack.back().sp_ == sp) { 382 HLOGW("we found a same frame, stop here"); 383 break; 384 } 385 callStack.emplace_back(ip, sp); 386 } else { 387 HLOGV("no more frame step found. ret %d:%s", ret, GetUnwErrorName(ret).c_str()); 388 break; 389 } 390 } 391} 392#endif 393 394bool CallStack::GetIpSP(uint64_t &ip, uint64_t &sp, const u64 *regs, size_t regNum) const 395{ 396 if (regNum > 0) { 397 UNWIND_CHECK_TRUE(RegisterGetSPValue(sp, arch_, regs, regNum), false, "unable get sp"); 398 UNWIND_CHECK_TRUE(RegisterGetIPValue(ip, arch_, regs, regNum), false, "unable get ip"); 399 if (ip != 0) { 400 return true; 401 } 402 } else { 403 HLOGW("reg size is 0"); 404 return false; 405 } 406 return false; 407} 408 409#if defined(HAVE_LIBUNWIND) && HAVE_LIBUNWIND 410bool CallStack::DoUnwind(const VirtualThread &thread, std::vector<CallFrame> &callStack, 411 size_t maxStackLevel) 412{ 413 unw_addr_space_t addr_space; 414 UnwindInfo unwindInfo = { 415 .thread = thread, 416 .callStack = *this, 417 }; 418 unw_cursor_t c; 419 if (unwindAddrSpaceMap_.count(thread.tid_) == 0) { 420 addr_space = unw_create_addr_space(&accessors_, 0); 421 UNWIND_CHECK_TRUE(addr_space, false, "Can't create unwind vaddress space."); 422 unwindAddrSpaceMap_.emplace(thread.tid_, addr_space); 423 unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); 424 unw_flush_cache(addr_space, 0, 0); 425 } else { 426 addr_space = unwindAddrSpaceMap_.at(thread.tid_); 427 } 428 429 int ret = unw_init_remote(&c, addr_space, &unwindInfo); 430 if (ret) { 431 HLOGE("unwind error %d:%s see unw_error_t.", ret, GetUnwErrorName(ret).c_str()); 432 return false; 433 } else { 434 UnwindStep(c, callStack, maxStackLevel); 435 } 436 return true; 437} 438#endif 439 440bool CallStack::UnwindCallStack(const VirtualThread &thread, u64 *regs, u64 regsNum, 441 const u8 *stack, u64 stackSize, std::vector<CallFrame> &callStack, 442 size_t maxStackLevel, int maxjsDepth, bool jsReport) 443{ 444 regs_ = regs; 445 regsNum_ = regsNum; 446 stack_ = stack; 447 stackSize_ = stackSize; 448 449 arch_ = buildArchType; 450 UpdateRegForABI(arch_, regs_); 451 if (!RegisterGetSPValue(stackPoint_, arch_, regs_, regsNum_)) { 452 HLOGE("RegisterGetSPValue failed"); 453 return false; 454 } else { 455 stackEnd_ = stackPoint_ + stackSize_; 456 } 457 458 uint64_t ip = 0; 459 uint64_t sp = 0; 460 if (!GetIpSP(ip, sp, regs_, regsNum_)) { 461 HLOGW("unable get sp or sp , unable unwind"); 462 return false; 463 } else { 464 if (ip != 0) { 465 HLOGV("unwind:%zu: ip 0x%" PRIx64 " sp 0x%" PRIx64 "", callStack.size(), ip, sp); 466 callStack.emplace_back(ip, sp); 467 } 468 } 469 470 /* 471 * If we need more than one entry, do the DWARF 472 * unwind itself. 473 */ 474 if (maxStackLevel - 1 > 0) { 475#if defined(HAVE_LIBUNWIND) && HAVE_LIBUNWIND 476 return DoUnwind(thread, callStack, maxStackLevel); 477#else 478 return DoUnwind2(thread, callStack, maxStackLevel, maxjsDepth, jsReport); 479#endif 480 } 481 return true; 482} 483 484/* 485we should have CallStack cache for each thread 486 4870. A -> B -> C -> E -> F 4881. C -> E -> F 4892. B -> C 4903. A -> B -> C 4914. B -> G -> H 4925. J -> C 493 4940 is our cache 4951 2 3... is from record 496 497use expendLimit to setup how may frame match is needs 498 499*/ 500#if defined(HAVE_LIBUNWINDER) && HAVE_LIBUNWINDER 501 502bool CallStack::DoUnwind2(const VirtualThread &thread, std::vector<CallFrame> &callStack, 503 size_t maxStackLevel, int maxjsDepth, bool jsReport) 504{ 505 UnwindInfo unwindInfo = { 506 .thread = thread, 507 .callStack = *this, 508 }; 509 510 if (pidUnwinder_.count(thread.pid_) == 0) { 511 pidUnwinder_.emplace(thread.pid_, std::make_shared<Unwinder>(accessor_)); 512 } 513 auto unwinder = pidUnwinder_[thread.pid_]; 514 515#ifdef target_cpu_arm 516 std::shared_ptr<DfxRegs> regs = std::make_shared<DfxRegsArm>(); 517 std::vector<uintptr_t> tempRegs; 518 for (size_t i = 0; i < regsNum_; ++i) { 519 tempRegs.push_back((uintptr_t)regs_[i]); 520 } 521 regs->SetRegsData(tempRegs); 522#else 523 std::shared_ptr<DfxRegs> regs = std::make_shared<DfxRegsArm64>(); 524 regs->SetRegsData(reinterpret_cast<uintptr_t*>(regs_), regsNum_); 525#endif 526 527 unwinder->SetRegs(regs); 528 529 unwinder->Unwind(&unwindInfo, maxStackLevel + maxjsDepth); 530 std::vector<DfxFrame> dfxcallStack; 531 532 dfxcallStack = unwinder->GetFrames(); 533 callStack.clear(); 534 if (jsReport) { 535 for (auto& frame: dfxcallStack) { 536 HLOGD("pc 0x%" PRIx64 " sp 0x%" PRIx64 " isJsFrame = %d", frame.pc, frame.sp, frame.isJsFrame); 537 callStack.emplace_back(frame.pc, frame.sp, frame.isJsFrame); 538 } 539 } else { 540 for (auto& frame: dfxcallStack) { 541 if (frame.isJsFrame) { 542 break; 543 } 544 HLOGD("pc 0x%" PRIx64 " sp 0x%" PRIx64 " isJsFrame = %d", frame.pc, frame.sp, frame.isJsFrame); 545 callStack.emplace_back(frame.pc, frame.sp, frame.isJsFrame); 546 } 547 } 548 return true; 549} 550 551void CallStack::DumpTableInfo(UnwindTableInfo &outTableInfo) 552{ 553 HLOGV("unwind_table info: "); 554 HLOGV(" start_ip: 0x%016" UNW_WORD_PFLAG "", outTableInfo.startPc); 555 HLOGV(" end_ip: 0x%016" UNW_WORD_PFLAG "", outTableInfo.endPc); 556 HLOGV(" segbase: 0x%016" UNW_WORD_PFLAG "", outTableInfo.segbase); 557 HLOGV(" table_data: 0x%016" UNW_WORD_PFLAG "", outTableInfo.tableData); 558 HLOGV(" table_len: 0x%016" UNW_WORD_PFLAG "", outTableInfo.tableLen); 559} 560 561int CallStack::FillUnwindTable(SymbolsFile *symbolsFile, std::shared_ptr<DfxMap> map, UnwindInfo *unwindInfoPtr, 562 uintptr_t pc, UnwindTableInfo& outTableInfo) 563{ 564 HLOGM("try search debug info at %s", symbolsFile->filePath_.c_str()); 565 auto &tableInfoMap = unwindInfoPtr->callStack.unwindTableInfoMap_; 566 // all the thread in same process have same mmap and symbols 567 if (tableInfoMap.find(unwindInfoPtr->thread.pid_) == tableInfoMap.end()) { 568 tableInfoMap.emplace(unwindInfoPtr->thread.pid_, DsoUnwindTableInfoMap {}); 569 } 570 DsoUnwindTableInfoMap &unwTabMap = tableInfoMap[unwindInfoPtr->thread.pid_]; 571 // find use dso name as key 572 if (unwTabMap.find(symbolsFile->filePath_) == unwTabMap.end()) { 573 UnwindTableInfo uti; 574 auto elf = symbolsFile->GetElfFile(); 575 if (elf == nullptr) { 576 return -1; 577 } 578 if (elf->FindUnwindTableInfo(pc, map, uti) == 0) { 579 if (uti.format == -1) { 580 HLOGV("parse unwind table failed."); 581 return -1; 582 } 583 unwTabMap[symbolsFile->filePath_] = uti; 584 outTableInfo = unwTabMap[symbolsFile->filePath_]; 585 DumpTableInfo(uti); 586 return 0; 587 } else { 588 HLOGV("FillUnwindTable failed"); 589 return -1; 590 } 591 } else { 592 outTableInfo = unwTabMap[symbolsFile->filePath_]; 593 return 0; 594 } 595 return -1; 596} 597 598int CallStack::FindUnwindTable(uintptr_t pc, UnwindTableInfo& outTableInfo, void *arg) 599{ 600 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg); 601 auto map = unwindInfoPtr->thread.FindMapByAddr(pc); 602 if (map != nullptr) { 603 SymbolsFile *symbolsFile = unwindInfoPtr->thread.FindSymbolsFileByMap(map); 604 if (symbolsFile != nullptr) { 605 return FillUnwindTable(symbolsFile, map, unwindInfoPtr, pc, outTableInfo); 606 } else { 607 HLOGW("no symbols file found for thread %d:%s", unwindInfoPtr->thread.tid_, 608 unwindInfoPtr->thread.name_.c_str()); 609 } 610 } else { 611 HLOGE("pc 0x%016" UNW_WORD_PFLAG " not found in thread %d:%s", pc, 612 unwindInfoPtr->thread.tid_, unwindInfoPtr->thread.name_.c_str()); 613 } 614 return -1; 615} 616 617int CallStack::AccessMem2(uintptr_t addr, uintptr_t *val, void *arg) 618{ 619 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg); 620 *val = 0; 621 622 /* Check overflow. */ 623 if (addr + sizeof(uintptr_t) < addr) { 624 HLOGE("address overflow at 0x%" UNW_WORD_PFLAG " increase 0x%zu", addr, sizeof(uintptr_t)); 625 return -1; 626 } 627 628 if (addr < unwindInfoPtr->callStack.stackPoint_ or 629 addr + sizeof(uintptr_t) >= unwindInfoPtr->callStack.stackEnd_) { 630 if (ReadVirtualThreadMemory(*unwindInfoPtr, addr, val)) { 631 HLOGM("access_mem addr get val 0x%" UNW_WORD_PFLAG ", from mmap", *val); 632 } else { 633 HLOGW("access_mem mmap 0x%" PRIx64 " failed, STACK RANGE 0x%" PRIx64 "- 0x%" PRIx64 "(0x%" PRIx64 ")", 634 (uint64_t)addr, 635 unwindInfoPtr->callStack.stackPoint_, unwindInfoPtr->callStack.stackEnd_, 636 unwindInfoPtr->callStack.stackEnd_ - unwindInfoPtr->callStack.stackPoint_); 637 return -1; 638 } 639 } else { 640 size_t stackOffset = addr - unwindInfoPtr->callStack.stackPoint_; 641 *val = *(uintptr_t *)&unwindInfoPtr->callStack.stack_[stackOffset]; 642 HLOGM("access_mem addr %p val %" UNW_WORD_PFLAG ", from stack offset %zu", 643 reinterpret_cast<void *>(addr), *val, stackOffset); 644 } 645 646 return 0; 647} 648 649int CallStack::GetMapByPc([[maybe_unused]] uintptr_t pc, [[maybe_unused]] std::shared_ptr<DfxMap>& map, 650 [[maybe_unused]] void *arg) 651{ 652 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg); 653 map = unwindInfoPtr->thread.FindMapByAddr(pc); 654 if (map != nullptr) { 655 HLOGW("GetMapByPc success"); 656 return 0; 657 } 658 HLOGW("GetMapByPc failed"); 659 return -1; 660} 661#endif 662 663CallStack::CallStack() 664{ 665#if defined(HAVE_LIBUNWINDER) && HAVE_LIBUNWINDER 666 accessor_ = std::make_shared<OHOS::HiviewDFX::UnwindAccessors>(); 667 accessor_->FindUnwindTable = &CallStack::FindUnwindTable; 668 accessor_->AccessMem = &CallStack::AccessMem2; 669 accessor_->AccessReg = nullptr; 670 accessor_->GetMapByPc = &CallStack::GetMapByPc; 671#endif 672} 673 674CallStack::~CallStack() 675{ 676#if defined(HAVE_LIBUNWIND) && HAVE_LIBUNWIND 677 for (auto &pair : unwindAddrSpaceMap_) { 678 unw_destroy_addr_space(pair.second); 679 } 680#endif 681} 682} // namespace NativeDaemon 683} // namespace Developtools 684} // namespace OHOS