• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #define HILOG_TAG "Symbols"
17 
18 #include "symbols_file.h"
19 
20 #include <algorithm>
21 #include <chrono>
22 #include <cxxabi.h>
23 #include <cstdlib>
24 #include <fcntl.h>
25 #include <fstream>
26 #include <string_view>
27 #include <type_traits>
28 
29 #if defined(is_mingw) && is_mingw
30 #include <memoryapi.h>
31 #else
32 #include <sys/mman.h>
33 #include <sys/stat.h>
34 #endif
35 #include <unistd.h>
36 
37 #include "dfx_ark.h"
38 #include "dfx_extractor_utils.h"
39 #include "dfx_symbols.h"
40 #include "dwarf_encoding.h"
41 #include "hiperf_hilog.h"
42 #include "unwinder_config.h"
43 #include "utilities.h"
44 
45 using namespace OHOS::HiviewDFX;
46 using namespace std::chrono;
47 
48 namespace OHOS {
49 namespace Developtools {
50 namespace HiPerf {
51 bool SymbolsFile::onRecording_ = true;
52 bool SymbolsFile::needParseJsFunc_ = false;
53 
GetBuildId() const54 const std::string SymbolsFile::GetBuildId() const
55 {
56     return buildId_;
57 }
58 
UpdateBuildIdIfMatch(std::string buildId)59 bool SymbolsFile::UpdateBuildIdIfMatch(std::string buildId)
60 {
61     /*
62         here we have two case
63         1 buildId_ is empty
64             a) always return match
65         2 buildId_ is not empty
66             a) really check if the same one
67     */
68 
69     if (buildId_.empty()) {
70         // we have new empty build
71         if (buildId.empty()) {
72             // both empty , no build id provided
73             HLOGD("build id is empty.");
74             return true;
75         } else {
76             buildId_ = buildId;
77             HLOGD("new buildId %s", buildId_.c_str());
78             return true;
79         }
80     } else {
81         // we already have a build id
82         // so this is not the first time load symbol
83         // we need check if it match
84         HLOGV("expected buildid: %s vs %s", buildId_.c_str(), buildId.c_str());
85 
86         if (buildId_ != buildId) {
87             HLOGW("id not match");
88             return false;
89         } else {
90             HLOGD("id match");
91             return true;
92         }
93     }
94 }
95 
SearchReadableFile(const std::vector<std::string> & searchPaths,const std::string & filePath) const96 std::string SymbolsFile::SearchReadableFile(const std::vector<std::string> &searchPaths,
97                                             const std::string &filePath) const
98 {
99     if (filePath.empty()) {
100         HLOGW("nothing to found");
101         return filePath;
102     }
103     for (auto searchPath : searchPaths) {
104         if (searchPath.back() != PATH_SEPARATOR) {
105             searchPath += PATH_SEPARATOR;
106         }
107         std::string PossibleFilePath = searchPath + filePath;
108         if (CheckPathReadable(PossibleFilePath)) {
109             return PossibleFilePath;
110         }
111         HLOGW("have not found '%s' in search paths %s", filePath.c_str(), searchPath.c_str());
112     }
113     return EMPTY_STRING;
114 }
115 
FindSymbolFile(const std::vector<std::string> & symbolsFileSearchPaths,std::string symboleFilePath) const116 const std::string SymbolsFile::FindSymbolFile(
117     const std::vector<std::string> &symbolsFileSearchPaths, std::string symboleFilePath) const
118 {
119     /*
120         this function do 2 things:
121         find by name:
122             1 find dso path
123             2 find search path
124                 a) search path + dso path
125                 b) search path + dso name
126 
127         show we should return filePath_ as default ?
128     */
129     if (symboleFilePath.empty()) {
130         symboleFilePath = filePath_;
131         HLOGD("use default filename: %s ", symboleFilePath.c_str());
132     }
133     symboleFilePath = PlatformPathConvert(symboleFilePath);
134     std::string foundPath;
135     // search first if we have path
136     if (symbolsFileSearchPaths.size() != 0) {
137         foundPath = SearchReadableFile(symbolsFileSearchPaths, symboleFilePath);
138         if (foundPath.empty()) {
139             HLOGV("try base name for: %s split with %s", symboleFilePath.c_str(),
140                   PATH_SEPARATOR_STR.c_str());
141             auto pathSplit = StringSplit(symboleFilePath, PATH_SEPARATOR_STR);
142             if (pathSplit.size() > 1) {
143                 HLOGV("base name is: %s ", pathSplit.back().c_str());
144                 // found it again with base name , split it and get last name
145                 foundPath = SearchReadableFile(symbolsFileSearchPaths, pathSplit.back());
146             }
147         }
148     }
149 
150     // only access the patch in onRecording_
151     // in report mode we don't load any thing in runtime path
152     if (foundPath.empty() and onRecording_) {
153         // try access direct at last
154         if (CheckPathReadable(symboleFilePath)) {
155             // found direct folder
156             HLOGD("find %s in current work dir", symboleFilePath.c_str());
157             return symboleFilePath;
158         }
159     }
160     return foundPath;
161 }
162 
163 class ElfFileSymbols : public SymbolsFile {
164 public:
ElfFileSymbols(const std::string & symbolFilePath,const SymbolsFileType symbolsFileType=SYMBOL_ELF_FILE)165     explicit ElfFileSymbols(const std::string &symbolFilePath,
166                             const SymbolsFileType symbolsFileType = SYMBOL_ELF_FILE)
167         : SymbolsFile(symbolsFileType, symbolFilePath)
168     {
169     }
170 
~ElfFileSymbols()171     virtual ~ElfFileSymbols()
172     {
173     }
174 
GetSymbolWithPcAndMap(uint64_t pc,std::shared_ptr<DfxMap> map)175     DfxSymbol GetSymbolWithPcAndMap(uint64_t pc, std::shared_ptr<DfxMap> map) override
176     {
177         const DfxSymbol symbol;
178         return symbol;
179     }
180 
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)181     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
182     {
183         symbolsLoaded_ = true;
184         std::string findPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
185         if (findPath.empty() && elfFile_ == nullptr) { // elf not compressed in hap has been initialized before
186             HLOGW("elf found failed (belong to %s)", filePath_.c_str());
187             return false;
188         }
189         if (LoadElfSymbols(map, findPath)) {
190             return true;
191         } else {
192             HLOGW("elf open failed with '%s'", findPath.c_str());
193             return false;
194         }
195         return false;
196     }
197 
EnableMiniDebugInfo()198     void EnableMiniDebugInfo() override
199     {
200         UnwinderConfig::SetEnableMiniDebugInfo(true);
201     }
202 
GetElfFile()203     std::shared_ptr<DfxElf> GetElfFile() override
204     {
205         return elfFile_;
206     }
207 
GetPtLoads()208     const std::unordered_map<uint64_t, ElfLoadInfo> GetPtLoads() override
209     {
210         CHECK_TRUE(elfFile_ == nullptr, info_, 0, "");
211         return elfFile_->GetPtLoads();
212     }
213 
214 protected:
LoadDebugInfo(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)215     bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
216     {
217         std::lock_guard<std::mutex> lock(mutex_);
218         if (debugInfoLoadResult_) {
219             return true; // it must have been loaded
220         } else if (debugInfoLoaded_) {
221             return debugInfoLoadResult_; // return the result of loaded
222         } else {
223             debugInfoLoaded_ = true;
224         }
225         std::string elfPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
226         if (elfPath.empty()) {
227             HLOGW("elf found failed (belong to %s)", filePath_.c_str());
228             return false;
229         }
230 
231         if (elfFile_ == nullptr) {
232             if (StringEndsWith(elfPath, ".hap")) {
233                 if (map == nullptr) {
234                     HLOGW("map should not be nullptr.");
235                     return false;
236                 }
237                 if (!map->IsMapExec()) {
238                     HLOGW("map is not exec, no need parse elf.");
239                     return false;
240                 }
241                 elfFile_ = DfxElf::CreateFromHap(elfPath, map->prevMap, map->offset);
242                 HLOGD("try create elf from hap");
243             } else {
244                 elfFile_ = std::make_shared<DfxElf>(elfPath);
245             }
246         }
247 
248         CHECK_TRUE(elfFile_ == nullptr, false, 1, "Failed to create elf file for %s.", elfPath.c_str());
249 
250         CHECK_TRUE(!elfFile_->IsValid(), false, 1, "parser elf file failed.");
251 
252         HLOGD("loaded elf %s", elfPath.c_str());
253         // update path for so in hap
254         if (StringEndsWith(elfPath, ".hap")) {
255             filePath_ = elfPath + "!" + elfFile_->GetElfName();
256             HLOGD("update path for so in hap %s.", filePath_.c_str());
257             if (map == nullptr) {
258                 HLOGW("map should not be nullptr.");
259                 return false;
260             }
261             map->name = filePath_;
262             map->elf = elfFile_;
263             map->prevMap->name = filePath_;
264             map->prevMap->elf = elfFile_;
265         }
266 
267         textExecVaddr_ = elfFile_->GetStartVaddr();
268         textExecVaddrFileOffset_ = elfFile_->GetStartOffset();
269 
270         HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
271               textExecVaddrFileOffset_);
272 
273 #ifndef __arm__
274         ShdrInfo shinfo;
275         if (elfFile_->GetSectionInfo(shinfo, ".eh_frame_hdr")) {
276             auto mmapPtr = elfFile_->GetMmapPtr();
277             CHECK_TRUE(mmapPtr == nullptr, false, 1, "mmapPtr should not be nullptr.");
278             LoadEhFrameHDR(mmapPtr + shinfo.offset, shinfo.size, shinfo.offset);
279         }
280 #endif
281 
282         HLOGD("LoadDebugInfo success!");
283         debugInfoLoadResult_ = true;
284         return true;
285     }
286 
287 private:
288     bool EhFrameHDRValid_ {false};
289     uint64_t ehFrameHDRElfOffset_ {0};
290     uint64_t ehFrameHDRFdeCount_ {0};
291     uint64_t ehFrameHDRFdeTableItemSize_ {0};
292     uint64_t ehFrameHDRFdeTableElfOffset_ {0};
293     std::shared_ptr<DfxElf> elfFile_;
294     std::unordered_map<uint64_t, ElfLoadInfo> info_;
295 
GetSectionInfo(const std::string & name,uint64_t & sectionVaddr,uint64_t & sectionSize,uint64_t & sectionFileOffset) const296     bool GetSectionInfo(const std::string &name, uint64_t &sectionVaddr, uint64_t &sectionSize,
297                         uint64_t &sectionFileOffset) const override
298     {
299         struct ShdrInfo shdrInfo;
300         if (elfFile_->GetSectionInfo(shdrInfo, name)) {
301             sectionVaddr = shdrInfo.addr;
302             sectionSize = shdrInfo.size;
303             sectionFileOffset = shdrInfo.offset;
304             HLOGM("Get Section '%s' %" PRIx64 " - %" PRIx64 "", name.c_str(), sectionVaddr, sectionSize);
305             return true;
306         } else {
307             HLOGW("Section '%s' not found", name.c_str());
308             return false;
309         }
310     }
311 
312 #ifndef __arm__
GetHDRSectionInfo(uint64_t & ehFrameHdrElfOffset,uint64_t & fdeTableElfOffset,uint64_t & fdeTableSize)313     bool GetHDRSectionInfo(uint64_t &ehFrameHdrElfOffset, uint64_t &fdeTableElfOffset,
314                            uint64_t &fdeTableSize) override
315     {
316         CHECK_TRUE(elfFile_ == nullptr, false, 0, "");
317         ShdrInfo shinfo;
318         if (!elfFile_->GetSectionInfo(shinfo, ".eh_frame_hdr")) {
319             return false;
320         }
321 
322         ehFrameHDRElfOffset_ = shinfo.offset;
323         if (EhFrameHDRValid_) {
324             ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
325             fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
326             fdeTableSize = ehFrameHDRFdeCount_;
327             return true;
328         }
329         auto mmapPtr = elfFile_->GetMmapPtr();
330         if (mmapPtr == nullptr) {
331             HLOGE("mmapPtr should not be nullptr.");
332             return false;
333         }
334         if (!LoadEhFrameHDR(mmapPtr + shinfo.offset, elfFile_->GetMmapSize(), shinfo.offset)) {
335             HLOGW("Failed to load eh_frame_hdr");
336             return false;
337         }
338 
339         ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
340         fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
341         fdeTableSize = ehFrameHDRFdeCount_;
342         return true;
343     }
344 #endif
345 
DumpEhFrameHDR()346     void DumpEhFrameHDR()
347     {
348         HLOGD("  ehFrameHDRElfOffset_:          0x%" PRIx64 "", ehFrameHDRElfOffset_);
349         HLOGD("  ehFrameHDRFdeCount_:           0x%" PRIx64 "", ehFrameHDRFdeCount_);
350         HLOGD("  ehFrameHDRFdeTableElfOffset_:  0x%" PRIx64 "", ehFrameHDRFdeTableElfOffset_);
351         HLOGD("  ehFrameHDRFdeTableItemSize_:   0x%" PRIx64 "", ehFrameHDRFdeTableItemSize_);
352     }
353 
LoadEhFrameHDR(const unsigned char * buffer,size_t bufferSize,uint64_t shdrOffset)354     bool LoadEhFrameHDR(const unsigned char *buffer, size_t bufferSize, uint64_t shdrOffset)
355     {
356         const eh_frame_hdr *ehFrameHdr = reinterpret_cast<const eh_frame_hdr *>(buffer);
357         CHECK_TRUE(ehFrameHdr == nullptr, false, 0, "");
358         const uint8_t *dataPtr = ehFrameHdr->encode_data;
359         DwarfEncoding dwEhFramePtr(ehFrameHdr->eh_frame_ptr_enc, dataPtr);
360         DwarfEncoding dwFdeCount(ehFrameHdr->fde_count_enc, dataPtr);
361         DwarfEncoding dwTable(ehFrameHdr->table_enc, dataPtr);
362         DwarfEncoding dwTableValue(ehFrameHdr->table_enc, dataPtr);
363 
364         HLOGD("eh_frame_hdr:");
365         HexDump(ehFrameHdr, BITS_OF_FOUR_BYTE, bufferSize);
366         unsigned char version = ehFrameHdr->version;
367         HLOGD("  version:             %02x:%s", version, (version == 1) ? "valid" : "invalid");
368         HLOGD("  eh_frame_ptr_enc:    %s", dwEhFramePtr.ToString().c_str());
369         HLOGD("  fde_count_enc:       %s", dwFdeCount.ToString().c_str());
370         HLOGD("  table_enc:           %s", dwTable.ToString().c_str());
371         HLOGD("  table_value_enc:     %s", dwTableValue.ToString().c_str());
372         HLOGD("  table_item_size:     %zd", dwTable.GetSize() + dwTableValue.GetSize());
373         HLOGD("  table_offset_in_hdr: %zu", dwTable.GetData() - buffer);
374 
375         CHECK_TRUE(version != 1, false, 1, "eh_frame_hdr version is invalid");
376         EhFrameHDRValid_ = true;
377         ehFrameHDRElfOffset_ = shdrOffset;
378         ehFrameHDRFdeCount_ = dwFdeCount.GetAppliedValue();
379         ehFrameHDRFdeTableElfOffset_ = dwTable.GetData() - buffer + shdrOffset;
380         ehFrameHDRFdeTableItemSize_ = dwTable.GetSize() + dwTableValue.GetSize();
381         DumpEhFrameHDR();
382 
383         if (!dwFdeCount.IsOmit() && dwFdeCount.GetValue() > 0) {
384             return true;
385         } else {
386             HLOGW("fde table not found.\n");
387         }
388         return false;
389     }
390 
UpdateSymbols(std::vector<DfxSymbol> & symbolsTable,const std::string & elfPath)391     void UpdateSymbols(std::vector<DfxSymbol> &symbolsTable, const std::string &elfPath)
392     {
393         symbols_.clear();
394         HLOGD("%zu symbols loadded from symbolsTable.", symbolsTable.size());
395 
396         symbols_.swap(symbolsTable);
397 
398         AdjustSymbols();
399         HLOGD("%zu symbols loadded from elf '%s'.", symbols_.size(), elfPath.c_str());
400         for (auto& symbol: symbols_) {
401             HLOGD("symbol %s", symbol.ToDebugString().c_str());
402         }
403         if (buildId_.empty()) {
404             HLOGD("buildId not found from elf '%s'.", elfPath.c_str());
405             // don't failed. some time the lib have not got the build id
406             // buildId not found from elf '/system/bin/ld-musl-arm.so.1'.
407         }
408     }
409 
AddSymbols(std::vector<DfxSymbol> & symbolsTable,std::shared_ptr<DfxElf> elf,const std::string & filePath)410     void AddSymbols(std::vector<DfxSymbol>& symbolsTable, std::shared_ptr<DfxElf> elf, const std::string& filePath)
411     {
412         // use elfFile_ to get symbolsTable
413         DfxSymbols::ParseSymbols(symbolsTable, elf, filePath);
414         DfxSymbols::AddSymbolsByPlt(symbolsTable, elf, filePath);
415     }
416 
LoadElfSymbols(std::shared_ptr<DfxMap> map,std::string elfPath)417     bool LoadElfSymbols(std::shared_ptr<DfxMap> map, std::string elfPath)
418     {
419 #ifdef HIPERF_DEBUG_TIME
420         const auto startTime = steady_clock::now();
421 #endif
422         if (elfFile_ == nullptr) {
423             if (StringEndsWith(elfPath, ".hap") && map != nullptr) {
424                 if (!map->IsMapExec()) {
425                     HLOGW("map is not exec, no need parse elf.");
426                     return false;
427                 }
428                 elfFile_ = DfxElf::CreateFromHap(elfPath, map->prevMap, map->offset);
429                 map->elf = elfFile_;
430             } else {
431                 elfFile_ = std::make_shared<DfxElf>(elfPath);
432             }
433         }
434         CHECK_TRUE(elfFile_ == nullptr, false, 1, "Failed to create elf file for %s.", elfPath.c_str());
435         HLOGD("loaded elf %s", elfPath.c_str());
436         if (!elfFile_->IsValid()) {
437             HLOGD("parser elf file failed.");
438             return false;
439         }
440 
441         textExecVaddr_ = elfFile_->GetStartVaddr();
442         textExecVaddrFileOffset_ = elfFile_->GetStartOffset();
443         HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
444               textExecVaddrFileOffset_);
445 
446         // we prepare two table here
447         // only one we will push in to symbols_
448         // or both drop if build id is not same
449         std::string buildIdFound = elfFile_->GetBuildId();
450         std::vector<DfxSymbol> symbolsTable;
451         AddSymbols(symbolsTable, elfFile_, filePath_);
452         if (UpdateBuildIdIfMatch(buildIdFound)) {
453             UpdateSymbols(symbolsTable, elfPath);
454         } else {
455             HLOGW("symbols will not update for '%s' because buildId is not match.",
456                   elfPath.c_str());
457             // this mean failed . we don't goon for this.
458             return false;
459         }
460 
461 #ifdef HIPERF_DEBUG_TIME
462         auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
463         if (usedTime.count() != 0) {
464             HLOGV("cost %0.3f ms to load symbols '%s'",
465                   usedTime.count() / static_cast<double>(milliseconds::duration::period::den),
466                   elfPath.c_str());
467         }
468 #endif
469         return true;
470     }
471 
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapPageOffset) const472     uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
473                                uint64_t mapPageOffset) const override
474     {
475         /*
476             00200000-002c5000 r--p 00000000 08:02 46400311
477             002c5000-00490000 r-xp 000c5000 08:02 4640031
478 
479             [14] .text             PROGBITS         00000000002c5000  000c5000
480 
481             if ip is 0x46e6ab
482             1. find the map range is 002c5000-00490000
483             2. ip - map start(002c5000) = map section offset
484             3. map section offset + map page offset(000c5000) = elf file offset
485             4. elf file offset - exec file offset(000c5000)
486                 = ip offset (ip always in exec file offset)
487             5. ip offset + exec begin vaddr(2c5000) = virtual ip in elf
488         */
489         uint64_t vaddr = ip - mapStart + mapPageOffset - textExecVaddrFileOffset_ + textExecVaddr_;
490         HLOGM(" ip :0x%016" PRIx64 " -> elf offset :0x%016" PRIx64 " -> vaddr :0x%016" PRIx64 " ",
491               ip, ip - mapStart + mapPageOffset, vaddr);
492         HLOGM("(minExecAddrFileOffset_ is 0x%" PRIx64 " textExecVaddr_ is 0x%" PRIx64 ")",
493               textExecVaddrFileOffset_, textExecVaddr_);
494         return vaddr;
495     }
496 };
497 
498 class KernelSymbols : public ElfFileSymbols {
499 public:
KernelSymbols(const std::string & symbolFilePath)500     explicit KernelSymbols(const std::string &symbolFilePath)
501         : ElfFileSymbols(symbolFilePath, SYMBOL_KERNEL_FILE)
502     {
503     }
504 
KernelSymbols(const std::string & symbolFilePath,const SymbolsFileType symbolsFileType)505     KernelSymbols(const std::string &symbolFilePath,
506                   const SymbolsFileType symbolsFileType)
507         : ElfFileSymbols(symbolFilePath, symbolsFileType)
508     {
509     }
510 
511     static constexpr const int KSYM_MIN_TOKENS = 3;
512     static constexpr const int KSYM_DEFAULT_LINE = 35000;
513     static constexpr const int KSYM_DEFAULT_SIZE = 1024 * 1024 * 1; // 1MB
514 
ParseKallsymsLine(const std::string & kallsymsPath)515     bool ParseKallsymsLine(const std::string &kallsymsPath)
516     {
517 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
518         const auto startTime = steady_clock::now();
519         std::chrono::microseconds parseLineTime = std::chrono::microseconds::zero();
520         std::chrono::microseconds sscanfTime = std::chrono::microseconds::zero();
521         std::chrono::microseconds newTime = std::chrono::microseconds::zero();
522         std::chrono::microseconds readFileTime = std::chrono::microseconds::zero();
523 #endif
524         size_t lines = 0;
525 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
526         const auto eachFileStartTime = steady_clock::now();
527 #endif
528         std::string kallsym;
529         CHECK_TRUE(!ReadFileToString(kallsymsPath, kallsym, KSYM_DEFAULT_SIZE) || kallsym.empty(), false, 1,
530                    "%s load failed.", kallsymsPath.c_str());
531 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
532         // any way we finish the line scan
533         readFileTime += duration_cast<milliseconds>(steady_clock::now() - eachFileStartTime);
534 #endif
535         // reduce the mem alloc
536         symbols_.reserve(KSYM_DEFAULT_LINE);
537 
538         char *lineBegin = kallsym.data();
539         char *dataEnd = lineBegin + kallsym.size();
540         while (lineBegin < dataEnd) {
541             char *lineEnd = strchr(lineBegin, '\n');
542             if (lineEnd != nullptr) {
543                 *lineEnd = '\0';
544             } else {
545                 lineEnd = dataEnd;
546             }
547             size_t lineSize = (lineEnd != nullptr) ? (lineEnd - lineBegin) : (dataEnd - lineBegin);
548 
549 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
550             const auto eachLineStartTime = steady_clock::now();
551 #endif
552             lines++;
553             uint64_t addr = 0;
554             char type = '\0';
555 
556             char nameRaw[lineSize];
557             char moduleRaw[lineSize];
558             int ret = sscanf_s(lineBegin, "%" PRIx64 " %c %s%s", &addr, &type, sizeof(type),
559                                nameRaw, sizeof(nameRaw), moduleRaw, sizeof(moduleRaw));
560 
561 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
562             // any way we finish the line scan
563             sscanfTime += duration_cast<milliseconds>(steady_clock::now() - eachLineStartTime);
564 #endif
565             if (ret >= KSYM_MIN_TOKENS) {
566                 if (ret == KSYM_MIN_TOKENS) {
567                     moduleRaw[0] = '\0';
568                 }
569                 HLOGM(" 0x%016" PRIx64 " %c '%s' '%s'", addr, type, nameRaw, moduleRaw);
570             } else {
571                 HLOGW("unknown line %d: '%s'", ret, lineBegin);
572                 lineBegin = lineEnd + 1;
573                 continue;
574             }
575             lineBegin = lineEnd + 1;
576             std::string name = nameRaw;
577             std::string module = moduleRaw;
578 
579             /*
580             T
581             The symbol is in the text (code) section.
582 
583             W
584             The symbol is a weak symbol that has not been specifically
585             tagged as a weak object symbol. When a weak defined symbol is
586             linked with a normal defined symbol, the normal defined symbol
587             is used with no error. When a weak undefined symbol is linked
588             and the symbol is not defined, the value of the weak symbol
589             becomes zero with no error.
590             */
591             if (addr != 0 && strchr("TtWw", type)) {
592 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
593                 const auto eachNewSymbolTime = steady_clock::now();
594 #endif
595                 // we only need text symbols
596                 symbols_.emplace_back(addr, name, module.empty() ? filePath_ : module);
597 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
598                 newTime += duration_cast<milliseconds>(steady_clock::now() - eachNewSymbolTime);
599 #endif
600             }
601 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
602             parseLineTime += duration_cast<milliseconds>(steady_clock::now() - eachLineStartTime);
603 #endif
604         }
605 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
606         std::chrono::microseconds usedTime =
607             duration_cast<milliseconds>(steady_clock::now() - startTime);
608         printf("parse kernel symbols use : %0.3f ms\n", usedTime.count() / MS_DURATION);
609         printf("parse line use : %0.3f ms\n", parseLineTime.count() / MS_DURATION);
610         printf("sscanf line use : %0.3f ms\n", sscanfTime.count() / MS_DURATION);
611         printf("new symbols use : %0.3f ms\n", newTime.count() / MS_DURATION);
612         printf("read file use : %0.3f ms\n", readFileTime.count() / MS_DURATION);
613 #endif
614         HLOGD("load %s: %zu line processed(%zu symbols)", kallsymsPath.c_str(), lines, symbols_.size());
615         return true;
616     }
617 
618     const std::string KPTR_RESTRICT = "/proc/sys/kernel/kptr_restrict";
619 
LoadKernelSyms()620     bool LoadKernelSyms()
621     {
622         if (!IsRoot()) {
623             return false;
624         }
625         HLOGD("try read /proc/kallsyms");
626         if (access("/proc/kallsyms", R_OK) != 0) {
627             printf("No vmlinux path is given, and kallsyms cannot be opened\n");
628             return false;
629         }
630         bool hasChangeKptr = false;
631         std::string oldKptrRestrict = ReadFileToString(KPTR_RESTRICT);
632         if (oldKptrRestrict.front() != '0') {
633             printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n");
634             hasChangeKptr = WriteStringToFile(KPTR_RESTRICT, "0");
635             if (!hasChangeKptr) {
636                 printf("/proc/sys/kernel/kptr_restrict write failed and we can't not change it.\n");
637             }
638         }
639 
640         // getline end
641         CHECK_TRUE(!ParseKallsymsLine("/proc/kallsyms"), false, 0, "");
642 
643         if (hasChangeKptr) {
644             if (!WriteStringToFile(KPTR_RESTRICT, oldKptrRestrict)) {
645                 printf("recover /proc/sys/kernel/kptr_restrict fail.\n");
646             }
647         }
648 
649         if (symbols_.empty()) {
650             printf("The symbol table addresses in /proc/kallsyms are all 0.\n"
651                    "Please check the value of /proc/sys/kernel/kptr_restrict, it "
652                    "should be 0.\n"
653                    "Or provide a separate vmlinux path.\n");
654 
655             if (buildId_.size() != 0) {
656                 // but we got the buildid , so we make a dummpy symbols
657                 HLOGD("kallsyms not found. but we have the buildid");
658                 return true;
659             } else {
660                 // we got nothing
661                 return false;
662             }
663         } else {
664             AdjustSymbols();
665             HLOGV("%zu symbols_ loadded from kallsyms.\n", symbols_.size());
666             return true;
667         }
668     }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)669     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
670     {
671         symbolsLoaded_ = true;
672         HLOGV("KernelSymbols try read '%s' search paths size %zu, inDeviceRecord %d",
673               symbolFilePath.c_str(), symbolsFileSearchPaths_.size(), onRecording_);
674 
675         if (onRecording_) {
676             const auto startTime = std::chrono::steady_clock::now();
677             if (!LoadKernelSyms()) {
678                 if (IsRoot()) {
679                     printf("parse kalsyms failed.\n");
680                 }
681                 return false;
682             } else {
683                 const auto thisTime = std::chrono::steady_clock::now();
684                 const auto usedTimeMsTick =
685                     std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime);
686                 HLOGV("Load kernel symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count());
687                 // load complete
688                 return true;
689             }
690 
691             // try read
692             HLOGD("try read /sys/kernel/notes");
693             std::string notes = ReadFileToString("/sys/kernel/notes");
694             if (notes.empty()) {
695                 printf("notes cannot be opened, unable get buildid\n");
696                 return false;
697             } else {
698                 HLOGD("kernel notes size: %zu", notes.size());
699                 buildId_ = DfxElf::GetBuildId((uint64_t)notes.data(), (uint64_t)notes.size());
700             }
701         } // no search path
702 
703         // try vmlinux
704         return ElfFileSymbols::LoadSymbols(nullptr, KERNEL_ELF_NAME);
705     }
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t) const706     uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
707     {
708         // ip is vaddr in /proc/kallsyms
709         return ip;
710     }
~KernelSymbols()711     ~KernelSymbols() override {}
712 };
713 
714 class KernelThreadSymbols : public KernelSymbols {
715 public:
KernelThreadSymbols(const std::string & symbolFilePath)716     explicit KernelThreadSymbols(const std::string &symbolFilePath)
717         : KernelSymbols(symbolFilePath, SYMBOL_KERNEL_THREAD_FILE)
718     {
719     }
720 
LoadKernelSyms()721     bool LoadKernelSyms()
722     {
723         if (!IsRoot()) {
724             return false;
725         }
726         // find real proc path by filePath_
727         std::string procPath;
728         if (filePath_ == SYSMGR_FILE_NAME) {
729             procPath = StringPrintf("/proc/%u/uallsyms", SYSMGR_PID);
730         } else if (filePath_ == DEVHOST_FILE_NAME) {
731             procPath = "/proc/devhost/root/kallsyms";
732         }
733         HLOGD("try read kernel thread symbol file %s in %s", filePath_.c_str(), procPath.c_str());
734         CHECK_TRUE(access(procPath.c_str(), R_OK) != 0, false, LOG_TYPE_PRINTF,
735                    "kernel thread symbol file %s cannot be opened\n", filePath_.c_str());
736         bool hasChangeKptr = false;
737         std::string oldKptrRestrict = ReadFileToString(KPTR_RESTRICT);
738         if (oldKptrRestrict.front() != '0') {
739             printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n");
740             hasChangeKptr = WriteStringToFile(KPTR_RESTRICT, "0");
741             if (!hasChangeKptr) {
742                 printf("/proc/sys/kernel/kptr_restrict write failed and we can't not change it.\n");
743             }
744         }
745 
746         // getline end
747         CHECK_TRUE(!ParseKallsymsLine(procPath), false, 0, "");
748 
749         if (hasChangeKptr) {
750             if (!WriteStringToFile(KPTR_RESTRICT, oldKptrRestrict)) {
751                 printf("recover /proc/sys/kernel/kptr_restrict fail.\n");
752             }
753         }
754 
755         if (symbols_.empty()) {
756             printf("The symbol table addresses in %s are all 0.\n"
757                    "Please check the value of /proc/sys/kernel/kptr_restrict, it "
758                    "should be 0.\n", filePath_.c_str());
759             return false;
760         } else {
761             AdjustSymbols();
762             HLOGV("%zu symbols_ loadded from %s.\n", symbols_.size(), procPath.c_str());
763             return true;
764         }
765     }
766 
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)767     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
768     {
769         symbolsLoaded_ = true;
770         HLOGV("KernelThreadSymbols try read '%s', inDeviceRecord %d",
771               filePath_.c_str(), onRecording_);
772 
773         if (onRecording_) {
774             const auto startTime = std::chrono::steady_clock::now();
775             if (!LoadKernelSyms()) {
776                 if (IsRoot()) {
777                     printf("parse %s failed.\n", filePath_.c_str());
778                 }
779             } else {
780                 const auto thisTime = std::chrono::steady_clock::now();
781                 const auto usedTimeMsTick =
782                     std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime);
783                 HLOGV("Load kernel thread symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count());
784                 // load complete
785                 return true;
786             }
787         } // no search path
788 
789         // try elf
790         return ElfFileSymbols::LoadSymbols(nullptr, filePath_);
791     }
~KernelThreadSymbols()792     ~KernelThreadSymbols() override {}
793 };
794 
795 class KernelModuleSymbols : public ElfFileSymbols {
796 public:
KernelModuleSymbols(const std::string & symbolFilePath)797     explicit KernelModuleSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
798     {
799         HLOGV("create %s", symbolFilePath.c_str());
800         symbolFileType_ = SYMBOL_KERNEL_MODULE_FILE;
801         module_ = symbolFilePath;
802     }
~KernelModuleSymbols()803     ~KernelModuleSymbols() override {};
804 
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)805     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
806     {
807         symbolsLoaded_ = true;
808         if (module_ == filePath_ and onRecording_) {
809             // file name still not convert to ko file path
810             // this is in record mode
811             HLOGV("find ko name %s", module_.c_str());
812             for (const std::string &path : kernelModulePaths) {
813                 if (access(path.c_str(), R_OK) == 0) {
814                     std::string koPath = path + module_ + KERNEL_MODULES_EXT_NAME;
815                     HLOGV("found ko in %s", koPath.c_str());
816                     if (access(koPath.c_str(), R_OK) == 0) {
817                         // create symbol
818                         filePath_ = koPath;
819                         break; // find next ko
820                     }
821                 }
822             }
823             LoadBuildId();
824         } else {
825             HLOGV("we have file path, load with %s", filePath_.c_str());
826             return ElfFileSymbols::LoadSymbols(nullptr, filePath_);
827         }
828         return false;
829     }
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t) const830     uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
831     {
832         return ip - mapStart;
833     }
834 
835 private:
LoadBuildId()836     bool LoadBuildId()
837     {
838         std::string sysFile = "/sys/module/" + module_ + "/notes/.note.gnu.build-id";
839         std::string buildIdRaw = ReadFileToString(sysFile);
840         if (!buildIdRaw.empty()) {
841             buildId_ = DfxElf::GetBuildId((uint64_t)buildIdRaw.data(), (uint64_t)buildIdRaw.size());
842             HLOGD("kerne module %s(%s) build id %s", module_.c_str(), filePath_.c_str(),
843                   buildId_.c_str());
844             return buildId_.empty() ? false : true;
845         }
846         return false;
847     }
848 
849     const std::vector<std::string> kernelModulePaths = {"/vendor/modules/"};
850     std::string module_ = "";
851 };
852 
853 class JavaFileSymbols : public ElfFileSymbols {
854 public:
JavaFileSymbols(const std::string & symbolFilePath)855     explicit JavaFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
856     {
857         symbolFileType_ = SYMBOL_KERNEL_FILE;
858     }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)859     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
860     {
861         symbolsLoaded_ = true;
862         return false;
863     }
~JavaFileSymbols()864     ~JavaFileSymbols() override {}
865 
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapPageOffset) const866     uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
867                                        uint64_t mapPageOffset) const override
868     {
869         // this is different with elf
870         // elf use  ip - mapStart + mapPageOffset - minExecAddrFileOffset_ + textExecVaddr_
871         return ip - mapStart + mapPageOffset;
872     }
873 };
874 
875 class JSFileSymbols : public ElfFileSymbols {
876 public:
JSFileSymbols(const std::string & symbolFilePath)877     explicit JSFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
878     {
879         symbolFileType_ = SYMBOL_KERNEL_FILE;
880     }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)881     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
882     {
883         symbolsLoaded_ = true;
884         return false;
885     }
~JSFileSymbols()886     ~JSFileSymbols() override {}
887 };
888 
889 class HapFileSymbols : public ElfFileSymbols {
890 private:
891 #if defined(is_ohos) && is_ohos
892     std::unique_ptr<DfxExtractor> dfxExtractor_;
893     bool hapExtracted_ = false;
894 #endif
895     std::unique_ptr<uint8_t[]> abcDataPtr_ = nullptr;
896     [[maybe_unused]] uintptr_t loadOffSet_ = 0;
897     [[maybe_unused]] size_t abcDataSize_ = 0;
898     [[maybe_unused]] uintptr_t arkExtractorptr_ = 0;
899     bool isHapAbc_ = false;
900     pid_t pid_ = 0;
901 public:
HapFileSymbols(const std::string & symbolFilePath,pid_t pid)902     explicit HapFileSymbols(const std::string &symbolFilePath, pid_t pid)
903         : ElfFileSymbols(symbolFilePath, SYMBOL_HAP_FILE)
904     {
905         pid_ = pid;
906     }
907 
IsHapAbc()908     bool IsHapAbc()
909     {
910 #if defined(is_ohos) && is_ohos
911         if (hapExtracted_) {
912             return isHapAbc_;
913         }
914         hapExtracted_ = true;
915         HLOGD("the symbol file is %s, pid is %d.", filePath_.c_str(), pid_);
916         if (IsRoot()) {
917             if (IsApplicationEncryped(pid_)) {
918                 HLOGD("no need to parse js symbols");
919                 return false;
920             }
921         }
922 
923         CHECK_TRUE(StringEndsWith(filePath_, ".hap") && map_->IsMapExec(), false, 1,
924                    "map is exec not abc file , the symbol file is:%s", map_->name.c_str());
925 
926         if (StringEndsWith(filePath_, ".hap") || StringEndsWith(filePath_, ".hsp")) {
927             dfxExtractor_ = std::make_unique<DfxExtractor>(filePath_);
928             CHECK_TRUE(!dfxExtractor_->GetHapAbcInfo(loadOffSet_, abcDataPtr_, abcDataSize_), false, 1,
929                        "failed to call GetHapAbcInfo, the symbol file is:%s", filePath_.c_str());
930             HLOGD("loadOffSet %u", (uint32_t)loadOffSet_);
931             if (abcDataPtr_ != nullptr) {
932                 isHapAbc_ = true;
933                 HLOGD("symbol file : %s, isAbc: %d", filePath_.c_str(), isHapAbc_);
934             }
935         } else {
936             loadOffSet_ = map_->offset;
937             abcDataSize_ = map_->end - map_->begin;
938             abcDataPtr_ = std::make_unique<uint8_t[]>(abcDataSize_);
939             auto size = DfxMemory::ReadProcMemByPid(pid_, map_->begin, abcDataPtr_.get(), map_->end - map_->begin);
940             if (size != abcDataSize_) {
941                 HLOGD("return size is small abcDataPtr : %s, isAbc: %d", abcDataPtr_.get(), isHapAbc_);
942                 return false;
943             }
944             isHapAbc_ = true;
945             HLOGD("symbol file name %s loadOffSet %u abcDataSize_ %u",
946                   filePath_.c_str(), (uint32_t)loadOffSet_, (uint32_t)abcDataSize_);
947         }
948         auto ret = DfxArk::ArkCreateJsSymbolExtractor(&arkExtractorptr_);
949         if (ret < 0) {
950             arkExtractorptr_ = 0;
951             HLOGE("failed to call ArkCreateJsSymbolExtractor, the symbol file is:%s", filePath_.c_str());
952         }
953 #endif
954         return isHapAbc_;
955     }
956 
IsAbc()957     bool IsAbc() override
958     {
959         return isHapAbc_ == true;
960     }
961 
SetBoolValue(bool value)962     void SetBoolValue(bool value) override
963     {
964         isHapAbc_ = value;
965     }
966 
LoadDebugInfo(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)967     bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
968     {
969         HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str());
970         if (debugInfoLoaded_) {
971             return true;
972         }
973         CHECK_TRUE(!onRecording_, true, 0, "");
974 
975         if (!IsHapAbc() && map_->IsMapExec()) {
976             ElfFileSymbols::LoadDebugInfo(map, "");
977         }
978         debugInfoLoaded_ = true;
979         debugInfoLoadResult_ = true;
980         return true;
981     }
982 
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)983     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
984     {
985         HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str());
986         CHECK_TRUE(symbolsLoaded_ || !onRecording_, true, 0, "");
987         symbolsLoaded_ = true;
988         if (!IsHapAbc() && map_->IsMapExec()) {
989             ElfFileSymbols::LoadSymbols(map, "");
990         }
991         return true;
992     }
993 
GetSymbolWithPcAndMap(uint64_t ip,std::shared_ptr<DfxMap> map)994     DfxSymbol GetSymbolWithPcAndMap(uint64_t ip, std::shared_ptr<DfxMap> map) override
995     {
996         // get cache
997         auto iter = symbolsMap_.find(ip);
998         if (iter != symbolsMap_.end()) {
999             return iter->second;
1000         }
1001         if (map == nullptr) {
1002             return DfxSymbol(ip, "");
1003         }
1004         HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str());
1005 
1006 #if defined(is_ohos) && is_ohos
1007         if (IsAbc() && needParseJsFunc_) {
1008             JsFunction jsFunc;
1009             std::string module = map->name;
1010             HLOGD("map->name module:%s", module.c_str());
1011             auto ret = DfxArk::ParseArkFrameInfo(static_cast<uintptr_t>(ip), static_cast<uintptr_t>(map->begin),
1012                                                  loadOffSet_, abcDataPtr_.get(), abcDataSize_,
1013                                                  arkExtractorptr_, &jsFunc);
1014             if (ret == -1) {
1015                 HLOGD("failed to call ParseArkFrameInfo, the symbol file is : %s", map->name.c_str());
1016                 return DfxSymbol(ip, "");
1017             }
1018             this->symbolsMap_.insert(std::make_pair(ip,
1019                                                     DfxSymbol(ip,
1020                                                     jsFunc.codeBegin,
1021                                                     jsFunc.functionName,
1022                                                     jsFunc.ToString(),
1023                                                     map->name)));
1024 
1025             DfxSymbol &foundSymbol = symbolsMap_[ip];
1026             if (!foundSymbol.matched_) {
1027                 foundSymbol.matched_ = true;
1028                 foundSymbol.symbolFileIndex_ = id_;
1029                 matchedSymbols_.push_back(&(symbolsMap_[ip]));
1030             }
1031 
1032             HLOGD("ip : 0x%" PRIx64 " the symbol file is : %s, function is %s demangle_ : %s", ip,
1033                   symbolsMap_[ip].module_.data(), jsFunc.functionName, matchedSymbols_.back()->demangle_.data());
1034             return symbolsMap_[ip];
1035         }
1036 #endif
1037         DfxSymbol symbol(ip, "");
1038         return symbol;
1039     }
1040 };
1041 
1042 class UnknowFileSymbols : public SymbolsFile {
1043 public:
UnknowFileSymbols(const std::string & symbolFilePath)1044     explicit UnknowFileSymbols(const std::string &symbolFilePath)
1045         : SymbolsFile(SYMBOL_UNKNOW_FILE, symbolFilePath)
1046     {
1047     }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)1048     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
1049     {
1050         symbolsLoaded_ = true;
1051         return false;
1052     }
~UnknowFileSymbols()1053     ~UnknowFileSymbols() override {}
1054 };
1055 
~SymbolsFile()1056 SymbolsFile::~SymbolsFile() {}
1057 
CreateSymbolsFile(SymbolsFileType symbolType,const std::string symbolFilePath,pid_t pid)1058 std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(SymbolsFileType symbolType,
1059                                                             const std::string symbolFilePath, pid_t pid)
1060 {
1061     switch (symbolType) {
1062         case SYMBOL_KERNEL_FILE:
1063             return std::make_unique<KernelSymbols>(symbolFilePath.empty() ? KERNEL_MMAP_NAME
1064                                                                           : symbolFilePath);
1065         case SYMBOL_KERNEL_MODULE_FILE:
1066             return std::make_unique<KernelModuleSymbols>(symbolFilePath);
1067         case SYMBOL_KERNEL_THREAD_FILE:
1068             return std::make_unique<KernelThreadSymbols>(symbolFilePath);
1069         case SYMBOL_ELF_FILE:
1070             return std::make_unique<ElfFileSymbols>(symbolFilePath);
1071         case SYMBOL_JAVA_FILE:
1072             return std::make_unique<JavaFileSymbols>(symbolFilePath);
1073         case SYMBOL_JS_FILE:
1074             return std::make_unique<JSFileSymbols>(symbolFilePath);
1075         case SYMBOL_HAP_FILE:
1076             return std::make_unique<HapFileSymbols>(symbolFilePath, pid);
1077         default:
1078             return std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, symbolFilePath);
1079     }
1080 }
1081 
CreateSymbolsFile(const std::string & symbolFilePath,pid_t pid)1082 std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(const std::string &symbolFilePath, pid_t pid)
1083 {
1084     // we need check file name here
1085     if (symbolFilePath == KERNEL_MMAP_NAME) {
1086         return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_FILE, symbolFilePath);
1087     } else if (symbolFilePath == SYSMGR_FILE_NAME ||
1088                symbolFilePath == DEVHOST_LINUX_FILE_NAME ||
1089                StringStartsWith(symbolFilePath, DEVHOST_LINUX_PREFIX)) {
1090         return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, symbolFilePath);
1091     } else if (StringEndsWith(symbolFilePath, KERNEL_MODULES_EXT_NAME)) {
1092         return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, symbolFilePath);
1093     } else if (IsArkJsFile(symbolFilePath)) {
1094         return SymbolsFile::CreateSymbolsFile(SYMBOL_HAP_FILE, symbolFilePath, pid);
1095     } else {
1096         // default is elf, this may be problematic in the future.
1097         return SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, symbolFilePath);
1098     }
1099 }
1100 
AdjustSymbols()1101 void SymbolsFile::AdjustSymbols()
1102 {
1103     if (symbols_.size() <= 0) {
1104         return;
1105     }
1106 
1107     // order
1108     sort(symbols_.begin(), symbols_.end(), [](const DfxSymbol& a, const DfxSymbol& b) {
1109         return a.funcVaddr_ < b.funcVaddr_;
1110     });
1111     HLOGV("sort completed");
1112 
1113     size_t fullSize = symbols_.size();
1114     size_t erased = 0;
1115 
1116     // Check for duplicate vaddr
1117     auto last = std::unique(symbols_.begin(), symbols_.end(), [](const DfxSymbol &a, const DfxSymbol &b) {
1118         return (a.funcVaddr_ == b.funcVaddr_);
1119     });
1120     symbols_.erase(last, symbols_.end());
1121     erased = fullSize - symbols_.size();
1122     HLOGV("uniqued completed");
1123     auto it = symbols_.begin();
1124     while (it != symbols_.end()) {
1125         it->index_ = it - symbols_.begin();
1126         it++;
1127     }
1128     HLOGV("indexed completed");
1129 
1130     HLOG_ASSERT(symbols_.size() != 0);
1131 
1132     if (textExecVaddrRange_ == maxVaddr) {
1133         textExecVaddrRange_ = symbols_.back().funcVaddr_ - symbols_.front().funcVaddr_;
1134     }
1135 
1136     HLOGDDD("%zu symbols after adjust (%zu erased) 0x%016" PRIx64 " - 0x%016" PRIx64
1137             " @0x%016" PRIx64 " ",
1138             symbols_.size(), erased, symbols_.front().funcVaddr_, symbols_.back().funcVaddr_,
1139             textExecVaddrFileOffset_);
1140 }
1141 
SortMatchedSymbols()1142 void SymbolsFile::SortMatchedSymbols()
1143 {
1144     if (matchedSymbols_.size() <= 1u) {
1145         return;
1146     }
1147     sort(matchedSymbols_.begin(), matchedSymbols_.end(), [](const DfxSymbol* a, const DfxSymbol* b) {
1148         if (a == nullptr || b == nullptr) {
1149             return true;
1150         }
1151         return a->funcVaddr_ < b->funcVaddr_;
1152     });
1153 }
1154 
GetSymbols()1155 const std::vector<DfxSymbol> &SymbolsFile::GetSymbols()
1156 {
1157     return symbols_;
1158 }
1159 
GetMatchedSymbols()1160 const std::vector<DfxSymbol *> &SymbolsFile::GetMatchedSymbols()
1161 {
1162     return matchedSymbols_;
1163 }
1164 
GetSymbolWithVaddr(uint64_t vaddrInFile)1165 const DfxSymbol SymbolsFile::GetSymbolWithVaddr(uint64_t vaddrInFile)
1166 {
1167 #ifdef HIPERF_DEBUG_TIME
1168     const auto startTime = steady_clock::now();
1169 #endif
1170     DfxSymbol symbol;
1171     // it should be already order from small to large
1172     auto found =
1173         std::upper_bound(symbols_.begin(), symbols_.end(), vaddrInFile, DfxSymbol::ValueLessThen);
1174     /*
1175     if data is { 1, 2, 4, 5, 5, 6 };
1176     upper_bound for each val :
1177         0 < 1 at index 0
1178         1 < 2 at index 1
1179         2 < 4 at index 2
1180         3 < 4 at index 2
1181         4 < 5 at index 3
1182         5 < 6 at index 5
1183         6 < not found
1184     if key symbol vaddr is { 1, 2, 4, 5, 5, 6 };
1185      check ip vaddr for each val :
1186        ip   sym
1187         0   not found
1188         1   1
1189         1   1
1190         2   2
1191         3   3
1192         4   4
1193         5   5
1194         6   6
1195         7   7
1196     */
1197     if (found != symbols_.begin()) {
1198         found = std::prev(found);
1199         if (found != symbols_.end()) {
1200             if (found->Contain(vaddrInFile)) {
1201                 found->offsetToVaddr_ = vaddrInFile - found->funcVaddr_;
1202                 if (!found->matched_) {
1203                     found->matched_ = true;
1204                     matchedSymbols_.push_back(&(*found));
1205                 }
1206                 symbol = *found; // copy
1207                 HLOGV("found '%s' for vaddr 0x%016" PRIx64 "", symbol.ToString().c_str(), vaddrInFile);
1208             }
1209         }
1210     }
1211 
1212     if (!symbol.IsValid()) {
1213         HLOGV("NOT found vaddr 0x%" PRIx64 " in symbole file %s(%zu)", vaddrInFile,
1214               filePath_.c_str(), symbols_.size());
1215     }
1216     symbol.fileVaddr_ = vaddrInFile;
1217     symbol.symbolFileIndex_ = id_;
1218 
1219 #ifdef HIPERF_DEBUG_TIME
1220     auto usedTime = duration_cast<milliseconds>(steady_clock::now() - startTime);
1221     if (usedTime > 1ms) {
1222         HLOGW("cost %" PRId64 "ms to search ", usedTime.count());
1223     }
1224 #endif
1225     return symbol;
1226 }
1227 
CheckPathReadable(const std::string & path) const1228 bool SymbolsFile::CheckPathReadable(const std::string &path) const
1229 {
1230     if (access(path.c_str(), R_OK) == 0) {
1231         return true;
1232     }
1233     // access does not have the access permission in some scenarios
1234     struct stat st;
1235     if (stat(path.c_str(), &st) == 0) {
1236         return true;
1237     } else {
1238 #if defined(is_ohos) && is_ohos
1239         char errInfo[ERRINFOLEN] = { 0 };
1240         strerror_r(errno, errInfo, ERRINFOLEN);
1241         HLOGM("'%s' is unable read,errno: %d, errmsg: %s", path.c_str(), errno, errInfo);
1242         HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s is unable read errno:%" HILOG_PUBLIC
1243                       "d , errInfo: %" HILOG_PUBLIC "s\n",
1244                       path.c_str(), errno, errInfo);
1245 #endif
1246         return false;
1247     }
1248 }
1249 
setSymbolsFilePath(const std::vector<std::string> & symbolsSearchPaths)1250 bool SymbolsFile::setSymbolsFilePath(const std::vector<std::string> &symbolsSearchPaths)
1251 {
1252     symbolsFileSearchPaths_.clear();
1253     for (auto &symbolsSearchPath : symbolsSearchPaths) {
1254         if (CheckPathReadable(symbolsSearchPath)) {
1255             symbolsFileSearchPaths_.emplace_back(symbolsSearchPath);
1256             HLOGV("'%s' is add to symbolsSearchPath", symbolsSearchPath.c_str());
1257         }
1258     }
1259     return (symbolsFileSearchPaths_.size() > 0);
1260 }
1261 
LoadSymbolsFromSaved(const SymbolFileStruct & symbolFileStruct)1262 std::unique_ptr<SymbolsFile> SymbolsFile::LoadSymbolsFromSaved(
1263     const SymbolFileStruct &symbolFileStruct)
1264 {
1265     bool isHapSymbolFile = static_cast<SymbolsFileType>(symbolFileStruct.symbolType_) == SYMBOL_HAP_FILE;
1266     HLOGD("isHapSymbolFile : %d", isHapSymbolFile);
1267     auto symbolsFile = CreateSymbolsFile(symbolFileStruct.filePath_);
1268 
1269     // default create elf file. but hap file need special operation.
1270     symbolsFile->filePath_ = symbolFileStruct.filePath_;
1271     symbolsFile->symbolFileType_ = static_cast<SymbolsFileType>(symbolFileStruct.symbolType_);
1272     symbolsFile->textExecVaddr_ = symbolFileStruct.textExecVaddr_;
1273     symbolsFile->textExecVaddrFileOffset_ = symbolFileStruct.textExecVaddrFileOffset_;
1274     symbolsFile->buildId_ = symbolFileStruct.buildId_;
1275     for (auto &symbolStruct : symbolFileStruct.symbolStructs_) {
1276         symbolsFile->symbols_.emplace_back(symbolStruct.vaddr_, symbolStruct.len_,
1277                                            symbolStruct.symbolName_, symbolFileStruct.filePath_);
1278     }
1279     symbolsFile->AdjustSymbols(); // reorder
1280     if (isHapSymbolFile) {
1281         for (const auto& symbol : symbolsFile->symbols_) {
1282             symbolsFile->symbolsMap_.emplace(symbol.funcVaddr_, symbol);
1283         }
1284         symbolsFile->SetBoolValue(true);
1285     }
1286     symbolsFile->debugInfoLoadResult_ = true;
1287     symbolsFile->symbolsLoaded_ = true; // all ready LoadFrom perf.data
1288     HLOGV("load %zu symbol from SymbolFileStruct for file '%s'", symbolsFile->symbols_.size(),
1289           symbolsFile->filePath_.c_str());
1290     return symbolsFile;
1291 }
1292 
SetBoolValue(bool value)1293 void SymbolsFile::SetBoolValue(bool value)
1294 {
1295 }
1296 
ExportSymbolToFileFormat(SymbolFileStruct & symbolFileStruct)1297 void SymbolsFile::ExportSymbolToFileFormat(SymbolFileStruct &symbolFileStruct)
1298 {
1299     symbolFileStruct.filePath_ = filePath_;
1300     symbolFileStruct.symbolType_ = symbolFileType_;
1301     symbolFileStruct.textExecVaddr_ = textExecVaddr_;
1302     symbolFileStruct.textExecVaddrFileOffset_ = textExecVaddrFileOffset_;
1303     symbolFileStruct.buildId_ = buildId_;
1304 
1305     SortMatchedSymbols();
1306     auto symbols = GetMatchedSymbols();
1307     symbolFileStruct.symbolStructs_.reserve(symbols.size());
1308     for (const auto symbol : symbols) {
1309         auto &symbolStruct = symbolFileStruct.symbolStructs_.emplace_back();
1310         symbolStruct.vaddr_ = symbol->funcVaddr_;
1311         symbolStruct.len_ = symbol->size_;
1312         symbolStruct.symbolName_ = symbol->GetName();
1313     }
1314 
1315     HLOGV("export %zu symbol to SymbolFileStruct from %s", symbolFileStruct.symbolStructs_.size(),
1316           filePath_.c_str());
1317 }
1318 
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapOffset) const1319 uint64_t SymbolsFile::GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t mapOffset) const
1320 {
1321     // no convert
1322     return ip;
1323 }
1324 
AddSymbol(DfxSymbol symbol)1325 void SymbolsFile::AddSymbol(DfxSymbol symbol)
1326 {
1327     symbolsLoaded_ = true;
1328     symbols_.emplace_back(symbol);
1329 }
1330 } // namespace HiPerf
1331 } // namespace Developtools
1332 } // namespace OHOS
1333