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