• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 #include <gtest/gtest.h>
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 NativeDaemon {
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 const std::string NOTE_GNU_BUILD_ID = ".note.gnu.build-id";
39 const std::string EH_FRAME_HR = ".eh_frame_hdr";
40 const std::string EH_FRAME = ".eh_frame";
41 const std::string ARM_EXIDX = ".ARM.exidx";
42 const std::string SYMTAB = ".symtab";
43 const std::string DYNSYM = ".dynsym";
44 const std::string GNU_DEBUGDATA = ".gnu_debugdata";
45 const std::string PLT = ".plt";
46 const std::string LINKER_PREFIX = "__dl_";
47 const std::string LINKER_PREFIX_NAME = "[linker]";
48 
49 const int MAX_SYMBOLS_TYPE_NAME_LEN = 10;
50 
51 class FileSymbol {
52     [[maybe_unused]] uint64_t vaddr_ = 0;
53     [[maybe_unused]] uint64_t len_ = 0;
54     std::string name_ = "";
55     std::string demangle_ = ""; // demangle string
FileSymbol(uint64_t vaddr,uint64_t len,const char * name,const char * demangle)56     FileSymbol(uint64_t vaddr, uint64_t len, const char *name, const char *demangle)
57         : vaddr_(vaddr), len_(len), name_(name), demangle_(demangle)
58     {
59     }
60 };
61 
62 struct Symbol {
63     uint64_t funcVaddr_ = 0;
64     uint64_t fileVaddr_ = 0;
65     uint64_t taskVaddr_ = 0;
66     uint64_t offset_ = 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     std::string_view symbolName_ = "";
74     mutable std::string_view unknow_ = "";
75     mutable bool matched_ = false; // if some callstack match this
76     int32_t hit_ = 0;
77 
78     // elf use this
SymbolSymbol79     Symbol(uint64_t vaddr, uint64_t len, const std::string &name, const std::string &demangle,
80            const std::string module)
81         : funcVaddr_(vaddr),
82           fileVaddr_(vaddr),
83           offset_(fileVaddr_ - funcVaddr_),
84           len_(len),
85           name_(memHolder.HoldStringView(name)),
86           demangle_(memHolder.HoldStringView(demangle)),
87           module_(memHolder.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 
SameVaddrSymbol99     static bool SameVaddr(const Symbol &a, const Symbol &b)
100     {
101         return (a.funcVaddr_ == b.funcVaddr_);
102     }
SameSymbol103     bool Same(const Symbol &b) const
104     {
105         return (funcVaddr_ == b.funcVaddr_ and demangle_ == b.demangle_);
106     }
107     bool operator==(const Symbol &b) const
108     {
109         return Same(b);
110     }
111 
112     bool operator!=(const Symbol &b) const
113     {
114         return !Same(b);
115     }
116 
isValidSymbol117     bool isValid() const
118     {
119         return !module_.empty();
120     }
121 
SetMatchFlagSymbol122     void SetMatchFlag() const
123     {
124         matched_ = true;
125     }
126 
HasMatchedSymbol127     bool HasMatched() const
128     {
129         return matched_;
130     }
131 
setIpVAddressSymbol132     void setIpVAddress(uint64_t vaddr)
133     {
134         fileVaddr_ = vaddr;
135         offset_ = fileVaddr_ - funcVaddr_;
136     }
137 
NameSymbol138     std::string_view Name() const
139     {
140         if (!demangle_.empty()) {
141             return demangle_;
142         }
143         if (!name_.empty()) {
144             return name_;
145         }
146         if (unknow_.empty()) {
147             std::stringstream sstream;
148             if (!module_.empty()) {
149                 sstream << module_ << "+0x" << std::hex << fileVaddr_;
150             } else {
151                 sstream << comm_ << "@0x" << std::hex << taskVaddr_;
152             }
153             unknow_ = memHolder.HoldStringView(sstream.str());
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 
182         return sstream.str();
183     };
184 
ContainSymbol185     bool Contain(uint64_t addr) const
186     {
187         if (len_ == 0) {
188             return funcVaddr_ <= addr;
189         } else {
190             return (funcVaddr_ <= addr) and ((funcVaddr_ + len_) > addr);
191         }
192     }
193 
194     // The range [first, last) must be partitioned with respect to the expression !(value < element)
195     // or !comp(value, element)
ValueLessThenSymbol196     static bool ValueLessThen(uint64_t vaddr, const Symbol &a)
197     {
198         return vaddr < a.funcVaddr_;
199     }
200 
ValueLessEqualSymbol201     static bool ValueLessEqual(uint64_t vaddr, const Symbol &a)
202     {
203         return vaddr <= a.funcVaddr_;
204     }
205 
CompareLessThenSymbol206     static bool CompareLessThen(const Symbol &a, const Symbol &b)
207     {
208         return a.funcVaddr_ < b.funcVaddr_; // we should use vaddr to sort
209     }
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 infor 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 
354 class CCompareSymbolsFile {
355 public:
operator()356     bool operator() (const std::unique_ptr<SymbolsFile>& left, const std::unique_ptr<SymbolsFile>& right) const
357     {
358         return left->filePath_ < right->filePath_;
359     }
360 };
361 } // namespace NativeDaemon
362 } // namespace Developtools
363 } // namespace OHOS
364 #endif