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