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