xref: /developtools/hiperf/src/symbols_file.cpp (revision 48f512ce)
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
45using namespace OHOS::HiviewDFX;
46using namespace std::chrono;
47
48namespace OHOS {
49namespace Developtools {
50namespace HiPerf {
51bool SymbolsFile::onRecording_ = true;
52bool SymbolsFile::needParseJsFunc_ = false;
53
54const std::string SymbolsFile::GetBuildId() const
55{
56    return buildId_;
57}
58
59bool 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
96std::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
116const 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
163class ElfFileSymbols : public SymbolsFile {
164public:
165    explicit ElfFileSymbols(const std::string &symbolFilePath,
166                            const SymbolsFileType symbolsFileType = SYMBOL_ELF_FILE)
167        : SymbolsFile(symbolsFileType, symbolFilePath)
168    {
169    }
170
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
214protected:
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
280private:
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
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
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
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
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
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
487class KernelSymbols : public ElfFileSymbols {
488public:
489    explicit KernelSymbols(const std::string &symbolFilePath)
490        : ElfFileSymbols(symbolFilePath, SYMBOL_KERNEL_FILE)
491    {
492    }
493
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
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
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
703class KernelThreadSymbols : public KernelSymbols {
704public:
705    explicit KernelThreadSymbols(const std::string &symbolFilePath)
706        : KernelSymbols(symbolFilePath, SYMBOL_KERNEL_THREAD_FILE)
707    {
708    }
709
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
784class KernelModuleSymbols : public ElfFileSymbols {
785public:
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
824private:
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
842class JavaFileSymbols : public ElfFileSymbols {
843public:
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
864class JSFileSymbols : public ElfFileSymbols {
865public:
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
878class HapFileSymbols : public ElfFileSymbols {
879private:
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;
890public:
891    explicit HapFileSymbols(const std::string &symbolFilePath, pid_t pid)
892        : ElfFileSymbols(symbolFilePath, SYMBOL_HAP_FILE)
893    {
894        pid_ = pid;
895    }
896
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
1032class UnknowFileSymbols : public SymbolsFile {
1033public:
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
1046SymbolsFile::~SymbolsFile() {}
1047
1048std::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
1072std::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
1091void 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
1132void 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
1145const std::vector<DfxSymbol> &SymbolsFile::GetSymbols()
1146{
1147    return symbols_;
1148}
1149
1150const std::vector<DfxSymbol *> &SymbolsFile::GetMatchedSymbols()
1151{
1152    return matchedSymbols_;
1153}
1154
1155const 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
1218bool 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
1228bool 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
1240std::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
1271void SymbolsFile::SetBoolValue(bool value)
1272{
1273}
1274
1275void 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
1297uint64_t SymbolsFile::GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t mapOffset) const
1298{
1299    // no convert
1300    return ip;
1301}
1302
1303void SymbolsFile::AddSymbol(DfxSymbol symbol)
1304{
1305    symbolsLoaded_ = true;
1306    symbols_.emplace_back(symbol);
1307}
1308} // namespace HiPerf
1309} // namespace Developtools
1310} // namespace OHOS
1311