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