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#define HILOG_TAG "CallStack" 16 17#include "callstack.h" 18 19#include <dlfcn.h> 20#include <pthread.h> 21#include <iostream> 22 23#include <string> 24#include <utility> 25#if HAVE_LIBUNWIND 26#include <libunwind.h> 27#include <libunwind_i.h> 28#endif 29 30#include "dfx_regs.h" 31#include "hiperf_hilog.h" 32#include "register.h" 33 34#ifdef target_cpu_arm 35// reg size is int (unw_word_t) 36#define UNW_WORD_PFLAG "x" 37#else 38// reg size is long (unw_word_t) 39#define UNW_WORD_PFLAG "zx" 40#endif 41namespace OHOS { 42namespace Developtools { 43namespace HiPerf { 44using namespace OHOS::HiviewDFX; 45 46bool CallStack::ReadVirtualThreadMemory(UnwindInfo &unwindInfoPtr, ADDR_TYPE vaddr, ADDR_TYPE *data) 47{ 48 if (__builtin_expect(unwindInfoPtr.thread.pid_ == unwindInfoPtr.callStack.lastPid_ && 49 vaddr == unwindInfoPtr.callStack.lastAddr_, true)) { 50 *data = unwindInfoPtr.callStack.lastData_; 51 return true; 52 } 53 54 if (unwindInfoPtr.thread.ReadRoMemory(vaddr, reinterpret_cast<uint8_t*>(data), sizeof(ADDR_TYPE))) { 55 unwindInfoPtr.callStack.lastPid_ = unwindInfoPtr.thread.pid_; 56 unwindInfoPtr.callStack.lastAddr_ = vaddr; 57 unwindInfoPtr.callStack.lastData_ = *data; 58 return true; 59 } else { 60 unwindInfoPtr.callStack.lastPid_ = -1; 61 unwindInfoPtr.callStack.lastAddr_ = 0; 62 return false; 63 } 64} 65 66#if HAVE_LIBUNWIND 67const std::map<unw_error_t, const std::string> UNW_ERROR_MAP = { 68 {UNW_ESUCCESS, std::to_string(UNW_ESUCCESS)}, 69 {UNW_EUNSPEC, std::to_string(UNW_EUNSPEC)}, 70 {UNW_ENOMEM, std::to_string(UNW_ENOMEM)}, 71 {UNW_EBADREG, std::to_string(UNW_EBADREG)}, 72 {UNW_EREADONLYREG, std::to_string(UNW_EREADONLYREG)}, 73 {UNW_ESTOPUNWIND, std::to_string(UNW_ESTOPUNWIND)}, 74 {UNW_EINVALIDIP, std::to_string(UNW_EINVALIDIP)}, 75 {UNW_EBADFRAME, std::to_string(UNW_EBADFRAME)}, 76 {UNW_EINVAL, std::to_string(UNW_EINVAL)}, 77 {UNW_EBADVERSION, std::to_string(UNW_EBADVERSION)}, 78 {UNW_ENOINFO, std::to_string(UNW_ENOINFO)}, 79}; 80const std::string CallStack::GetUnwErrorName(int error) 81{ 82 if (UNW_ERROR_MAP.count(static_cast<unw_error_t>(-error)) > 0) { 83 return UNW_ERROR_MAP.at(static_cast<unw_error_t>(-error)); 84 } else { 85 return "UNKNOW_UNW_ERROR"; 86 } 87} 88 89void CallStack::dumpUDI(unw_dyn_info_t &di) 90{ 91 HLOGV("unwind_table info: "); 92 HLOGV(" di.start_ip: 0x%016" UNW_WORD_PFLAG "", di.start_ip); 93 HLOGV(" di.end_ip: 0x%016" UNW_WORD_PFLAG "", di.end_ip); 94 HLOGV(" di.u.rti.segbase: 0x%016" UNW_WORD_PFLAG "", di.u.rti.segbase); 95 HLOGV(" di.u.rti.table_data: 0x%016" UNW_WORD_PFLAG "", di.u.rti.table_data); 96 HLOGV(" di.u.rti.table_len: 0x%016" UNW_WORD_PFLAG "", di.u.rti.table_len); 97} 98 99bool CallStack::fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, std::shared_ptr<DfxMap> map, 100 const VirtualThread &thread) 101{ 102 di.start_ip = map->begin; 103 di.end_ip = map->end; 104#ifndef target_cpu_arm 105 uint64_t fdeTableElfOffset; 106 uint64_t fdeTableSize; 107 uint64_t ehFrameHdrElfOffset; 108 if ((UNW_INFO_FORMAT_REMOTE_TABLE == di.format) && 109 symbolsFile.GetHDRSectionInfo(ehFrameHdrElfOffset, fdeTableElfOffset, fdeTableSize)) { 110 /* 111 unw_word_t name_ptr; // addr. of table name (e.g., library name) 112 unw_word_t segbase; // segment base 113 unw_word_t table_len; // must be a multiple of sizeof(unw_word_t)! 114 unw_word_t table_data; 115 */ 116 /* 117 all the rti addr is offset of the elf file 118 begin - page offset = elf file base addr in vaddr user space 119 begin - page offset + elf offset = vaddr in real word.(for this thread) 120 */ 121 122 // segbase is file offset . 123 /* 124 00200000-00344000 r--p 00000000 08:02 46404365 125 00344000-005c4000 r-xp 00143000 08:02 46404365 126 127 LOAD 0x00000000001439c0 0x00000000003449c0 0x00000000003449c0 128 0x000000000027f3c0 0x000000000027f3c0 R E 0x1000 129 130 GNU_EH_FRAME 0x00000000000f3248 0x00000000002f3248 0x00000000002f3248 131 0x000000000000bb04 0x000000000000bb04 R 0x4 132 133 */ 134 auto ehFrameMap = thread.FindMapByFileInfo(map->name, ehFrameHdrElfOffset); 135 if (ehFrameMap == nullptr) { 136 HLOGE("no ehframe map found."); 137 return false; 138 } 139 140 di.u.rti.segbase = ehFrameMap->begin + ehFrameHdrElfOffset - ehFrameMap->offset; 141 di.u.rti.table_data = ehFrameMap->begin + fdeTableElfOffset - ehFrameMap->offset; 142 di.u.rti.table_len = fdeTableSize / sizeof(uintptr_t); 143 144 HLOGV(" map pageoffset: 0x%016" PRIx64 "", map->offset); 145 HLOGV(" ehFrameHdrElfOffset: 0x%016" PRIx64 "", ehFrameHdrElfOffset); 146 HLOGV(" fdeTableElfOffset: 0x%016" PRIx64 "", fdeTableElfOffset); 147 HLOGV(" fdeTableSize: 0x%016" PRIx64 "", fdeTableSize); 148 return true; 149 } else { 150 HLOGD("SymbolsFile::GetHDRSectionInfo() failed"); 151 } 152#else 153 uint64_t SectionVaddr; 154 uint64_t SectionSize; 155 uint64_t SectionFileOffset; 156 if ((UNW_INFO_FORMAT_ARM_EXIDX == di.format) && 157 symbolsFile.GetSectionInfo(ARM_EXIDX, SectionVaddr, SectionSize, SectionFileOffset)) { 158 auto targetMap = thread.FindMapByFileInfo(map->name, SectionFileOffset); 159 if (targetMap == nullptr) { 160 HLOGE("no debug map found."); 161 return false; 162 } 163 HLOGV(" begin: %" PRIx64 " offset:%" PRIx64 "", targetMap->begin, 164 targetMap->offset); 165 166 di.u.rti.table_data = targetMap->begin + SectionFileOffset - targetMap->offset; 167 di.u.rti.table_len = SectionSize; 168 HLOGV(" SectionName: %s", std::string(ARM_EXIDX).c_str()); 169 HLOGV(" SectionVaddrt: 0x%016" PRIx64 "", SectionVaddr); 170 HLOGV(" SectionFileOffset 0x%016" PRIx64 "", SectionFileOffset); 171 HLOGV(" SectionSize: 0x%016" PRIx64 "", SectionSize); 172 173 // GetSectionInfo return true, but SectionVaddr || SectionSize is 0 ??? 174 HLOG_ASSERT(SectionVaddr != 0 && SectionSize != 0); 175 return true; 176 } else { 177 HLOGD("SymbolsFile::GetSectionInfo() failed"); 178 } 179#endif 180 return false; 181} 182 183/* 184 https://www.nongnu.org/libunwind/man/libunwind-dynamic(3).html 185*/ 186int CallStack::FindUnwindTable(SymbolsFile *symbolsFile, std::shared_ptr<DfxMap> map, 187 UnwindInfo *unwindInfoPtr, unw_addr_space_t as, unw_word_t ip, 188 unw_proc_info_t *pi, int need_unwind_info, void *arg) 189{ 190 HLOGM("try search debug info at %s", symbolsFile->filePath_.c_str()); 191 auto &dynInfoProcessMap = unwindInfoPtr->callStack.unwindTableInfoMap_; 192 // all the thread in same process have same map and symbols 193 if (dynInfoProcessMap.find(unwindInfoPtr->thread.pid_) == dynInfoProcessMap.end()) { 194 dynInfoProcessMap.emplace(unwindInfoPtr->thread.pid_, dsoUnwDynInfoMap {}); 195 } 196 dsoUnwDynInfoMap &dynFileMap = dynInfoProcessMap[unwindInfoPtr->thread.pid_]; 197 // find use dso name as key 198 if (dynFileMap.find(symbolsFile->filePath_) == dynFileMap.end()) { 199 unw_dyn_info_t newdi; 200 if (memset_s(&newdi, sizeof(unw_dyn_info_t), 0, sizeof(unw_dyn_info_t)) != EOK) { 201 HLOGE("memset_s() failed"); 202 return -UNW_EUNSPEC; 203 } 204#ifdef target_cpu_arm 205 // arm use .ARM.exidx , not use ehframe 206 newdi.format = UNW_INFO_FORMAT_ARM_EXIDX; 207#else 208 // otherwise we use EH FRAME 209 newdi.format = UNW_INFO_FORMAT_REMOTE_TABLE; 210#endif 211 if (fillUDI(newdi, *symbolsFile, map, unwindInfoPtr->thread)) { 212 dumpUDI(newdi); 213 // we make a option empty value first 214 std::optional<unw_dyn_info_t> &odi = dynFileMap[symbolsFile->filePath_]; 215 odi = newdi; 216 } else { 217 HLOGV("fillUDI failed()"); 218 return -UNW_EUNSPEC; 219 } 220 } 221 222 HLOG_ASSERT(dynInfoProcessMap.find(unwindInfoPtr->thread.pid_) != dynInfoProcessMap.end()); 223 HLOG_ASSERT_MESSAGE(dynFileMap.find(symbolsFile->filePath_) != dynFileMap.end(), "%s", 224 symbolsFile->filePath_.c_str()); 225 std::optional<unw_dyn_info_t> &odi = 226 dynInfoProcessMap.at(unwindInfoPtr->thread.pid_).at(symbolsFile->filePath_); 227 228 if (odi.has_value()) { 229 unw_dyn_info_t &di = odi.value(); 230 /* 231 we don't use dwarf_search_unwind_table 232 because in arm it will search two function: 233 1 arm_search_unwind_table first 234 2 dwarf_search_unwind_table 235 236 see libunwind_i.h for arm 237 define tdep_search_unwind_table UNW_OBJ(search_unwind_table) 238 239 */ 240 int ret = static_cast<unw_error_t>( 241 tdep_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg)); 242 243 HLOGM("search_unwind_table ret %d:%s", ret, GetUnwErrorName(ret).c_str()); 244 245 if (UNW_ESUCCESS != ret) { 246 if (UNW_ENOINFO != ret) { 247 HLOGW("search_unwind_table ret error %d:%s", ret, GetUnwErrorName(ret).c_str()); 248 } 249 return -UNW_EUNSPEC; 250 } else { 251 return UNW_ESUCCESS; 252 } 253 } else { 254 HLOGW("no debug info found for thread %d:%s", unwindInfoPtr->thread.tid_, 255 unwindInfoPtr->thread.name_.c_str()); 256 return -UNW_EUNSPEC; 257 } 258} 259 260int CallStack::FindProcInfo(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, 261 int need_unwind_info, void *arg) 262{ 263 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg); 264 265 HLOGM("need_unwind_info ret %d ip %" UNW_WORD_PFLAG "", need_unwind_info, ip); 266 auto map = unwindInfoPtr->thread.FindMapByAddr(ip); 267 if (map != nullptr) { 268 SymbolsFile *symbolsFile = unwindInfoPtr->thread.FindSymbolsFileByMap(map); 269 if (symbolsFile != nullptr) { 270 return FindUnwindTable(symbolsFile, map, unwindInfoPtr, as, ip, pi, need_unwind_info, arg); 271 } else { 272 HLOGW("no symbols file found for thread %d:%s", unwindInfoPtr->thread.tid_, 273 unwindInfoPtr->thread.name_.c_str()); 274 } 275 } else { 276 HLOGE("ip 0x%016" UNW_WORD_PFLAG " not found in thread %d:%s", ip, 277 unwindInfoPtr->thread.tid_, unwindInfoPtr->thread.name_.c_str()); 278 } 279 280 return -UNW_EUNSPEC; 281} 282 283int CallStack::AccessMem([[maybe_unused]] unw_addr_space_t as, unw_word_t addr, 284 unw_word_t *valuePoint, int writeOperation, void *arg) 285{ 286 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg); 287 *valuePoint = 0; 288 289 /* Check overflow. */ 290 if (addr + sizeof(unw_word_t) < addr) { 291 HLOGE("address overfolw at 0x%" UNW_WORD_PFLAG " increase 0x%zu", addr, sizeof(unw_word_t)); 292 return -UNW_EUNSPEC; 293 } 294 295 if (addr < unwindInfoPtr->callStack.stackPoint_ or 296 addr + sizeof(unw_word_t) >= unwindInfoPtr->callStack.stackEnd_) { 297 if (ReadVirtualThreadMemory(*unwindInfoPtr, addr, valuePoint)) { 298 HLOGM("access_mem addr get val 0x%" UNW_WORD_PFLAG ", from mmap", *valuePoint); 299 } else { 300 HLOGW("access_mem addr failed, from mmap, STACK RANGE 0x%" PRIx64 "- 0x%" PRIx64 "(0x%" PRIx64 ")", 301 unwindInfoPtr->callStack.stackPoint_, unwindInfoPtr->callStack.stackEnd_, 302 unwindInfoPtr->callStack.stackEnd_ - unwindInfoPtr->callStack.stackPoint_); 303 return -UNW_EUNSPEC; 304 } 305 } else { 306 size_t stackOffset = addr - unwindInfoPtr->callStack.stackPoint_; 307 *valuePoint = *(unw_word_t *)&unwindInfoPtr->callStack.stack_[stackOffset]; 308 HLOGM("access_mem addr %p val %" UNW_WORD_PFLAG ", from stack offset %zu", 309 reinterpret_cast<void *>(addr), *valuePoint, stackOffset); 310 } 311 312 return UNW_ESUCCESS; 313} 314 315int CallStack::AccessReg([[maybe_unused]] unw_addr_space_t as, unw_regnum_t regnum, 316 unw_word_t *valuePoint, int writeOperation, void *arg) 317{ 318 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg); 319 uint64_t val; 320 int perfRegIndex = LibunwindRegIdToPerfReg(regnum); 321 if (perfRegIndex < 0) { 322 HLOGE("can't read reg %d", perfRegIndex); 323 return perfRegIndex; 324 } 325 /* Don't support write, I suspect we don't need it. */ 326 if (writeOperation) { 327 HLOGE("access_reg %d", regnum); 328 return -UNW_EINVAL; 329 } 330 331 if (unwindInfoPtr->callStack.regsNum_ == 0) { 332 return -UNW_EUNSPEC; 333 } 334 335 if (!RegisterGetValue(val, unwindInfoPtr->callStack.regs_, static_cast<size_t>(perfRegIndex), 336 unwindInfoPtr->callStack.regsNum_)) { 337 HLOGE("can't read reg %d", perfRegIndex); 338 return -UNW_EUNSPEC; 339 } 340 341 *valuePoint = (unw_word_t)val; 342 HLOGM("reg %d:%s, val 0x%" UNW_WORD_PFLAG "", regnum, RegisterGetName(static_cast<size_t>(perfRegIndex)).c_str(), 343 *valuePoint); 344 return UNW_ESUCCESS; 345} 346 347void CallStack::PutUnwindInfo([[maybe_unused]] unw_addr_space_t as, 348 [[maybe_unused]] unw_proc_info_t *pi, [[maybe_unused]] void *arg) 349{ 350} 351 352int CallStack::AccessFpreg([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_regnum_t num, 353 [[maybe_unused]] unw_fpreg_t *val, [[maybe_unused]] int writeOperation, 354 [[maybe_unused]] void *arg) 355{ 356 return -UNW_EINVAL; 357} 358 359int CallStack::GetDynInfoListAaddr([[maybe_unused]] unw_addr_space_t as, 360 [[maybe_unused]] unw_word_t *dil_vaddr, 361 [[maybe_unused]] void *arg) 362{ 363 return -UNW_ENOINFO; 364} 365 366int CallStack::Resume([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_cursor_t *cu, 367 [[maybe_unused]] void *arg) 368{ 369 return -UNW_EINVAL; 370} 371 372int CallStack::getProcName([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_word_t addr, 373 [[maybe_unused]] char *bufp, [[maybe_unused]] size_t buf_len, 374 [[maybe_unused]] unw_word_t *offp, [[maybe_unused]] void *arg) 375{ 376 return -UNW_EINVAL; 377} 378 379void CallStack::UnwindStep(unw_cursor_t &c, std::vector<DfxFrame> &callStack, size_t maxStackLevel) 380{ 381 while (callStack.size() < maxStackLevel) { 382 int ret = unw_step(&c); 383 if (ret > 0) { 384 unw_word_t ip; 385 unw_word_t sp; 386 unw_get_reg(&c, UNW_REG_IP, &ip); 387 unw_get_reg(&c, UNW_REG_SP, &sp); 388 389 if (ip == 0) { 390 HLOGD("ip == 0 something is wrong. break"); 391 break; 392 } 393 394 /* 395 * Decrement the IP for any non-activation frames. 396 * this is required to properly find the srcline 397 * for caller frames. 398 * See also the documentation for dwfl_frame_pc(), 399 * which this code tries to replicate. 400 */ 401 if (unw_is_signal_frame(&c) <= 0) { 402 --ip; 403 } 404 HLOGV("unwind:%zu: ip 0x%" UNW_WORD_PFLAG " sp 0x%" UNW_WORD_PFLAG "", callStack.size(), 405 ip, sp); 406 if (callStack.back().pc == ip && callStack.back().sp == sp) { 407 HLOGW("we found a same frame, stop here"); 408 break; 409 } 410 callStack.emplace_back(ip, sp); 411 } else { 412 HLOGV("no more frame step found. ret %d:%s", ret, GetUnwErrorName(ret).c_str()); 413 break; 414 } 415 } 416} 417#endif 418 419bool CallStack::GetIpSP(uint64_t &ip, uint64_t &sp, const u64 *regs, size_t regNum) const 420{ 421 if (regNum > 0) { 422 CHECK_TRUE(!RegisterGetSPValue(sp, arch_, regs, regNum), false, 1, "unable get sp"); 423 CHECK_TRUE(!RegisterGetIPValue(ip, arch_, regs, regNum), false, 1, "unable get ip"); 424 if (ip != 0) { 425 return true; 426 } 427 } else { 428 HLOGW("reg size is 0"); 429 return false; 430 } 431 return false; 432} 433 434#if HAVE_LIBUNWIND 435bool CallStack::DoUnwind(const VirtualThread &thread, std::vector<DfxFrame> &callStack, 436 size_t maxStackLevel) 437{ 438 unw_addr_space_t addr_space; 439 UnwindInfo unwindInfo = { 440 .thread = thread, 441 .callStack = *this, 442 }; 443 unw_cursor_t c; 444 if (unwindAddrSpaceMap_.count(thread.tid_) == 0) { 445 addr_space = unw_create_addr_space(&accessors_, 0); 446 if (!addr_space) { 447 HLOGE("Can't create unwind vaddress space."); 448 return false; 449 } 450 unwindAddrSpaceMap_.emplace(thread.tid_, addr_space); 451 unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); 452 unw_flush_cache(addr_space, 0, 0); 453 } else { 454 addr_space = unwindAddrSpaceMap_.at(thread.tid_); 455 } 456 457 int ret = unw_init_remote(&c, addr_space, &unwindInfo); 458 if (ret) { 459 HLOGE("unwind error %d:%s see unw_error_t.", ret, GetUnwErrorName(ret).c_str()); 460 return false; 461 } else { 462 UnwindStep(c, callStack, maxStackLevel); 463 } 464 return true; 465} 466#endif 467 468bool CallStack::UnwindCallStack(const VirtualThread &thread, bool abi32, u64 *regs, u64 regsNum, 469 const u8 *stack, u64 stackSize, std::vector<DfxFrame> &callStack, 470 size_t maxStackLevel) 471{ 472 regs_ = regs; 473 regsNum_ = regsNum; 474 stack_ = stack; 475 stackSize_ = stackSize; 476 477 arch_ = GetArchTypeFromABI(abi32); 478 UpdateRegForABI(arch_, regs_); 479 if (!RegisterGetSPValue(stackPoint_, arch_, regs_, regsNum_)) { 480 HLOGE("RegisterGetSPValue failed"); 481 return false; 482 } else { 483 stackEnd_ = stackPoint_ + stackSize_; 484 } 485 486 uint64_t ip; 487 uint64_t sp; 488 if (!GetIpSP(ip, sp, regs_, regsNum_)) { 489 HLOGW("unable get sp or sp , unable unwind"); 490 return false; 491 } else { 492 if (ip != 0) { 493 HLOGV("unwind:%zu: ip 0x%" PRIx64 " sp 0x%" PRIx64 "", callStack.size(), ip, sp); 494 callStack.emplace_back(ip, sp); 495 } 496 } 497 498 /* 499 * If we need more than one entry, do the DWARF 500 * unwind itself. 501 */ 502 if (maxStackLevel - 1 > 0) { 503#if HAVE_LIBUNWIND 504 return DoUnwind(thread, callStack, maxStackLevel); 505#else 506 return DoUnwind2(thread, callStack, maxStackLevel); 507#endif 508 } 509 return true; 510} 511 512void CallStack::LogFrame(const std::string msg, const std::vector<DfxFrame> &frames) 513{ 514 HLOGM("%s", msg.c_str()); 515 int level = 0; 516 for (auto& frame : frames) { 517 HLOGM("%d:%s", level++, frame.ToString().c_str()); 518 } 519} 520 521/* 522we should have CallStack cache for each thread 523end begin 5240. A -> B -> C -> E -> F 5251. C -> E -> F 5262. B -> C 5273. A -> B -> C 5284. B -> G -> H 5295. J -> C 530 5310 is our cache 5321 2 3... is from record 533 534use expandLimit to setup how may frame match is needs 535 536*/ 537size_t CallStack::DoExpandCallStack(std::vector<DfxFrame> &newCallFrames, 538 const std::vector<DfxFrame> &cachedCallFrames, 539 size_t expandLimit) 540{ 541 int maxCycle = 0; 542 543 if (expandLimit == 0 or newCallFrames.size() < expandLimit or 544 cachedCallFrames.size() < expandLimit or 545 cachedCallFrames.size() >= MAX_CALL_FRAME_UNWIND_SIZE) { 546 HLOGM("expandLimit %zu not match new %zu cache %zu", expandLimit, newCallFrames.size(), 547 cachedCallFrames.size()); 548 return 0; // size not enough 549 } 550 551 // called (Stack Bottom) , this will NOT change when compare 552 // in case1 newIt -> C 553 // in case2 newIt -> B 554 const auto newIt = newCallFrames.end() - expandLimit; 555 if (newIt != newCallFrames.end()) { 556 HLOGM("try find new call chain bottom %s for limit %zu", newIt->ToString().c_str(), 557 expandLimit); 558 } 559 560 // first frame search, from called - > caller 561 // for case 2 it should found B 562 size_t distances = expandLimit - 1; 563 auto cachedIt = find(cachedCallFrames.begin(), cachedCallFrames.end(), *newIt); 564 if (cachedIt == cachedCallFrames.end()) { 565 HLOGM("not found in first search"); 566 } 567 568 // cache frame found 569 while (std::distance(cachedIt, cachedCallFrames.end()) >= signed(expandLimit)) { 570 HLOG_ASSERT_MESSAGE(maxCycle++ < MAX_CALL_FRAME_EXPAND_CYCLE, "MAX_UNWIND_CYCLE = %d reach", 571 MAX_CALL_FRAME_EXPAND_CYCLE); 572 573 if (std::equal(newIt, newIt + expandLimit, cachedIt)) { 574 HLOGM("match %s + %zu", newIt->ToString().c_str(), expandLimit); 575 cachedIt += expandLimit; // in while we check the boundary safe 576 if (cachedIt == cachedCallFrames.end()) { 577 // same but no more need expand 578 break; 579 } 580 581 // expand the frame and make some log ? 582 LogFrame("newCallStack:", newCallFrames); 583 LogFrame("cachedCallStack:", cachedCallFrames); 584 585 newCallFrames.insert(newCallFrames.end(), cachedIt, cachedCallFrames.end()); 586 auto expands = std::distance(cachedIt, cachedCallFrames.end()); 587 HLOGV("merge callstack increse to %zu (+%zd) ", newCallFrames.size(), expands); 588 // we done the deal 589 return expands; 590 } else { 591 // quick search next same farme again 592 cachedIt++; 593 if (cachedIt != cachedCallFrames.end()) { 594 HLOGM("search next"); 595 cachedIt = find(cachedIt, cachedCallFrames.end(), *newIt); 596 } 597 } 598 } 599 HLOGM("cachedIt distance %zd , need %zd", std::distance(cachedCallFrames.begin(), cachedIt), 600 distances); 601 return 0u; // nothing expand 602} 603 604size_t CallStack::ExpandCallStack(pid_t tid, std::vector<DfxFrame> &callFrames, size_t expandLimit) 605{ 606 size_t expand = 0u; 607 if (expandLimit == 0) { 608 return expand; // nothing need to do 609 } else if (callFrames.size() < expandLimit) { 610 HLOGM("new callstack is too small, skip it"); 611 return expand; 612 } 613 if (!cachedCallFramesMap_.count(tid)) { 614 cachedCallFramesMap_[tid].reserve(MAX_CALL_FRAME_EXPAND_CACHE_SIZE); 615 } 616 if (callFrames.size() >= 1u) { 617 // get top (Earliest caller) 618 HashList<uint64_t, std::vector<DfxFrame>> &cachedCallFrames = cachedCallFramesMap_[tid]; 619 HLOGV("find call stack frames in cache size %zu", cachedCallFrames.size()); 620 // compare 621 using namespace std::rel_ops; // enable complement comparing operators 622 for (auto itr = cachedCallFrames.begin(); itr < cachedCallFrames.end(); ++itr) { 623 // each cached callstack 624 /* 625 stack 2 1 0 626 cache A -> B -> C 627 new B -> C 628 check: 629 1 if new B == cache C 630 2 if new B == cache B 631 3 if new C == new C (if limit > 0) 632 4 insert A after B in new stack 633 */ 634 const std::vector<DfxFrame> &cachedCallStack = *itr; 635 if (cachedCallStack.size() < expandLimit) { 636 HLOGM("cache callstack is too small, skip it"); 637 continue; // check next 638 } 639 expand = DoExpandCallStack(callFrames, cachedCallStack, expandLimit); 640 if (expand > 0) { 641 break; 642 } 643 } 644 // add new one in to cache cachedCallFrames. 645 // further optimization can be done by caching pointer which avoids copying 646 // vector 647 cachedCallFrames[callFrames[0].pc] = callFrames; 648 } 649 HLOGM("expand %zu", expand); 650 return expand; 651} 652 653#if defined(HAVE_LIBUNWINDER) && HAVE_LIBUNWINDER 654bool CallStack::DoUnwind2(const VirtualThread &thread, std::vector<DfxFrame> &callStack, 655 size_t maxStackLevel) 656{ 657#ifdef target_cpu_x86_64 658 return false; 659#else 660 UnwindInfo unwindInfo = { 661 .thread = thread, 662 .callStack = *this, 663 }; 664 665 if (pidUnwinder_.count(thread.pid_) == 0) { 666 pidUnwinder_.emplace(thread.pid_, std::make_shared<Unwinder>(accessor_)); 667 } 668 auto unwinder = pidUnwinder_[thread.pid_]; 669 670#ifdef target_cpu_arm 671 static std::shared_ptr<DfxRegs> regs = std::make_shared<DfxRegsArm>(); 672 std::vector<uintptr_t> tempRegs; 673 for (auto i = 0; i < regsNum_; ++i) { 674 tempRegs.push_back(static_cast<uintptr_t>(regs_[i])); 675 } 676 regs->SetRegsData(tempRegs); 677#else 678 static std::shared_ptr<DfxRegs> regs = std::make_shared<DfxRegsArm64>(); 679 regs->SetRegsData(reinterpret_cast<uintptr_t*>(regs_), regsNum_); 680#endif 681 CHECK_TRUE(unwinder == nullptr, false, 0, ""); 682 unwinder->SetRegs(regs); 683 unwinder->Unwind(&unwindInfo); 684 callStack = unwinder->GetFrames(); 685 HLOGD("callStack size:%zu", callStack.size()); 686 for (auto frame: callStack) { 687 HLOGD("pc 0x%" PRIx64 " sp 0x%" PRIx64 "", frame.pc, frame.sp); 688 } 689 auto lastIt = callStack.end() - 1; 690 auto preIt = lastIt - 1; 691 if (lastIt != callStack.end() && preIt != callStack.end() && 692 callStack.size() > 1 && lastIt->pc == preIt->pc && lastIt->sp == preIt->sp) { 693 callStack.erase(lastIt); 694 HLOGD("remove last callframe"); 695 } 696 return true; 697#endif 698} 699 700void CallStack::DumpTableInfo(UnwindTableInfo &outTableInfo) 701{ 702 HLOGV("unwind_table info: "); 703 HLOGV(" start_ip: 0x%016" UNW_WORD_PFLAG "", outTableInfo.startPc); 704 HLOGV(" end_ip: 0x%016" UNW_WORD_PFLAG "", outTableInfo.endPc); 705 HLOGV(" segbase: 0x%016" UNW_WORD_PFLAG "", outTableInfo.segbase); 706 HLOGV(" table_data: 0x%016" UNW_WORD_PFLAG "", outTableInfo.tableData); 707 HLOGV(" table_len: 0x%016" UNW_WORD_PFLAG "", outTableInfo.tableLen); 708} 709 710int CallStack::FillUnwindTable(SymbolsFile *symbolsFile, std::shared_ptr<DfxMap> map, UnwindInfo *unwindInfoPtr, 711 uintptr_t pc, UnwindTableInfo& outTableInfo) 712{ 713 HLOGM("try search debug info at %s", symbolsFile->filePath_.c_str()); 714 CHECK_TRUE(unwindInfoPtr == nullptr, -1, 0, ""); 715 auto &tableInfoMap = unwindInfoPtr->callStack.unwindTableInfoMap_; 716 // all the thread in same process have same mmap and symbols 717 if (tableInfoMap.find(unwindInfoPtr->thread.pid_) == tableInfoMap.end()) { 718 tableInfoMap.emplace(unwindInfoPtr->thread.pid_, DsoUnwindTableInfoMap {}); 719 } 720 DsoUnwindTableInfoMap &unwTabMap = tableInfoMap[unwindInfoPtr->thread.pid_]; 721 // find use dso name as key 722 if (unwTabMap.find(symbolsFile->filePath_) == unwTabMap.end()) { 723 UnwindTableInfo uti; 724 auto elf = symbolsFile->GetElfFile(); 725 if (elf == nullptr) { 726 return -1; 727 } 728 if (elf->FindUnwindTableInfo(pc, map, uti) == 0) { 729 CHECK_TRUE(uti.format == -1, -1, 1, "parse unwind table failed."); 730 unwTabMap[symbolsFile->filePath_] = uti; 731 outTableInfo = unwTabMap[symbolsFile->filePath_]; 732 DumpTableInfo(uti); 733 return 0; 734 } else { 735 HLOGV("FillUnwindTable failed"); 736 return -1; 737 } 738 } else { 739 outTableInfo = unwTabMap[symbolsFile->filePath_]; 740 return 0; 741 } 742 return -1; 743} 744 745int CallStack::FindUnwindTable(uintptr_t pc, UnwindTableInfo& outTableInfo, void *arg) 746{ 747 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg); 748 CHECK_TRUE(unwindInfoPtr == nullptr, -1, 0, ""); 749 int64_t mapIndex = unwindInfoPtr->thread.FindMapIndexByAddr(pc); 750 if (mapIndex >= 0) { 751 auto map = unwindInfoPtr->thread.GetMaps()[mapIndex]; 752 if (map != nullptr) { 753 SymbolsFile *symbolsFile = unwindInfoPtr->thread.FindSymbolsFileByMap(map); 754 if (symbolsFile != nullptr) { 755 return FillUnwindTable(symbolsFile, map, unwindInfoPtr, pc, outTableInfo); 756 } else { 757 HLOGD("no symbols file found for thread %d:%s", unwindInfoPtr->thread.tid_, 758 unwindInfoPtr->thread.name_.c_str()); 759 } 760 } else { 761 HLOGD("pc 0x%016" UNW_WORD_PFLAG " not found in thread %d:%s", pc, 762 unwindInfoPtr->thread.tid_, unwindInfoPtr->thread.name_.c_str()); 763 } 764 } else { 765 HLOGD("map index is -1"); 766 } 767 return -1; 768} 769 770int CallStack::AccessMem2(uintptr_t addr, uintptr_t *val, void *arg) 771{ 772 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg); 773 *val = 0; 774 775 /* Check overflow. */ 776 CHECK_TRUE(unwindInfoPtr == nullptr || (addr + sizeof(uintptr_t) < addr), -1, 1, 777 "unwindInfoPtr is null or address overflow at 0x%" UNW_WORD_PFLAG " increase 0x%zu", 778 addr, sizeof(uintptr_t)); 779 780 if (addr < unwindInfoPtr->callStack.stackPoint_ or 781 addr + sizeof(uintptr_t) >= unwindInfoPtr->callStack.stackEnd_) { 782 if (ReadVirtualThreadMemory(*unwindInfoPtr, addr, val)) { 783 HLOGM("access_mem addr get val 0x%" UNW_WORD_PFLAG ", from mmap", *val); 784 } else { 785 HLOGW("access_mem mmap 0x%" PRIx64 " failed, STACK RANGE 0x%" PRIx64 "- 0x%" PRIx64 "(0x%" PRIx64 ")", 786 (uint64_t)addr, 787 unwindInfoPtr->callStack.stackPoint_, unwindInfoPtr->callStack.stackEnd_, 788 unwindInfoPtr->callStack.stackEnd_ - unwindInfoPtr->callStack.stackPoint_); 789 return -1; 790 } 791 } else { 792 size_t stackOffset = addr - unwindInfoPtr->callStack.stackPoint_; 793 *val = *(uintptr_t *)&unwindInfoPtr->callStack.stack_[stackOffset]; 794 HLOGM("access_mem addr %p val %" UNW_WORD_PFLAG ", from stack offset %zu", 795 reinterpret_cast<void *>(addr), *val, stackOffset); 796 } 797 798 return 0; 799} 800int CallStack::GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg) 801{ 802 UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg); 803 int64_t mapIndex = unwindInfoPtr->thread.FindMapIndexByAddr(pc); 804 if (mapIndex >= 0) { 805 map = unwindInfoPtr->thread.GetMaps()[mapIndex]; 806 if (map != nullptr) { 807 return 0; 808 } 809 } 810 HLOGD("pc 0x%016" UNW_WORD_PFLAG " not found in thread %d:%s", pc, 811 unwindInfoPtr->thread.tid_, unwindInfoPtr->thread.name_.c_str()); 812 return -1; 813} 814#endif 815 816CallStack::CallStack() 817{ 818#if defined(HAVE_LIBUNWINDER) && HAVE_LIBUNWINDER 819 accessor_ = std::make_shared<OHOS::HiviewDFX::UnwindAccessors>(); 820 accessor_->FindUnwindTable = &CallStack::FindUnwindTable; 821 accessor_->AccessMem = &CallStack::AccessMem2; 822 accessor_->AccessReg = nullptr; 823 accessor_->GetMapByPc = &CallStack::GetMapByPc; 824#endif 825} 826 827CallStack::~CallStack() 828{ 829#if HAVE_LIBUNWIND 830 for (auto &pair : unwindAddrSpaceMap_) { 831 unw_destroy_addr_space(pair.second); 832 } 833#endif 834} 835} // namespace HiPerf 836} // namespace Developtools 837} // namespace OHOS 838