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 §ionVaddr, 295 [[maybe_unused]] uint64_t §ionSize, 296 [[maybe_unused]] uint64_t §ionFileOffset) 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