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