• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef HIPERF_SYMBOLS_H
17 #define HIPERF_SYMBOLS_H
18 
19 #include <cinttypes>
20 #include <iomanip>
21 #include <sstream>
22 #include <string>
23 
24 #include "perf_file_format.h"
25 #include "utilities.h"
26 
27 #define HIPERF_ELF_READ_USE_MMAP
28 
29 namespace OHOS {
30 namespace Developtools {
31 namespace HiPerf {
32 constexpr const char KERNEL_MMAP_NAME[] = "[kernel.kallsyms]";
33 constexpr const char KERNEL_MODULES_EXT_NAME[] = ".ko";
34 constexpr const char KERNEL_ELF_NAME[] = "vmlinux";
35 constexpr const char MMAP_VDSO_NAME[] = "[vdso]";
36 constexpr const char MMAP_ANONYMOUS_NAME[] = "[anon]";
37 constexpr const char MMAP_ANONYMOUS_OHOS_NAME[] = "//anon";
38 
39 const std::string NOTE_GNU_BUILD_ID = ".note.gnu.build-id";
40 const std::string EH_FRAME_HR = ".eh_frame_hdr";
41 const std::string EH_FRAME = ".eh_frame";
42 const std::string ARM_EXIDX = ".ARM.exidx";
43 const std::string SYMTAB = ".symtab";
44 const std::string DYNSYM = ".dynsym";
45 const std::string GNU_DEBUGDATA = ".gnu_debugdata";
46 const std::string PLT = ".plt";
47 const std::string LINKER_PREFIX = "__dl_";
48 const std::string LINKER_PREFIX_NAME = "[linker]";
49 
50 const int MAX_SYMBOLS_TYPE_NAME_LEN = 10;
51 
52 class FileSymbol {
53     [[maybe_unused]] uint64_t vaddr_ = 0;
54     [[maybe_unused]] uint64_t len_ = 0;
55     std::string name_ = "";
56     std::string demangle_ = ""; // demangle string
FileSymbol(uint64_t vaddr,uint64_t len,const char * name,const char * demangle)57     FileSymbol(uint64_t vaddr, uint64_t len, const char *name, const char *demangle)
58         : vaddr_(vaddr), len_(len), name_(name), demangle_(demangle)
59     {
60     }
61 };
62 
63 struct Symbol {
64     uint64_t funcVaddr_ = 0;
65     uint64_t offsetToVaddr_ = 0;
66     uint64_t fileVaddr_ = 0;
67     uint64_t taskVaddr_ = 0;
68     uint64_t len_ = 0;
69     int32_t symbolFileIndex_ = -1; // symbols file index, used to report protobuf file
70     int32_t index_ = -1;
71     std::string_view name_ = "";
72     std::string_view demangle_ = ""; // demangle string
73     std::string_view module_ = "";   // maybe empty
74     std::string_view comm_ = "";     // we need a comm name like comm@0x1234
75     mutable std::string_view unknow_ = "";
76     mutable bool matched_ = false; // if some callstack match this
77     int32_t hit_ = 0;
78 
79     // elf use this
SymbolSymbol80     Symbol(uint64_t vaddr, uint64_t len, const std::string &name, const std::string &demangle,
81            const std::string module)
82         : funcVaddr_(vaddr),
83           fileVaddr_(vaddr),
84           len_(len),
85           name_(MemoryHold::Get().HoldStringView(name)),
86           demangle_(MemoryHold::Get().HoldStringView(demangle)),
87           module_(MemoryHold::Get().HoldStringView(module)) {}
SymbolSymbol88     Symbol(uint64_t vaddr, uint64_t len, const std::string &name, const std::string &module)
89         : Symbol(vaddr, len, name, name, module) {}
90 
91     // kernel use this
SymbolSymbol92     Symbol(uint64_t vaddr, const std::string &name, const std::string &module)
93         : Symbol(vaddr, 0, name, name, module) {}
94 
95     // Symbolic use this
96     Symbol(uint64_t taskVaddr = 0, const std::string &comm = "")
taskVaddr_Symbol97         : taskVaddr_(taskVaddr), comm_(comm)
98     {
99     }
100 
101     // copy
102     Symbol(const Symbol &other) = default;
103 
SameVaddrSymbol104     static bool SameVaddr(const Symbol &a, const Symbol &b)
105     {
106         return (a.funcVaddr_ == b.funcVaddr_);
107     }
SameSymbol108     bool Same(const Symbol &b) const
109     {
110         return (funcVaddr_ == b.funcVaddr_ and demangle_ == b.demangle_);
111     }
112     bool operator==(const Symbol &b) const
113     {
114         return Same(b);
115     }
116 
117     bool operator!=(const Symbol &b) const
118     {
119         return !Same(b);
120     }
121 
isValidSymbol122     bool isValid() const
123     {
124         return !module_.empty();
125     }
126 
SetMatchFlagSymbol127     void SetMatchFlag() const
128     {
129         matched_ = true;
130     }
131 
HasMatchedSymbol132     inline bool HasMatched() const
133     {
134         return matched_;
135     }
136 
NameSymbol137     std::string_view Name() const
138     {
139         if (!demangle_.empty()) {
140             return demangle_;
141         }
142         if (!name_.empty()) {
143             return name_;
144         }
145         if (unknow_.empty()) {
146             std::stringstream sstream;
147             if (!module_.empty()) {
148                 sstream << module_ << "+0x" << std::hex << fileVaddr_;
149             } else {
150                 sstream << comm_ << "@0x" << std::hex << taskVaddr_;
151             }
152             std::string hold = sstream.str();
153             unknow_ = MemoryHold::Get().HoldStringView(hold);
154         }
155         return unknow_;
156     }
157 
ToStringSymbol158     std::string ToString() const
159     {
160         std::stringstream sstream;
161         if (fileVaddr_ != 0) {
162             sstream << "0x" << std::hex << fileVaddr_;
163         } else {
164             sstream << "0x" << std::hex << taskVaddr_;
165         }
166         sstream << " " << Name();
167         return sstream.str();
168     };
169 
ToDebugStringSymbol170     std::string ToDebugString() const
171     {
172         std::stringstream sstream;
173         sstream << "0x" << std::setfill('0') << std::setw(sizeof(funcVaddr_) * BYTE_PRINT_WIDTH)
174                 << std::hex << funcVaddr_;
175         sstream << "|";
176         sstream << std::setfill('0') << std::setw(sizeof(len_)) << len_;
177         sstream << "|";
178         sstream << demangle_ << "|";
179         sstream << name_ << "|";
180         sstream << (matched_ ? "matched" : "");
181         sstream << " unknowname:" << unknow_.size();
182         sstream << " task:" << (comm_.size() > 0 ? comm_ : "");
183         sstream << "@" << taskVaddr_;
184         sstream << " file:" << (module_.size() > 0 ? module_ : "");
185         sstream << "@" << fileVaddr_;
186 
187         return sstream.str();
188     };
189 
ContainSymbol190     bool Contain(uint64_t addr) const
191     {
192         if (len_ == 0) {
193             return funcVaddr_ <= addr;
194         } else {
195             return (funcVaddr_ <= addr) and ((funcVaddr_ + len_) > addr);
196         }
197     }
198 
199     // The range [first, last) must be partitioned with respect to the expression !(value < element)
200     // or !comp(value, element)
ValueLessThenSymbol201     static bool ValueLessThen(uint64_t vaddr, const Symbol &a)
202     {
203         return vaddr < a.funcVaddr_;
204     }
ValueLessEqualSymbol205     static bool ValueLessEqual(uint64_t vaddr, const Symbol &a)
206     {
207         return vaddr <= a.funcVaddr_;
208     }
CompareLessThenSymbol209     static bool CompareLessThen(const Symbol &a, const Symbol &b)
210     {
211         return a.funcVaddr_ < b.funcVaddr_; // we should use vaddr to sort
212     };
CompareByPointerSymbol213     static bool CompareByPointer(const Symbol *a, const Symbol *b)
214     {
215         return a->funcVaddr_ < b->funcVaddr_; // we should use vaddr to sort
216     };
217 };
218 
219 enum SymbolsFileType {
220     SYMBOL_KERNEL_FILE,
221     SYMBOL_KERNEL_MODULE_FILE,
222     SYMBOL_ELF_FILE,
223     SYMBOL_JAVA_FILE,
224     SYMBOL_JS_FILE,
225     SYMBOL_UNKNOW_FILE,
226 };
227 
228 class SymbolsFile {
229 public:
230     SymbolsFileType symbolFileType_;
231     std::string filePath_ = "";
232     int32_t id_ = -1; // used to report protobuf file
233 
234     // [14] .text             PROGBITS         00000000002c5000  000c5000
235     // min exec addr , general it point to .text
236     // we make a default value for min compare
237     static const uint64_t maxVaddr = std::numeric_limits<uint64_t>::max();
238 
239     uint64_t textExecVaddr_ = maxVaddr;
240     uint64_t textExecVaddrFileOffset_ = 0;
241     uint64_t textExecVaddrRange_ = maxVaddr;
242 
SymbolsFile(SymbolsFileType symbolType,const std::string path)243     SymbolsFile(SymbolsFileType symbolType, const std::string path)
244         : symbolFileType_(symbolType), filePath_(path) {};
245     virtual ~SymbolsFile();
246 
247     // create the symbols file object
248     static std::unique_ptr<SymbolsFile> CreateSymbolsFile(
249         SymbolsFileType = SYMBOL_UNKNOW_FILE, const std::string symbolFilePath = EMPTY_STRING);
250     static std::unique_ptr<SymbolsFile> CreateSymbolsFile(const std::string &symbolFilePath);
251 
252     // set symbols path
setSymbolsFilePath(const std::string & symbolsSearchPath)253     bool setSymbolsFilePath(const std::string &symbolsSearchPath)
254     {
255         std::vector<std::string> symbolsSearchPaths = {symbolsSearchPath};
256         return setSymbolsFilePath(symbolsSearchPaths);
257     };
258     bool setSymbolsFilePath(const std::vector<std::string> &);
259 
260     // load symbol from file
261     virtual bool LoadSymbols([[maybe_unused]] const std::string &symbolFilePath = EMPTY_STRING)
262     {
263         HLOGV("virtual dummy function called");
264         symbolsLoaded_ = true;
265         return false;
266     };
267     // load debug info for unwind
268     virtual bool LoadDebugInfo([[maybe_unused]] const std::string &symbolFilePath = EMPTY_STRING)
269     {
270         HLOGV("virtual dummy function called");
271         debugInfoLoaded_ = true;
272         return false;
273     };
274     // get the build if from symbols
275     const std::string GetBuildId() const;
276 
277     // get the symbols vector
278     const std::vector<Symbol> &GetSymbols();
279     const std::vector<Symbol *> &GetMatchedSymbols();
280 
281     // get vaddr(in symbol) from ip(real addr , after mmap reloc)
282     virtual uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t mapOffset) const;
283 
284     // get symbols from vaddr
285     const Symbol GetSymbolWithVaddr(uint64_t vaddr);
286 
287     // read the .text section and .eh_frame section (RO) memory from elf mmap
288     // unwind use this to check the DWARF and so on
ReadRoMemory(uint64_t,uint8_t * const,size_t)289     virtual size_t ReadRoMemory(uint64_t, uint8_t * const, size_t) const
290     {
291         HLOGV("virtual dummy function called");
292         return 0; // default not support
293     }
294 
295     // get the section info , like .ARM.exidx
GetSectionInfo(const std::string & name,uint64_t & sectionVaddr,uint64_t & sectionSize,uint64_t & sectionFileOffset)296     virtual bool GetSectionInfo([[maybe_unused]] const std::string &name,
297                                 [[maybe_unused]] uint64_t &sectionVaddr,
298                                 [[maybe_unused]] uint64_t &sectionSize,
299                                 [[maybe_unused]] uint64_t &sectionFileOffset) const
300     {
301         HLOGV("virtual dummy function called");
302         return false;
303     }
304 #ifndef __arm__
305     // get hdr info for unwind , need provide the fde table location and entry count
GetHDRSectionInfo(uint64_t & ehFrameHdrElfOffset,uint64_t & fdeTableElfOffset,uint64_t & fdeTableSize)306     virtual bool GetHDRSectionInfo([[maybe_unused]] uint64_t &ehFrameHdrElfOffset,
307                                    [[maybe_unused]] uint64_t &fdeTableElfOffset,
308                                    [[maybe_unused]] uint64_t &fdeTableSize) const
309     {
310         HLOGV("virtual dummy function called");
311         return false;
312     }
313 #endif
314     // load from symbols from the perf.data format
315     static std::unique_ptr<SymbolsFile> LoadSymbolsFromSaved(const SymbolFileStruct &);
316     // save the symbols to perf.data format
317     void ExportSymbolToFileFormat(SymbolFileStruct &symbolFileStruct);
318 
SymbolsLoaded()319     bool SymbolsLoaded()
320     {
321         return symbolsLoaded_;
322     }
323 
324     // this means we are in recording
325     // will try read some elf in runtime path
326     static bool onRecording_;
327 
328 protected:
329     bool symbolsLoaded_ = false;
330     bool debugInfoLoaded_ = false;
331     bool debugInfoLoadResult_ = false;
332     const std::string FindSymbolFile(const std::vector<std::string> &,
333                                      std::string symboleFilePath = EMPTY_STRING) const;
334 
335     std::string SearchReadableFile(const std::vector<std::string> &searchPaths,
336                                    const std::string &filePath) const;
337     bool UpdateBuildIdIfMatch(std::string buildId);
338     std::string buildId_;
339     std::vector<std::string> symbolsFileSearchPaths_;
340     std::vector<Symbol> symbols_ {};
341     std::vector<Symbol *> matchedSymbols_ {};
342     std::vector<FileSymbol> fileSymbols_ {};
343 
344     void AdjustSymbols();
345     void SortMatchedSymbols();
346     bool CheckPathReadable(const std::string &path) const;
347 
348     FRIEND_TEST(SymbolsFileTest, FindSymbolFile);
349     FRIEND_TEST(SymbolsFileTest, UpdateBuildIdIfMatch);
350     FRIEND_TEST(SymbolsFileTest, exportSymbolToFileFormat);
351     FRIEND_TEST(SymbolsFileTest, exportSymbolToFileFormatMatched);
352     friend class VirtualRuntimeTest;
353     FRIEND_TEST(ReportJsonFileTest, ProcessSymbolsFiles);
354     FRIEND_TEST(ReportProtobufFileTest, ProcessSymbolsFiles);
355     friend class ReportProtobufFileTest;
356 };
357 } // namespace HiPerf
358 } // namespace Developtools
359 } // namespace OHOS
360 #endif // HIPERF_SYMBOLS_H
361