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