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