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