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