1 /*
2 * Copyright (c) 2021-2022 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 #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
36 #include "dwarf_encoding.h"
37 #include "elf_parser.h"
38 #include "utilities.h"
39
40 using namespace OHOS::Developtools::HiPerf::ELF;
41 using namespace std::chrono;
42
43 namespace OHOS {
44 namespace Developtools {
45 namespace HiPerf {
46 bool SymbolsFile::onRecording_ = true;
GetBuildId() const47 const std::string SymbolsFile::GetBuildId() const
48 {
49 return buildId_;
50 }
51
UpdateBuildIdIfMatch(std::string buildId)52 bool SymbolsFile::UpdateBuildIdIfMatch(std::string buildId)
53 {
54 /*
55 here we have two case
56 1 buildId_ is empty
57 a) always return match
58 2 buildId_ is not empty
59 a) really check if the same one
60 */
61
62 if (buildId_.empty()) {
63 // we have new empty build
64 if (buildId.empty()) {
65 // both empty , no build id provided
66 HLOGD("build id is empty.");
67 return true;
68 } else {
69 buildId_ = buildId;
70 HLOGD("new buildId %s", buildId_.c_str());
71 return true;
72 }
73 } else {
74 // we already have a build id
75 // so this is not the first time load symbol
76 // we need check if it match
77 HLOGV("expected buildid: %s vs %s", buildId_.c_str(), buildId.c_str());
78
79 if (buildId_ != buildId) {
80 HLOGW("id not match");
81 return false;
82 } else {
83 HLOGD("id match");
84 return true;
85 }
86 }
87 }
88
SearchReadableFile(const std::vector<std::string> & searchPaths,const std::string & filePath) const89 std::string SymbolsFile::SearchReadableFile(const std::vector<std::string> &searchPaths,
90 const std::string &filePath) const
91 {
92 if (filePath.empty()) {
93 HLOGW("nothing to found");
94 return filePath;
95 }
96 for (auto searchPath : searchPaths) {
97 if (searchPath.back() != PATH_SEPARATOR) {
98 searchPath += PATH_SEPARATOR;
99 }
100 std::string PossibleFilePath = searchPath + filePath;
101 if (CheckPathReadable(PossibleFilePath)) {
102 return PossibleFilePath;
103 }
104 HLOGW("have not found '%s' in search paths %s", filePath.c_str(), searchPath.c_str());
105 }
106 return EMPTY_STRING;
107 }
108
FindSymbolFile(const std::vector<std::string> & symbolsFileSearchPaths,std::string symboleFilePath) const109 const std::string SymbolsFile::FindSymbolFile(
110 const std::vector<std::string> &symbolsFileSearchPaths, std::string symboleFilePath) const
111 {
112 /*
113 this function do 2 things:
114 find by name:
115 1 find dso path
116 2 find search path
117 a) search path + dso path
118 b) search path + dso name
119
120 show we should return filePath_ as default ?
121 */
122 if (symboleFilePath.empty()) {
123 symboleFilePath = filePath_;
124 HLOGD("use default filename: %s ", symboleFilePath.c_str());
125 }
126 symboleFilePath = PlatformPathConvert(symboleFilePath);
127 std::string foundPath;
128 // search first if we have path
129 if (symbolsFileSearchPaths.size() != 0) {
130 foundPath = SearchReadableFile(symbolsFileSearchPaths, symboleFilePath);
131 if (foundPath.empty()) {
132 HLOGV("try base name for: %s split with %s", symboleFilePath.c_str(),
133 PATH_SEPARATOR_STR.c_str());
134 auto pathSplit = StringSplit(symboleFilePath, PATH_SEPARATOR_STR);
135 if (pathSplit.size() > 1) {
136 HLOGV("base name is: %s ", pathSplit.back().c_str());
137 // found it again with base name , split it and get last name
138 foundPath = SearchReadableFile(symbolsFileSearchPaths, pathSplit.back());
139 }
140 }
141 }
142
143 // only access the patch in onRecording_
144 // in report mode we don't load any thing in runtime path
145 if (foundPath.empty() and onRecording_) {
146 // try access direct at last
147 if (CheckPathReadable(symboleFilePath)) {
148 // found direct folder
149 HLOGD("find %s in current work dir", symboleFilePath.c_str());
150 return symboleFilePath;
151 }
152 }
153 return foundPath;
154 }
155
156 class ElfFileSymbols : public SymbolsFile {
157 public:
ElfFileSymbols(const std::string & symbolFilePath,const SymbolsFileType symbolsFileType=SYMBOL_ELF_FILE)158 explicit ElfFileSymbols(const std::string &symbolFilePath,
159 const SymbolsFileType symbolsFileType = SYMBOL_ELF_FILE)
160 : SymbolsFile(symbolsFileType, symbolFilePath)
161 {
162 }
163
~ElfFileSymbols()164 virtual ~ElfFileSymbols()
165 {
166 if (mmap_ != MMAP_FAILED) {
167 if (munmap(mmap_, mmapSize_) != 0) {
168 HLOGE("munmap failed");
169 }
170 }
171 }
172
LoadSymbols(const std::string & symbolFilePath)173 bool LoadSymbols(const std::string &symbolFilePath) override
174 {
175 symbolsLoaded_ = true;
176 std::string findPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
177 if (findPath.empty()) {
178 HLOGW("elf found failed (belong to %s)", filePath_.c_str());
179 return false;
180 }
181 if (LoadElfSymbols(findPath)) {
182 return true;
183 } else {
184 HLOGW("elf open failed with '%s'", findPath.c_str());
185 return false;
186 }
187 return false;
188 }
189
ReadRoMemory(uint64_t addr,uint8_t * const data,size_t size) const190 size_t ReadRoMemory(uint64_t addr, uint8_t * const data, size_t size) const override
191 {
192 size_t readSize = 0;
193 const int wideBytes = 8;
194 const unsigned int wideMaskPosi = 7;
195 const int shortBytes = 4;
196 const unsigned int shortMaskPosi = 3;
197 if (mmap_ != MMAP_FAILED) {
198 if ((addr + size) <= mmapSize_) {
199 if (size == wideBytes && (addr & wideMaskPosi) == 0) {
200 *(uint64_t * const)data = *(uint64_t *) (static_cast<uint8_t *>(mmap_) + addr);
201 } else if (size == shortBytes && (addr & shortMaskPosi) == 0) {
202 *(uint32_t * const)data = *(uint32_t *) (static_cast<uint8_t *>(mmap_) + addr);
203 } else {
204 std::copy_n(static_cast<uint8_t *>(mmap_) + addr, size, data);
205 }
206 readSize = size;
207 } else {
208 HLOGW("read out of range.");
209 HLOGW("try read 0x%" PRIx64 "(elf offset)+%zu max is 0x%" PRIx64 "", addr, size,
210 mmapSize_);
211 }
212 } else {
213 if (readFd_ != nullptr) {
214 if (fseek(readFd_.get(), addr, SEEK_SET) != 0) {
215 return 0;
216 }
217 if (fread(data, size, 1u, readFd_.get())) {
218 readSize = size;
219 } else {
220 HLOGEP("read at %" PRIx64 " failed for %s", addr, filePath_.c_str());
221 }
222 }
223 HLOGM("no mmap files loaded");
224 }
225
226 HLOGM("read %zu/%zu bytes at %" PRIx64 "(elf offset)", readSize, size, addr);
227
228 return readSize;
229 }
230
231 protected:
CovertByteBufferToHexString(const unsigned char * buffer,size_t size) const232 std::string CovertByteBufferToHexString(const unsigned char *buffer, size_t size) const
233 {
234 if (buffer == nullptr) {
235 HLOGE("param is null");
236 return "";
237 }
238 std::string descString;
239 size_t i = 0;
240 while (i < size) {
241 descString.append(ToHex(buffer[i]));
242 i++; // move to next char
243 }
244 return descString;
245 }
246
ElfGetBuildId(const unsigned char * buffer,size_t size) const247 std::string ElfGetBuildId(const unsigned char *buffer, size_t size) const
248 {
249 if (buffer == nullptr) {
250 HLOGE("param is null");
251 return "";
252 }
253 const unsigned char *end = buffer + size;
254 HLOGV("size:%zu", size);
255
256 /*
257 Note Section
258 A vendor or system engineer might need to mark an object file with special
259 information that other programs can check for conformance or compatibility. Sections
260 of type SHT_NOTE and program header elements of type PT_NOTE can be used for this
261 purpose.
262
263 The note information in sections and program header elements holds any number of
264 entries, as shown in the following figure. For 64–bit objects and 32–bit objects,
265 each entry is an array of 4-byte words in the format of the target processor. Labels
266 are shown in Figure 12-6 to help explain note information organization, but are not
267 part of the specification.
268
269 Figure 12-5 Note Information
270
271 image:ELF note section information.
272 namesz and name
273 The first namesz bytes in name contain a null-terminated character representation of
274 the entry's owner or originator. No formal mechanism exists for avoiding name
275 conflicts. By convention, vendors use their own name, such as “XYZ Computer
276 Company,” as the identifier. If no name is present, namesz contains the value zero.
277 Padding is present, if necessary, to ensure 4-byte alignment for the descriptor.
278 Such padding is not included in namesz.
279
280 descsz and desc
281 The first descsz bytes in desc hold the note descriptor. If no descriptor is
282 present, descsz contains the value zero. Padding is present, if necessary, to ensure
283 4-byte alignment for the next note entry. Such padding is not included in descsz.
284
285 type
286 Provides the interpretation of the descriptor. Each originator controls its own
287 types. Multiple interpretations of a single type value can exist. A program must
288 recognize both the name and the type to understand a descriptor. Types currently
289 must be nonnegative.
290
291 The note segment that is shown in the following figure holds two entries.
292 */
293
294 // namesz + descsz + type
295 static constexpr const int elfNoteSectionLens = sizeof(uint32_t) * 3;
296
297 while (end - buffer >= elfNoteSectionLens) {
298 uint32_t namesz;
299 uint32_t descsz;
300 uint32_t type;
301 CopyFromBufferAndMove(buffer, &namesz);
302 CopyFromBufferAndMove(buffer, &descsz);
303 CopyFromBufferAndMove(buffer, &type);
304
305 // to ensure 4-byte alignment for the descriptor.
306 constexpr const int elfNoteSectionNameAlign = 4;
307
308 namesz = RoundUp(namesz, elfNoteSectionNameAlign);
309 descsz = RoundUp(descsz, elfNoteSectionNameAlign);
310 HLOGM("namesz:%u descsz:%u type:%u", namesz, descsz, type);
311
312 // size enough ?
313 if (buffer >= end) {
314 return EMPTY_STRING;
315 }
316 if (type == NT_GNU_BUILD_ID) {
317 char name[namesz + 1];
318 CopyFromBufferAndMove(buffer, &name[0], namesz);
319 name[namesz] = 0;
320 HLOGM("found buildid name:%s", name);
321 if (strcmp(name, ELF_NOTE_GNU) == 0) {
322 std::string descString = CovertByteBufferToHexString(buffer, descsz);
323 HLOGD("found buildid:%s", descString.c_str());
324 return descString;
325 } else {
326 // next
327 buffer += descsz;
328 }
329 } else {
330 // next
331 buffer += namesz + descsz;
332 }
333 }
334 return EMPTY_STRING; // found nothing
335 }
336
LoadDebugInfo(const std::string & symbolFilePath)337 bool LoadDebugInfo(const std::string &symbolFilePath) override
338 {
339 if (debugInfoLoadResult_) {
340 return true; // it must have been loaded
341 } else if (debugInfoLoaded_) {
342 return debugInfoLoadResult_; // return the result of loaded
343 } else {
344 debugInfoLoaded_ = true;
345 }
346 std::string elfPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
347 if (elfPath.empty()) {
348 HLOGW("elf found failed (belong to %s)", filePath_.c_str());
349 return false;
350 }
351 std::unique_ptr<ElfFile> elfFile = LoadElfFile(elfPath);
352 if (elfFile == nullptr) {
353 HLOGD("elf load failed");
354 return false;
355 } else {
356 HLOGD("loaded elf %s", elfPath.c_str());
357 }
358 for (const auto &phdr : elfFile->phdrs_) {
359 if ((phdr->type_ == PT_LOAD) && (phdr->flags_ & PF_X)) {
360 // find the min addr
361 if (textExecVaddr_ != std::min(textExecVaddr_, phdr->vaddr_)) {
362 textExecVaddr_ = std::min(textExecVaddr_, phdr->vaddr_);
363 textExecVaddrFileOffset_ = phdr->offset_;
364 }
365 }
366 }
367
368 HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
369 textExecVaddrFileOffset_);
370
371 if (!ParseShdr(std::move(elfFile))) {
372 return false;
373 }
374
375 // mmap it for later use
376 LoadFileToMemory(elfPath);
377 debugInfoLoadResult_ = true;
378 return true;
379 }
380
381 private:
382 bool EhFrameHDRValid_ {false};
383 uint64_t ehFrameHDRElfOffset_ {0};
384 uint64_t ehFrameHDRFdeCount_ {0};
385 uint64_t ehFrameHDRFdeTableItemSize_ {0};
386 uint64_t ehFrameHDRFdeTableElfOffset_ {0};
387 OHOS::UniqueFd fd_ {-1};
388 std::unique_ptr<FILE, decltype(&fclose)> readFd_ {nullptr, &fclose};
389 struct ShdrInfo {
390 uint64_t sectionVaddr_;
391 uint64_t sectionSize_;
392 uint64_t sectionFileOffset_;
ShdrInfoOHOS::Developtools::HiPerf::ElfFileSymbols::ShdrInfo393 ShdrInfo(uint64_t sectionVaddr, uint64_t sectionSize, uint64_t sectionFileOffset)
394 : sectionVaddr_(sectionVaddr),
395 sectionSize_(sectionSize),
396 sectionFileOffset_(sectionFileOffset)
397 {
398 }
399 };
400 std::map<const std::string, ShdrInfo> shdrMap_ {};
401 void *mmap_ {MMAP_FAILED};
402 uint64_t mmapSize_ = {0};
403
GetReadableName(const std::string & name) const404 const std::string GetReadableName(const std::string &name) const
405 {
406 int status = 0;
407 const char *nameStart = name.c_str();
408 bool linkerName = false;
409 if (StringStartsWith(name, LINKER_PREFIX)) {
410 nameStart += LINKER_PREFIX.size();
411 linkerName = true;
412 }
413
414 char *demangle = abi::__cxa_demangle(nameStart, nullptr, nullptr, &status);
415 if (status == 0) {
416 std::string demangleName = demangle;
417 free(static_cast<void *>(demangle));
418 return linkerName ? (LINKER_PREFIX_NAME + demangleName) : demangleName;
419 } else {
420 return linkerName ? (LINKER_PREFIX_NAME + nameStart) : nameStart;
421 }
422 }
423
ElfStTypeName(unsigned char stt) const424 const std::string ElfStTypeName(unsigned char stt) const
425 {
426 switch (stt) {
427 case STT_FUNC:
428 return "function";
429 case STT_GNU_IFUNC:
430 return "gun_func";
431 case STT_OBJECT:
432 return " object";
433 default:
434 return " unknown";
435 }
436 }
437
GetSectionInfo(const std::string & name,uint64_t & sectionVaddr,uint64_t & sectionSize,uint64_t & sectionFileOffset) const438 bool GetSectionInfo(const std::string &name, uint64_t §ionVaddr, uint64_t §ionSize,
439 uint64_t §ionFileOffset) const override
440 {
441 HLOGM("Section '%s' found in %zu", name.c_str(), shdrMap_.size());
442 if (shdrMap_.count(name) > 0) {
443 HLOGM("Section '%s' found", name.c_str());
444 const auto &shdrInfo = shdrMap_.at(name);
445 sectionVaddr = shdrInfo.sectionVaddr_;
446 sectionSize = shdrInfo.sectionSize_;
447 sectionFileOffset = shdrInfo.sectionFileOffset_;
448 HLOGM("Get Section '%s' %" PRIx64 " - %" PRIx64 "", name.c_str(), sectionVaddr,
449 sectionSize);
450 return true;
451 } else {
452 HLOGW("Section '%s' not found", name.c_str());
453 return false;
454 }
455 }
456
457 #ifndef __arm__
GetHDRSectionInfo(uint64_t & ehFrameHdrElfOffset,uint64_t & fdeTableElfOffset,uint64_t & fdeTableSize) const458 bool GetHDRSectionInfo(uint64_t &ehFrameHdrElfOffset, uint64_t &fdeTableElfOffset,
459 uint64_t &fdeTableSize) const override
460 {
461 if (EhFrameHDRValid_) {
462 ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
463 fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
464 fdeTableSize = ehFrameHDRFdeCount_ * ehFrameHDRFdeTableItemSize_;
465 return true;
466 } else {
467 HLOGW("!EhFrameHDRValid_");
468 return false;
469 }
470 }
471 #endif
472
DumpEhFrameHDR() const473 void DumpEhFrameHDR() const
474 {
475 HLOGD(" ehFrameHDRElfOffset_: 0x%" PRIx64 "", ehFrameHDRElfOffset_);
476 HLOGD(" ehFrameHDRFdeCount_: 0x%" PRIx64 "", ehFrameHDRFdeCount_);
477 HLOGD(" ehFrameHDRFdeTableElfOffset_: 0x%" PRIx64 "", ehFrameHDRFdeTableElfOffset_);
478 HLOGD(" ehFrameHDRFdeTableItemSize_: 0x%" PRIx64 "", ehFrameHDRFdeTableItemSize_);
479 }
480
LoadEhFrameHDR(const unsigned char * buffer,size_t bufferSize,uint64_t shdrOffset)481 bool LoadEhFrameHDR(const unsigned char *buffer, size_t bufferSize, uint64_t shdrOffset)
482 {
483 eh_frame_hdr *ehFrameHdr = (eh_frame_hdr *)buffer;
484 const uint8_t *dataPtr = ehFrameHdr->encode_data;
485 DwarfEncoding dwEhFramePtr(ehFrameHdr->eh_frame_ptr_enc, dataPtr);
486 DwarfEncoding dwFdeCount(ehFrameHdr->fde_count_enc, dataPtr);
487 DwarfEncoding dwTable(ehFrameHdr->table_enc, dataPtr);
488 DwarfEncoding dwTableValue(ehFrameHdr->table_enc, dataPtr);
489
490 HLOGD("eh_frame_hdr:");
491 HexDump(ehFrameHdr, BITS_OF_FOUR_BYTE, bufferSize);
492 unsigned char version = ehFrameHdr->version;
493 HLOGD(" version: %02x:%s", version, (version == 1) ? "valid" : "invalid");
494 HLOGD(" eh_frame_ptr_enc: %s", dwEhFramePtr.ToString().c_str());
495 HLOGD(" fde_count_enc: %s", dwFdeCount.ToString().c_str());
496 HLOGD(" table_enc: %s", dwTable.ToString().c_str());
497 HLOGD(" table_enc: %s", dwTable.ToString().c_str());
498 HLOGD(" table_value_enc: %s", dwTableValue.ToString().c_str());
499 HLOGD(" table_iteam_size: %zd", dwTable.GetSize() + dwTableValue.GetSize());
500 HLOGD(" table_offset_in_hdr: %zu", dwTable.GetData() - buffer);
501
502 if (version != 1) {
503 HLOGD("eh_frame_hdr version is invalid");
504 return false;
505 }
506 EhFrameHDRValid_ = true;
507 ehFrameHDRElfOffset_ = shdrOffset;
508 ehFrameHDRFdeCount_ = dwFdeCount.GetAppliedValue();
509 ehFrameHDRFdeTableElfOffset_ = dwTable.GetData() - buffer + shdrOffset;
510 ehFrameHDRFdeTableItemSize_ = dwTable.GetSize() + dwTableValue.GetSize();
511 DumpEhFrameHDR();
512
513 if (!dwFdeCount.IsOmit() && dwFdeCount.GetValue() > 0) {
514 return true;
515 } else {
516 HLOGW("fde table not found.");
517 }
518 return false;
519 }
520
LoadFileToMemory(const std::string & loadElfPath)521 void LoadFileToMemory(const std::string &loadElfPath)
522 {
523 #ifndef HIPERF_ELF_READ_USE_MMAP
524 if (readFd_ == nullptr) {
525 std::string resolvedPath = CanonicalizeSpecPath(loadElfPath.c_str());
526 FILE *fp = fopen(resolvedPath.c_str(), "rb");
527 if (fp == nullptr) {
528 return;
529 }
530 readFd_ =
531 std::unique_ptr<FILE, decltype(&fclose)>(fp, &fclose);
532 return;
533 }
534 #else
535 if (fd_ != -1) {
536 return;
537 }
538 #if is_mingw
539 std::string resolvedPath = CanonicalizeSpecPath(loadElfPath.c_str());
540 fd_ = OHOS::UniqueFd(open(resolvedPath.c_str(), O_RDONLY | O_BINARY));
541 #else
542 std::string resolvedPath = CanonicalizeSpecPath(loadElfPath.c_str());
543 fd_ = OHOS::UniqueFd(open(resolvedPath.c_str(), O_RDONLY));
544 #endif
545 if (fd_ != -1) {
546 struct stat sb = {};
547
548 if (fstat(fd_, &sb) == -1) {
549 HLOGE("unable to check the file size");
550 } else {
551 HLOGV("file stat size %" PRIu64 "", sb.st_size);
552
553 // unmap it first
554 if (mmap_ != MMAP_FAILED) {
555 munmap(mmap_, mmapSize_);
556 }
557
558 mmap_ = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd_, 0);
559 if (mmap_ == MMAP_FAILED) {
560 HLOGE("unable to map the file size %" PRIu64 " ", sb.st_size);
561 mmapSize_ = 0;
562 } else {
563 mmapSize_ = sb.st_size;
564 HLOGD("mmap build with size %" PRIu64 " ", mmapSize_);
565 }
566 }
567 } else {
568 HLOGD("elf file open failed with %s by %d", loadElfPath.c_str(), errno);
569 return;
570 }
571 #endif
572 }
573
ReadSymTab(const std::unique_ptr<ElfFile> & elfFile,const ELF::SectionHeader * shdr,std::vector<Symbol> & symbolsTable) const574 bool ReadSymTab(const std::unique_ptr<ElfFile> &elfFile, const ELF::SectionHeader *shdr,
575 std::vector<Symbol> &symbolsTable) const
576 {
577 if (shdr == nullptr) {
578 HLOGE("param is null");
579 return false;
580 }
581 HLOGV("ParseSymTable");
582 if (!elfFile->ParseSymTable(shdr)) {
583 return false;
584 }
585
586 HLOGV("Symbol Table:%s", shdr->secTypeName_.c_str());
587 HLOGM("%*s|%16s|%4s|%s", MAX_SYMBOLS_TYPE_NAME_LEN, "type", "addr", "size", "name");
588
589 for (const std::unique_ptr<ElfSymbol> &symbol : elfFile->symTable_->symbols_) {
590 if (ELF64_ST_TYPE(symbol->symInfo_) == STT_FUNC or
591 ELF64_ST_TYPE(symbol->symInfo_) == STT_GNU_IFUNC) {
592 /*
593 name| addr|size|name
594 function|00000000c0102b8c| 56|__lookup_processor_type
595 function|00000000c0102bd4| 0|__error_p
596 function|00000000c0008224| 64|__vet_atags
597 function|00000000c0008144| 128|__fixup_smp
598 function|00000000c00081d0| 64|__fixup_pv_table
599 function|00000000c000808c| 168|__create_page_tables
600 function|00000000c0b002e0| 68|__mmap_switched
601 function|00000000c0102acc| 20|__enable_mmu
602 object|00000000c0102ac0| 0|__secondary_data
603 function|00000000c0102ae0| 20|__do_fixup_smp_on_up
604 */
605
606 std::string name = elfFile->GetStrPtr(shdr->link_, symbol->nameIndex_);
607 std::string type = ElfStTypeName(ELF64_ST_TYPE(symbol->symInfo_));
608 // this will cause malloc , maybe need do this in report ?
609 std::string demangle = GetReadableName(name);
610 HLOGV("%10s|%016" PRIx64 "|%4" PRIu64 "|%s", type.c_str(), symbol->symValue_,
611 symbol->symSize_, demangle.c_str());
612
613 if (symbol->symValue_ == 0) {
614 continue; // we don't need 0 addr symbol
615 }
616 symbolsTable.emplace_back(symbol->symValue_, symbol->symSize_, name, demangle,
617 filePath_);
618 } else {
619 continue;
620 }
621 } // for symbols
622 return true;
623 }
624
ParseShdr(const std::unique_ptr<ElfFile> elfFile)625 bool ParseShdr(const std::unique_ptr<ElfFile> elfFile)
626 {
627 // only save the section info , not actually read any file content
628 for (const auto &shdrPair : elfFile->shdrs_) {
629 const auto &shdr = shdrPair.second;
630 const char *sh_name =
631 elfFile->GetStrPtr(elfFile->ehdr_->shdrStrTabIdx_, shdr->nameIndex_);
632 const unsigned char *data = elfFile->GetSectionData(shdr->secIndex_);
633
634 if (sh_name == nullptr || data == nullptr) {
635 HLOGE("name %p or data %p get failed.", sh_name, data);
636 return false;
637 }
638
639 HLOGV("shdr name '%s' vaddr 0x%" PRIx64 " offset 0x%" PRIx64 " size 0x%" PRIx64
640 " type 0x%" PRIx64 "(%s) index %u link 0x%u entry 0x%" PRIx64 "",
641 sh_name, shdr->secVaddr_, shdr->fileOffset_, shdr->secSize_, shdr->secType_,
642 shdr->secTypeName_.c_str(), shdr->secIndex_, shdr->link_, shdr->secEntrySize_);
643
644 shdrMap_.emplace(sh_name, ShdrInfo(shdr->secVaddr_, shdr->secSize_, shdr->fileOffset_));
645 #ifndef __arm__
646 if (shdr->secType_ == SHT_PROGBITS) {
647 if (EH_FRAME_HR == sh_name) {
648 LoadEhFrameHDR(data, shdr->secSize_, shdr->fileOffset_);
649 break;
650 }
651 } // for shdr
652 #endif
653 }
654 return true;
655 }
656
AddSectionAsSymbol(const std::unique_ptr<ELF::SectionHeader> & shdr,const char * name,std::vector<Symbol> & symbolsTable) const657 void AddSectionAsSymbol(const std::unique_ptr<ELF::SectionHeader> &shdr, const char *name,
658 std::vector<Symbol> &symbolsTable) const
659 {
660 HLOGV("add section %s as function symbol from 0x%" PRIx64 " size 0x%" PRIx64 "", name,
661 shdr->secVaddr_, shdr->secSize_);
662 symbolsTable.emplace_back(shdr->secVaddr_, shdr->secSize_, name, name, filePath_);
663 }
664
ParseShdr(const std::unique_ptr<ElfFile> elfFile,std::vector<Symbol> & symbolsTable,std::string & buildIdFound)665 bool ParseShdr(const std::unique_ptr<ElfFile> elfFile, std::vector<Symbol> &symbolsTable,
666 std::string &buildIdFound)
667 {
668 const ELF::SectionHeader *symTableShdr = nullptr;
669 // walkthough
670 for (const auto &shdrPair : elfFile->shdrs_) {
671 const auto &shdr = shdrPair.second;
672 const char *sh_name =
673 elfFile->GetStrPtr(elfFile->ehdr_->shdrStrTabIdx_, shdr->nameIndex_);
674 const unsigned char *data = elfFile->GetSectionData(shdr->secIndex_);
675
676 if (sh_name == nullptr || data == nullptr) {
677 HLOGE("name or data get failed.");
678 return false;
679 }
680
681 HLOGVVV("shdr name '%s' vaddr 0x%" PRIx64 " offset 0x%" PRIx64 " size 0x%" PRIx64
682 " type 0x%" PRIx64 "(%s) index %u link 0x%u entry 0x%" PRIx64 "",
683 sh_name, shdr->secVaddr_, shdr->fileOffset_, shdr->secSize_, shdr->secType_,
684 shdr->secTypeName_.c_str(), shdr->secIndex_, shdr->link_, shdr->secEntrySize_);
685
686 shdrMap_.emplace(sh_name, ShdrInfo(shdr->secVaddr_, shdr->secSize_, shdr->fileOffset_));
687 switch (shdr->secType_) {
688 case SHT_SYMTAB:
689 symTableShdr = shdr.get();
690 break;
691 case SHT_DYNSYM:
692 // if we already have SHT_SYMTAB ?
693 if (symTableShdr == nullptr) {
694 symTableShdr = shdr.get();
695 }
696 break;
697 case SHT_NOTE:
698 // notes
699 if (buildIdFound.empty()) {
700 // we use our function, not from gelf_getnote
701 HLOGM("found NOTE_GNU_BUILD_ID size: %" PRIu64 "", shdr->secSize_);
702
703 // there will be a log of note sh , we just need the right one
704 buildIdFound = ElfGetBuildId(data, shdr->secSize_);
705 }
706 break;
707 case SHT_PROGBITS:
708 if (PLT == sh_name) {
709 // this is a plt section, PLT table will put here
710 // we make it as named PLT function
711 AddSectionAsSymbol(shdr, sh_name, symbolsTable);
712 }
713 break;
714 default:
715 HLOGM("skip shdr.sh_type %" PRIx64 "", shdr->secType_);
716 break;
717 } // for shdr
718 } // for each shdrs_
719 // load symtab
720 if (symTableShdr != nullptr) {
721 if (!ReadSymTab(elfFile, symTableShdr, symbolsTable)) {
722 return false;
723 }
724 }
725 return true;
726 }
727
LoadElfFile(std::string & elfPath) const728 std::unique_ptr<ElfFile> LoadElfFile(std::string &elfPath) const
729 {
730 HLOGD("try load elf %s", elfPath.c_str());
731 if (elfPath.empty()) {
732 elfPath = filePath_;
733 HLOGD("use default elf path %s\n", elfPath.c_str());
734 }
735 return ElfFile::MakeUnique(elfPath);
736 }
737
UpdateSymbols(std::vector<Symbol> & symbolsTable,const std::string & elfPath)738 void UpdateSymbols(std::vector<Symbol> &symbolsTable, const std::string &elfPath)
739 {
740 symbols_.clear();
741 HLOGD("%zu symbols loadded from symbolsTable.", symbolsTable.size());
742
743 symbols_.swap(symbolsTable);
744
745 AdjustSymbols();
746 HLOGD("%zu symbols loadded from elf '%s'.", symbols_.size(), elfPath.c_str());
747 if (buildId_.empty()) {
748 HLOGD("buildId not found from elf '%s'.", elfPath.c_str());
749 // don't failed. some time the lib have not got the build id
750 // buildId not found from elf '/system/bin/ld-musl-arm.so.1'.
751 }
752 }
753
LoadElfSymbols(std::string elfPath)754 bool LoadElfSymbols(std::string elfPath)
755 {
756 #ifdef HIPERF_DEBUG_TIME
757 const auto startTime = steady_clock::now();
758 #endif
759 std::unique_ptr<ElfFile> elfFile = LoadElfFile(elfPath);
760 if (elfFile == nullptr) {
761 HLOGD("elf load failed");
762 return false;
763 } else {
764 HLOGD("loaded elf %s", elfPath.c_str());
765 }
766 // we prepare two table here
767 // only one we will push in to symbols_
768 // or both drop if build id is not same
769 std::vector<Symbol> symbolsTable;
770 std::string buildIdFound;
771 for (const auto &phdr : elfFile->phdrs_) {
772 if ((phdr->type_ == PT_LOAD) && (phdr->flags_ & PF_X)) {
773 // find the min addr
774 if (textExecVaddr_ != std::min(textExecVaddr_, phdr->vaddr_)) {
775 textExecVaddr_ = std::min(textExecVaddr_, phdr->vaddr_);
776 textExecVaddrFileOffset_ = phdr->offset_;
777 }
778 }
779 }
780
781 HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
782 textExecVaddrFileOffset_);
783
784 if (!ParseShdr(std::move(elfFile), symbolsTable, buildIdFound)) {
785 return false;
786 }
787
788 if (UpdateBuildIdIfMatch(buildIdFound)) {
789 UpdateSymbols(symbolsTable, elfPath);
790 } else {
791 HLOGW("symbols will not update for '%s' because buildId is not match.",
792 elfPath.c_str());
793 // this mean failed . we don't goon for this.
794 return false;
795 }
796
797 // mmap it for later use
798 LoadFileToMemory(elfPath);
799 #ifdef HIPERF_DEBUG_TIME
800 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
801 if (usedTime.count() != 0) {
802 HLOGV("cost %0.3f ms to load symbols '%s'",
803 usedTime.count() / static_cast<double>(milliseconds::duration::period::den),
804 elfPath.c_str());
805 }
806 #endif
807 return true;
808 }
809
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapPageOffset) const810 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
811 uint64_t mapPageOffset) const override
812 {
813 /*
814 00200000-002c5000 r--p 00000000 08:02 46400311
815 002c5000-00490000 r-xp 000c5000 08:02 4640031
816
817 [14] .text PROGBITS 00000000002c5000 000c5000
818
819 if ip is 0x46e6ab
820 1. find the map range is 002c5000-00490000
821 2. ip - map start(002c5000) = map section offset
822 3. map section offset + map page offset(000c5000) = elf file offset
823 4. elf file offset - exec file offset(000c5000)
824 = ip offset (ip always in exec file offset)
825 5. ip offset + exec begin vaddr(2c5000) = virtual ip in elf
826 */
827 uint64_t vaddr = ip - mapStart + mapPageOffset - textExecVaddrFileOffset_ + textExecVaddr_;
828 HLOGM(" ip :0x%016" PRIx64 " -> elf offset :0x%016" PRIx64 " -> vaddr :0x%016" PRIx64 " ",
829 ip, ip - mapStart + mapPageOffset, vaddr);
830 HLOGM("(minExecAddrFileOffset_ is 0x%" PRIx64 " textExecVaddr_ is 0x%" PRIx64 ")",
831 textExecVaddrFileOffset_, textExecVaddr_);
832 return vaddr;
833 }
834 };
835
836 class KernelSymbols : public ElfFileSymbols {
837 public:
KernelSymbols(const std::string & symbolFilePath)838 explicit KernelSymbols(const std::string &symbolFilePath)
839 : ElfFileSymbols(symbolFilePath, SYMBOL_KERNEL_FILE)
840 {
841 }
842
843 static constexpr const int KSYM_MIN_TOKENS = 3;
844 static constexpr const int KSYM_DEFAULT_LINE = 35000;
845 static constexpr const int KSYM_DEFAULT_SIZE = 1024 * 1024 * 1; // 1MB
846
ParseKallsymsLine()847 bool ParseKallsymsLine()
848 {
849 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
850 const auto startTime = steady_clock::now();
851 std::chrono::microseconds parseLineTime = std::chrono::microseconds::zero();
852 std::chrono::microseconds sscanfTime = std::chrono::microseconds::zero();
853 std::chrono::microseconds newTime = std::chrono::microseconds::zero();
854 std::chrono::microseconds readFileTime = std::chrono::microseconds::zero();
855 #endif
856 size_t lines = 0;
857 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
858 const auto eachFileStartTime = steady_clock::now();
859 #endif
860 std::string kallsym;
861 if (!ReadFileToString("/proc/kallsyms", kallsym, KSYM_DEFAULT_SIZE) || kallsym.empty()) {
862 HLOGW("/proc/kallsyms load failed.");
863 return false;
864 }
865 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
866 // any way we finish the line scan
867 readFileTime += duration_cast<milliseconds>(steady_clock::now() - eachFileStartTime);
868 #endif
869 // reduce the mem alloc
870 symbols_.reserve(KSYM_DEFAULT_LINE);
871
872 char *lineBegin = kallsym.data();
873 char *dataEnd = lineBegin + kallsym.size();
874 while (lineBegin < dataEnd) {
875 char *lineEnd = strchr(lineBegin, '\n');
876 if (lineEnd != nullptr) {
877 *lineEnd = '\0';
878 } else {
879 lineEnd = dataEnd;
880 }
881 size_t lineSize = (lineEnd != nullptr) ? (lineEnd - lineBegin) : (dataEnd - lineBegin);
882
883 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
884 const auto eachLineStartTime = steady_clock::now();
885 #endif
886 lines++;
887 uint64_t addr = 0;
888 char type = '\0';
889
890 char nameRaw[lineSize];
891 char moduleRaw[lineSize];
892 int ret = sscanf_s(lineBegin, "%" PRIx64 " %c %s%s", &addr, &type, sizeof(type),
893 nameRaw, sizeof(nameRaw), moduleRaw, sizeof(moduleRaw));
894
895 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
896 // any way we finish the line scan
897 sscanfTime += duration_cast<milliseconds>(steady_clock::now() - eachLineStartTime);
898 #endif
899 if (ret >= KSYM_MIN_TOKENS) {
900 if (ret == KSYM_MIN_TOKENS) {
901 moduleRaw[0] = '\0';
902 }
903 HLOGM(" 0x%016" PRIx64 " %c '%s' '%s'", addr, type, nameRaw, moduleRaw);
904 } else {
905 HLOGW("unknown line %d: '%s'", ret, lineBegin);
906 lineBegin = lineEnd + 1;
907 continue;
908 }
909 lineBegin = lineEnd + 1;
910 std::string name = nameRaw;
911 std::string module = moduleRaw;
912
913 /*
914 T
915 The symbol is in the text (code) section.
916
917 W
918 The symbol is a weak symbol that has not been specifically
919 tagged as a weak object symbol. When a weak defined symbol is
920 linked with a normal defined symbol, the normal defined symbol
921 is used with no error. When a weak undefined symbol is linked
922 and the symbol is not defined, the value of the weak symbol
923 becomes zero with no error.
924 */
925 if (addr != 0 && strchr("TtWw", type)) {
926 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
927 const auto eachNewSymbolTime = steady_clock::now();
928 #endif
929 // we only need text symbols
930 symbols_.emplace_back(addr, name, module.empty() ? filePath_ : module);
931 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
932 newTime += duration_cast<milliseconds>(steady_clock::now() - eachNewSymbolTime);
933 #endif
934 }
935 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
936 parseLineTime += duration_cast<milliseconds>(steady_clock::now() - eachLineStartTime);
937 #endif
938 }
939 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
940 std::chrono::microseconds usedTime =
941 duration_cast<milliseconds>(steady_clock::now() - startTime);
942 printf("parse kernel symbols use : %0.3f ms\n", usedTime.count() / MS_DUARTION);
943 printf("parse line use : %0.3f ms\n", parseLineTime.count() / MS_DUARTION);
944 printf("sscanf line use : %0.3f ms\n", sscanfTime.count() / MS_DUARTION);
945 printf("new symbols use : %0.3f ms\n", newTime.count() / MS_DUARTION);
946 printf("read file use : %0.3f ms\n", readFileTime.count() / MS_DUARTION);
947 #endif
948 HLOGD("%zu line processed(%zu symbols)", lines, symbols_.size());
949 return true;
950 }
951
952 const std::string KPTR_RESTRICT = "/proc/sys/kernel/kptr_restrict";
953
LoadKernelSyms()954 bool LoadKernelSyms()
955 {
956 HLOGD("try read /proc/kallsyms");
957 if (access("/proc/kallsyms", R_OK) != 0) {
958 printf("No vmlinux path is given, and kallsyms cannot be opened\n");
959 return false;
960 }
961 bool hasChangeKptr = false;
962 std::string oldKptrRestrict = ReadFileToString(KPTR_RESTRICT);
963 if (oldKptrRestrict.front() != '0') {
964 if (!IsSupportNonDebuggableApp()) {
965 HLOGD("user mode do not load kernel syms");
966 printf("Hiperf is not running as root mode. Do not need load kernel syms\n");
967 return false;
968 }
969 printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n");
970 hasChangeKptr = WriteStringToFile(KPTR_RESTRICT, "0");
971 if (!hasChangeKptr) {
972 printf("/proc/sys/kernel/kptr_restrict write failed and we can't not change it.\n");
973 }
974 }
975
976 // getline end
977 if (!ParseKallsymsLine()) {
978 return false;
979 }
980
981 if (hasChangeKptr) {
982 if (!WriteStringToFile(KPTR_RESTRICT, oldKptrRestrict)) {
983 printf("recover /proc/sys/kernel/kptr_restrict fail.\n");
984 }
985 }
986
987 if (symbols_.empty()) {
988 printf("The symbol table addresses in /proc/kallsyms are all 0.\n"
989 "Please check the value of /proc/sys/kernel/kptr_restrict, it "
990 "should be 0.\n"
991 "Or provide a separate vmlinux path.\n");
992
993 if (buildId_.size() != 0) {
994 // but we got the buildid , so we make a dummpy symbols
995 HLOGD("kallsyms not found. but we have the buildid");
996 return true;
997 } else {
998 // we got nothing
999 return false;
1000 }
1001 } else {
1002 AdjustSymbols();
1003 HLOGV("%zu symbols_ loadded from kallsyms.\n", symbols_.size());
1004 return true;
1005 }
1006 }
LoadSymbols(const std::string & symbolFilePath)1007 bool LoadSymbols(const std::string &symbolFilePath) override
1008 {
1009 symbolsLoaded_ = true;
1010 HLOGV("KernelSymbols try read '%s' search paths size %zu, inDeviceRecord %d",
1011 symbolFilePath.c_str(), symbolsFileSearchPaths_.size(), onRecording_);
1012
1013 if (onRecording_) {
1014 const auto startTime = std::chrono::steady_clock::now();
1015 if (!LoadKernelSyms()) {
1016 if (IsRoot()) {
1017 printf("parse kalsyms failed.\n");
1018 }
1019 return false;
1020 } else {
1021 const auto thisTime = std::chrono::steady_clock::now();
1022 const auto usedTimeMsTick =
1023 std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime);
1024 HLOGV("Load kernel symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count());
1025 // load complete
1026 return true;
1027 }
1028
1029 // try read
1030 HLOGD("try read /sys/kernel/notes");
1031 std::string notes = ReadFileToString("/sys/kernel/notes");
1032 if (notes.empty()) {
1033 printf("notes cannot be opened, unable get buildid\n");
1034 return false;
1035 } else {
1036 HLOGD("kernel notes size: %zu", notes.size());
1037 buildId_ = ElfGetBuildId(reinterpret_cast<const unsigned char*>(notes.data()), notes.size());
1038 }
1039 } // no search path
1040
1041 // try vmlinux
1042 return ElfFileSymbols::LoadSymbols(KERNEL_ELF_NAME);
1043 }
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t) const1044 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
1045 {
1046 // ip is vaddr in /proc/kallsyms
1047 return ip;
1048 }
~KernelSymbols()1049 ~KernelSymbols() override {}
1050 };
1051
1052 class KernelModuleSymbols : public ElfFileSymbols {
1053 public:
KernelModuleSymbols(const std::string & symbolFilePath)1054 explicit KernelModuleSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
1055 {
1056 HLOGV("create %s", symbolFilePath.c_str());
1057 symbolFileType_ = SYMBOL_KERNEL_MODULE_FILE;
1058 module_ = symbolFilePath;
1059 }
~KernelModuleSymbols()1060 ~KernelModuleSymbols() override {};
1061
LoadSymbols(const std::string & symbolFilePath)1062 bool LoadSymbols(const std::string &symbolFilePath) override
1063 {
1064 symbolsLoaded_ = true;
1065 if (module_ == filePath_ and onRecording_) {
1066 // file name still not convert to ko file path
1067 // this is in record mode
1068 HLOGV("find ko name %s", module_.c_str());
1069 for (const std::string &path : kernelModulePaths) {
1070 if (access(path.c_str(), R_OK) == 0) {
1071 std::string koPath = path + module_ + KERNEL_MODULES_EXT_NAME;
1072 HLOGV("found ko in %s", koPath.c_str());
1073 if (access(koPath.c_str(), R_OK) == 0) {
1074 // create symbol
1075 filePath_ = koPath;
1076 break; // find next ko
1077 }
1078 }
1079 }
1080 LoadBuildId();
1081 } else {
1082 HLOGV("we have file path, load with %s", filePath_.c_str());
1083 return ElfFileSymbols::LoadSymbols(filePath_);
1084 }
1085 return false;
1086 }
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t) const1087 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
1088 {
1089 return ip - mapStart;
1090 }
1091
1092 private:
LoadBuildId()1093 bool LoadBuildId()
1094 {
1095 std::string sysFile = "/sys/module/" + module_ + "/notes/.note.gnu.build-id";
1096 std::string buildIdRaw = ReadFileToString(sysFile);
1097 if (!buildIdRaw.empty()) {
1098 buildId_ = ElfGetBuildId(reinterpret_cast<const unsigned char*>(buildIdRaw.data()), buildIdRaw.size());
1099 HLOGD("kerne module %s(%s) build id %s", module_.c_str(), filePath_.c_str(),
1100 buildId_.c_str());
1101 return buildId_.empty() ? false : true;
1102 }
1103 return false;
1104 }
1105
1106 const std::vector<std::string> kernelModulePaths = {"/vendor/modules/"};
1107 std::string module_ = "";
1108 };
1109
1110 class JavaFileSymbols : public ElfFileSymbols {
1111 public:
JavaFileSymbols(const std::string & symbolFilePath)1112 explicit JavaFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
1113 {
1114 symbolFileType_ = SYMBOL_KERNEL_FILE;
1115 }
LoadSymbols(const std::string & symbolFilePath)1116 bool LoadSymbols(const std::string &symbolFilePath) override
1117 {
1118 symbolsLoaded_ = true;
1119 return false;
1120 }
~JavaFileSymbols()1121 ~JavaFileSymbols() override {}
1122
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapPageOffset) const1123 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
1124 uint64_t mapPageOffset) const override
1125 {
1126 // this is different with elf
1127 // elf use ip - mapStart + mapPageOffset - minExecAddrFileOffset_ + textExecVaddr_
1128 return ip - mapStart + mapPageOffset;
1129 }
1130 };
1131
1132 class JSFileSymbols : public ElfFileSymbols {
1133 public:
JSFileSymbols(const std::string & symbolFilePath)1134 explicit JSFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
1135 {
1136 symbolFileType_ = SYMBOL_KERNEL_FILE;
1137 }
LoadSymbols(const std::string & symbolFilePath)1138 bool LoadSymbols(const std::string &symbolFilePath) override
1139 {
1140 symbolsLoaded_ = true;
1141 return false;
1142 }
~JSFileSymbols()1143 ~JSFileSymbols() override {}
1144 };
1145
1146 class UnknowFileSymbols : public SymbolsFile {
1147 public:
UnknowFileSymbols(const std::string & symbolFilePath)1148 explicit UnknowFileSymbols(const std::string &symbolFilePath)
1149 : SymbolsFile(SYMBOL_UNKNOW_FILE, symbolFilePath)
1150 {
1151 }
LoadSymbols(const std::string & symbolFilePath)1152 bool LoadSymbols(const std::string &symbolFilePath) override
1153 {
1154 symbolsLoaded_ = true;
1155 return false;
1156 }
~UnknowFileSymbols()1157 ~UnknowFileSymbols() override {}
1158 };
1159
~SymbolsFile()1160 SymbolsFile::~SymbolsFile() {}
1161
CreateSymbolsFile(SymbolsFileType symbolType,const std::string symbolFilePath)1162 std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(SymbolsFileType symbolType,
1163 const std::string symbolFilePath)
1164 {
1165 switch (symbolType) {
1166 case SYMBOL_KERNEL_FILE:
1167 return std::make_unique<KernelSymbols>(symbolFilePath.empty() ? KERNEL_MMAP_NAME
1168 : symbolFilePath);
1169 case SYMBOL_KERNEL_MODULE_FILE:
1170 return std::make_unique<KernelModuleSymbols>(symbolFilePath);
1171 case SYMBOL_ELF_FILE:
1172 return std::make_unique<ElfFileSymbols>(symbolFilePath);
1173 case SYMBOL_JAVA_FILE:
1174 return std::make_unique<JavaFileSymbols>(symbolFilePath);
1175 case SYMBOL_JS_FILE:
1176 return std::make_unique<JSFileSymbols>(symbolFilePath);
1177 default:
1178 return std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, symbolFilePath);
1179 }
1180 }
1181
CreateSymbolsFile(const std::string & symbolFilePath)1182 std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(const std::string &symbolFilePath)
1183 {
1184 // we need check file name here
1185 if (symbolFilePath == KERNEL_MMAP_NAME) {
1186 return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_FILE, symbolFilePath);
1187 } else if (StringEndsWith(symbolFilePath, KERNEL_MODULES_EXT_NAME)) {
1188 return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, symbolFilePath);
1189 } else {
1190 // default is elf
1191 return SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, symbolFilePath);
1192 }
1193 }
1194
AdjustSymbols()1195 void SymbolsFile::AdjustSymbols()
1196 {
1197 if (symbols_.size() <= 0) {
1198 return;
1199 }
1200
1201 // order
1202 sort(symbols_.begin(), symbols_.end(), Symbol::CompareLessThen);
1203 HLOGV("sort completed");
1204
1205 size_t fullSize = symbols_.size();
1206 size_t erased = 0;
1207
1208 // Check for duplicate vaddr
1209 auto last = std::unique(symbols_.begin(), symbols_.end(), &Symbol::SameVaddr);
1210 symbols_.erase(last, symbols_.end());
1211 erased = fullSize - symbols_.size();
1212 HLOGV("uniqued completed");
1213 auto it = symbols_.begin();
1214 while (it != symbols_.end()) {
1215 it->index_ = it - symbols_.begin();
1216 it++;
1217 }
1218 HLOGV("indexed completed");
1219
1220 HLOG_ASSERT(symbols_.size() != 0);
1221
1222 if (textExecVaddrRange_ == maxVaddr) {
1223 textExecVaddrRange_ = symbols_.back().funcVaddr_ - symbols_.front().funcVaddr_;
1224 }
1225
1226 HLOGDDD("%zu symbols after adjust (%zu erased) 0x%016" PRIx64 " - 0x%016" PRIx64
1227 " @0x%016" PRIx64 " ",
1228 symbols_.size(), erased, symbols_.front().funcVaddr_, symbols_.back().funcVaddr_,
1229 textExecVaddrFileOffset_);
1230 }
1231
SortMatchedSymbols()1232 void SymbolsFile::SortMatchedSymbols()
1233 {
1234 if (matchedSymbols_.size() <= 1u) {
1235 return;
1236 }
1237 sort(matchedSymbols_.begin(), matchedSymbols_.end(), Symbol::CompareByPointer);
1238 }
1239
GetSymbols()1240 const std::vector<Symbol> &SymbolsFile::GetSymbols()
1241 {
1242 return symbols_;
1243 }
1244
GetMatchedSymbols()1245 const std::vector<Symbol *> &SymbolsFile::GetMatchedSymbols()
1246 {
1247 return matchedSymbols_;
1248 }
1249
GetSymbolWithVaddr(uint64_t vaddrInFile)1250 const Symbol SymbolsFile::GetSymbolWithVaddr(uint64_t vaddrInFile)
1251 {
1252 #ifdef HIPERF_DEBUG_TIME
1253 const auto startTime = steady_clock::now();
1254 #endif
1255 Symbol symbol;
1256 // it should be already order from small to large
1257 auto found =
1258 std::upper_bound(symbols_.begin(), symbols_.end(), vaddrInFile, Symbol::ValueLessThen);
1259 /*
1260 if data is { 1, 2, 4, 5, 5, 6 };
1261 upper_bound for each val :
1262 0 < 1 at index 0
1263 1 < 2 at index 1
1264 2 < 4 at index 2
1265 3 < 4 at index 2
1266 4 < 5 at index 3
1267 5 < 6 at index 5
1268 6 < not found
1269 if key symbol vaddr is { 1, 2, 4, 5, 5, 6 };
1270 check ip vaddr for each val :
1271 ip sym
1272 0 not found
1273 1 1
1274 1 1
1275 2 2
1276 3 3
1277 4 4
1278 5 5
1279 6 6
1280 7 7
1281 */
1282 if (found != symbols_.begin()) {
1283 found = std::prev(found);
1284 if (found->Contain(vaddrInFile)) {
1285 found->offsetToVaddr_ = vaddrInFile - found->funcVaddr_;
1286 if (!found->HasMatched()) {
1287 found->SetMatchFlag();
1288 matchedSymbols_.push_back(&(*found));
1289 }
1290 symbol = *found; // copy
1291 HLOGV("found '%s' for vaddr 0x%016" PRIx64 "", symbol.ToString().c_str(), vaddrInFile);
1292 }
1293 }
1294
1295 if (!symbol.isValid()) {
1296 HLOGV("NOT found vaddr 0x%" PRIx64 " in symbole file %s(%zu)", vaddrInFile,
1297 filePath_.c_str(), symbols_.size());
1298 }
1299 symbol.fileVaddr_ = vaddrInFile;
1300 symbol.symbolFileIndex_ = id_;
1301
1302 #ifdef HIPERF_DEBUG_TIME
1303 auto usedTime = duration_cast<milliseconds>(steady_clock::now() - startTime);
1304 if (usedTime > 1ms) {
1305 HLOGW("cost %" PRId64 "ms to search ", usedTime.count());
1306 }
1307 #endif
1308 return symbol;
1309 }
1310
CheckPathReadable(const std::string & path) const1311 bool SymbolsFile::CheckPathReadable(const std::string &path) const
1312 {
1313 if (access(path.c_str(), R_OK) == 0) {
1314 return true;
1315 } else {
1316 HLOGM("'%s' is unable read", path.c_str());
1317 return false;
1318 }
1319 }
1320
setSymbolsFilePath(const std::vector<std::string> & symbolsSearchPaths)1321 bool SymbolsFile::setSymbolsFilePath(const std::vector<std::string> &symbolsSearchPaths)
1322 {
1323 symbolsFileSearchPaths_.clear();
1324 for (auto &symbolsSearchPath : symbolsSearchPaths) {
1325 if (CheckPathReadable(symbolsSearchPath)) {
1326 symbolsFileSearchPaths_.emplace_back(symbolsSearchPath);
1327 HLOGV("'%s' is add to symbolsSearchPath", symbolsSearchPath.c_str());
1328 }
1329 }
1330 return (symbolsFileSearchPaths_.size() > 0);
1331 }
1332
LoadSymbolsFromSaved(const SymbolFileStruct & symbolFileStruct)1333 std::unique_ptr<SymbolsFile> SymbolsFile::LoadSymbolsFromSaved(
1334 const SymbolFileStruct &symbolFileStruct)
1335 {
1336 auto symbolsFile = CreateSymbolsFile(symbolFileStruct.filePath_);
1337 symbolsFile->filePath_ = symbolFileStruct.filePath_;
1338 symbolsFile->symbolFileType_ = (SymbolsFileType)symbolFileStruct.symbolType_;
1339 symbolsFile->textExecVaddr_ = symbolFileStruct.textExecVaddr_;
1340 symbolsFile->textExecVaddrFileOffset_ = symbolFileStruct.textExecVaddrFileOffset_;
1341 symbolsFile->buildId_ = symbolFileStruct.buildId_;
1342 for (auto &symbolStruct : symbolFileStruct.symbolStructs_) {
1343 symbolsFile->symbols_.emplace_back(symbolStruct.vaddr_, symbolStruct.len_,
1344 symbolStruct.symbolName_, symbolFileStruct.filePath_);
1345 }
1346 symbolsFile->AdjustSymbols(); // reorder
1347 symbolsFile->debugInfoLoadResult_ = true;
1348 HLOGV("load %zu symbol from SymbolFileStruct for file '%s'", symbolsFile->symbols_.size(),
1349 symbolsFile->filePath_.c_str());
1350 return symbolsFile;
1351 }
1352
ExportSymbolToFileFormat(SymbolFileStruct & symbolFileStruct)1353 void SymbolsFile::ExportSymbolToFileFormat(SymbolFileStruct &symbolFileStruct)
1354 {
1355 symbolFileStruct.filePath_ = filePath_;
1356 symbolFileStruct.symbolType_ = symbolFileType_;
1357 symbolFileStruct.textExecVaddr_ = textExecVaddr_;
1358 symbolFileStruct.textExecVaddrFileOffset_ = textExecVaddrFileOffset_;
1359 symbolFileStruct.buildId_ = buildId_;
1360
1361 SortMatchedSymbols();
1362 auto symbols = GetMatchedSymbols();
1363 symbolFileStruct.symbolStructs_.reserve(symbols.size());
1364 for (auto symbol : symbols) {
1365 auto &symbolStruct = symbolFileStruct.symbolStructs_.emplace_back();
1366 symbolStruct.vaddr_ = symbol->funcVaddr_;
1367 symbolStruct.len_ = symbol->len_;
1368 symbolStruct.symbolName_ = symbol->Name();
1369 }
1370
1371 HLOGV("export %zu symbol to SymbolFileStruct from %s", symbolFileStruct.symbolStructs_.size(),
1372 filePath_.c_str());
1373 }
1374
GetVaddrInSymbols(uint64_t ip,uint64_t,uint64_t) const1375 uint64_t SymbolsFile::GetVaddrInSymbols(uint64_t ip, uint64_t, uint64_t) const
1376 {
1377 // no convert
1378 return ip;
1379 }
1380 } // namespace HiPerf
1381 } // namespace Developtools
1382 } // namespace OHOS
1383