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 #define HILOG_TAG "Symbols"
17
18 #include "symbols_file.h"
19
20 #include <algorithm>
21 #include <chrono>
22 #include <cxxabi.h>
23 #include <fcntl.h>
24 #include <fstream>
25
26 #if is_mingw
27 #include <memoryapi.h>
28 #else
29 #include <sys/mman.h>
30 #include <sys/stat.h>
31 #endif
32
33 #include <cstdlib>
34 #include <unistd.h>
35
36 #include "dwarf_encoding.h"
37 #include "elf_parser.h"
38 #include "utilities.h"
39
40 using namespace OHOS::Developtools::HiPerf::ELF;
41 using namespace std::chrono;
42
43 namespace OHOS {
44 namespace Developtools {
45 namespace HiPerf {
46 bool SymbolsFile::onRecording_ = true;
GetBuildId() const47 const std::string SymbolsFile::GetBuildId() const
48 {
49 return buildId_;
50 }
51
UpdateBuildIdIfMatch(std::string buildId)52 bool SymbolsFile::UpdateBuildIdIfMatch(std::string buildId)
53 {
54 /*
55 here we have two case
56 1 buildId_ is empty
57 a) always return match
58 2 buildId_ is not empty
59 a) really check if the same one
60 */
61
62 if (buildId_.empty()) {
63 // we have new empty build
64 if (buildId.empty()) {
65 // both empty , no build id provided
66 HLOGD("build id is empty.");
67 return true;
68 } else {
69 buildId_ = buildId;
70 HLOGD("new buildId %s", buildId_.c_str());
71 return true;
72 }
73 } else {
74 // we already have a build id
75 // so this is not the first time load symbol
76 // we need check if it match
77 HLOGV("expected buildid: %s vs %s", buildId_.c_str(), buildId.c_str());
78
79 if (buildId_ != buildId) {
80 HLOGW("id not match");
81 return false;
82 } else {
83 HLOGD("id match");
84 return true;
85 }
86 }
87 }
88
SearchReadableFile(const std::vector<std::string> & searchPaths,const std::string & filePath) const89 std::string SymbolsFile::SearchReadableFile(const std::vector<std::string> &searchPaths,
90 const std::string &filePath) const
91 {
92 if (filePath.empty()) {
93 HLOGW("nothing to found");
94 return filePath;
95 }
96 for (auto searchPath : searchPaths) {
97 if (searchPath.back() != PATH_SEPARATOR) {
98 searchPath += PATH_SEPARATOR;
99 }
100 std::string PossibleFilePath = searchPath + filePath;
101 if (CheckPathReadable(PossibleFilePath)) {
102 return PossibleFilePath;
103 }
104 HLOGW("have not found '%s' in search paths %s", filePath.c_str(), searchPath.c_str());
105 }
106 return EMPTY_STRING;
107 }
108
FindSymbolFile(const std::vector<std::string> & symbolsFileSearchPaths,std::string symboleFilePath) const109 const std::string SymbolsFile::FindSymbolFile(
110 const std::vector<std::string> &symbolsFileSearchPaths, std::string symboleFilePath) const
111 {
112 /*
113 this function do 2 things:
114 find by name:
115 1 find dso path
116 2 find search path
117 a) search path + dso path
118 b) search path + dso name
119
120 show we should return filePath_ as default ?
121 */
122 if (symboleFilePath.empty()) {
123 symboleFilePath = filePath_;
124 HLOGD("use default filename: %s ", symboleFilePath.c_str());
125 }
126 symboleFilePath = PlatformPathConvert(symboleFilePath);
127 std::string foundPath;
128 // search first if we have path
129 if (symbolsFileSearchPaths.size() != 0) {
130 foundPath = SearchReadableFile(symbolsFileSearchPaths, symboleFilePath);
131 if (foundPath.empty()) {
132 HLOGV("try base name for: %s split with %s", symboleFilePath.c_str(),
133 PATH_SEPARATOR_STR.c_str());
134 auto pathSplit = StringSplit(symboleFilePath, PATH_SEPARATOR_STR);
135 if (pathSplit.size() > 1) {
136 HLOGV("base name is: %s ", pathSplit.back().c_str());
137 // found it again with base name , split it and get last name
138 foundPath = SearchReadableFile(symbolsFileSearchPaths, pathSplit.back());
139 }
140 }
141 }
142
143 // only access the patch in onRecording_
144 // in report mode we don't load any thing in runtime path
145 if (foundPath.empty() and onRecording_) {
146 // try access direct at last
147 if (CheckPathReadable(symboleFilePath)) {
148 // found direct folder
149 HLOGD("find %s in current work dir", symboleFilePath.c_str());
150 return symboleFilePath;
151 }
152 }
153 return foundPath;
154 }
155
156 class ElfFileSymbols : public SymbolsFile {
157 public:
ElfFileSymbols(const std::string & symbolFilePath,const SymbolsFileType symbolsFileType=SYMBOL_ELF_FILE)158 explicit ElfFileSymbols(const std::string &symbolFilePath,
159 const SymbolsFileType symbolsFileType = SYMBOL_ELF_FILE)
160 : SymbolsFile(symbolsFileType, symbolFilePath)
161 {
162 }
163
~ElfFileSymbols()164 virtual ~ElfFileSymbols()
165 {
166 if (mmap_ != MMAP_FAILED) {
167 if (munmap(mmap_, mmapSize_) != 0) {
168 HLOGE("munmap failed with %p", munmap);
169 }
170 }
171 }
172
LoadSymbols(const std::string & symbolFilePath)173 bool LoadSymbols(const std::string &symbolFilePath) override
174 {
175 symbolsLoaded_ = true;
176 std::string findPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
177 if (findPath.empty()) {
178 HLOGW("elf found failed (belong to %s)", filePath_.c_str());
179 return false;
180 }
181 if (LoadElfSymbols(findPath)) {
182 return true;
183 } else {
184 HLOGW("elf open failed with '%s'", findPath.c_str());
185 return false;
186 }
187 return false;
188 }
189
ReadRoMemory(uint64_t addr,uint8_t * const data,size_t size) const190 size_t ReadRoMemory(uint64_t addr, uint8_t * const data, size_t size) const override
191 {
192 size_t readSize = 0;
193
194 if (mmap_ != MMAP_FAILED) {
195 if ((addr + size) <= mmapSize_) {
196 std::copy_n(static_cast<uint8_t *>(mmap_) + addr, size, data);
197 readSize = size;
198 } else {
199 HLOGW("read out of range.");
200 HLOGW("try read 0x%" PRIx64 "(elf offset)+%zu max is 0x%" PRIx64 "", addr, size,
201 mmapSize_);
202 }
203 } else {
204 if (readFd_ != nullptr) {
205 if (fseek(readFd_.get(), addr, SEEK_SET) != 0) {
206 return 0;
207 }
208 if (fread(data, size, 1u, readFd_.get())) {
209 readSize = size;
210 } else {
211 HLOGEP("read at %" PRIx64 " failed for %s", addr, filePath_.c_str());
212 }
213 }
214 HLOGM("no mmap files loaded");
215 }
216
217 HLOGM("read %zu/%zu bytes at %" PRIx64 "(elf offset)", readSize, size, addr);
218
219 return readSize;
220 }
221
222 protected:
CovertByteBufferToHexString(const unsigned char * buffer,size_t size) const223 std::string CovertByteBufferToHexString(const unsigned char *buffer, size_t size) const
224 {
225 std::string descString;
226 size_t i = 0;
227 while (i < size) {
228 descString.append(ToHex(buffer[i]));
229 i++; // move to next char
230 }
231 return descString;
232 }
233
ElfGetBuildId(const unsigned char * buffer,size_t size) const234 std::string ElfGetBuildId(const unsigned char *buffer, size_t size) const
235 {
236 const unsigned char *end = buffer + size;
237 HLOGV("size:%zu", size);
238
239 /*
240 Note Section
241 A vendor or system engineer might need to mark an object file with special
242 information that other programs can check for conformance or compatibility. Sections
243 of type SHT_NOTE and program header elements of type PT_NOTE can be used for this
244 purpose.
245
246 The note information in sections and program header elements holds any number of
247 entries, as shown in the following figure. For 64–bit objects and 32–bit objects,
248 each entry is an array of 4-byte words in the format of the target processor. Labels
249 are shown in Figure 12-6 to help explain note information organization, but are not
250 part of the specification.
251
252 Figure 12-5 Note Information
253
254 image:ELF note section information.
255 namesz and name
256 The first namesz bytes in name contain a null-terminated character representation of
257 the entry's owner or originator. No formal mechanism exists for avoiding name
258 conflicts. By convention, vendors use their own name, such as “XYZ Computer
259 Company,” as the identifier. If no name is present, namesz contains the value zero.
260 Padding is present, if necessary, to ensure 4-byte alignment for the descriptor.
261 Such padding is not included in namesz.
262
263 descsz and desc
264 The first descsz bytes in desc hold the note descriptor. If no descriptor is
265 present, descsz contains the value zero. Padding is present, if necessary, to ensure
266 4-byte alignment for the next note entry. Such padding is not included in descsz.
267
268 type
269 Provides the interpretation of the descriptor. Each originator controls its own
270 types. Multiple interpretations of a single type value can exist. A program must
271 recognize both the name and the type to understand a descriptor. Types currently
272 must be nonnegative.
273
274 The note segment that is shown in the following figure holds two entries.
275 */
276
277 // namesz + descsz + type
278 static constexpr const int elfNoteSectionLens = sizeof(uint32_t) * 3;
279
280 while (end - buffer >= elfNoteSectionLens) {
281 uint32_t namesz;
282 uint32_t descsz;
283 uint32_t type;
284 CopyFromBufferAndMove(buffer, &namesz);
285 CopyFromBufferAndMove(buffer, &descsz);
286 CopyFromBufferAndMove(buffer, &type);
287
288 // to ensure 4-byte alignment for the descriptor.
289 constexpr const int elfNoteSectionNameAlign = 4;
290
291 namesz = RoundUp(namesz, elfNoteSectionNameAlign);
292 descsz = RoundUp(descsz, elfNoteSectionNameAlign);
293 HLOGM("namesz:%u descsz:%u type:%u", namesz, descsz, type);
294
295 // size enough ?
296 if (buffer >= end) {
297 return EMPTY_STRING;
298 }
299 if (type == NT_GNU_BUILD_ID) {
300 char name[namesz + 1];
301 CopyFromBufferAndMove(buffer, &name[0], namesz);
302 name[namesz] = 0;
303 HLOGM("found buildid name:%s", name);
304 if (strcmp(name, ELF_NOTE_GNU) == 0) {
305 std::string descString = CovertByteBufferToHexString(buffer, descsz);
306 HLOGD("found buildid:%s", descString.c_str());
307 return descString;
308 } else {
309 // next
310 buffer += descsz;
311 }
312 } else {
313 // next
314 buffer += namesz + descsz;
315 }
316 }
317 return EMPTY_STRING; // found nothing
318 }
319
LoadDebugInfo(const std::string & symbolFilePath)320 bool LoadDebugInfo(const std::string &symbolFilePath) override
321 {
322 if (debugInfoLoaded_) {
323 return true;
324 } else {
325 debugInfoLoaded_ = true;
326 }
327 std::string elfPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
328 if (elfPath.empty()) {
329 HLOGW("elf found failed (belong to %s)", filePath_.c_str());
330 return false;
331 }
332 std::unique_ptr<ElfFile> elfFile = LoadElfFile(elfPath);
333 if (elfFile == nullptr) {
334 HLOGD("elf load failed");
335 return false;
336 } else {
337 HLOGD("loaded elf %s", elfPath.c_str());
338 }
339 for (auto &phdr : elfFile->phdrs_) {
340 if ((phdr->type_ == PT_LOAD) && (phdr->flags_ & PF_X)) {
341 // find the min addr
342 if (textExecVaddr_ != std::min(textExecVaddr_, phdr->vaddr_)) {
343 textExecVaddr_ = std::min(textExecVaddr_, phdr->vaddr_);
344 textExecVaddrFileOffset_ = phdr->offset_;
345 }
346 }
347 }
348
349 HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
350 textExecVaddrFileOffset_);
351
352 if (!ParseShdr(std::move(elfFile))) {
353 return false;
354 }
355
356 // mmap it for later use
357 LoadFileToMemory(elfPath);
358 return true;
359 }
360
361 private:
362 bool EhFrameHDRValid_ {false};
363 uint64_t ehFrameHDRElfOffset_ {0};
364 uint64_t ehFrameHDRFdeCount_ {0};
365 uint64_t ehFrameHDRFdeTableItemSize_ {0};
366 uint64_t ehFrameHDRFdeTableElfOffset_ {0};
367 OHOS::UniqueFd fd_ {-1};
368 std::unique_ptr<FILE, decltype(&fclose)> readFd_ {nullptr, &fclose};
369 struct ShdrInfo {
370 uint64_t sectionVaddr_;
371 uint64_t sectionSize_;
372 uint64_t sectionFileOffset_;
ShdrInfoOHOS::Developtools::HiPerf::ElfFileSymbols::ShdrInfo373 ShdrInfo(uint64_t sectionVaddr, uint64_t sectionSize, uint64_t sectionFileOffset)
374 : sectionVaddr_(sectionVaddr),
375 sectionSize_(sectionSize),
376 sectionFileOffset_(sectionFileOffset)
377 {
378 }
379 };
380 std::map<const std::string, ShdrInfo> shdrMap_ {};
381 void *mmap_ {MMAP_FAILED};
382 uint64_t mmapSize_ = {0};
383
GetReadableName(const std::string & name) const384 const std::string GetReadableName(const std::string &name) const
385 {
386 int status = 0;
387 const char *nameStart = name.c_str();
388 bool linkerName = false;
389 if (StringStartsWith(name, LINKER_PREFIX)) {
390 nameStart += LINKER_PREFIX.size();
391 linkerName = true;
392 }
393
394 char *demangle = abi::__cxa_demangle(nameStart, nullptr, nullptr, &status);
395 if (status == 0) {
396 std::string demangleName = demangle;
397 free(static_cast<void *>(demangle));
398 return linkerName ? (LINKER_PREFIX_NAME + demangleName) : demangleName;
399 } else {
400 return linkerName ? (LINKER_PREFIX_NAME + nameStart) : nameStart;
401 }
402 }
403
ElfStTypeName(unsigned char stt) const404 const std::string ElfStTypeName(unsigned char stt) const
405 {
406 switch (stt) {
407 case STT_FUNC:
408 return "function";
409 case STT_GNU_IFUNC:
410 return "gun_func";
411 case STT_OBJECT:
412 return " object";
413 default:
414 return " unknown";
415 }
416 }
417
GetSectionInfo(const std::string & name,uint64_t & sectionVaddr,uint64_t & sectionSize,uint64_t & sectionFileOffset) const418 bool GetSectionInfo(const std::string &name, uint64_t §ionVaddr, uint64_t §ionSize,
419 uint64_t §ionFileOffset) const override
420 {
421 HLOGM("Section '%s' found in %zu", name.c_str(), shdrMap_.size());
422 if (shdrMap_.count(name) > 0) {
423 HLOGM("Section '%s' found", name.c_str());
424 const auto &shdrInfo = shdrMap_.at(name);
425 sectionVaddr = shdrInfo.sectionVaddr_;
426 sectionSize = shdrInfo.sectionSize_;
427 sectionFileOffset = shdrInfo.sectionFileOffset_;
428 HLOGM("Get Section '%s' %" PRIx64 " - %" PRIx64 "", name.c_str(), sectionVaddr,
429 sectionSize);
430 return true;
431 } else {
432 HLOGW("Section '%s' not found", name.c_str());
433 return false;
434 }
435 }
436
437 #ifndef __arm__
GetHDRSectionInfo(uint64_t & ehFrameHdrElfOffset,uint64_t & fdeTableElfOffset,uint64_t & fdeTableSize) const438 bool GetHDRSectionInfo(uint64_t &ehFrameHdrElfOffset, uint64_t &fdeTableElfOffset,
439 uint64_t &fdeTableSize) const override
440 {
441 if (EhFrameHDRValid_) {
442 ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
443 fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
444 fdeTableSize = ehFrameHDRFdeCount_ * ehFrameHDRFdeTableItemSize_;
445 return true;
446 } else {
447 HLOGW("!EhFrameHDRValid_");
448 return false;
449 }
450 }
451 #endif
452
DumpEhFrameHDR() const453 void DumpEhFrameHDR() const
454 {
455 HLOGD(" ehFrameHDRElfOffset_: 0x%" PRIx64 "", ehFrameHDRElfOffset_);
456 HLOGD(" ehFrameHDRFdeCount_: 0x%" PRIx64 "", ehFrameHDRFdeCount_);
457 HLOGD(" ehFrameHDRFdeTableElfOffset_: 0x%" PRIx64 "", ehFrameHDRFdeTableElfOffset_);
458 HLOGD(" ehFrameHDRFdeTableItemSize_: 0x%" PRIx64 "", ehFrameHDRFdeTableItemSize_);
459 }
460
LoadEhFrameHDR(const unsigned char * buffer,size_t bufferSize,uint64_t shdrOffset)461 bool LoadEhFrameHDR(const unsigned char *buffer, size_t bufferSize, uint64_t shdrOffset)
462 {
463 eh_frame_hdr *ehFrameHdr = (eh_frame_hdr *)buffer;
464 const uint8_t *dataPtr = ehFrameHdr->encode_data;
465 DwarfEncoding dwEhFramePtr(ehFrameHdr->eh_frame_ptr_enc, dataPtr);
466 DwarfEncoding dwFdeCount(ehFrameHdr->fde_count_enc, dataPtr);
467 DwarfEncoding dwTable(ehFrameHdr->table_enc, dataPtr);
468 DwarfEncoding dwTableValue(ehFrameHdr->table_enc, dataPtr);
469
470 HLOGD("eh_frame_hdr:");
471 HexDump(ehFrameHdr, BITS_OF_FOUR_BYTE, bufferSize);
472 unsigned char version = ehFrameHdr->version;
473 HLOGD(" version: %02x:%s", version, (version == 1) ? "valid" : "invalid");
474 HLOGD(" eh_frame_ptr_enc: %s", dwEhFramePtr.ToString().c_str());
475 HLOGD(" fde_count_enc: %s", dwFdeCount.ToString().c_str());
476 HLOGD(" table_enc: %s", dwTable.ToString().c_str());
477 HLOGD(" table_enc: %s", dwTable.ToString().c_str());
478 HLOGD(" table_value_enc: %s", dwTableValue.ToString().c_str());
479 HLOGD(" table_iteam_size: %zd", dwTable.GetSize() + dwTableValue.GetSize());
480 HLOGD(" table_offset_in_hdr: %zu", dwTable.GetData() - buffer);
481
482 if (version != 1) {
483 HLOGD("eh_frame_hdr version is invalid");
484 return false;
485 }
486 EhFrameHDRValid_ = true;
487 ehFrameHDRElfOffset_ = shdrOffset;
488 ehFrameHDRFdeCount_ = dwFdeCount.GetAppliedValue();
489 ehFrameHDRFdeTableElfOffset_ = dwTable.GetData() - buffer + shdrOffset;
490 ehFrameHDRFdeTableItemSize_ = dwTable.GetSize() + dwTableValue.GetSize();
491 DumpEhFrameHDR();
492
493 if (!dwFdeCount.IsOmit() && dwFdeCount.GetValue() > 0) {
494 return true;
495 } else {
496 HLOGW("fde table not found.");
497 }
498 return false;
499 }
500
LoadFileToMemory(const std::string & loadElfPath)501 void LoadFileToMemory(const std::string &loadElfPath)
502 {
503 #ifndef HIPERF_ELF_READ_USE_MMAP
504 if (readFd_ == nullptr) {
505 std::string resolvedPath = CanonicalizeSpecPath(loadElfPath.c_str());
506 FILE *fp = fopen(resolvedPath.c_str(), "rb");
507 if (fp == nullptr) {
508 return;
509 }
510 readFd_ =
511 std::unique_ptr<FILE, decltype(&fclose)>(fp, &fclose);
512 return;
513 }
514 #else
515 if (fd_ != -1) {
516 return;
517 }
518 #if is_mingw
519 std::string resolvedPath = CanonicalizeSpecPath(loadElfPath.c_str());
520 fd_ = OHOS::UniqueFd(open(resolvedPath.c_str(), O_RDONLY | O_BINARY));
521 #else
522 std::string resolvedPath = CanonicalizeSpecPath(loadElfPath.c_str());
523 fd_ = OHOS::UniqueFd(open(resolvedPath.c_str(), O_RDONLY));
524 #endif
525 if (fd_ != -1) {
526 struct stat sb = {};
527
528 if (fstat(fd_, &sb) == -1) {
529 HLOGE("unable to check the file size");
530 } else {
531 HLOGV("file stat size %" PRIu64 "", sb.st_size);
532
533 // unmap it first
534 if (mmap_ != MMAP_FAILED) {
535 munmap(mmap_, mmapSize_);
536 }
537
538 mmap_ = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd_, 0);
539 if (mmap_ == MMAP_FAILED) {
540 HLOGE("unable to map the file size %" PRIu64 " ", sb.st_size);
541 mmapSize_ = 0;
542 } else {
543 mmapSize_ = sb.st_size;
544 HLOGD("mmap build with size %" PRIu64 " ", mmapSize_);
545 }
546 }
547 } else {
548 HLOGD("elf file open failed with %s by %d", loadElfPath.c_str(), errno);
549 return;
550 }
551 #endif
552 }
553
ReadSymTab(const std::unique_ptr<ElfFile> & elfFile,const ELF::SectionHeader * shdr,std::vector<Symbol> & symbolsTable) const554 bool ReadSymTab(const std::unique_ptr<ElfFile> &elfFile, const ELF::SectionHeader *shdr,
555 std::vector<Symbol> &symbolsTable) const
556 {
557 HLOGV("ParseSymTable");
558 if (!elfFile->ParseSymTable(shdr)) {
559 return false;
560 }
561
562 HLOGV("Symbol Table:%s", shdr->secTypeName_.c_str());
563 HLOGM("%*s|%16s|%4s|%s", MAX_SYMBOLS_TYPE_NAME_LEN, "type", "addr", "size", "name");
564
565 for (const std::unique_ptr<ElfSymbol> &symbol : elfFile->symTable_->symbols_) {
566 if (ELF64_ST_TYPE(symbol->symInfo_) == STT_FUNC or
567 ELF64_ST_TYPE(symbol->symInfo_) == STT_GNU_IFUNC) {
568 /*
569 name| addr|size|name
570 function|00000000c0102b8c| 56|__lookup_processor_type
571 function|00000000c0102bd4| 0|__error_p
572 function|00000000c0008224| 64|__vet_atags
573 function|00000000c0008144| 128|__fixup_smp
574 function|00000000c00081d0| 64|__fixup_pv_table
575 function|00000000c000808c| 168|__create_page_tables
576 function|00000000c0b002e0| 68|__mmap_switched
577 function|00000000c0102acc| 20|__enable_mmu
578 object|00000000c0102ac0| 0|__secondary_data
579 function|00000000c0102ae0| 20|__do_fixup_smp_on_up
580 */
581
582 std::string name = elfFile->GetStrPtr(shdr->link_, symbol->nameIndex_);
583 std::string type = ElfStTypeName(ELF64_ST_TYPE(symbol->symInfo_));
584 // this will cause malloc , maybe need do this in report ?
585 std::string demangle = GetReadableName(name);
586 HLOGV("%10s|%016" PRIx64 "|%4" PRIu64 "|%s", type.c_str(), symbol->symValue_,
587 symbol->symSize_, demangle.c_str());
588
589 if (symbol->symValue_ == 0) {
590 continue; // we don't need 0 addr symbol
591 }
592 symbolsTable.emplace_back(symbol->symValue_, symbol->symSize_, name, demangle,
593 filePath_);
594 } else {
595 continue;
596 }
597 } // for symbols
598 return true;
599 }
600
ParseShdr(const std::unique_ptr<ElfFile> elfFile)601 bool ParseShdr(const std::unique_ptr<ElfFile> elfFile)
602 {
603 // only save the section info , not actually read any file content
604 for (const auto &shdrPair : elfFile->shdrs_) {
605 const auto &shdr = shdrPair.second;
606 const char *sh_name =
607 elfFile->GetStrPtr(elfFile->ehdr_->shdrStrTabIdx_, shdr->nameIndex_);
608 const unsigned char *data = elfFile->GetSectionData(shdr->secIndex_);
609
610 if (sh_name == nullptr || data == nullptr) {
611 HLOGE("name %p or data %p get failed.", sh_name, data);
612 return false;
613 }
614
615 HLOGV("shdr name '%s' vaddr 0x%" PRIx64 " offset 0x%" PRIx64 " size 0x%" PRIx64
616 " type 0x%" PRIx64 "(%s) index %u link 0x%u entry 0x%" PRIx64 "",
617 sh_name, shdr->secVaddr_, shdr->fileOffset_, shdr->secSize_, shdr->secType_,
618 shdr->secTypeName_.c_str(), shdr->secIndex_, shdr->link_, shdr->secEntrySize_);
619
620 shdrMap_.emplace(sh_name, ShdrInfo(shdr->secVaddr_, shdr->secSize_, shdr->fileOffset_));
621 }
622 return true;
623 }
624
AddSectionAsSymbol(const std::unique_ptr<ELF::SectionHeader> & shdr,const char * name,std::vector<Symbol> & symbolsTable) const625 void AddSectionAsSymbol(const std::unique_ptr<ELF::SectionHeader> &shdr, const char *name,
626 std::vector<Symbol> &symbolsTable) const
627 {
628 HLOGV("add section %s as function symbol from 0x%" PRIx64 " size 0x%" PRIx64 "", name,
629 shdr->secVaddr_, shdr->secSize_);
630 symbolsTable.emplace_back(shdr->secVaddr_, shdr->secSize_, name, name, filePath_);
631 }
632
ParseShdr(const std::unique_ptr<ElfFile> elfFile,std::vector<Symbol> & symbolsTable,std::string & buildIdFound)633 bool ParseShdr(const std::unique_ptr<ElfFile> elfFile, std::vector<Symbol> &symbolsTable,
634 std::string &buildIdFound)
635 {
636 const ELF::SectionHeader *symTableShdr = nullptr;
637 // walkthough
638 for (const auto &shdrPair : elfFile->shdrs_) {
639 const auto &shdr = shdrPair.second;
640 const char *sh_name =
641 elfFile->GetStrPtr(elfFile->ehdr_->shdrStrTabIdx_, shdr->nameIndex_);
642 const unsigned char *data = elfFile->GetSectionData(shdr->secIndex_);
643
644 if (sh_name == nullptr || data == nullptr) {
645 HLOGE("name %p or data %p get failed.", sh_name, data);
646 return false;
647 }
648
649 HLOGVVV("shdr name '%s' vaddr 0x%" PRIx64 " offset 0x%" PRIx64 " size 0x%" PRIx64
650 " type 0x%" PRIx64 "(%s) index %u link 0x%u entry 0x%" PRIx64 "",
651 sh_name, shdr->secVaddr_, shdr->fileOffset_, shdr->secSize_, shdr->secType_,
652 shdr->secTypeName_.c_str(), shdr->secIndex_, shdr->link_, shdr->secEntrySize_);
653
654 shdrMap_.emplace(sh_name, ShdrInfo(shdr->secVaddr_, shdr->secSize_, shdr->fileOffset_));
655 switch (shdr->secType_) {
656 case SHT_SYMTAB:
657 symTableShdr = shdr.get();
658 break;
659 case SHT_DYNSYM:
660 // if we already have SHT_SYMTAB ?
661 if (symTableShdr == nullptr) {
662 symTableShdr = shdr.get();
663 }
664 break;
665 case SHT_NOTE:
666 // notes
667 if (buildIdFound.empty()) {
668 // we use our function, not from gelf_getnote
669 HLOGM("found NOTE_GNU_BUILD_ID size: %" PRIu64 "", shdr->secSize_);
670
671 // there will be a log of note sh , we just need the right one
672 buildIdFound = ElfGetBuildId(data, shdr->secSize_);
673 }
674 break;
675 case SHT_PROGBITS:
676 #ifndef __arm__
677 if (EH_FRAME_HR == sh_name) {
678 LoadEhFrameHDR(data, shdr->secSize_, shdr->fileOffset_);
679 break;
680 }
681 #endif
682 if (PLT == sh_name) {
683 // this is a plt section, PLT table will put here
684 // we make it as named PLT function
685 AddSectionAsSymbol(shdr, sh_name, symbolsTable);
686 }
687 break;
688 default:
689 HLOGM("skip shdr.sh_type %" PRIx64 "", shdr->secType_);
690 break;
691 } // for shdr
692 } // for each shdrs_
693 // load symtab
694 if (symTableShdr != nullptr) {
695 if (!ReadSymTab(elfFile, symTableShdr, symbolsTable)) {
696 return false;
697 }
698 }
699 return true;
700 }
701
LoadElfFile(std::string & elfPath) const702 std::unique_ptr<ElfFile> LoadElfFile(std::string &elfPath) const
703 {
704 HLOGD("try load elf %s", elfPath.c_str());
705 if (elfPath.empty()) {
706 elfPath = filePath_;
707 HLOGD("use default elf path %s\n", elfPath.c_str());
708 }
709 return ElfFile::MakeUnique(elfPath);
710 }
711
UpdateSymbols(std::vector<Symbol> & symbolsTable,const std::string & elfPath)712 void UpdateSymbols(std::vector<Symbol> &symbolsTable, const std::string &elfPath)
713 {
714 symbols_.clear();
715 HLOGD("%zu symbols loadded from symbolsTable.", symbolsTable.size());
716
717 symbols_.swap(symbolsTable);
718
719 AdjustSymbols();
720 HLOGD("%zu symbols loadded from elf '%s'.", symbols_.size(), elfPath.c_str());
721 if (buildId_.empty()) {
722 HLOGD("buildId not found from elf '%s'.", elfPath.c_str());
723 // don't failed. some time the lib have not got the build id
724 // buildId not found from elf '/system/bin/ld-musl-arm.so.1'.
725 }
726 }
727
LoadElfSymbols(std::string elfPath)728 bool LoadElfSymbols(std::string elfPath)
729 {
730 #ifdef HIPERF_DEBUG_TIME
731 const auto startTime = steady_clock::now();
732 #endif
733 std::unique_ptr<ElfFile> elfFile = LoadElfFile(elfPath);
734 if (elfFile == nullptr) {
735 HLOGD("elf load failed");
736 return false;
737 } else {
738 HLOGD("loaded elf %s", elfPath.c_str());
739 }
740 // we prepare two table here
741 // only one we will push in to symbols_
742 // or both drop if build id is not same
743 std::vector<Symbol> symbolsTable;
744 std::string buildIdFound;
745 for (auto &phdr : elfFile->phdrs_) {
746 if ((phdr->type_ == PT_LOAD) && (phdr->flags_ & PF_X)) {
747 // find the min addr
748 if (textExecVaddr_ != std::min(textExecVaddr_, phdr->vaddr_)) {
749 textExecVaddr_ = std::min(textExecVaddr_, phdr->vaddr_);
750 textExecVaddrFileOffset_ = phdr->offset_;
751 }
752 }
753 }
754
755 HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
756 textExecVaddrFileOffset_);
757
758 if (!ParseShdr(std::move(elfFile), symbolsTable, buildIdFound)) {
759 return false;
760 }
761
762 if (UpdateBuildIdIfMatch(buildIdFound)) {
763 UpdateSymbols(symbolsTable, elfPath);
764 } else {
765 HLOGW("symbols will not update for '%s' because buildId is not match.",
766 elfPath.c_str());
767 // this mean failed . we don't goon for this.
768 return false;
769 }
770
771 // mmap it for later use
772 LoadFileToMemory(elfPath);
773 #ifdef HIPERF_DEBUG_TIME
774 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
775 if (usedTime.count() != 0) {
776 HLOGV("cost %0.3f ms to load symbols '%s'",
777 usedTime.count() / static_cast<double>(milliseconds::duration::period::den),
778 elfPath.c_str());
779 }
780 #endif
781 return true;
782 }
783
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapPageOffset) const784 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
785 uint64_t mapPageOffset) const override
786 {
787 /*
788 00200000-002c5000 r--p 00000000 08:02 46400311
789 002c5000-00490000 r-xp 000c5000 08:02 4640031
790
791 [14] .text PROGBITS 00000000002c5000 000c5000
792
793 if ip is 0x46e6ab
794 1. find the map range is 002c5000-00490000
795 2. ip - map start(002c5000) = map section offset
796 3. map section offset + map page offset(000c5000) = elf file offset
797 4. elf file offset - exec file offset(000c5000)
798 = ip offset (ip always in exec file offset)
799 5. ip offset + exec begin vaddr(2c5000) = virtual ip in elf
800 */
801 uint64_t vaddr = ip - mapStart + mapPageOffset - textExecVaddrFileOffset_ + textExecVaddr_;
802 HLOGM(" ip :0x%016" PRIx64 " -> elf offset :0x%016" PRIx64 " -> vaddr :0x%016" PRIx64 " ",
803 ip, ip - mapStart + mapPageOffset, vaddr);
804 HLOGM("(minExecAddrFileOffset_ is 0x%" PRIx64 " textExecVaddr_ is 0x%" PRIx64 ")",
805 textExecVaddrFileOffset_, textExecVaddr_);
806 return vaddr;
807 }
808 };
809
810 class KernelSymbols : public ElfFileSymbols {
811 public:
KernelSymbols(const std::string & symbolFilePath)812 explicit KernelSymbols(const std::string &symbolFilePath)
813 : ElfFileSymbols(symbolFilePath, SYMBOL_KERNEL_FILE)
814 {
815 }
816
817 static constexpr const int KSYM_MIN_TOKENS = 3;
818 static constexpr const int KSYM_DEFAULT_LINE = 35000;
819 static constexpr const int KSYM_DEFAULT_SIZE = 1024 * 1024 * 1; // 1MB
820
ParseKallsymsLine()821 bool ParseKallsymsLine()
822 {
823 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
824 const auto startTime = steady_clock::now();
825 std::chrono::microseconds parseLineTime = std::chrono::microseconds::zero();
826 std::chrono::microseconds sscanfTime = std::chrono::microseconds::zero();
827 std::chrono::microseconds newTime = std::chrono::microseconds::zero();
828 std::chrono::microseconds readFileTime = std::chrono::microseconds::zero();
829 #endif
830 size_t lines = 0;
831 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
832 const auto eachFileStartTime = steady_clock::now();
833 #endif
834 std::string kallsym;
835 if (!ReadFileToString("/proc/kallsyms", kallsym, KSYM_DEFAULT_SIZE)) {
836 HLOGW("/proc/kallsyms load failed.");
837 return false;
838 }
839 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
840 // any way we finish the line scan
841 readFileTime += duration_cast<milliseconds>(steady_clock::now() - eachFileStartTime);
842 #endif
843 // reduce the mem alloc
844 symbols_.reserve(KSYM_DEFAULT_LINE);
845
846 char *lineBegin = kallsym.data();
847 char *dataEnd = lineBegin + kallsym.size();
848 while (lineBegin < dataEnd) {
849 char *lineEnd = strchr(lineBegin, '\n');
850 if (lineEnd != nullptr) {
851 *lineEnd = '\0';
852 }
853 size_t lineSize = (lineEnd != nullptr) ? (lineEnd - lineBegin) : (dataEnd - lineBegin);
854
855 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
856 const auto eachLineStartTime = steady_clock::now();
857 #endif
858 lines++;
859 uint64_t addr = 0;
860 char type = '\0';
861
862 char nameRaw[lineSize];
863 char moduleRaw[lineSize];
864 int ret = sscanf_s(lineBegin, "%" PRIx64 " %c %s%s", &addr, &type, sizeof(type),
865 nameRaw, sizeof(nameRaw), moduleRaw, sizeof(moduleRaw));
866
867 lineBegin = lineEnd + 1;
868 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
869 // any way we finish the line scan
870 sscanfTime += duration_cast<milliseconds>(steady_clock::now() - eachLineStartTime);
871 #endif
872 if (ret >= KSYM_MIN_TOKENS) {
873 if (ret == KSYM_MIN_TOKENS) {
874 moduleRaw[0] = '\0';
875 }
876 HLOGM(" 0x%016" PRIx64 " %c '%s' '%s'", addr, type, nameRaw, moduleRaw);
877 } else {
878 HLOGW("unknown line %d: '%s'", ret, lineBegin);
879 continue;
880 }
881 std::string name = nameRaw;
882 std::string module = moduleRaw;
883
884 /*
885 T
886 The symbol is in the text (code) section.
887
888 W
889 The symbol is a weak symbol that has not been specifically
890 tagged as a weak object symbol. When a weak defined symbol is
891 linked with a normal defined symbol, the normal defined symbol
892 is used with no error. When a weak undefined symbol is linked
893 and the symbol is not defined, the value of the weak symbol
894 becomes zero with no error.
895 */
896 if (addr != 0 && strchr("TtWw", type)) {
897 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
898 const auto eachNewSymbolTime = steady_clock::now();
899 #endif
900 // we only need text symbols
901 symbols_.emplace_back(addr, name, module.empty() ? filePath_ : module);
902 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
903 newTime += duration_cast<milliseconds>(steady_clock::now() - eachNewSymbolTime);
904 #endif
905 }
906 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
907 parseLineTime += duration_cast<milliseconds>(steady_clock::now() - eachLineStartTime);
908 #endif
909 }
910 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
911 std::chrono::microseconds usedTime =
912 duration_cast<milliseconds>(steady_clock::now() - startTime);
913 printf("parse kernel symbols use : %0.3f ms\n", usedTime.count() / MS_DUARTION);
914 printf("parse line use : %0.3f ms\n", parseLineTime.count() / MS_DUARTION);
915 printf("sscanf line use : %0.3f ms\n", sscanfTime.count() / MS_DUARTION);
916 printf("new symbols use : %0.3f ms\n", newTime.count() / MS_DUARTION);
917 printf("read file use : %0.3f ms\n", readFileTime.count() / MS_DUARTION);
918 #endif
919 HLOGD("%zu line processed(%zu symbols)", lines, symbols_.size());
920 return true;
921 }
922
923 const std::string KPTR_RESTRICT = "/proc/sys/kernel/kptr_restrict";
924
LoadKernelSyms()925 bool LoadKernelSyms()
926 {
927 HLOGD("try read /proc/kallsyms");
928 if (access("/proc/kallsyms", R_OK) != 0) {
929 printf("No vmlinux path is given, and kallsyms cannot be opened\n");
930 return false;
931 }
932
933 if (ReadFileToString(KPTR_RESTRICT).front() != '0') {
934 printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n");
935 if (!WriteStringToFile(KPTR_RESTRICT, "0")) {
936 printf("/proc/sys/kernel/kptr_restrict write failed and we can't not change it.\n");
937 }
938 }
939
940 // getline end
941 if (!ParseKallsymsLine()) {
942 return false;
943 }
944
945 if (symbols_.empty()) {
946 printf("The symbol table addresses in /proc/kallsyms are all 0.\n"
947 "Please check the value of /proc/sys/kernel/kptr_restrict, it "
948 "should be 0.\n"
949 "Or provide a separate vmlinux path.\n");
950
951 if (buildId_.size() != 0) {
952 // but we got the buildid , so we make a dummpy symbols
953 HLOGD("kallsyms not found. but we have the buildid");
954 return true;
955 } else {
956 // we got nothing
957 return false;
958 }
959 } else {
960 AdjustSymbols();
961 HLOGV("%zu symbols_ loadded from kallsyms.\n", symbols_.size());
962 return true;
963 }
964 }
LoadSymbols(const std::string & symbolFilePath)965 virtual bool LoadSymbols(const std::string &symbolFilePath) override
966 {
967 symbolsLoaded_ = true;
968 HLOGV("KernelSymbols try read '%s' search paths size %zu, inDeviceRecord %d",
969 symbolFilePath.c_str(), symbolsFileSearchPaths_.size(), onRecording_);
970
971 if (onRecording_) {
972 // try read
973 HLOGD("try read /sys/kernel/notes");
974 std::string notes = ReadFileToString("/sys/kernel/notes");
975 if (notes.empty()) {
976 printf("notes cannot be opened, unable get buildid\n");
977 return false;
978 } else {
979 HLOGD("kernel notes size: %zu", notes.size());
980 buildId_ = ElfGetBuildId((const unsigned char *)notes.data(), notes.size());
981 }
982
983 const auto startTime = std::chrono::steady_clock::now();
984 if (!LoadKernelSyms()) {
985 printf("parse kalsyms failed.\n");
986 return false;
987 } else {
988 const auto thisTime = std::chrono::steady_clock::now();
989 const auto usedTimeMsTick =
990 std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime);
991 HLOGV("Load kernel symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count());
992 // load complete
993 return true;
994 }
995 } // no search path
996
997 // try vmlinux
998 return ElfFileSymbols::LoadSymbols(KERNEL_ELF_NAME);
999 }
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t) const1000 virtual uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
1001 {
1002 // ip is vaddr in /proc/kallsyms
1003 return ip;
1004 }
~KernelSymbols()1005 ~KernelSymbols() override {}
1006 };
1007
1008 class KernelModuleSymbols : public ElfFileSymbols {
1009 public:
KernelModuleSymbols(const std::string & symbolFilePath)1010 explicit KernelModuleSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
1011 {
1012 HLOGV("create %s", symbolFilePath.c_str());
1013 symbolFileType_ = SYMBOL_KERNEL_MODULE_FILE;
1014 module_ = symbolFilePath;
1015 }
~KernelModuleSymbols()1016 ~KernelModuleSymbols() override {};
1017
LoadSymbols(const std::string & symbolFilePath)1018 virtual bool LoadSymbols(const std::string &symbolFilePath) override
1019 {
1020 symbolsLoaded_ = true;
1021 if (module_ == filePath_ and onRecording_) {
1022 // file name still not convert to ko file path
1023 // this is in record mode
1024 HLOGV("find ko name %s", module_.c_str());
1025 for (const std::string &path : kernelModulePaths) {
1026 if (access(path.c_str(), R_OK) == 0) {
1027 std::string koPath = path + module_ + KERNEL_MODULES_EXT_NAME;
1028 HLOGV("found ko in %s", koPath.c_str());
1029 if (access(koPath.c_str(), R_OK) == 0) {
1030 // create symbol
1031 filePath_ = koPath;
1032 break; // find next ko
1033 }
1034 }
1035 }
1036 LoadBuildId();
1037 } else {
1038 HLOGV("we have file path, load with %s", filePath_.c_str());
1039 return ElfFileSymbols::LoadSymbols(filePath_);
1040 }
1041 return false;
1042 }
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t) const1043 virtual uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
1044 {
1045 return ip - mapStart;
1046 }
1047
1048 private:
LoadBuildId()1049 bool LoadBuildId()
1050 {
1051 std::string sysFile = "/sys/module/" + module_ + "/notes/.note.gnu.build-id";
1052 std::string buildIdRaw = ReadFileToString(sysFile);
1053 if (!buildIdRaw.empty()) {
1054 buildId_ = ElfGetBuildId((const unsigned char *)buildIdRaw.data(), buildIdRaw.size());
1055 HLOGD("kerne module %s(%s) build id %s", module_.c_str(), filePath_.c_str(),
1056 buildId_.c_str());
1057 return buildId_.empty() ? false : true;
1058 }
1059 return false;
1060 }
1061
1062 const std::vector<std::string> kernelModulePaths = {"/vendor/modules/"};
1063 std::string module_ = "";
1064 };
1065
1066 class JavaFileSymbols : public ElfFileSymbols {
1067 public:
JavaFileSymbols(const std::string & symbolFilePath)1068 explicit JavaFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
1069 {
1070 symbolFileType_ = SYMBOL_KERNEL_FILE;
1071 }
LoadSymbols(const std::string & symbolFilePath)1072 virtual bool LoadSymbols(const std::string &symbolFilePath) override
1073 {
1074 symbolsLoaded_ = true;
1075 return false;
1076 }
~JavaFileSymbols()1077 ~JavaFileSymbols() override {}
1078
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapPageOffset) const1079 virtual uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
1080 uint64_t mapPageOffset) const override
1081 {
1082 // this is different with elf
1083 // elf use ip - mapStart + mapPageOffset - minExecAddrFileOffset_ + textExecVaddr_
1084 return ip - mapStart + mapPageOffset;
1085 }
1086 };
1087
1088 class JSFileSymbols : public ElfFileSymbols {
1089 public:
JSFileSymbols(const std::string & symbolFilePath)1090 explicit JSFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
1091 {
1092 symbolFileType_ = SYMBOL_KERNEL_FILE;
1093 }
LoadSymbols(const std::string & symbolFilePath)1094 virtual bool LoadSymbols(const std::string &symbolFilePath) override
1095 {
1096 symbolsLoaded_ = true;
1097 return false;
1098 }
~JSFileSymbols()1099 ~JSFileSymbols() override {}
1100 };
1101
1102 class UnknowFileSymbols : public SymbolsFile {
1103 public:
UnknowFileSymbols(const std::string & symbolFilePath)1104 explicit UnknowFileSymbols(const std::string &symbolFilePath)
1105 : SymbolsFile(SYMBOL_UNKNOW_FILE, symbolFilePath)
1106 {
1107 }
LoadSymbols(const std::string & symbolFilePath)1108 virtual bool LoadSymbols(const std::string &symbolFilePath) override
1109 {
1110 symbolsLoaded_ = true;
1111 return false;
1112 }
~UnknowFileSymbols()1113 ~UnknowFileSymbols() override {}
1114 };
1115
~SymbolsFile()1116 SymbolsFile::~SymbolsFile() {}
1117
CreateSymbolsFile(SymbolsFileType symbolType,const std::string symbolFilePath)1118 std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(SymbolsFileType symbolType,
1119 const std::string symbolFilePath)
1120 {
1121 switch (symbolType) {
1122 case SYMBOL_KERNEL_FILE:
1123 return std::make_unique<KernelSymbols>(symbolFilePath.empty() ? KERNEL_MMAP_NAME
1124 : symbolFilePath);
1125 case SYMBOL_KERNEL_MODULE_FILE:
1126 return std::make_unique<KernelModuleSymbols>(symbolFilePath);
1127 case SYMBOL_ELF_FILE:
1128 return std::make_unique<ElfFileSymbols>(symbolFilePath);
1129 case SYMBOL_JAVA_FILE:
1130 return std::make_unique<JavaFileSymbols>(symbolFilePath);
1131 case SYMBOL_JS_FILE:
1132 return std::make_unique<JSFileSymbols>(symbolFilePath);
1133 default:
1134 return std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, symbolFilePath);
1135 }
1136 }
1137
CreateSymbolsFile(const std::string & symbolFilePath)1138 std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(const std::string &symbolFilePath)
1139 {
1140 // we need check file name here
1141 if (symbolFilePath == KERNEL_MMAP_NAME) {
1142 return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_FILE, symbolFilePath);
1143 } else if (StringEndsWith(symbolFilePath, KERNEL_MODULES_EXT_NAME)) {
1144 return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, symbolFilePath);
1145 } else {
1146 // default is elf
1147 return SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, symbolFilePath);
1148 }
1149 }
1150
AdjustSymbols()1151 void SymbolsFile::AdjustSymbols()
1152 {
1153 if (symbols_.size() <= 1u) {
1154 return;
1155 }
1156
1157 // order
1158 sort(symbols_.begin(), symbols_.end(), Symbol::CompareLessThen);
1159 HLOGV("sort completed");
1160
1161 size_t fullSize = symbols_.size();
1162 size_t erased = 0;
1163
1164 // Check for duplicate vaddr
1165 auto last = std::unique(symbols_.begin(), symbols_.end(), &Symbol::SameVaddr);
1166 symbols_.erase(last, symbols_.end());
1167 erased = fullSize - symbols_.size();
1168 HLOGV("uniqued completed");
1169 auto it = symbols_.begin();
1170 while (it != symbols_.end()) {
1171 it->index_ = it - symbols_.begin();
1172 it++;
1173 }
1174 HLOGV("indexed completed");
1175
1176 HLOG_ASSERT(symbols_.size() != 0);
1177
1178 if (textExecVaddrRange_ == maxVaddr) {
1179 textExecVaddrRange_ = symbols_.back().funcVaddr_ - symbols_.front().funcVaddr_;
1180 }
1181
1182 HLOGDDD("%zu symbols after adjust (%zu erased) 0x%016" PRIx64 " - 0x%016" PRIx64
1183 " @0x%016" PRIx64 " ",
1184 symbols_.size(), erased, symbols_.front().funcVaddr_, symbols_.back().funcVaddr_,
1185 textExecVaddrFileOffset_);
1186 }
1187
SortMatchedSymbols()1188 void SymbolsFile::SortMatchedSymbols()
1189 {
1190 if (matchedSymbols_.size() <= 1u) {
1191 return;
1192 }
1193 sort(matchedSymbols_.begin(), matchedSymbols_.end(), Symbol::CompareByPointer);
1194 }
1195
GetSymbols()1196 const std::vector<Symbol> &SymbolsFile::GetSymbols()
1197 {
1198 return symbols_;
1199 }
1200
GetMatchedSymbols()1201 const std::vector<Symbol *> &SymbolsFile::GetMatchedSymbols()
1202 {
1203 return matchedSymbols_;
1204 }
1205
GetSymbolWithVaddr(uint64_t vaddrInFile)1206 const Symbol SymbolsFile::GetSymbolWithVaddr(uint64_t vaddrInFile)
1207 {
1208 #ifdef HIPERF_DEBUG_TIME
1209 const auto startTime = steady_clock::now();
1210 #endif
1211 Symbol symbol;
1212 // it should be already order from small to large
1213 auto found =
1214 std::upper_bound(symbols_.begin(), symbols_.end(), vaddrInFile, Symbol::ValueLessThen);
1215 /*
1216 if data is { 1, 2, 4, 5, 5, 6 };
1217 upper_bound for each val :
1218 0 < 1 at index 0
1219 1 < 2 at index 1
1220 2 < 4 at index 2
1221 3 < 4 at index 2
1222 4 < 5 at index 3
1223 5 < 6 at index 5
1224 6 < not found
1225 if key symbol vaddr is { 1, 2, 4, 5, 5, 6 };
1226 check ip vaddr for each val :
1227 ip sym
1228 0 not found
1229 1 1
1230 1 1
1231 2 2
1232 3 3
1233 4 4
1234 5 5
1235 6 6
1236 7 7
1237 */
1238 if (found != symbols_.begin()) {
1239 found = std::prev(found);
1240 if (found->Contain(vaddrInFile)) {
1241 if (!found->HasMatched()) {
1242 found->SetMatchFlag();
1243 matchedSymbols_.push_back(&(*found));
1244 }
1245 symbol = *found; // copy
1246 HLOGV("found '%s' for vaddr 0x%016" PRIx64 "", symbol.ToString().c_str(), vaddrInFile);
1247 }
1248 }
1249
1250 if (!symbol.isValid()) {
1251 HLOGV("NOT found vaddr 0x%" PRIx64 " in symbole file %s(%zu)", vaddrInFile,
1252 filePath_.c_str(), symbols_.size());
1253 }
1254 symbol.fileVaddr_ = vaddrInFile;
1255
1256 #ifdef HIPERF_DEBUG_TIME
1257 auto usedTime = duration_cast<milliseconds>(steady_clock::now() - startTime);
1258 if (usedTime > 1ms) {
1259 HLOGW("cost %" PRId64 "ms to search ", usedTime.count());
1260 }
1261 #endif
1262 return symbol;
1263 }
1264
CheckPathReadable(const std::string & path) const1265 bool SymbolsFile::CheckPathReadable(const std::string &path) const
1266 {
1267 if (access(path.c_str(), R_OK) == 0) {
1268 return true;
1269 } else {
1270 HLOGM("'%s' is unable read", path.c_str());
1271 return false;
1272 }
1273 }
1274
setSymbolsFilePath(const std::vector<std::string> & symbolsSearchPaths)1275 bool SymbolsFile::setSymbolsFilePath(const std::vector<std::string> &symbolsSearchPaths)
1276 {
1277 symbolsFileSearchPaths_.clear();
1278 for (auto &symbolsSearchPath : symbolsSearchPaths) {
1279 if (CheckPathReadable(symbolsSearchPath)) {
1280 symbolsFileSearchPaths_.emplace_back(symbolsSearchPath);
1281 HLOGV("'%s' is add to symbolsSearchPath", symbolsSearchPath.c_str());
1282 }
1283 }
1284 return (symbolsFileSearchPaths_.size() > 0);
1285 }
1286
LoadSymbolsFromSaved(const SymbolFileStruct & symbolFileStruct)1287 std::unique_ptr<SymbolsFile> SymbolsFile::LoadSymbolsFromSaved(
1288 const SymbolFileStruct &symbolFileStruct)
1289 {
1290 auto symbolsFile = CreateSymbolsFile(symbolFileStruct.filePath_);
1291 symbolsFile->filePath_ = symbolFileStruct.filePath_;
1292 symbolsFile->symbolFileType_ = (SymbolsFileType)symbolFileStruct.symbolType_;
1293 symbolsFile->textExecVaddr_ = symbolFileStruct.textExecVaddr_;
1294 symbolsFile->textExecVaddrFileOffset_ = symbolFileStruct.textExecVaddrFileOffset_;
1295 symbolsFile->buildId_ = symbolFileStruct.buildId_;
1296 for (auto &symbolStruct : symbolFileStruct.symbolStructs_) {
1297 symbolsFile->symbols_.emplace_back(symbolStruct.vaddr_, symbolStruct.len_,
1298 symbolStruct.symbolName_, symbolFileStruct.filePath_);
1299 }
1300 symbolsFile->AdjustSymbols(); // reorder
1301 HLOGV("load %zu symbol from SymbolFileStruct for file '%s'", symbolsFile->symbols_.size(),
1302 symbolsFile->filePath_.c_str());
1303 return symbolsFile;
1304 }
1305
ExportSymbolToFileFormat(SymbolFileStruct & symbolFileStruct)1306 void SymbolsFile::ExportSymbolToFileFormat(SymbolFileStruct &symbolFileStruct)
1307 {
1308 symbolFileStruct.filePath_ = filePath_;
1309 symbolFileStruct.symbolType_ = symbolFileType_;
1310 symbolFileStruct.textExecVaddr_ = textExecVaddr_;
1311 symbolFileStruct.textExecVaddrFileOffset_ = textExecVaddrFileOffset_;
1312 symbolFileStruct.buildId_ = buildId_;
1313
1314 SortMatchedSymbols();
1315 auto symbols = GetMatchedSymbols();
1316 symbolFileStruct.symbolStructs_.reserve(symbols.size());
1317 for (auto symbol : symbols) {
1318 auto &symbolStruct = symbolFileStruct.symbolStructs_.emplace_back();
1319 symbolStruct.vaddr_ = symbol->funcVaddr_;
1320 symbolStruct.len_ = symbol->len_;
1321 symbolStruct.symbolName_ = symbol->Name();
1322 }
1323
1324 HLOGV("export %zu symbol to SymbolFileStruct from %s", symbolFileStruct.symbolStructs_.size(),
1325 filePath_.c_str());
1326 }
1327
GetVaddrInSymbols(uint64_t ip,uint64_t,uint64_t) const1328 uint64_t SymbolsFile::GetVaddrInSymbols(uint64_t ip, uint64_t, uint64_t) const
1329 {
1330 // no convert
1331 return ip;
1332 }
1333 } // namespace HiPerf
1334 } // namespace Developtools
1335 } // namespace OHOS