• 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                 CHECK_TRUE(map == nullptr, false, 1, "map should not be nullptr.");
234                 elfFile_ = DfxElf::CreateFromHap(elfPath, map->prevMap, map->offset);
235                 HLOGD("try create elf from hap");
236             } else {
237                 elfFile_ = std::make_shared<DfxElf>(elfPath);
238             }
239         }
240 
241         CHECK_TRUE(elfFile_ == nullptr, false, 1, "Failed to create elf file for %s.", elfPath.c_str());
242 
243         CHECK_TRUE(!elfFile_->IsValid(), false, 1, "parser elf file failed.");
244 
245         HLOGD("loaded elf %s", elfPath.c_str());
246         // update path for so in hap
247         if (StringEndsWith(elfPath, ".hap")) {
248             filePath_ = elfPath + "!" + elfFile_->GetElfName();
249             HLOGD("update path for so in hap %s.", filePath_.c_str());
250             if (map == nullptr) {
251                 HLOGW("map should not be nullptr.");
252                 return false;
253             }
254             map->name = filePath_;
255             map->elf = elfFile_;
256             map->prevMap->name = filePath_;
257             map->prevMap->elf = elfFile_;
258         }
259 
260         textExecVaddr_ = elfFile_->GetStartVaddr();
261         textExecVaddrFileOffset_ = elfFile_->GetStartOffset();
262 
263         HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
264               textExecVaddrFileOffset_);
265 
266 #ifndef __arm__
267         ShdrInfo shinfo;
268         if (elfFile_->GetSectionInfo(shinfo, ".eh_frame_hdr")) {
269             auto mmapPtr = elfFile_->GetMmapPtr();
270             CHECK_TRUE(mmapPtr == nullptr, false, 1, "mmapPtr should not be nullptr.");
271             LoadEhFrameHDR(mmapPtr + shinfo.offset, shinfo.size, shinfo.offset);
272         }
273 #endif
274 
275         HLOGD("LoadDebugInfo success!");
276         debugInfoLoadResult_ = true;
277         return true;
278     }
279 
280 private:
281     bool EhFrameHDRValid_ {false};
282     uint64_t ehFrameHDRElfOffset_ {0};
283     uint64_t ehFrameHDRFdeCount_ {0};
284     uint64_t ehFrameHDRFdeTableItemSize_ {0};
285     uint64_t ehFrameHDRFdeTableElfOffset_ {0};
286     std::shared_ptr<DfxElf> elfFile_;
287     std::unordered_map<uint64_t, ElfLoadInfo> info_;
288 
GetSectionInfo(const std::string & name,uint64_t & sectionVaddr,uint64_t & sectionSize,uint64_t & sectionFileOffset) const289     bool GetSectionInfo(const std::string &name, uint64_t &sectionVaddr, uint64_t &sectionSize,
290                         uint64_t &sectionFileOffset) const override
291     {
292         struct ShdrInfo shdrInfo;
293         if (elfFile_->GetSectionInfo(shdrInfo, name)) {
294             sectionVaddr = shdrInfo.addr;
295             sectionSize = shdrInfo.size;
296             sectionFileOffset = shdrInfo.offset;
297             HLOGM("Get Section '%s' %" PRIx64 " - %" PRIx64 "", name.c_str(), sectionVaddr, sectionSize);
298             return true;
299         } else {
300             HLOGW("Section '%s' not found", name.c_str());
301             return false;
302         }
303     }
304 
305 #ifndef __arm__
GetHDRSectionInfo(uint64_t & ehFrameHdrElfOffset,uint64_t & fdeTableElfOffset,uint64_t & fdeTableSize)306     bool GetHDRSectionInfo(uint64_t &ehFrameHdrElfOffset, uint64_t &fdeTableElfOffset,
307                            uint64_t &fdeTableSize) override
308     {
309         CHECK_TRUE(elfFile_ == nullptr, false, 0, "");
310         ShdrInfo shinfo;
311         if (!elfFile_->GetSectionInfo(shinfo, ".eh_frame_hdr")) {
312             return false;
313         }
314 
315         ehFrameHDRElfOffset_ = shinfo.offset;
316         if (EhFrameHDRValid_) {
317             ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
318             fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
319             fdeTableSize = ehFrameHDRFdeCount_;
320             return true;
321         }
322         auto mmapPtr = elfFile_->GetMmapPtr();
323         if (mmapPtr == nullptr) {
324             HLOGE("mmapPtr should not be nullptr.");
325             return false;
326         }
327         if (!LoadEhFrameHDR(mmapPtr + shinfo.offset, elfFile_->GetMmapSize(), shinfo.offset)) {
328             HLOGW("Failed to load eh_frame_hdr");
329             return false;
330         }
331 
332         ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
333         fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
334         fdeTableSize = ehFrameHDRFdeCount_;
335         return true;
336     }
337 #endif
338 
DumpEhFrameHDR()339     void DumpEhFrameHDR()
340     {
341         HLOGD("  ehFrameHDRElfOffset_:          0x%" PRIx64 "", ehFrameHDRElfOffset_);
342         HLOGD("  ehFrameHDRFdeCount_:           0x%" PRIx64 "", ehFrameHDRFdeCount_);
343         HLOGD("  ehFrameHDRFdeTableElfOffset_:  0x%" PRIx64 "", ehFrameHDRFdeTableElfOffset_);
344         HLOGD("  ehFrameHDRFdeTableItemSize_:   0x%" PRIx64 "", ehFrameHDRFdeTableItemSize_);
345     }
346 
LoadEhFrameHDR(const unsigned char * buffer,size_t bufferSize,uint64_t shdrOffset)347     bool LoadEhFrameHDR(const unsigned char *buffer, size_t bufferSize, uint64_t shdrOffset)
348     {
349         const eh_frame_hdr *ehFrameHdr = reinterpret_cast<const eh_frame_hdr *>(buffer);
350         CHECK_TRUE(ehFrameHdr == nullptr, false, 0, "");
351         const uint8_t *dataPtr = ehFrameHdr->encode_data;
352         DwarfEncoding dwEhFramePtr(ehFrameHdr->eh_frame_ptr_enc, dataPtr);
353         DwarfEncoding dwFdeCount(ehFrameHdr->fde_count_enc, dataPtr);
354         DwarfEncoding dwTable(ehFrameHdr->table_enc, dataPtr);
355         DwarfEncoding dwTableValue(ehFrameHdr->table_enc, dataPtr);
356 
357         HLOGD("eh_frame_hdr:");
358         HexDump(ehFrameHdr, BITS_OF_FOUR_BYTE, bufferSize);
359         unsigned char version = ehFrameHdr->version;
360         HLOGD("  version:             %02x:%s", version, (version == 1) ? "valid" : "invalid");
361         HLOGD("  eh_frame_ptr_enc:    %s", dwEhFramePtr.ToString().c_str());
362         HLOGD("  fde_count_enc:       %s", dwFdeCount.ToString().c_str());
363         HLOGD("  table_enc:           %s", dwTable.ToString().c_str());
364         HLOGD("  table_value_enc:     %s", dwTableValue.ToString().c_str());
365         HLOGD("  table_item_size:     %zd", dwTable.GetSize() + dwTableValue.GetSize());
366         HLOGD("  table_offset_in_hdr: %zu", dwTable.GetData() - buffer);
367 
368         CHECK_TRUE(version != 1, false, 1, "eh_frame_hdr version is invalid");
369         EhFrameHDRValid_ = true;
370         ehFrameHDRElfOffset_ = shdrOffset;
371         ehFrameHDRFdeCount_ = dwFdeCount.GetAppliedValue();
372         ehFrameHDRFdeTableElfOffset_ = dwTable.GetData() - buffer + shdrOffset;
373         ehFrameHDRFdeTableItemSize_ = dwTable.GetSize() + dwTableValue.GetSize();
374         DumpEhFrameHDR();
375 
376         if (!dwFdeCount.IsOmit() && dwFdeCount.GetValue() > 0) {
377             return true;
378         } else {
379             HLOGW("fde table not found.\n");
380         }
381         return false;
382     }
383 
UpdateSymbols(std::vector<DfxSymbol> & symbolsTable,const std::string & elfPath)384     void UpdateSymbols(std::vector<DfxSymbol> &symbolsTable, const std::string &elfPath)
385     {
386         symbols_.clear();
387         HLOGD("%zu symbols loadded from symbolsTable.", symbolsTable.size());
388 
389         symbols_.swap(symbolsTable);
390 
391         AdjustSymbols();
392         HLOGD("%zu symbols loadded from elf '%s'.", symbols_.size(), elfPath.c_str());
393         for (auto& symbol: symbols_) {
394             HLOGD("symbol %s", symbol.ToDebugString().c_str());
395         }
396         if (buildId_.empty()) {
397             HLOGD("buildId not found from elf '%s'.", elfPath.c_str());
398             // don't failed. some time the lib have not got the build id
399             // buildId not found from elf '/system/bin/ld-musl-arm.so.1'.
400         }
401     }
402 
AddSymbols(std::vector<DfxSymbol> & symbolsTable,std::shared_ptr<DfxElf> elf,const std::string & filePath)403     void AddSymbols(std::vector<DfxSymbol>& symbolsTable, std::shared_ptr<DfxElf> elf, const std::string& filePath)
404     {
405         // use elfFile_ to get symbolsTable
406         DfxSymbols::ParseSymbols(symbolsTable, elf, filePath);
407         DfxSymbols::AddSymbolsByPlt(symbolsTable, elf, filePath);
408     }
409 
LoadElfSymbols(std::shared_ptr<DfxMap> map,std::string elfPath)410     bool LoadElfSymbols(std::shared_ptr<DfxMap> map, std::string elfPath)
411     {
412 #ifdef HIPERF_DEBUG_TIME
413         const auto startTime = steady_clock::now();
414 #endif
415         if (elfFile_ == nullptr) {
416             if (StringEndsWith(elfPath, ".hap") && map != nullptr) {
417                 elfFile_ = DfxElf::CreateFromHap(elfPath, map->prevMap, map->offset);
418                 map->elf = elfFile_;
419             } else {
420                 elfFile_ = std::make_shared<DfxElf>(elfPath);
421             }
422         }
423         CHECK_TRUE(elfFile_ == nullptr, false, 1, "Failed to create elf file for %s.", elfPath.c_str());
424         HLOGD("loaded elf %s", elfPath.c_str());
425         if (!elfFile_->IsValid()) {
426             HLOGD("parser elf file failed.");
427             return false;
428         }
429 
430         textExecVaddr_ = elfFile_->GetStartVaddr();
431         textExecVaddrFileOffset_ = elfFile_->GetStartOffset();
432         HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
433               textExecVaddrFileOffset_);
434 
435         // we prepare two table here
436         // only one we will push in to symbols_
437         // or both drop if build id is not same
438         std::string buildIdFound = elfFile_->GetBuildId();
439         std::vector<DfxSymbol> symbolsTable;
440         AddSymbols(symbolsTable, elfFile_, filePath_);
441         if (UpdateBuildIdIfMatch(buildIdFound)) {
442             UpdateSymbols(symbolsTable, elfPath);
443         } else {
444             HLOGW("symbols will not update for '%s' because buildId is not match.",
445                   elfPath.c_str());
446             // this mean failed . we don't goon for this.
447             return false;
448         }
449 
450 #ifdef HIPERF_DEBUG_TIME
451         auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
452         if (usedTime.count() != 0) {
453             HLOGV("cost %0.3f ms to load symbols '%s'",
454                   usedTime.count() / static_cast<double>(milliseconds::duration::period::den),
455                   elfPath.c_str());
456         }
457 #endif
458         return true;
459     }
460 
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapPageOffset) const461     uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
462                                uint64_t mapPageOffset) const override
463     {
464         /*
465             00200000-002c5000 r--p 00000000 08:02 46400311
466             002c5000-00490000 r-xp 000c5000 08:02 4640031
467 
468             [14] .text             PROGBITS         00000000002c5000  000c5000
469 
470             if ip is 0x46e6ab
471             1. find the map range is 002c5000-00490000
472             2. ip - map start(002c5000) = map section offset
473             3. map section offset + map page offset(000c5000) = elf file offset
474             4. elf file offset - exec file offset(000c5000)
475                 = ip offset (ip always in exec file offset)
476             5. ip offset + exec begin vaddr(2c5000) = virtual ip in elf
477         */
478         uint64_t vaddr = ip - mapStart + mapPageOffset - textExecVaddrFileOffset_ + textExecVaddr_;
479         HLOGM(" ip :0x%016" PRIx64 " -> elf offset :0x%016" PRIx64 " -> vaddr :0x%016" PRIx64 " ",
480               ip, ip - mapStart + mapPageOffset, vaddr);
481         HLOGM("(minExecAddrFileOffset_ is 0x%" PRIx64 " textExecVaddr_ is 0x%" PRIx64 ")",
482               textExecVaddrFileOffset_, textExecVaddr_);
483         return vaddr;
484     }
485 };
486 
487 class KernelSymbols : public ElfFileSymbols {
488 public:
KernelSymbols(const std::string & symbolFilePath)489     explicit KernelSymbols(const std::string &symbolFilePath)
490         : ElfFileSymbols(symbolFilePath, SYMBOL_KERNEL_FILE)
491     {
492     }
493 
KernelSymbols(const std::string & symbolFilePath,const SymbolsFileType symbolsFileType)494     KernelSymbols(const std::string &symbolFilePath,
495                   const SymbolsFileType symbolsFileType)
496         : ElfFileSymbols(symbolFilePath, symbolsFileType)
497     {
498     }
499 
500     static constexpr const int KSYM_MIN_TOKENS = 3;
501     static constexpr const int KSYM_DEFAULT_LINE = 35000;
502     static constexpr const int KSYM_DEFAULT_SIZE = 1024 * 1024 * 1; // 1MB
503 
ParseKallsymsLine(const std::string & kallsymsPath)504     bool ParseKallsymsLine(const std::string &kallsymsPath)
505     {
506 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
507         const auto startTime = steady_clock::now();
508         std::chrono::microseconds parseLineTime = std::chrono::microseconds::zero();
509         std::chrono::microseconds sscanfTime = std::chrono::microseconds::zero();
510         std::chrono::microseconds newTime = std::chrono::microseconds::zero();
511         std::chrono::microseconds readFileTime = std::chrono::microseconds::zero();
512 #endif
513         size_t lines = 0;
514 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
515         const auto eachFileStartTime = steady_clock::now();
516 #endif
517         std::string kallsym;
518         CHECK_TRUE(!ReadFileToString(kallsymsPath, kallsym, KSYM_DEFAULT_SIZE) || kallsym.empty(), false, 1,
519                    "%s load failed.", kallsymsPath.c_str());
520 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
521         // any way we finish the line scan
522         readFileTime += duration_cast<milliseconds>(steady_clock::now() - eachFileStartTime);
523 #endif
524         // reduce the mem alloc
525         symbols_.reserve(KSYM_DEFAULT_LINE);
526 
527         char *lineBegin = kallsym.data();
528         char *dataEnd = lineBegin + kallsym.size();
529         while (lineBegin < dataEnd) {
530             char *lineEnd = strchr(lineBegin, '\n');
531             if (lineEnd != nullptr) {
532                 *lineEnd = '\0';
533             } else {
534                 lineEnd = dataEnd;
535             }
536             size_t lineSize = (lineEnd != nullptr) ? (lineEnd - lineBegin) : (dataEnd - lineBegin);
537 
538 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
539             const auto eachLineStartTime = steady_clock::now();
540 #endif
541             lines++;
542             uint64_t addr = 0;
543             char type = '\0';
544 
545             char nameRaw[lineSize];
546             char moduleRaw[lineSize];
547             int ret = sscanf_s(lineBegin, "%" PRIx64 " %c %s%s", &addr, &type, sizeof(type),
548                                nameRaw, sizeof(nameRaw), moduleRaw, sizeof(moduleRaw));
549 
550 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
551             // any way we finish the line scan
552             sscanfTime += duration_cast<milliseconds>(steady_clock::now() - eachLineStartTime);
553 #endif
554             if (ret >= KSYM_MIN_TOKENS) {
555                 if (ret == KSYM_MIN_TOKENS) {
556                     moduleRaw[0] = '\0';
557                 }
558                 HLOGM(" 0x%016" PRIx64 " %c '%s' '%s'", addr, type, nameRaw, moduleRaw);
559             } else {
560                 HLOGW("unknown line %d: '%s'", ret, lineBegin);
561                 lineBegin = lineEnd + 1;
562                 continue;
563             }
564             lineBegin = lineEnd + 1;
565             std::string name = nameRaw;
566             std::string module = moduleRaw;
567 
568             /*
569             T
570             The symbol is in the text (code) section.
571 
572             W
573             The symbol is a weak symbol that has not been specifically
574             tagged as a weak object symbol. When a weak defined symbol is
575             linked with a normal defined symbol, the normal defined symbol
576             is used with no error. When a weak undefined symbol is linked
577             and the symbol is not defined, the value of the weak symbol
578             becomes zero with no error.
579             */
580             if (addr != 0 && strchr("TtWw", type)) {
581 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
582                 const auto eachNewSymbolTime = steady_clock::now();
583 #endif
584                 // we only need text symbols
585                 symbols_.emplace_back(addr, name, module.empty() ? filePath_ : module);
586 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
587                 newTime += duration_cast<milliseconds>(steady_clock::now() - eachNewSymbolTime);
588 #endif
589             }
590 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
591             parseLineTime += duration_cast<milliseconds>(steady_clock::now() - eachLineStartTime);
592 #endif
593         }
594 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
595         std::chrono::microseconds usedTime =
596             duration_cast<milliseconds>(steady_clock::now() - startTime);
597         printf("parse kernel symbols use : %0.3f ms\n", usedTime.count() / MS_DURATION);
598         printf("parse line use : %0.3f ms\n", parseLineTime.count() / MS_DURATION);
599         printf("sscanf line use : %0.3f ms\n", sscanfTime.count() / MS_DURATION);
600         printf("new symbols use : %0.3f ms\n", newTime.count() / MS_DURATION);
601         printf("read file use : %0.3f ms\n", readFileTime.count() / MS_DURATION);
602 #endif
603         HLOGD("load %s: %zu line processed(%zu symbols)", kallsymsPath.c_str(), lines, symbols_.size());
604         return true;
605     }
606 
607     const std::string KPTR_RESTRICT = "/proc/sys/kernel/kptr_restrict";
608 
LoadKernelSyms()609     bool LoadKernelSyms()
610     {
611         if (!IsRoot()) {
612             return false;
613         }
614         HLOGD("try read /proc/kallsyms");
615         if (access("/proc/kallsyms", R_OK) != 0) {
616             printf("No vmlinux path is given, and kallsyms cannot be opened\n");
617             return false;
618         }
619         bool hasChangeKptr = false;
620         std::string oldKptrRestrict = ReadFileToString(KPTR_RESTRICT);
621         if (oldKptrRestrict.front() != '0') {
622             printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n");
623             hasChangeKptr = WriteStringToFile(KPTR_RESTRICT, "0");
624             if (!hasChangeKptr) {
625                 printf("/proc/sys/kernel/kptr_restrict write failed and we can't not change it.\n");
626             }
627         }
628 
629         // getline end
630         CHECK_TRUE(!ParseKallsymsLine("/proc/kallsyms"), false, 0, "");
631 
632         if (hasChangeKptr) {
633             if (!WriteStringToFile(KPTR_RESTRICT, oldKptrRestrict)) {
634                 printf("recover /proc/sys/kernel/kptr_restrict fail.\n");
635             }
636         }
637 
638         if (symbols_.empty()) {
639             printf("The symbol table addresses in /proc/kallsyms are all 0.\n"
640                    "Please check the value of /proc/sys/kernel/kptr_restrict, it "
641                    "should be 0.\n"
642                    "Or provide a separate vmlinux path.\n");
643 
644             if (buildId_.size() != 0) {
645                 // but we got the buildid , so we make a dummpy symbols
646                 HLOGD("kallsyms not found. but we have the buildid");
647                 return true;
648             } else {
649                 // we got nothing
650                 return false;
651             }
652         } else {
653             AdjustSymbols();
654             HLOGV("%zu symbols_ loadded from kallsyms.\n", symbols_.size());
655             return true;
656         }
657     }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)658     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
659     {
660         symbolsLoaded_ = true;
661         HLOGV("KernelSymbols try read '%s' search paths size %zu, inDeviceRecord %d",
662               symbolFilePath.c_str(), symbolsFileSearchPaths_.size(), onRecording_);
663 
664         if (onRecording_) {
665             const auto startTime = std::chrono::steady_clock::now();
666             if (!LoadKernelSyms()) {
667                 if (IsRoot()) {
668                     printf("parse kalsyms failed.\n");
669                 }
670                 return false;
671             } else {
672                 const auto thisTime = std::chrono::steady_clock::now();
673                 const auto usedTimeMsTick =
674                     std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime);
675                 HLOGV("Load kernel symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count());
676                 // load complete
677                 return true;
678             }
679 
680             // try read
681             HLOGD("try read /sys/kernel/notes");
682             std::string notes = ReadFileToString("/sys/kernel/notes");
683             if (notes.empty()) {
684                 printf("notes cannot be opened, unable get buildid\n");
685                 return false;
686             } else {
687                 HLOGD("kernel notes size: %zu", notes.size());
688                 buildId_ = DfxElf::GetBuildId((uint64_t)notes.data(), (uint64_t)notes.size());
689             }
690         } // no search path
691 
692         // try vmlinux
693         return ElfFileSymbols::LoadSymbols(nullptr, KERNEL_ELF_NAME);
694     }
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t) const695     uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
696     {
697         // ip is vaddr in /proc/kallsyms
698         return ip;
699     }
~KernelSymbols()700     ~KernelSymbols() override {}
701 };
702 
703 class KernelThreadSymbols : public KernelSymbols {
704 public:
KernelThreadSymbols(const std::string & symbolFilePath)705     explicit KernelThreadSymbols(const std::string &symbolFilePath)
706         : KernelSymbols(symbolFilePath, SYMBOL_KERNEL_THREAD_FILE)
707     {
708     }
709 
LoadKernelSyms()710     bool LoadKernelSyms()
711     {
712         if (!IsRoot()) {
713             return false;
714         }
715         // find real proc path by filePath_
716         std::string procPath;
717         if (filePath_ == SYSMGR_FILE_NAME) {
718             procPath = StringPrintf("/proc/%u/uallsyms", SYSMGR_PID);
719         } else if (filePath_ == DEVHOST_FILE_NAME) {
720             procPath = "/proc/devhost/root/kallsyms";
721         }
722         HLOGD("try read kernel thread symbol file %s in %s", filePath_.c_str(), procPath.c_str());
723         CHECK_TRUE(access(procPath.c_str(), R_OK) != 0, false, LOG_TYPE_PRINTF,
724                    "kernel thread symbol file %s cannot be opened\n", filePath_.c_str());
725         bool hasChangeKptr = false;
726         std::string oldKptrRestrict = ReadFileToString(KPTR_RESTRICT);
727         if (oldKptrRestrict.front() != '0') {
728             printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n");
729             hasChangeKptr = WriteStringToFile(KPTR_RESTRICT, "0");
730             if (!hasChangeKptr) {
731                 printf("/proc/sys/kernel/kptr_restrict write failed and we can't not change it.\n");
732             }
733         }
734 
735         // getline end
736         CHECK_TRUE(!ParseKallsymsLine(procPath), false, 0, "");
737 
738         if (hasChangeKptr) {
739             if (!WriteStringToFile(KPTR_RESTRICT, oldKptrRestrict)) {
740                 printf("recover /proc/sys/kernel/kptr_restrict fail.\n");
741             }
742         }
743 
744         if (symbols_.empty()) {
745             printf("The symbol table addresses in %s are all 0.\n"
746                    "Please check the value of /proc/sys/kernel/kptr_restrict, it "
747                    "should be 0.\n", filePath_.c_str());
748             return false;
749         } else {
750             AdjustSymbols();
751             HLOGV("%zu symbols_ loadded from %s.\n", symbols_.size(), procPath.c_str());
752             return true;
753         }
754     }
755 
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)756     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
757     {
758         symbolsLoaded_ = true;
759         HLOGV("KernelThreadSymbols try read '%s', inDeviceRecord %d",
760               filePath_.c_str(), onRecording_);
761 
762         if (onRecording_) {
763             const auto startTime = std::chrono::steady_clock::now();
764             if (!LoadKernelSyms()) {
765                 if (IsRoot()) {
766                     printf("parse %s failed.\n", filePath_.c_str());
767                 }
768             } else {
769                 const auto thisTime = std::chrono::steady_clock::now();
770                 const auto usedTimeMsTick =
771                     std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime);
772                 HLOGV("Load kernel thread symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count());
773                 // load complete
774                 return true;
775             }
776         } // no search path
777 
778         // try elf
779         return ElfFileSymbols::LoadSymbols(nullptr, filePath_);
780     }
~KernelThreadSymbols()781     ~KernelThreadSymbols() override {}
782 };
783 
784 class KernelModuleSymbols : public ElfFileSymbols {
785 public:
KernelModuleSymbols(const std::string & symbolFilePath)786     explicit KernelModuleSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
787     {
788         HLOGV("create %s", symbolFilePath.c_str());
789         symbolFileType_ = SYMBOL_KERNEL_MODULE_FILE;
790         module_ = symbolFilePath;
791     }
~KernelModuleSymbols()792     ~KernelModuleSymbols() override {};
793 
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)794     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
795     {
796         symbolsLoaded_ = true;
797         if (module_ == filePath_ and onRecording_) {
798             // file name still not convert to ko file path
799             // this is in record mode
800             HLOGV("find ko name %s", module_.c_str());
801             for (const std::string &path : kernelModulePaths) {
802                 if (access(path.c_str(), R_OK) == 0) {
803                     std::string koPath = path + module_ + KERNEL_MODULES_EXT_NAME;
804                     HLOGV("found ko in %s", koPath.c_str());
805                     if (access(koPath.c_str(), R_OK) == 0) {
806                         // create symbol
807                         filePath_ = koPath;
808                         break; // find next ko
809                     }
810                 }
811             }
812             LoadBuildId();
813         } else {
814             HLOGV("we have file path, load with %s", filePath_.c_str());
815             return ElfFileSymbols::LoadSymbols(nullptr, filePath_);
816         }
817         return false;
818     }
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t) const819     uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
820     {
821         return ip - mapStart;
822     }
823 
824 private:
LoadBuildId()825     bool LoadBuildId()
826     {
827         std::string sysFile = "/sys/module/" + module_ + "/notes/.note.gnu.build-id";
828         std::string buildIdRaw = ReadFileToString(sysFile);
829         if (!buildIdRaw.empty()) {
830             buildId_ = DfxElf::GetBuildId((uint64_t)buildIdRaw.data(), (uint64_t)buildIdRaw.size());
831             HLOGD("kerne module %s(%s) build id %s", module_.c_str(), filePath_.c_str(),
832                   buildId_.c_str());
833             return buildId_.empty() ? false : true;
834         }
835         return false;
836     }
837 
838     const std::vector<std::string> kernelModulePaths = {"/vendor/modules/"};
839     std::string module_ = "";
840 };
841 
842 class JavaFileSymbols : public ElfFileSymbols {
843 public:
JavaFileSymbols(const std::string & symbolFilePath)844     explicit JavaFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
845     {
846         symbolFileType_ = SYMBOL_KERNEL_FILE;
847     }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)848     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
849     {
850         symbolsLoaded_ = true;
851         return false;
852     }
~JavaFileSymbols()853     ~JavaFileSymbols() override {}
854 
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapPageOffset) const855     uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
856                                        uint64_t mapPageOffset) const override
857     {
858         // this is different with elf
859         // elf use  ip - mapStart + mapPageOffset - minExecAddrFileOffset_ + textExecVaddr_
860         return ip - mapStart + mapPageOffset;
861     }
862 };
863 
864 class JSFileSymbols : public ElfFileSymbols {
865 public:
JSFileSymbols(const std::string & symbolFilePath)866     explicit JSFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
867     {
868         symbolFileType_ = SYMBOL_KERNEL_FILE;
869     }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)870     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
871     {
872         symbolsLoaded_ = true;
873         return false;
874     }
~JSFileSymbols()875     ~JSFileSymbols() override {}
876 };
877 
878 class HapFileSymbols : public ElfFileSymbols {
879 private:
880 #if defined(is_ohos) && is_ohos
881     std::unique_ptr<DfxExtractor> dfxExtractor_;
882     bool hapExtracted_ = false;
883 #endif
884     std::unique_ptr<uint8_t[]> abcDataPtr_ = nullptr;
885     [[maybe_unused]] uintptr_t loadOffSet_ = 0;
886     [[maybe_unused]] size_t abcDataSize_ = 0;
887     [[maybe_unused]] uintptr_t arkExtractorptr_ = 0;
888     bool isHapAbc_ = false;
889     pid_t pid_ = 0;
890 public:
HapFileSymbols(const std::string & symbolFilePath,pid_t pid)891     explicit HapFileSymbols(const std::string &symbolFilePath, pid_t pid)
892         : ElfFileSymbols(symbolFilePath, SYMBOL_HAP_FILE)
893     {
894         pid_ = pid;
895     }
896 
IsHapAbc()897     bool IsHapAbc()
898     {
899 #if defined(is_ohos) && is_ohos
900         if (hapExtracted_) {
901             return isHapAbc_;
902         }
903         hapExtracted_ = true;
904         HLOGD("the symbol file is %s, pid is %d.", filePath_.c_str(), pid_);
905         if (IsRoot()) {
906             if (IsApplicationEncryped(pid_)) {
907                 HLOGD("no need to parse js symbols");
908                 return false;
909             }
910         }
911 
912         CHECK_TRUE(StringEndsWith(filePath_, ".hap") && map_->IsMapExec(), false, 1,
913                    "map is exec not abc file , the symbol file is:%s", map_->name.c_str());
914 
915         if (StringEndsWith(filePath_, ".hap") || StringEndsWith(filePath_, ".hsp")) {
916             dfxExtractor_ = std::make_unique<DfxExtractor>(filePath_);
917             CHECK_TRUE(!dfxExtractor_->GetHapAbcInfo(loadOffSet_, abcDataPtr_, abcDataSize_), false, 1,
918                        "failed to call GetHapAbcInfo, the symbol file is:%s", filePath_.c_str());
919             HLOGD("loadOffSet %u", (uint32_t)loadOffSet_);
920             if (abcDataPtr_ != nullptr) {
921                 isHapAbc_ = true;
922                 HLOGD("symbol file : %s, isAbc: %d", filePath_.c_str(), isHapAbc_);
923             }
924         } else {
925             loadOffSet_ = map_->offset;
926             abcDataSize_ = map_->end - map_->begin;
927             abcDataPtr_ = std::make_unique<uint8_t[]>(abcDataSize_);
928             auto size = DfxMemory::ReadProcMemByPid(pid_, map_->begin, abcDataPtr_.get(), map_->end - map_->begin);
929             if (size != abcDataSize_) {
930                 HLOGD("return size is small abcDataPtr : %s, isAbc: %d", abcDataPtr_.get(), isHapAbc_);
931                 return false;
932             }
933             isHapAbc_ = true;
934             HLOGD("symbol file name %s loadOffSet %u abcDataSize_ %u",
935                   filePath_.c_str(), (uint32_t)loadOffSet_, (uint32_t)abcDataSize_);
936         }
937         auto ret = DfxArk::ArkCreateJsSymbolExtractor(&arkExtractorptr_);
938         if (ret < 0) {
939             arkExtractorptr_ = 0;
940             HLOGE("failed to call ArkCreateJsSymbolExtractor, the symbol file is:%s", filePath_.c_str());
941         }
942 #endif
943         return isHapAbc_;
944     }
945 
IsAbc()946     bool IsAbc() override
947     {
948         return isHapAbc_ == true;
949     }
950 
SetBoolValue(bool value)951     void SetBoolValue(bool value) override
952     {
953         isHapAbc_ = value;
954     }
955 
LoadDebugInfo(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)956     bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
957     {
958         HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str());
959         if (debugInfoLoaded_) {
960             return true;
961         }
962         CHECK_TRUE(!onRecording_, true, 0, "");
963 
964         if (!IsHapAbc()) {
965             ElfFileSymbols::LoadDebugInfo(map, "");
966         }
967         debugInfoLoaded_ = true;
968         debugInfoLoadResult_ = true;
969         return true;
970     }
971 
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)972     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
973     {
974         HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str());
975         CHECK_TRUE(symbolsLoaded_ || !onRecording_, true, 0, "");
976         symbolsLoaded_ = true;
977         if (!IsHapAbc()) {
978             ElfFileSymbols::LoadSymbols(map, "");
979         }
980         return true;
981     }
982 
GetSymbolWithPcAndMap(uint64_t ip,std::shared_ptr<DfxMap> map)983     DfxSymbol GetSymbolWithPcAndMap(uint64_t ip, std::shared_ptr<DfxMap> map) override
984     {
985         // get cache
986         auto iter = symbolsMap_.find(ip);
987         if (iter != symbolsMap_.end()) {
988             return iter->second;
989         }
990         if (map == nullptr) {
991             return DfxSymbol(ip, "");
992         }
993         HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str());
994 
995 #if defined(is_ohos) && is_ohos
996         if (IsAbc() && needParseJsFunc_) {
997             JsFunction jsFunc;
998             std::string module = map->name;
999             HLOGD("map->name module:%s", module.c_str());
1000             auto ret = DfxArk::ParseArkFrameInfo(static_cast<uintptr_t>(ip), static_cast<uintptr_t>(map->begin),
1001                                                  loadOffSet_, abcDataPtr_.get(), abcDataSize_,
1002                                                  arkExtractorptr_, &jsFunc);
1003             if (ret == -1) {
1004                 HLOGD("failed to call ParseArkFrameInfo, the symbol file is : %s", map->name.c_str());
1005                 return DfxSymbol(ip, "");
1006             }
1007             this->symbolsMap_.insert(std::make_pair(ip,
1008                                                     DfxSymbol(ip,
1009                                                     jsFunc.codeBegin,
1010                                                     jsFunc.functionName,
1011                                                     jsFunc.ToString(),
1012                                                     map->name)));
1013 
1014             DfxSymbol &foundSymbol = symbolsMap_[ip];
1015             if (!foundSymbol.matched_) {
1016                 foundSymbol.matched_ = true;
1017                 foundSymbol.symbolFileIndex_ = id_;
1018                 matchedSymbols_.push_back(&(symbolsMap_[ip]));
1019             }
1020 
1021             HLOGD("ip : 0x%" PRIx64 " the symbol file is : %s, function is %s demangle_ : %s", ip,
1022                   symbolsMap_[ip].module_.data(), jsFunc.functionName, matchedSymbols_.back()->demangle_.data());
1023             return symbolsMap_[ip];
1024         }
1025 #endif
1026         DfxSymbol symbol(ip, "");
1027         return symbol;
1028     }
1029 };
1030 
1031 class UnknowFileSymbols : public SymbolsFile {
1032 public:
UnknowFileSymbols(const std::string & symbolFilePath)1033     explicit UnknowFileSymbols(const std::string &symbolFilePath)
1034         : SymbolsFile(SYMBOL_UNKNOW_FILE, symbolFilePath)
1035     {
1036     }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)1037     bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
1038     {
1039         symbolsLoaded_ = true;
1040         return false;
1041     }
~UnknowFileSymbols()1042     ~UnknowFileSymbols() override {}
1043 };
1044 
~SymbolsFile()1045 SymbolsFile::~SymbolsFile() {}
1046 
CreateSymbolsFile(SymbolsFileType symbolType,const std::string symbolFilePath,pid_t pid)1047 std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(SymbolsFileType symbolType,
1048                                                             const std::string symbolFilePath, pid_t pid)
1049 {
1050     switch (symbolType) {
1051         case SYMBOL_KERNEL_FILE:
1052             return std::make_unique<KernelSymbols>(symbolFilePath.empty() ? KERNEL_MMAP_NAME
1053                                                                           : symbolFilePath);
1054         case SYMBOL_KERNEL_MODULE_FILE:
1055             return std::make_unique<KernelModuleSymbols>(symbolFilePath);
1056         case SYMBOL_KERNEL_THREAD_FILE:
1057             return std::make_unique<KernelThreadSymbols>(symbolFilePath);
1058         case SYMBOL_ELF_FILE:
1059             return std::make_unique<ElfFileSymbols>(symbolFilePath);
1060         case SYMBOL_JAVA_FILE:
1061             return std::make_unique<JavaFileSymbols>(symbolFilePath);
1062         case SYMBOL_JS_FILE:
1063             return std::make_unique<JSFileSymbols>(symbolFilePath);
1064         case SYMBOL_HAP_FILE:
1065             return std::make_unique<HapFileSymbols>(symbolFilePath, pid);
1066         default:
1067             return std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, symbolFilePath);
1068     }
1069 }
1070 
CreateSymbolsFile(const std::string & symbolFilePath,pid_t pid)1071 std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(const std::string &symbolFilePath, pid_t pid)
1072 {
1073     // we need check file name here
1074     if (symbolFilePath == KERNEL_MMAP_NAME) {
1075         return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_FILE, symbolFilePath);
1076     } else if (symbolFilePath == SYSMGR_FILE_NAME ||
1077                symbolFilePath == DEVHOST_LINUX_FILE_NAME ||
1078                StringStartsWith(symbolFilePath, DEVHOST_LINUX_PREFIX)) {
1079         return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, symbolFilePath);
1080     } else if (StringEndsWith(symbolFilePath, KERNEL_MODULES_EXT_NAME)) {
1081         return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, symbolFilePath);
1082     } else if (IsArkJsFile(symbolFilePath)) {
1083         return SymbolsFile::CreateSymbolsFile(SYMBOL_HAP_FILE, symbolFilePath, pid);
1084     } else {
1085         // default is elf, this may be problematic in the future.
1086         return SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, symbolFilePath);
1087     }
1088 }
1089 
AdjustSymbols()1090 void SymbolsFile::AdjustSymbols()
1091 {
1092     if (symbols_.size() <= 0) {
1093         return;
1094     }
1095 
1096     // order
1097     sort(symbols_.begin(), symbols_.end(), [](const DfxSymbol& a, const DfxSymbol& b) {
1098         return a.funcVaddr_ < b.funcVaddr_;
1099     });
1100     HLOGV("sort completed");
1101 
1102     size_t fullSize = symbols_.size();
1103     size_t erased = 0;
1104 
1105     // Check for duplicate vaddr
1106     auto last = std::unique(symbols_.begin(), symbols_.end(), [](const DfxSymbol &a, const DfxSymbol &b) {
1107         return (a.funcVaddr_ == b.funcVaddr_);
1108     });
1109     symbols_.erase(last, symbols_.end());
1110     erased = fullSize - symbols_.size();
1111     HLOGV("uniqued completed");
1112     auto it = symbols_.begin();
1113     while (it != symbols_.end()) {
1114         it->index_ = it - symbols_.begin();
1115         it++;
1116     }
1117     HLOGV("indexed completed");
1118 
1119     HLOG_ASSERT(symbols_.size() != 0);
1120 
1121     if (textExecVaddrRange_ == maxVaddr) {
1122         textExecVaddrRange_ = symbols_.back().funcVaddr_ - symbols_.front().funcVaddr_;
1123     }
1124 
1125     HLOGDDD("%zu symbols after adjust (%zu erased) 0x%016" PRIx64 " - 0x%016" PRIx64
1126             " @0x%016" PRIx64 " ",
1127             symbols_.size(), erased, symbols_.front().funcVaddr_, symbols_.back().funcVaddr_,
1128             textExecVaddrFileOffset_);
1129 }
1130 
SortMatchedSymbols()1131 void SymbolsFile::SortMatchedSymbols()
1132 {
1133     if (matchedSymbols_.size() <= 1u) {
1134         return;
1135     }
1136     sort(matchedSymbols_.begin(), matchedSymbols_.end(), [](const DfxSymbol* a, const DfxSymbol* b) {
1137         if (a == nullptr || b == nullptr) {
1138             return true;
1139         }
1140         return a->funcVaddr_ < b->funcVaddr_;
1141     });
1142 }
1143 
GetSymbols()1144 const std::vector<DfxSymbol> &SymbolsFile::GetSymbols()
1145 {
1146     return symbols_;
1147 }
1148 
GetMatchedSymbols()1149 const std::vector<DfxSymbol *> &SymbolsFile::GetMatchedSymbols()
1150 {
1151     return matchedSymbols_;
1152 }
1153 
GetSymbolWithVaddr(uint64_t vaddrInFile)1154 const DfxSymbol SymbolsFile::GetSymbolWithVaddr(uint64_t vaddrInFile)
1155 {
1156 #ifdef HIPERF_DEBUG_TIME
1157     const auto startTime = steady_clock::now();
1158 #endif
1159     DfxSymbol symbol;
1160     // it should be already order from small to large
1161     auto found =
1162         std::upper_bound(symbols_.begin(), symbols_.end(), vaddrInFile, DfxSymbol::ValueLessThen);
1163     /*
1164     if data is { 1, 2, 4, 5, 5, 6 };
1165     upper_bound for each val :
1166         0 < 1 at index 0
1167         1 < 2 at index 1
1168         2 < 4 at index 2
1169         3 < 4 at index 2
1170         4 < 5 at index 3
1171         5 < 6 at index 5
1172         6 < not found
1173     if key symbol vaddr is { 1, 2, 4, 5, 5, 6 };
1174      check ip vaddr for each val :
1175        ip   sym
1176         0   not found
1177         1   1
1178         1   1
1179         2   2
1180         3   3
1181         4   4
1182         5   5
1183         6   6
1184         7   7
1185     */
1186     if (found != symbols_.begin()) {
1187         found = std::prev(found);
1188         if (found != symbols_.end()) {
1189             if (found->Contain(vaddrInFile)) {
1190                 found->offsetToVaddr_ = vaddrInFile - found->funcVaddr_;
1191                 if (!found->matched_) {
1192                     found->matched_ = true;
1193                     matchedSymbols_.push_back(&(*found));
1194                 }
1195                 symbol = *found; // copy
1196                 HLOGV("found '%s' for vaddr 0x%016" PRIx64 "", symbol.ToString().c_str(), vaddrInFile);
1197             }
1198         }
1199     }
1200 
1201     if (!symbol.IsValid()) {
1202         HLOGV("NOT found vaddr 0x%" PRIx64 " in symbole file %s(%zu)", vaddrInFile,
1203               filePath_.c_str(), symbols_.size());
1204     }
1205     symbol.fileVaddr_ = vaddrInFile;
1206     symbol.symbolFileIndex_ = id_;
1207 
1208 #ifdef HIPERF_DEBUG_TIME
1209     auto usedTime = duration_cast<milliseconds>(steady_clock::now() - startTime);
1210     if (usedTime > 1ms) {
1211         HLOGW("cost %" PRId64 "ms to search ", usedTime.count());
1212     }
1213 #endif
1214     return symbol;
1215 }
1216 
CheckPathReadable(const std::string & path) const1217 bool SymbolsFile::CheckPathReadable(const std::string &path) const
1218 {
1219     if (access(path.c_str(), R_OK) == 0) {
1220         return true;
1221     } else {
1222         HLOGM("'%s' is unable read", path.c_str());
1223         return false;
1224     }
1225 }
1226 
setSymbolsFilePath(const std::vector<std::string> & symbolsSearchPaths)1227 bool SymbolsFile::setSymbolsFilePath(const std::vector<std::string> &symbolsSearchPaths)
1228 {
1229     symbolsFileSearchPaths_.clear();
1230     for (auto &symbolsSearchPath : symbolsSearchPaths) {
1231         if (CheckPathReadable(symbolsSearchPath)) {
1232             symbolsFileSearchPaths_.emplace_back(symbolsSearchPath);
1233             HLOGV("'%s' is add to symbolsSearchPath", symbolsSearchPath.c_str());
1234         }
1235     }
1236     return (symbolsFileSearchPaths_.size() > 0);
1237 }
1238 
LoadSymbolsFromSaved(const SymbolFileStruct & symbolFileStruct)1239 std::unique_ptr<SymbolsFile> SymbolsFile::LoadSymbolsFromSaved(
1240     const SymbolFileStruct &symbolFileStruct)
1241 {
1242     bool isHapSymbolFile = static_cast<SymbolsFileType>(symbolFileStruct.symbolType_) == SYMBOL_HAP_FILE;
1243     HLOGD("isHapSymbolFile : %d", isHapSymbolFile);
1244     auto symbolsFile = CreateSymbolsFile(symbolFileStruct.filePath_);
1245 
1246     // default create elf file. but hap file need special operation.
1247     symbolsFile->filePath_ = symbolFileStruct.filePath_;
1248     symbolsFile->symbolFileType_ = static_cast<SymbolsFileType>(symbolFileStruct.symbolType_);
1249     symbolsFile->textExecVaddr_ = symbolFileStruct.textExecVaddr_;
1250     symbolsFile->textExecVaddrFileOffset_ = symbolFileStruct.textExecVaddrFileOffset_;
1251     symbolsFile->buildId_ = symbolFileStruct.buildId_;
1252     for (auto &symbolStruct : symbolFileStruct.symbolStructs_) {
1253         symbolsFile->symbols_.emplace_back(symbolStruct.vaddr_, symbolStruct.len_,
1254                                            symbolStruct.symbolName_, symbolFileStruct.filePath_);
1255     }
1256     symbolsFile->AdjustSymbols(); // reorder
1257     if (isHapSymbolFile) {
1258         for (const auto& symbol : symbolsFile->symbols_) {
1259             symbolsFile->symbolsMap_.emplace(symbol.funcVaddr_, symbol);
1260         }
1261         symbolsFile->SetBoolValue(true);
1262     }
1263     symbolsFile->debugInfoLoadResult_ = true;
1264     symbolsFile->symbolsLoaded_ = true; // all ready LoadFrom perf.data
1265     HLOGV("load %zu symbol from SymbolFileStruct for file '%s'", symbolsFile->symbols_.size(),
1266           symbolsFile->filePath_.c_str());
1267     return symbolsFile;
1268 }
1269 
SetBoolValue(bool value)1270 void SymbolsFile::SetBoolValue(bool value)
1271 {
1272 }
1273 
ExportSymbolToFileFormat(SymbolFileStruct & symbolFileStruct)1274 void SymbolsFile::ExportSymbolToFileFormat(SymbolFileStruct &symbolFileStruct)
1275 {
1276     symbolFileStruct.filePath_ = filePath_;
1277     symbolFileStruct.symbolType_ = symbolFileType_;
1278     symbolFileStruct.textExecVaddr_ = textExecVaddr_;
1279     symbolFileStruct.textExecVaddrFileOffset_ = textExecVaddrFileOffset_;
1280     symbolFileStruct.buildId_ = buildId_;
1281 
1282     SortMatchedSymbols();
1283     auto symbols = GetMatchedSymbols();
1284     symbolFileStruct.symbolStructs_.reserve(symbols.size());
1285     for (const auto symbol : symbols) {
1286         auto &symbolStruct = symbolFileStruct.symbolStructs_.emplace_back();
1287         symbolStruct.vaddr_ = symbol->funcVaddr_;
1288         symbolStruct.len_ = symbol->size_;
1289         symbolStruct.symbolName_ = symbol->GetName();
1290     }
1291 
1292     HLOGV("export %zu symbol to SymbolFileStruct from %s", symbolFileStruct.symbolStructs_.size(),
1293           filePath_.c_str());
1294 }
1295 
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapOffset) const1296 uint64_t SymbolsFile::GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t mapOffset) const
1297 {
1298     // no convert
1299     return ip;
1300 }
1301 
AddSymbol(DfxSymbol symbol)1302 void SymbolsFile::AddSymbol(DfxSymbol symbol)
1303 {
1304     symbolsLoaded_ = true;
1305     symbols_.emplace_back(symbol);
1306 }
1307 } // namespace HiPerf
1308 } // namespace Developtools
1309 } // namespace OHOS
1310