• 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 
104     Symbol& operator=(const Symbol& other) = default;
105 
SameVaddrSymbol106     static bool SameVaddr(const Symbol &a, const Symbol &b)
107     {
108         return (a.funcVaddr_ == b.funcVaddr_);
109     }
SameSymbol110     bool Same(const Symbol &b) const
111     {
112         return (funcVaddr_ == b.funcVaddr_ and demangle_ == b.demangle_);
113     }
114     bool operator==(const Symbol &b) const
115     {
116         return Same(b);
117     }
118 
119     bool operator!=(const Symbol &b) const
120     {
121         return !Same(b);
122     }
123 
isValidSymbol124     bool isValid() const
125     {
126         return !module_.empty();
127     }
128 
SetMatchFlagSymbol129     void SetMatchFlag() const
130     {
131         matched_ = true;
132     }
133 
HasMatchedSymbol134     inline bool HasMatched() const
135     {
136         return matched_;
137     }
138 
NameSymbol139     std::string_view Name() const
140     {
141         if (!demangle_.empty()) {
142             return demangle_;
143         }
144         if (!name_.empty()) {
145             return name_;
146         }
147         if (unknow_.empty()) {
148             std::stringstream sstream;
149             if (!module_.empty()) {
150                 sstream << module_ << "+0x" << std::hex << fileVaddr_;
151             } else {
152                 sstream << comm_ << "@0x" << std::hex << taskVaddr_;
153             }
154             std::string hold = sstream.str();
155             unknow_ = MemoryHold::Get().HoldStringView(hold);
156         }
157         return unknow_;
158     }
159 
ToStringSymbol160     std::string ToString() const
161     {
162         std::stringstream sstream;
163         if (fileVaddr_ != 0) {
164             sstream << "0x" << std::hex << fileVaddr_;
165         } else {
166             sstream << "0x" << std::hex << taskVaddr_;
167         }
168         sstream << " " << Name();
169         return sstream.str();
170     };
171 
ToDebugStringSymbol172     std::string ToDebugString() const
173     {
174         std::stringstream sstream;
175         sstream << "0x" << std::setfill('0') << std::setw(sizeof(funcVaddr_) * BYTE_PRINT_WIDTH)
176                 << std::hex << funcVaddr_;
177         sstream << "|";
178         sstream << std::setfill('0') << std::setw(sizeof(len_)) << len_;
179         sstream << "|";
180         sstream << demangle_ << "|";
181         sstream << name_ << "|";
182         sstream << (matched_ ? "matched" : "");
183         sstream << " unknowname:" << unknow_.size();
184         sstream << " task:" << (comm_.size() > 0 ? comm_ : "");
185         sstream << "@" << taskVaddr_;
186         sstream << " file:" << (module_.size() > 0 ? module_ : "");
187         sstream << "@" << fileVaddr_;
188 
189         return sstream.str();
190     };
191 
ContainSymbol192     bool Contain(uint64_t addr) const
193     {
194         if (len_ == 0) {
195             return funcVaddr_ <= addr;
196         } else {
197             return (funcVaddr_ <= addr) and ((funcVaddr_ + len_) > addr);
198         }
199     }
200 
201     // The range [first, last) must be partitioned with respect to the expression !(value < element)
202     // or !comp(value, element)
ValueLessThenSymbol203     static bool ValueLessThen(uint64_t vaddr, const Symbol &a)
204     {
205         return vaddr < a.funcVaddr_;
206     }
ValueLessEqualSymbol207     static bool ValueLessEqual(uint64_t vaddr, const Symbol &a)
208     {
209         return vaddr <= a.funcVaddr_;
210     }
CompareLessThenSymbol211     static bool CompareLessThen(const Symbol &a, const Symbol &b)
212     {
213         return a.funcVaddr_ < b.funcVaddr_; // we should use vaddr to sort
214     };
CompareByPointerSymbol215     static bool CompareByPointer(const Symbol *a, const Symbol *b)
216     {
217         return a->funcVaddr_ < b->funcVaddr_; // we should use vaddr to sort
218     };
219 };
220 
221 enum SymbolsFileType {
222     SYMBOL_KERNEL_FILE,
223     SYMBOL_KERNEL_MODULE_FILE,
224     SYMBOL_ELF_FILE,
225     SYMBOL_JAVA_FILE,
226     SYMBOL_JS_FILE,
227     SYMBOL_UNKNOW_FILE,
228 };
229 
230 class SymbolsFile {
231 public:
232     SymbolsFileType symbolFileType_;
233     std::string filePath_ = "";
234     int32_t id_ = -1; // used to report protobuf file
235 
236     // [14] .text             PROGBITS         00000000002c5000  000c5000
237     // min exec addr , general it point to .text
238     // we make a default value for min compare
239     static const uint64_t maxVaddr = std::numeric_limits<uint64_t>::max();
240 
241     uint64_t textExecVaddr_ = maxVaddr;
242     uint64_t textExecVaddrFileOffset_ = 0;
243     uint64_t textExecVaddrRange_ = maxVaddr;
244 
SymbolsFile(SymbolsFileType symbolType,const std::string path)245     SymbolsFile(SymbolsFileType symbolType, const std::string path)
246         : symbolFileType_(symbolType), filePath_(path) {};
247     virtual ~SymbolsFile();
248 
249     // create the symbols file object
250     static std::unique_ptr<SymbolsFile> CreateSymbolsFile(
251         SymbolsFileType = SYMBOL_UNKNOW_FILE, const std::string symbolFilePath = EMPTY_STRING);
252     static std::unique_ptr<SymbolsFile> CreateSymbolsFile(const std::string &symbolFilePath);
253 
254     // set symbols path
setSymbolsFilePath(const std::string & symbolsSearchPath)255     bool setSymbolsFilePath(const std::string &symbolsSearchPath)
256     {
257         std::vector<std::string> symbolsSearchPaths = {symbolsSearchPath};
258         return setSymbolsFilePath(symbolsSearchPaths);
259     };
260     bool setSymbolsFilePath(const std::vector<std::string> &);
261 
262     // load symbol from file
263     virtual bool LoadSymbols([[maybe_unused]] const std::string &symbolFilePath = EMPTY_STRING)
264     {
265         HLOGV("virtual dummy function called");
266         symbolsLoaded_ = true;
267         return false;
268     };
269     // load debug info for unwind
270     virtual bool LoadDebugInfo([[maybe_unused]] const std::string &symbolFilePath = EMPTY_STRING)
271     {
272         HLOGV("virtual dummy function called");
273         debugInfoLoaded_ = true;
274         return false;
275     };
276     // get the build if from symbols
277     const std::string GetBuildId() const;
278 
279     // get the symbols vector
280     const std::vector<Symbol> &GetSymbols();
281     const std::vector<Symbol *> &GetMatchedSymbols();
282 
283     // get vaddr(in symbol) from ip(real addr , after mmap reloc)
284     virtual uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t mapOffset) const;
285 
286     // get symbols from vaddr
287     const Symbol GetSymbolWithVaddr(uint64_t vaddr);
288 
289     // read the .text section and .eh_frame section (RO) memory from elf mmap
290     // unwind use this to check the DWARF and so on
ReadRoMemory(uint64_t,uint8_t * const,size_t)291     virtual size_t ReadRoMemory(uint64_t, uint8_t * const, size_t) const
292     {
293         HLOGV("virtual dummy function called");
294         return 0; // default not support
295     }
296 
297     // get the section info , like .ARM.exidx
GetSectionInfo(const std::string & name,uint64_t & sectionVaddr,uint64_t & sectionSize,uint64_t & sectionFileOffset)298     virtual bool GetSectionInfo([[maybe_unused]] const std::string &name,
299                                 [[maybe_unused]] uint64_t &sectionVaddr,
300                                 [[maybe_unused]] uint64_t &sectionSize,
301                                 [[maybe_unused]] uint64_t &sectionFileOffset) const
302     {
303         HLOGV("virtual dummy function called");
304         return false;
305     }
306 #ifndef __arm__
307     // get hdr info for unwind , need provide the fde table location and entry count
GetHDRSectionInfo(uint64_t & ehFrameHdrElfOffset,uint64_t & fdeTableElfOffset,uint64_t & fdeTableSize)308     virtual bool GetHDRSectionInfo([[maybe_unused]] uint64_t &ehFrameHdrElfOffset,
309                                    [[maybe_unused]] uint64_t &fdeTableElfOffset,
310                                    [[maybe_unused]] uint64_t &fdeTableSize) const
311     {
312         HLOGV("virtual dummy function called");
313         return false;
314     }
315 #endif
316     // load from symbols from the perf.data format
317     static std::unique_ptr<SymbolsFile> LoadSymbolsFromSaved(const SymbolFileStruct &);
318     // save the symbols to perf.data format
319     void ExportSymbolToFileFormat(SymbolFileStruct &symbolFileStruct);
320 
SymbolsLoaded()321     bool SymbolsLoaded()
322     {
323         return symbolsLoaded_;
324     }
325 
326     // this means we are in recording
327     // will try read some elf in runtime path
328     static bool onRecording_;
329 
330 protected:
331     bool symbolsLoaded_ = false;
332     bool debugInfoLoaded_ = false;
333     bool debugInfoLoadResult_ = false;
334     const std::string FindSymbolFile(const std::vector<std::string> &,
335                                      std::string symboleFilePath = EMPTY_STRING) const;
336 
337     std::string SearchReadableFile(const std::vector<std::string> &searchPaths,
338                                    const std::string &filePath) const;
339     bool UpdateBuildIdIfMatch(std::string buildId);
340     std::string buildId_;
341     std::vector<std::string> symbolsFileSearchPaths_;
342     std::vector<Symbol> symbols_ {};
343     std::vector<Symbol *> matchedSymbols_ {};
344     std::vector<FileSymbol> fileSymbols_ {};
345 
346     void AdjustSymbols();
347     void SortMatchedSymbols();
348     bool CheckPathReadable(const std::string &path) const;
349 
350     FRIEND_TEST(SymbolsFileTest, FindSymbolFile);
351     FRIEND_TEST(SymbolsFileTest, UpdateBuildIdIfMatch);
352     FRIEND_TEST(SymbolsFileTest, exportSymbolToFileFormat);
353     FRIEND_TEST(SymbolsFileTest, exportSymbolToFileFormatMatched);
354     friend class VirtualRuntimeTest;
355     FRIEND_TEST(ReportJsonFileTest, ProcessSymbolsFiles);
356     FRIEND_TEST(ReportProtobufFileTest, ProcessSymbolsFiles);
357     friend class ReportProtobufFileTest;
358 };
359 } // namespace HiPerf
360 } // namespace Developtools
361 } // namespace OHOS
362 #endif // HIPERF_SYMBOLS_H
363