1/* 2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. 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 16#define HILOG_TAG "Symbols" 17 18#include "symbols_file.h" 19 20#include <algorithm> 21#include <chrono> 22#include <cxxabi.h> 23#include <fcntl.h> 24#include <fstream> 25 26#if is_mingw 27#include <memoryapi.h> 28#else 29#include <sys/mman.h> 30#include <sys/stat.h> 31#endif 32 33#include <cstdlib> 34#include <unistd.h> 35#include "dfx_ark.h" 36#include "dfx_extractor_utils.h" 37#include "dfx_symbols.h" 38#include "dwarf_encoding.h" 39#include "utilities.h" 40 41using namespace OHOS::HiviewDFX; 42using namespace std::chrono; 43 44namespace OHOS { 45namespace Developtools { 46namespace NativeDaemon { 47bool SymbolsFile::onRecording_ = true; 48const std::string SymbolsFile::GetBuildId() const 49{ 50 return buildId_; 51} 52 53bool SymbolsFile::UpdateBuildIdIfMatch(std::string buildId) 54{ 55 /* 56 here we have two case 57 1 buildId_ is empty 58 a) always return match 59 2 buildId_ is not empty 60 a) really check if the same one 61 */ 62 63 if (buildId_.empty()) { 64 // we have new empty build 65 if (buildId.empty()) { 66 // both empty , no build id provided 67 HLOGD("build id is empty."); 68 return true; 69 } else { 70 buildId_ = buildId; 71 HLOGD("new buildId %s", buildId_.c_str()); 72 return true; 73 } 74 } else { 75 // we already have a build id 76 // so this is not the first time load symbol 77 // we need check if it match 78 HLOGV("expected buildid: %s vs %s", buildId_.c_str(), buildId.c_str()); 79 80 if (buildId_ != buildId) { 81 HLOGW("id not match"); 82 return false; 83 } else { 84 HLOGD("id match"); 85 return true; 86 } 87 } 88} 89 90std::string SymbolsFile::SearchReadableFile(const std::vector<std::string> &searchPaths, 91 const std::string &filePath) const 92{ 93 UNWIND_CHECK_TRUE(!filePath.empty(), filePath, "nothing to found"); 94 for (auto searchPath : searchPaths) { 95 if (searchPath.back() != PATH_SEPARATOR) { 96 searchPath += PATH_SEPARATOR; 97 } 98 std::string PossibleFilePath = searchPath + filePath; 99 if (CheckPathReadable(PossibleFilePath)) { 100 return PossibleFilePath; 101 } 102 HLOGW("have not found '%s' in search paths %s", filePath.c_str(), searchPath.c_str()); 103 } 104 return EMPTY_STRING; 105} 106 107const std::string SymbolsFile::FindSymbolFile( 108 const std::vector<std::string> &symbolsFileSearchPaths, std::string symboleFilePath) const 109{ 110 /* 111 this function do 2 things: 112 find by name: 113 1 find dso path 114 2 find search path 115 a) search path + dso path 116 b) search path + dso name 117 118 show we should return filePath_ as default ? 119 */ 120 if (symboleFilePath.empty()) { 121 symboleFilePath = filePath_; 122 HLOGD("use default filename: %s ", symboleFilePath.c_str()); 123 } 124 symboleFilePath = PlatformPathConvert(symboleFilePath); 125 std::string foundPath; 126 // search fisrt if we have path 127 if (symbolsFileSearchPaths.size() != 0) { 128 foundPath = SearchReadableFile(symbolsFileSearchPaths, symboleFilePath); 129 if (foundPath.empty()) { 130 HLOGV("try base name for: %s split with %s", symboleFilePath.c_str(), 131 PATH_SEPARATOR_STR.c_str()); 132 auto pathSplit = StringSplit(symboleFilePath, PATH_SEPARATOR_STR); 133 if (pathSplit.size() > 1) { 134 HLOGV("base name is: %s ", pathSplit.back().c_str()); 135 // found it again with base name , split it and get last name 136 foundPath = SearchReadableFile(symbolsFileSearchPaths, pathSplit.back()); 137 } 138 } 139 } 140 auto pathSplit = StringSplit(symboleFilePath, "!"); 141 if (pathSplit.size() > 1) { 142 HLOGV("base name is: %s ", pathSplit.back().c_str()); 143 if (StringEndsWith(pathSplit[0], ".hap")) { 144 foundPath = pathSplit[0]; 145 } 146 } 147 // only access the patch in onRecording_ 148 // in report mode we don't load any thing in runtime path 149 if (foundPath.empty() and onRecording_) { 150 // try access direct at last 151 if (CheckPathReadable(symboleFilePath)) { 152 // found direct folder 153 HLOGD("find %s in current work dir", symboleFilePath.c_str()); 154 return symboleFilePath; 155 } 156 } 157 return foundPath; 158} 159 160class ElfFileSymbols : public SymbolsFile { 161public: 162 explicit ElfFileSymbols(const std::string &symbolFilePath, 163 const SymbolsFileType symbolsFileType = SYMBOL_ELF_FILE) 164 : SymbolsFile(symbolsFileType, symbolFilePath) 165 { 166 } 167 168 virtual ~ElfFileSymbols() 169 { 170 } 171 172 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override 173 { 174 symbolsLoaded_ = true; 175 std::string findPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath); 176 UNWIND_CHECK_TRUE(!findPath.empty(), false, "elf found failed (belong to %s)", filePath_.c_str()); 177 if (LoadElfSymbols(map, findPath)) { 178 return true; 179 } else { 180 HLOGW("elf open failed with '%s'", findPath.c_str()); 181 return false; 182 } 183 return false; 184 } 185 186 std::shared_ptr<DfxElf> GetElfFile() override 187 { 188 return elfFile_; 189 } 190 191protected: 192 bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override 193 { 194 if (debugInfoLoaded_) { 195 return true; 196 } else { 197 debugInfoLoaded_ = true; 198 } 199 std::string elfPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath); 200 UNWIND_CHECK_TRUE(!elfPath.empty(), false, "elf found failed (belong to %s)", filePath_.c_str()); 201 if (elfFile_ == nullptr) { 202 if (StringEndsWith(elfPath, ".hap")) { 203 elfFile_ = DfxElf::CreateFromHap(elfPath, map->prevMap, map->offset); 204 map->elf = elfFile_; 205 } else { 206 elfFile_ = std::make_shared<DfxElf>(elfPath); 207 } 208 } 209 210 if (elfFile_ == nullptr) { 211 HLOGE("Failed to create elf file for %s.", elfPath.c_str()); 212 return false; 213 } 214 215 if (!elfFile_->IsValid()) { 216 HLOGE("parse elf file failed."); 217 return false; 218 } 219 220 HLOGD("loaded elf %s", elfPath.c_str()); 221 // update path for so in hap 222 if (StringEndsWith(elfPath, ".hap")) { 223 filePath_ = elfPath + "!" + elfFile_->GetElfName(); 224 HLOGD("update path for so in hap %s.", filePath_.c_str()); 225 map->name = filePath_; 226 map->elf = elfFile_; 227 map->prevMap->name = filePath_; 228 map->prevMap->elf = elfFile_; 229 } 230 textExecVaddr_ = elfFile_->GetStartVaddr(); 231 textExecVaddrFileOffset_ = elfFile_->GetStartOffset(); 232 HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_, 233 textExecVaddrFileOffset_); 234 235#ifndef __arm__ 236 ShdrInfo shinfo; 237 if (elfFile_->GetSectionInfo(shinfo, ".eh_frame_hdr")) { 238 LoadEhFrameHDR(elfFile_->GetMmapPtr() + shinfo.offset, shinfo.size, shinfo.offset); 239 } 240#endif 241 return true; 242 } 243 244private: 245 bool ehFrameHDRValid_ {false}; 246 uint64_t ehFrameHDRElfOffset_ {0}; 247 uint64_t ehFrameHDRFdeCount_ {0}; 248 uint64_t ehFrameHDRFdeTableItemSize_ {0}; 249 uint64_t ehFrameHDRFdeTableElfOffset_ {0}; 250 std::shared_ptr<DfxElf> elfFile_; 251 bool GetSectionInfo(const std::string &name, uint64_t §ionVaddr, uint64_t §ionSize, 252 uint64_t §ionFileOffset) const override 253 { 254 struct ShdrInfo shdrInfo; 255 if (elfFile_->GetSectionInfo(shdrInfo, name)) { 256 sectionVaddr = shdrInfo.addr; 257 sectionSize = shdrInfo.size; 258 sectionFileOffset = shdrInfo.offset; 259 HLOGM("Get Section '%s' %" PRIx64 " - %" PRIx64 "", name.c_str(), sectionVaddr, sectionSize); 260 return true; 261 } else { 262 HLOGW("Section '%s' not found", name.c_str()); 263 return false; 264 } 265 } 266 267#ifndef __arm__ 268 bool GetHDRSectionInfo(uint64_t &ehFrameHdrElfOffset, uint64_t &fdeTableElfOffset, 269 uint64_t &fdeTableSize) override 270 { 271 ShdrInfo shinfo; 272 if (!elfFile_->GetSectionInfo(shinfo, ".eh_frame_hdr")) { 273 return false; 274 } 275 276 ehFrameHDRElfOffset_ = shinfo.offset; 277 if (ehFrameHDRValid_) { 278 ehFrameHdrElfOffset = ehFrameHDRElfOffset_; 279 fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_; 280 fdeTableSize = ehFrameHDRFdeCount_; 281 return true; 282 } else { 283 HLOGW("!ehFrameHDRValid_"); 284 return false; 285 } 286 if (!LoadEhFrameHDR(elfFile_->GetMmapPtr() + shinfo.offset, elfFile_->GetMmapSize(), shinfo.offset)) { 287 HLOGW("Failed to load eh_frame_hdr"); 288 return false; 289 } 290 291 ehFrameHdrElfOffset = ehFrameHDRElfOffset_; 292 fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_; 293 fdeTableSize = ehFrameHDRFdeCount_; 294 return true; 295 } 296#endif 297 298 void DumpEhFrameHDR() const 299 { 300 HLOGD(" ehFrameHDRElfOffset_: 0x%" PRIx64 "", ehFrameHDRElfOffset_); 301 HLOGD(" ehFrameHDRFdeCount_: 0x%" PRIx64 "", ehFrameHDRFdeCount_); 302 HLOGD(" ehFrameHDRFdeTableElfOffset_: 0x%" PRIx64 "", ehFrameHDRFdeTableElfOffset_); 303 HLOGD(" ehFrameHDRFdeTableItemSize_: 0x%" PRIx64 "", ehFrameHDRFdeTableItemSize_); 304 } 305 306 bool LoadEhFrameHDR(const unsigned char *buffer, size_t bufferSize, uint64_t shdrOffset) 307 { 308 eh_frame_hdr *ehFrameHdr = (eh_frame_hdr *)buffer; 309 const uint8_t *dataPtr = ehFrameHdr->encode_data; 310 DwarfEncoding dwEhFramePtr(ehFrameHdr->eh_frame_ptr_enc, dataPtr); 311 DwarfEncoding dwFdeCount(ehFrameHdr->fde_count_enc, dataPtr); 312 DwarfEncoding dwTable(ehFrameHdr->table_enc, dataPtr); 313 DwarfEncoding dwTableValue(ehFrameHdr->table_enc, dataPtr); 314 315 HLOGD("eh_frame_hdr:"); 316 HexDump(buffer, BITS_OF_FOUR_BYTE, bufferSize); 317 unsigned char version = ehFrameHdr->version; 318 HLOGD(" version: %02x:%s", version, (version == 1) ? "valid" : "invalid"); 319 HLOGD(" eh_frame_ptr_enc: %s", dwEhFramePtr.ToString().c_str()); 320 HLOGD(" fde_count_enc: %s", dwFdeCount.ToString().c_str()); 321 HLOGD(" table_enc: %s", dwTable.ToString().c_str()); 322 HLOGD(" table_value_enc: %s", dwTableValue.ToString().c_str()); 323 HLOGD(" table_item_size: %zd", dwTable.GetSize() + dwTableValue.GetSize()); 324 HLOGD(" table_offset_in_hdr: %zu", dwTable.GetData() - buffer); 325 326 if (version != 1) { 327 HLOGD("eh_frame_hdr version is invalid"); 328 return false; 329 } 330 ehFrameHDRValid_ = true; 331 ehFrameHDRElfOffset_ = shdrOffset; 332 ehFrameHDRFdeCount_ = dwFdeCount.GetAppliedValue(); 333 ehFrameHDRFdeTableElfOffset_ = dwTable.GetData() - buffer + shdrOffset; 334 ehFrameHDRFdeTableItemSize_ = dwTable.GetSize() + dwTableValue.GetSize(); 335 DumpEhFrameHDR(); 336 337 if (!dwFdeCount.IsOmit() && dwFdeCount.GetValue() > 0) { 338 return true; 339 } else { 340 HLOGW("fde table not found.\n"); 341 } 342 return false; 343 } 344 345 void UpdateSymbols(std::vector<DfxSymbol> &symbolsTable, const std::string &elfPath) 346 { 347 symbols_.clear(); 348 HLOGD("%zu symbols loadded from symbolsTable.", symbolsTable.size()); 349 350 symbols_.swap(symbolsTable); 351 352 AdjustSymbols(); 353 HLOGD("%zu symbols loadded from elf '%s'.", symbols_.size(), elfPath.c_str()); 354 for (auto& symbol: symbols_) { 355 HLOGD("symbol %s", symbol.ToDebugString().c_str()); 356 } 357 if (buildId_.empty()) { 358 HLOGD("buildId not found from elf '%s'.", elfPath.c_str()); 359 // dont failed. some time the lib have not got the build id 360 // buildId not found from elf '/system/bin/ld-musl-arm.so.1'. 361 } 362 } 363 364 bool LoadElfSymbols(std::shared_ptr<DfxMap> map, std::string elfPath) 365 { 366#ifdef HIPERF_DEBUG_TIME 367 const auto startTime = steady_clock::now(); 368#endif 369 if (elfFile_ == nullptr) { 370 if (StringEndsWith(elfPath, ".hap") && map != nullptr) { 371 elfFile_ = DfxElf::CreateFromHap(elfPath, map->prevMap, map->offset); 372 map->elf = elfFile_; 373 HLOGD("loaded map %s", elfPath.c_str()); 374 } else { 375 elfFile_ = std::make_shared<DfxElf>(elfPath); 376 HLOGD("loaded elf %s", elfPath.c_str()); 377 } 378 } 379 380 if (elfFile_ == nullptr || !elfFile_->IsValid()) { 381 HLOGD("parser elf file failed."); 382 return false; 383 } 384 textExecVaddr_ = elfFile_->GetStartVaddr(); 385 textExecVaddrFileOffset_ = elfFile_->GetStartOffset(); 386 HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_, 387 textExecVaddrFileOffset_); 388 389 // we prepare two table here 390 // only one we will push in to symbols_ 391 // or both drop if build id is not same 392 std::string buildIdFound = elfFile_->GetBuildId(); 393 std::vector<DfxSymbol> symbolsTable; 394 395 // use elfFile_ to get symbolsTable 396 DfxSymbols::ParseSymbols(symbolsTable, elfFile_, elfPath); 397 DfxSymbols::AddSymbolsByPlt(symbolsTable, elfFile_, elfPath); 398 399 if (UpdateBuildIdIfMatch(buildIdFound)) { 400 UpdateSymbols(symbolsTable, elfPath); 401 } else { 402 HLOGW("symbols will not update for '%s' because buildId is not match.", 403 elfPath.c_str()); 404 // this mean failed . we dont goon for this. 405 return false; 406 } 407 408#ifdef HIPERF_DEBUG_TIME 409 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime); 410 if (usedTime.count() != 0) { 411 HLOGV("cost %0.3f ms to load symbols '%s'", 412 usedTime.count() / static_cast<double>(milliseconds::duration::period::den), 413 elfPath.c_str()); 414 } 415#endif 416 return true; 417 } 418 419 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, 420 uint64_t mapPageOffset) const override 421 { 422 /* 423 00200000-002c5000 r--p 00000000 08:02 46400311 424 002c5000-00490000 r-xp 000c5000 08:02 4640031 425 426 [14] .text PROGBITS 00000000002c5000 000c5000 427 428 if ip is 0x46e6ab 429 1. find the map range is 002c5000-00490000 430 2. ip - map start(002c5000) = map section offset 431 3. map section offset + map page offset(000c5000) = elf file offset 432 4. elf file offset - exec file offset(000c5000) 433 = ip offset (ip always in exec file offset) 434 5. ip offset + exec begin vaddr(2c5000) = virtual ip in elf 435 */ 436 uint64_t vaddr = ip - mapStart + mapPageOffset - textExecVaddrFileOffset_ + textExecVaddr_; 437 HLOGM(" ip :0x%016" PRIx64 " -> elf offset :0x%016" PRIx64 " -> vaddr :0x%016" PRIx64 " ", 438 ip, ip - mapStart + mapPageOffset, vaddr); 439 HLOGM("(minExecAddrFileOffset_ is 0x%" PRIx64 " textExecVaddr_ is 0x%" PRIx64 ")", 440 textExecVaddrFileOffset_, textExecVaddr_); 441 return vaddr; 442 } 443}; 444 445class KernelSymbols : public ElfFileSymbols { 446public: 447 explicit KernelSymbols(const std::string &symbolFilePath) 448 : ElfFileSymbols(symbolFilePath, SYMBOL_KERNEL_FILE) 449 { 450 } 451 452 static constexpr const int KSYM_MIN_TOKENS = 3; 453 static constexpr const int KSYM_DEFAULT_LINE = 35000; 454 static constexpr const int KSYM_DEFAULT_SIZE = 1024 * 1024 * 1; // 1MB 455 456 bool ParseKallsymsLine() 457 { 458 size_t lines = 0; 459 std::string kallsym; 460 if (!ReadFileToString("/proc/kallsyms", kallsym, KSYM_DEFAULT_SIZE)) { 461 HLOGW("/proc/kallsyms load failed."); 462 return false; 463 } 464 // reduce the mem alloc 465 symbols_.reserve(KSYM_DEFAULT_LINE); 466 467 char *lineBegin = kallsym.data(); 468 char *dataEnd = lineBegin + kallsym.size(); 469 while (lineBegin < dataEnd) { 470 char *lineEnd = strchr(lineBegin, '\n'); 471 if (lineEnd != nullptr) { 472 *lineEnd = '\0'; 473 } 474 size_t lineSize = (lineEnd != nullptr) ? (lineEnd - lineBegin) : (dataEnd - lineBegin); 475 476 lines++; 477 uint64_t addr = 0; 478 char type = '\0'; 479 480 char nameRaw[lineSize]; 481 char moduleRaw[lineSize]; 482 int ret = sscanf_s(lineBegin, "%" PRIx64 " %c %s%s", &addr, &type, sizeof(type), 483 nameRaw, sizeof(nameRaw), moduleRaw, sizeof(moduleRaw)); 484 485 lineBegin = lineEnd + 1; 486 if (ret >= KSYM_MIN_TOKENS) { 487 if (ret == KSYM_MIN_TOKENS) { 488 moduleRaw[0] = '\0'; 489 } 490 HLOGM(" 0x%016" PRIx64 " %c '%s' '%s'", addr, type, nameRaw, moduleRaw); 491 } else { 492 HLOGW("unknow line %d: '%s'", ret, lineBegin); 493 continue; 494 } 495 std::string name = nameRaw; 496 std::string module = moduleRaw; 497 498 /* 499 T 500 The symbol is in the text (code) section. 501 502 W 503 The symbol is a weak symbol that has not been specifically 504 tagged as a weak object symbol. When a weak defined symbol is 505 linked with a normal defined symbol, the normal defined symbol 506 is used with no error. When a weak undefined symbol is linked 507 and the symbol is not defined, the value of the weak symbol 508 becomes zero with no error. 509 */ 510 if (addr != 0 && strchr("TtWw", type)) { 511 // we only need text symbols 512 symbols_.emplace_back(addr, name, module.empty() ? filePath_ : module); 513 } 514 } 515 HLOGD("%zu line processed(%zu symbols)", lines, symbols_.size()); 516 return true; 517 } 518 519 const std::string KPTR_RESTRICT = "/proc/sys/kernel/kptr_restrict"; 520 521 bool LoadKernelSyms() 522 { 523 if (!IsRoot()) { 524 HLOGE("only root mode can access kernel symbols"); 525 return false; 526 } 527 HLOGD("try read /proc/kallsyms"); 528 if (access("/proc/kallsyms", R_OK) != 0) { 529 printf("No vmlinux path is given, and kallsyms cannot be opened\n"); 530 return false; 531 } 532 533 if (ReadFileToString(KPTR_RESTRICT).front() != '0') { 534 printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n"); 535 if (!WriteStringToFile(KPTR_RESTRICT, "0")) { 536 printf("/proc/sys/kernel/kptr_restrict write failed and we cant not change it.\n"); 537 } 538 } 539 540 // getline end 541 if (!ParseKallsymsLine()) { 542 return false; 543 } 544 545 if (symbols_.empty()) { 546 printf("The symbol table addresses in /proc/kallsyms are all 0.\n" 547 "Please check the value of /proc/sys/kernel/kptr_restrict, it " 548 "should be 0.\n" 549 "Or provide a separate vmlinux path.\n"); 550 551 if (buildId_.size() != 0) { 552 // but we got the buildid , so we make a dummpy symbols 553 HLOGD("kallsyms not found. but we have the buildid"); 554 return true; 555 } else { 556 // we got nothing 557 return false; 558 } 559 } else { 560 AdjustSymbols(); 561 HLOGV("%zu symbols_ loadded from kallsyms.\n", symbols_.size()); 562 return true; 563 } 564 } 565 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override 566 { 567 symbolsLoaded_ = true; 568 HLOGV("KernelSymbols try read '%s' search paths size %zu, inDeviceRecord %d", 569 symbolFilePath.c_str(), symbolsFileSearchPaths_.size(), onRecording_); 570 571 if (onRecording_) { 572 // try read 573 HLOGD("try read /sys/kernel/notes"); 574 std::string notes = ReadFileToString("/sys/kernel/notes"); 575 if (notes.empty()) { 576 printf("notes cannot be opened, unable get buildid\n"); 577 return false; 578 } else { 579 HLOGD("kernel notes size: %zu", notes.size()); 580 buildId_ = DfxElf::GetBuildId((uint64_t)notes.data(), (uint64_t)notes.size()); 581 } 582 583 const auto startTime = std::chrono::steady_clock::now(); 584 if (!LoadKernelSyms()) { 585 printf("parse kalsyms failed.\n"); 586 return false; 587 } else { 588 const auto thisTime = std::chrono::steady_clock::now(); 589 const auto usedTimeMsTick = 590 std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime); 591 HLOGV("Load kernel symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count()); 592 // load complete 593 return true; 594 } 595 } // no search path 596 597 // try vmlinux 598 return ElfFileSymbols::LoadSymbols(nullptr, KERNEL_ELF_NAME); 599 } 600 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override 601 { 602 // ip is vaddr in /proc/kallsyms 603 return ip; 604 } 605 ~KernelSymbols() override {} 606}; 607 608class KernelModuleSymbols : public ElfFileSymbols { 609public: 610 explicit KernelModuleSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath) 611 { 612 HLOGV("create %s", symbolFilePath.c_str()); 613 symbolFileType_ = SYMBOL_KERNEL_MODULE_FILE; 614 module_ = symbolFilePath; 615 } 616 ~KernelModuleSymbols() override {}; 617 618 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override 619 { 620 symbolsLoaded_ = true; 621 if (module_ == filePath_ and onRecording_) { 622 // file name sitll not convert to ko file path 623 // this is in record mode 624 HLOGV("find ko name %s", module_.c_str()); 625 for (const std::string &path : kernelModulePaths) { 626 if (access(path.c_str(), R_OK) == 0) { 627 std::string koPath = path + module_ + KERNEL_MODULES_EXT_NAME; 628 HLOGV("found ko in %s", koPath.c_str()); 629 if (access(koPath.c_str(), R_OK) == 0) { 630 // create symbol 631 filePath_ = koPath; 632 break; // find next ko 633 } 634 } 635 } 636 LoadBuildId(); 637 } else { 638 HLOGV("we have file path, load with %s", filePath_.c_str()); 639 return ElfFileSymbols::LoadSymbols(nullptr, filePath_); 640 } 641 return false; 642 } 643 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override 644 { 645 return ip - mapStart; 646 } 647 648private: 649 bool LoadBuildId() 650 { 651 std::string sysFile = "/sys/module/" + module_ + "/notes/.note.gnu.build-id"; 652 std::string buildIdRaw = ReadFileToString(sysFile); 653 if (!buildIdRaw.empty()) { 654 buildId_ = DfxElf::GetBuildId((uint64_t)buildIdRaw.data(), (uint64_t)buildIdRaw.size()); 655 HLOGD("kerne module %s(%s) build id %s", module_.c_str(), filePath_.c_str(), 656 buildId_.c_str()); 657 return buildId_.empty() ? false : true; 658 } 659 return false; 660 } 661 662 const std::vector<std::string> kernelModulePaths = {"/vendor/modules/"}; 663 std::string module_ = ""; 664}; 665 666class JavaFileSymbols : public ElfFileSymbols { 667public: 668 explicit JavaFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath) 669 { 670 symbolFileType_ = SYMBOL_KERNEL_FILE; 671 } 672 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override 673 { 674 symbolsLoaded_ = true; 675 return false; 676 } 677 ~JavaFileSymbols() override {} 678 679 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, 680 uint64_t mapPageOffset) const override 681 { 682 // this is different with elf 683 // elf use ip - mapStart + mapPageOffset - minExecAddrFileOffset_ + textExecVaddr_ 684 return ip - mapStart + mapPageOffset; 685 } 686}; 687 688class JSFileSymbols : public ElfFileSymbols { 689public: 690 explicit JSFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath) 691 { 692 symbolFileType_ = SYMBOL_KERNEL_FILE; 693 } 694 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override 695 { 696 symbolsLoaded_ = true; 697 return false; 698 } 699 ~JSFileSymbols() override {} 700}; 701 702class HapFileSymbols : public ElfFileSymbols { 703private: 704#if defined(is_ohos) && is_ohos 705 std::unique_ptr<DfxExtractor> dfxExtractor_; 706 bool hapExtracted_ = false; 707#endif 708 std::unique_ptr<uint8_t[]> abcDataPtr_ = nullptr; 709 [[maybe_unused]] uintptr_t loadOffSet_ = 0; 710 [[maybe_unused]] size_t abcDataSize_ = 0; 711 [[maybe_unused]] uintptr_t arkExtractorptr_ = 0; 712 bool isHapAbc_ = false; 713 pid_t pid_ = 0; 714public: 715 explicit HapFileSymbols(const std::string &symbolFilePath, pid_t pid) 716 : ElfFileSymbols(symbolFilePath, SYMBOL_HAP_FILE) 717 { 718 pid_ = pid; 719 } 720 721 ~HapFileSymbols() override 722 { 723#if defined(is_ohos) && is_ohos 724 if (arkExtractorptr_ != 0) { 725 DfxArk::ArkDestoryJsSymbolExtractor(arkExtractorptr_); 726 arkExtractorptr_ = 0; 727 } 728#endif 729 } 730 // abc is the ark bytecode 731 bool IsHapAbc() 732 { 733#if defined(is_ohos) && is_ohos 734 if (hapExtracted_) { 735 return isHapAbc_; 736 } 737 hapExtracted_ = true; 738 HLOGD("the symbol file is %s.", filePath_.c_str()); 739 if (StringEndsWith(filePath_, ".hap") && map_->IsMapExec()) { 740 HLOGD("map is exec not abc file , the symbol file is:%s", map_->name.c_str()); 741 return false; 742 } 743 if (StringEndsWith(filePath_, ".hap") || StringEndsWith(filePath_, ".hsp")) { 744 dfxExtractor_ = std::make_unique<DfxExtractor>(filePath_); 745 if (dfxExtractor_ == nullptr) { 746 HLOGD("DfxExtractor create failed."); 747 return false; 748 } 749 // extract the bytecode information of the ark in the hap package 750 if (!dfxExtractor_->GetHapAbcInfo(loadOffSet_, abcDataPtr_, abcDataSize_)) { 751 HLOGD("failed to call GetHapAbcInfo, the symbol file is:%s", filePath_.c_str()); 752 return false; 753 } 754 HLOGD("loadOffSet %u", (uint32_t)loadOffSet_); 755 if (abcDataPtr_ != nullptr) { 756 isHapAbc_ = true; 757 HLOGD("input abcDataPtr : %s, isAbc: %d", abcDataPtr_.get(), isHapAbc_); 758 } 759 } else { 760 if (map_ == nullptr) { 761 return false; 762 } 763 loadOffSet_ = map_->offset; 764 abcDataSize_ = map_->end - map_->begin; 765 abcDataPtr_ = std::make_unique<uint8_t[]>(abcDataSize_); 766 auto size = DfxMemory::ReadProcMemByPid(pid_, map_->begin, abcDataPtr_.get(), map_->end - map_->begin); 767 if (size != abcDataSize_) { 768 HLOGD("return size is small abcDataPtr : %s, isAbc: %d", abcDataPtr_.get(), isHapAbc_); 769 return false; 770 } 771 isHapAbc_ = true; 772 HLOGD("symbol file name %s loadOffSet %u abcDataSize_ %u abcDataPtr_ %s", 773 filePath_.c_str(), (uint32_t)loadOffSet_, (uint32_t)abcDataSize_, abcDataPtr_.get()); 774 } 775 auto ret = DfxArk::ArkCreateJsSymbolExtractor(&arkExtractorptr_); 776 if (ret < 0) { 777 arkExtractorptr_ = 0; 778 HLOGE("failed to call ArkCreateJsSymbolExtractor, the symbol file is:%s", filePath_.c_str()); 779 } 780#endif 781 return isHapAbc_; 782 } 783 784 bool IsAbc() override 785 { 786 return isHapAbc_ == true; 787 } 788 789 void SetBoolValue(bool value) override 790 { 791 isHapAbc_ = value; 792 } 793 794 bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override 795 { 796 HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str()); 797 if (debugInfoLoaded_) { 798 return true; 799 } 800 if (!onRecording_) { 801 return true; 802 } 803 804 if (!IsHapAbc()) { 805 ElfFileSymbols::LoadDebugInfo(map, ""); 806 } 807 debugInfoLoaded_ = true; 808 debugInfoLoadResult_ = true; 809 return true; 810 } 811 812 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override 813 { 814 HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str()); 815 if (symbolsLoaded_ || !onRecording_) { 816 return true; 817 } 818 symbolsLoaded_ = true; 819 if (!IsHapAbc()) { 820 ElfFileSymbols::LoadSymbols(map, ""); 821 } 822 return true; 823 } 824 825 DfxSymbol GetSymbolWithPcAndMap(uint64_t ip, std::shared_ptr<DfxMap> map) override 826 { 827 // get cache 828 auto iter = symbolsMap_.find(ip); 829 if (iter != symbolsMap_.end()) { 830 return iter->second; 831 } 832 if (map == nullptr) { 833 return DfxSymbol(ip, ""); 834 } 835 HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str()); 836 837#if defined(is_ohos) && is_ohos 838 if (IsAbc()) { 839 JsFunction jsFunc; 840 std::string module = map->name; 841 HLOGD("map->name module:%s", module.c_str()); 842 // symbolization based on ark bytecode 843 auto ret = DfxArk::ParseArkFrameInfo(static_cast<uintptr_t>(ip), static_cast<uintptr_t>(map->begin), 844 loadOffSet_, abcDataPtr_.get(), abcDataSize_, 845 arkExtractorptr_, &jsFunc); 846 if (ret == -1) { 847 HLOGD("failed to call ParseArkFrameInfo, the symbol file is : %s", map->name.c_str()); 848 return DfxSymbol(ip, ""); 849 } 850 this->symbolsMap_.insert(std::make_pair(ip, 851 DfxSymbol(ip, 852 jsFunc.codeBegin, 853 jsFunc.functionName, 854 jsFunc.ToString(), 855 map->name))); 856 857 DfxSymbol &foundSymbol = symbolsMap_[ip]; 858 if (!foundSymbol.matched_) { 859 foundSymbol.matched_ = true; 860 matchedSymbols_.push_back(&(symbolsMap_[ip])); 861 } 862 863 HLOGD("ip : 0x%" PRIx64 " the symbol file is : %s, function is %s demangle_ : %s", ip, 864 symbolsMap_[ip].module_.data(), jsFunc.functionName, matchedSymbols_.back()->demangle_.data()); 865 return symbolsMap_[ip]; 866 } 867#endif 868 DfxSymbol symbol(ip, ""); 869 return symbol; 870 } 871}; 872 873class UnknowFileSymbols : public SymbolsFile { 874public: 875 explicit UnknowFileSymbols(const std::string &symbolFilePath) 876 : SymbolsFile(SYMBOL_UNKNOW_FILE, symbolFilePath) 877 { 878 } 879 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override 880 { 881 symbolsLoaded_ = true; 882 return false; 883 } 884 ~UnknowFileSymbols() override {} 885}; 886 887SymbolsFile::~SymbolsFile() {} 888 889std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(SymbolsFileType symbolType, 890 const std::string symbolFilePath, pid_t pid) 891{ 892 switch (symbolType) { 893 case SYMBOL_KERNEL_FILE: 894 return std::make_unique<KernelSymbols>(symbolFilePath.empty() ? KERNEL_MMAP_NAME 895 : symbolFilePath); 896 case SYMBOL_KERNEL_MODULE_FILE: 897 return std::make_unique<KernelModuleSymbols>(symbolFilePath); 898 case SYMBOL_ELF_FILE: 899 return std::make_unique<ElfFileSymbols>(symbolFilePath); 900 case SYMBOL_JAVA_FILE: 901 return std::make_unique<JavaFileSymbols>(symbolFilePath); 902 case SYMBOL_JS_FILE: 903 return std::make_unique<JSFileSymbols>(symbolFilePath); 904 case SYMBOL_HAP_FILE: 905 return std::make_unique<HapFileSymbols>(symbolFilePath, pid); 906 default: 907 return std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, symbolFilePath); 908 } 909} 910 911std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(const std::string &symbolFilePath, pid_t pid) 912{ 913 // we need check file name here 914 if (symbolFilePath == KERNEL_MMAP_NAME) { 915 return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_FILE, symbolFilePath); 916 } else if (StringEndsWith(symbolFilePath, KERNEL_MODULES_EXT_NAME)) { 917 return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, symbolFilePath); 918 } else if (IsArkJsFile(symbolFilePath)) { 919 return SymbolsFile::CreateSymbolsFile(SYMBOL_HAP_FILE, symbolFilePath, pid); 920 } else { 921 // default is elf 922 return SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, symbolFilePath); 923 } 924} 925 926void SymbolsFile::AdjustSymbols() 927{ 928 if (symbols_.size() <= 1u) { 929 return; 930 } 931 932 // order 933 sort(symbols_.begin(), symbols_.end(), [](const DfxSymbol& a, const DfxSymbol& b) { 934 return a.funcVaddr_ < b.funcVaddr_; 935 }); 936 HLOGV("sort completed"); 937 938 size_t fullSize = symbols_.size(); 939 size_t erased = 0; 940 941 // Check for duplicate vaddr 942 auto last = std::unique(symbols_.begin(), symbols_.end(), [](const DfxSymbol &a, const DfxSymbol &b) { 943 return (a.funcVaddr_ == b.funcVaddr_); 944 }); 945 symbols_.erase(last, symbols_.end()); 946 erased = fullSize - symbols_.size(); 947 HLOGV("uniqued completed"); 948 auto it = symbols_.begin(); 949 while (it != symbols_.end()) { 950 it->index_ = it - symbols_.begin(); 951 it++; 952 } 953 HLOGV("indexed completed"); 954 955 HLOG_ASSERT(symbols_.size() != 0); 956 957 if (textExecVaddrRange_ == maxVaddr) { 958 textExecVaddrRange_ = symbols_.back().funcVaddr_ - symbols_.front().funcVaddr_; 959 } 960 961 HLOGDDD("%zu symbols after adjust (%zu erased) 0x%016" PRIx64 " - 0x%016" PRIx64 962 " @0x%016" PRIx64 " ", 963 symbols_.size(), erased, symbols_.front().funcVaddr_, symbols_.back().funcVaddr_, 964 textExecVaddrFileOffset_); 965} 966 967void SymbolsFile::SortMatchedSymbols() 968{ 969 if (matchedSymbols_.size() <= 1u) { 970 return; 971 } 972 sort(matchedSymbols_.begin(), matchedSymbols_.end(), [](const DfxSymbol* a, const DfxSymbol* b) { 973 return a->funcVaddr_ < b->funcVaddr_; 974 }); 975} 976 977const std::vector<DfxSymbol> &SymbolsFile::GetSymbols() 978{ 979 return symbols_; 980} 981 982const std::vector<DfxSymbol *> &SymbolsFile::GetMatchedSymbols() 983{ 984 return matchedSymbols_; 985} 986 987const DfxSymbol SymbolsFile::GetSymbolWithVaddr(uint64_t vaddrInFile) 988{ 989#ifdef HIPERF_DEBUG_TIME 990 const auto startTime = steady_clock::now(); 991#endif 992 DfxSymbol symbol; 993 // it should be already order from small to large 994 auto found = 995 std::upper_bound(symbols_.begin(), symbols_.end(), vaddrInFile, DfxSymbol::ValueLessThen); 996 /* 997 if data is { 1, 2, 4, 5, 5, 6 }; 998 upper_bound for each val : 999 0 < 1 at index 0 1000 1 < 2 at index 1 1001 2 < 4 at index 2 1002 3 < 4 at index 2 1003 4 < 5 at index 3 1004 5 < 6 at index 5 1005 6 < not found 1006 if key symbol vaddr is { 1, 2, 4, 5, 5, 6 }; 1007 check ip vaddr for each val : 1008 ip sym 1009 0 not found 1010 1 1 1011 1 1 1012 2 2 1013 3 3 1014 4 4 1015 5 5 1016 6 6 1017 7 7 1018 */ 1019 if (found != symbols_.begin()) { 1020 found = std::prev(found); 1021 if (found->Contain(vaddrInFile)) { 1022 if (!found->matched_) { 1023 found->matched_ = true; 1024 matchedSymbols_.push_back(&(*found)); 1025 } 1026 symbol = *found; // copy 1027 HLOGV("found '%s' for vaddr 0x%016" PRIx64 "", symbol.ToString().c_str(), vaddrInFile); 1028 } 1029 } 1030 1031 if (!symbol.IsValid()) { 1032 HLOGV("NOT found vaddr 0x%" PRIx64 " in symbole file %s(%zu)", vaddrInFile, 1033 filePath_.c_str(), symbols_.size()); 1034 } 1035 symbol.SetIpVAddress(vaddrInFile); 1036 1037#ifdef HIPERF_DEBUG_TIME 1038 auto usedTime = duration_cast<milliseconds>(steady_clock::now() - startTime); 1039 if (usedTime > 1ms) { 1040 HLOGW("cost %" PRId64 "ms to search ", usedTime.count()); 1041 } 1042#endif 1043 return symbol; 1044} 1045 1046bool SymbolsFile::CheckPathReadable(const std::string &path) const 1047{ 1048 if (access(path.c_str(), R_OK) == 0) { 1049 return true; 1050 } else { 1051 HLOGM("'%s' is unable read", path.c_str()); 1052 return false; 1053 } 1054} 1055 1056bool SymbolsFile::setSymbolsFilePath(const std::vector<std::string> &symbolsSearchPaths) 1057{ 1058 symbolsFileSearchPaths_.clear(); 1059 for (auto &symbolsSearchPath : symbolsSearchPaths) { 1060 if (CheckPathReadable(symbolsSearchPath)) { 1061 symbolsFileSearchPaths_.emplace_back(symbolsSearchPath); 1062 HLOGV("'%s' is add to symbolsSearchPath", symbolsSearchPath.c_str()); 1063 } 1064 } 1065 return (symbolsFileSearchPaths_.size() > 0); 1066} 1067 1068std::unique_ptr<SymbolsFile> SymbolsFile::LoadSymbolsFromSaved( 1069 const SymbolFileStruct &symbolFileStruct) 1070{ 1071 auto symbolsFile = CreateSymbolsFile(symbolFileStruct.filePath_); 1072 symbolsFile->filePath_ = symbolFileStruct.filePath_; 1073 symbolsFile->symbolFileType_ = (SymbolsFileType)symbolFileStruct.symbolType_; 1074 symbolsFile->textExecVaddr_ = symbolFileStruct.textExecVaddr_; 1075 symbolsFile->textExecVaddrFileOffset_ = symbolFileStruct.textExecVaddrFileOffset_; 1076 symbolsFile->buildId_ = symbolFileStruct.buildId_; 1077 for (auto &symbolStruct : symbolFileStruct.symbolStructs_) { 1078 symbolsFile->symbols_.emplace_back(symbolStruct.vaddr_, symbolStruct.len_, 1079 symbolStruct.symbolName_, symbolFileStruct.filePath_); 1080 } 1081 symbolsFile->AdjustSymbols(); // reorder 1082 HLOGV("load %zu symbol from SymbolFileStruct for file '%s'", symbolsFile->symbols_.size(), 1083 symbolsFile->filePath_.c_str()); 1084 return symbolsFile; 1085} 1086 1087void SymbolsFile::SetBoolValue(bool value) 1088{ 1089} 1090 1091void SymbolsFile::ExportSymbolToFileFormat(SymbolFileStruct &symbolFileStruct) 1092{ 1093 symbolFileStruct.filePath_ = filePath_; 1094 symbolFileStruct.symbolType_ = symbolFileType_; 1095 symbolFileStruct.textExecVaddr_ = textExecVaddr_; 1096 symbolFileStruct.textExecVaddrFileOffset_ = textExecVaddrFileOffset_; 1097 symbolFileStruct.buildId_ = buildId_; 1098 1099 SortMatchedSymbols(); 1100 auto symbols = GetMatchedSymbols(); 1101 symbolFileStruct.symbolStructs_.reserve(symbols.size()); 1102 for (auto symbol : symbols) { 1103 auto &symbolStruct = symbolFileStruct.symbolStructs_.emplace_back(); 1104 symbolStruct.vaddr_ = symbol->funcVaddr_; 1105 symbolStruct.len_ = symbol->size_; 1106 symbolStruct.symbolName_ = symbol->GetName(); 1107 } 1108 1109 HLOGV("export %zu symbol to SymbolFileStruct from %s", symbolFileStruct.symbolStructs_.size(), 1110 filePath_.c_str()); 1111} 1112 1113uint64_t SymbolsFile::GetVaddrInSymbols(uint64_t ip, uint64_t, uint64_t) const 1114{ 1115 // no convert 1116 return ip; 1117} 1118} // namespace NativeDaemon 1119} // namespace Developtools 1120} // namespace OHOS 1121