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