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