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 <cstdlib>
24 #include <fcntl.h>
25 #include <fstream>
26 #include <string_view>
27 #include <type_traits>
28
29 #if defined(is_mingw) && is_mingw
30 #include <memoryapi.h>
31 #else
32 #include <sys/mman.h>
33 #include <sys/stat.h>
34 #endif
35 #include <unistd.h>
36
37 #include "dfx_ark.h"
38 #include "dfx_extractor_utils.h"
39 #include "dfx_symbols.h"
40 #include "dwarf_encoding.h"
41 #include "hiperf_hilog.h"
42 #include "unwinder_config.h"
43 #include "utilities.h"
44
45 using namespace OHOS::HiviewDFX;
46 using namespace std::chrono;
47
48 namespace OHOS {
49 namespace Developtools {
50 namespace HiPerf {
51 bool SymbolsFile::onRecording_ = true;
52 bool SymbolsFile::needParseJsFunc_ = false;
53
GetBuildId() const54 const std::string SymbolsFile::GetBuildId() const
55 {
56 return buildId_;
57 }
58
UpdateBuildIdIfMatch(std::string buildId)59 bool SymbolsFile::UpdateBuildIdIfMatch(std::string buildId)
60 {
61 /*
62 here we have two case
63 1 buildId_ is empty
64 a) always return match
65 2 buildId_ is not empty
66 a) really check if the same one
67 */
68
69 if (buildId_.empty()) {
70 // we have new empty build
71 if (buildId.empty()) {
72 // both empty , no build id provided
73 HLOGD("build id is empty.");
74 return true;
75 } else {
76 buildId_ = buildId;
77 HLOGD("new buildId %s", buildId_.c_str());
78 return true;
79 }
80 } else {
81 // we already have a build id
82 // so this is not the first time load symbol
83 // we need check if it match
84 HLOGV("expected buildid: %s vs %s", buildId_.c_str(), buildId.c_str());
85
86 if (buildId_ != buildId) {
87 HLOGW("id not match");
88 return false;
89 } else {
90 HLOGD("id match");
91 return true;
92 }
93 }
94 }
95
SearchReadableFile(const std::vector<std::string> & searchPaths,const std::string & filePath) const96 std::string SymbolsFile::SearchReadableFile(const std::vector<std::string> &searchPaths,
97 const std::string &filePath) const
98 {
99 if (filePath.empty()) {
100 HLOGW("nothing to found");
101 return filePath;
102 }
103 for (auto searchPath : searchPaths) {
104 if (searchPath.back() != PATH_SEPARATOR) {
105 searchPath += PATH_SEPARATOR;
106 }
107 std::string PossibleFilePath = searchPath + filePath;
108 if (CheckPathReadable(PossibleFilePath)) {
109 return PossibleFilePath;
110 }
111 HLOGW("have not found '%s' in search paths %s", filePath.c_str(), searchPath.c_str());
112 }
113 return EMPTY_STRING;
114 }
115
FindSymbolFile(const std::vector<std::string> & symbolsFileSearchPaths,std::string symboleFilePath) const116 const std::string SymbolsFile::FindSymbolFile(
117 const std::vector<std::string> &symbolsFileSearchPaths, std::string symboleFilePath) const
118 {
119 /*
120 this function do 2 things:
121 find by name:
122 1 find dso path
123 2 find search path
124 a) search path + dso path
125 b) search path + dso name
126
127 show we should return filePath_ as default ?
128 */
129 if (symboleFilePath.empty()) {
130 symboleFilePath = filePath_;
131 HLOGD("use default filename: %s ", symboleFilePath.c_str());
132 }
133 symboleFilePath = PlatformPathConvert(symboleFilePath);
134 std::string foundPath;
135 // search first if we have path
136 if (symbolsFileSearchPaths.size() != 0) {
137 foundPath = SearchReadableFile(symbolsFileSearchPaths, symboleFilePath);
138 if (foundPath.empty()) {
139 HLOGV("try base name for: %s split with %s", symboleFilePath.c_str(),
140 PATH_SEPARATOR_STR.c_str());
141 auto pathSplit = StringSplit(symboleFilePath, PATH_SEPARATOR_STR);
142 if (pathSplit.size() > 1) {
143 HLOGV("base name is: %s ", pathSplit.back().c_str());
144 // found it again with base name , split it and get last name
145 foundPath = SearchReadableFile(symbolsFileSearchPaths, pathSplit.back());
146 }
147 }
148 }
149
150 // only access the patch in onRecording_
151 // in report mode we don't load any thing in runtime path
152 if (foundPath.empty() and onRecording_) {
153 // try access direct at last
154 if (CheckPathReadable(symboleFilePath)) {
155 // found direct folder
156 HLOGD("find %s in current work dir", symboleFilePath.c_str());
157 return symboleFilePath;
158 }
159 }
160 return foundPath;
161 }
162
163 class ElfFileSymbols : public SymbolsFile {
164 public:
ElfFileSymbols(const std::string & symbolFilePath,const SymbolsFileType symbolsFileType=SYMBOL_ELF_FILE)165 explicit ElfFileSymbols(const std::string &symbolFilePath,
166 const SymbolsFileType symbolsFileType = SYMBOL_ELF_FILE)
167 : SymbolsFile(symbolsFileType, symbolFilePath)
168 {
169 }
170
~ElfFileSymbols()171 virtual ~ElfFileSymbols()
172 {
173 }
174
GetSymbolWithPcAndMap(uint64_t pc,std::shared_ptr<DfxMap> map)175 DfxSymbol GetSymbolWithPcAndMap(uint64_t pc, std::shared_ptr<DfxMap> map) override
176 {
177 const DfxSymbol symbol;
178 return symbol;
179 }
180
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)181 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
182 {
183 symbolsLoaded_ = true;
184 std::string findPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
185 if (findPath.empty() && elfFile_ == nullptr) { // elf not compressed in hap has been initialized before
186 HLOGW("elf found failed (belong to %s)", filePath_.c_str());
187 return false;
188 }
189 if (LoadElfSymbols(map, findPath)) {
190 return true;
191 } else {
192 HLOGW("elf open failed with '%s'", findPath.c_str());
193 return false;
194 }
195 return false;
196 }
197
EnableMiniDebugInfo()198 void EnableMiniDebugInfo() override
199 {
200 UnwinderConfig::SetEnableMiniDebugInfo(true);
201 }
202
GetElfFile()203 std::shared_ptr<DfxElf> GetElfFile() override
204 {
205 return elfFile_;
206 }
207
GetPtLoads()208 const std::unordered_map<uint64_t, ElfLoadInfo> GetPtLoads() override
209 {
210 CHECK_TRUE(elfFile_ == nullptr, info_, 0, "");
211 return elfFile_->GetPtLoads();
212 }
213
214 protected:
LoadDebugInfo(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)215 bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
216 {
217 std::lock_guard<std::mutex> lock(mutex_);
218 if (debugInfoLoadResult_) {
219 return true; // it must have been loaded
220 } else if (debugInfoLoaded_) {
221 return debugInfoLoadResult_; // return the result of loaded
222 } else {
223 debugInfoLoaded_ = true;
224 }
225 std::string elfPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
226 if (elfPath.empty()) {
227 HLOGW("elf found failed (belong to %s)", filePath_.c_str());
228 return false;
229 }
230
231 if (elfFile_ == nullptr) {
232 if (StringEndsWith(elfPath, ".hap")) {
233 if (map == nullptr) {
234 HLOGW("map should not be nullptr.");
235 return false;
236 }
237 if (!map->IsMapExec()) {
238 HLOGW("map is not exec, no need parse elf.");
239 return false;
240 }
241 elfFile_ = DfxElf::CreateFromHap(elfPath, map->prevMap, map->offset);
242 HLOGD("try create elf from hap");
243 } else {
244 elfFile_ = std::make_shared<DfxElf>(elfPath);
245 }
246 }
247
248 CHECK_TRUE(elfFile_ == nullptr, false, 1, "Failed to create elf file for %s.", elfPath.c_str());
249
250 CHECK_TRUE(!elfFile_->IsValid(), false, 1, "parser elf file failed.");
251
252 HLOGD("loaded elf %s", elfPath.c_str());
253 // update path for so in hap
254 if (StringEndsWith(elfPath, ".hap")) {
255 filePath_ = elfPath + "!" + elfFile_->GetElfName();
256 HLOGD("update path for so in hap %s.", filePath_.c_str());
257 if (map == nullptr) {
258 HLOGW("map should not be nullptr.");
259 return false;
260 }
261 map->name = filePath_;
262 map->elf = elfFile_;
263 map->prevMap->name = filePath_;
264 map->prevMap->elf = elfFile_;
265 }
266
267 textExecVaddr_ = elfFile_->GetStartVaddr();
268 textExecVaddrFileOffset_ = elfFile_->GetStartOffset();
269
270 HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
271 textExecVaddrFileOffset_);
272
273 #ifndef __arm__
274 ShdrInfo shinfo;
275 if (elfFile_->GetSectionInfo(shinfo, ".eh_frame_hdr")) {
276 auto mmapPtr = elfFile_->GetMmapPtr();
277 CHECK_TRUE(mmapPtr == nullptr, false, 1, "mmapPtr should not be nullptr.");
278 LoadEhFrameHDR(mmapPtr + shinfo.offset, shinfo.size, shinfo.offset);
279 }
280 #endif
281
282 HLOGD("LoadDebugInfo success!");
283 debugInfoLoadResult_ = true;
284 return true;
285 }
286
287 private:
288 bool EhFrameHDRValid_ {false};
289 uint64_t ehFrameHDRElfOffset_ {0};
290 uint64_t ehFrameHDRFdeCount_ {0};
291 uint64_t ehFrameHDRFdeTableItemSize_ {0};
292 uint64_t ehFrameHDRFdeTableElfOffset_ {0};
293 std::shared_ptr<DfxElf> elfFile_;
294 std::unordered_map<uint64_t, ElfLoadInfo> info_;
295
GetSectionInfo(const std::string & name,uint64_t & sectionVaddr,uint64_t & sectionSize,uint64_t & sectionFileOffset) const296 bool GetSectionInfo(const std::string &name, uint64_t §ionVaddr, uint64_t §ionSize,
297 uint64_t §ionFileOffset) const override
298 {
299 struct ShdrInfo shdrInfo;
300 if (elfFile_->GetSectionInfo(shdrInfo, name)) {
301 sectionVaddr = shdrInfo.addr;
302 sectionSize = shdrInfo.size;
303 sectionFileOffset = shdrInfo.offset;
304 HLOGM("Get Section '%s' %" PRIx64 " - %" PRIx64 "", name.c_str(), sectionVaddr, sectionSize);
305 return true;
306 } else {
307 HLOGW("Section '%s' not found", name.c_str());
308 return false;
309 }
310 }
311
312 #ifndef __arm__
GetHDRSectionInfo(uint64_t & ehFrameHdrElfOffset,uint64_t & fdeTableElfOffset,uint64_t & fdeTableSize)313 bool GetHDRSectionInfo(uint64_t &ehFrameHdrElfOffset, uint64_t &fdeTableElfOffset,
314 uint64_t &fdeTableSize) override
315 {
316 CHECK_TRUE(elfFile_ == nullptr, false, 0, "");
317 ShdrInfo shinfo;
318 if (!elfFile_->GetSectionInfo(shinfo, ".eh_frame_hdr")) {
319 return false;
320 }
321
322 ehFrameHDRElfOffset_ = shinfo.offset;
323 if (EhFrameHDRValid_) {
324 ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
325 fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
326 fdeTableSize = ehFrameHDRFdeCount_;
327 return true;
328 }
329 auto mmapPtr = elfFile_->GetMmapPtr();
330 if (mmapPtr == nullptr) {
331 HLOGE("mmapPtr should not be nullptr.");
332 return false;
333 }
334 if (!LoadEhFrameHDR(mmapPtr + shinfo.offset, elfFile_->GetMmapSize(), shinfo.offset)) {
335 HLOGW("Failed to load eh_frame_hdr");
336 return false;
337 }
338
339 ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
340 fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
341 fdeTableSize = ehFrameHDRFdeCount_;
342 return true;
343 }
344 #endif
345
DumpEhFrameHDR()346 void DumpEhFrameHDR()
347 {
348 HLOGD(" ehFrameHDRElfOffset_: 0x%" PRIx64 "", ehFrameHDRElfOffset_);
349 HLOGD(" ehFrameHDRFdeCount_: 0x%" PRIx64 "", ehFrameHDRFdeCount_);
350 HLOGD(" ehFrameHDRFdeTableElfOffset_: 0x%" PRIx64 "", ehFrameHDRFdeTableElfOffset_);
351 HLOGD(" ehFrameHDRFdeTableItemSize_: 0x%" PRIx64 "", ehFrameHDRFdeTableItemSize_);
352 }
353
LoadEhFrameHDR(const unsigned char * buffer,size_t bufferSize,uint64_t shdrOffset)354 bool LoadEhFrameHDR(const unsigned char *buffer, size_t bufferSize, uint64_t shdrOffset)
355 {
356 const eh_frame_hdr *ehFrameHdr = reinterpret_cast<const eh_frame_hdr *>(buffer);
357 CHECK_TRUE(ehFrameHdr == nullptr, false, 0, "");
358 const uint8_t *dataPtr = ehFrameHdr->encode_data;
359 DwarfEncoding dwEhFramePtr(ehFrameHdr->eh_frame_ptr_enc, dataPtr);
360 DwarfEncoding dwFdeCount(ehFrameHdr->fde_count_enc, dataPtr);
361 DwarfEncoding dwTable(ehFrameHdr->table_enc, dataPtr);
362 DwarfEncoding dwTableValue(ehFrameHdr->table_enc, dataPtr);
363
364 HLOGD("eh_frame_hdr:");
365 HexDump(ehFrameHdr, BITS_OF_FOUR_BYTE, bufferSize);
366 unsigned char version = ehFrameHdr->version;
367 HLOGD(" version: %02x:%s", version, (version == 1) ? "valid" : "invalid");
368 HLOGD(" eh_frame_ptr_enc: %s", dwEhFramePtr.ToString().c_str());
369 HLOGD(" fde_count_enc: %s", dwFdeCount.ToString().c_str());
370 HLOGD(" table_enc: %s", dwTable.ToString().c_str());
371 HLOGD(" table_value_enc: %s", dwTableValue.ToString().c_str());
372 HLOGD(" table_item_size: %zd", dwTable.GetSize() + dwTableValue.GetSize());
373 HLOGD(" table_offset_in_hdr: %zu", dwTable.GetData() - buffer);
374
375 CHECK_TRUE(version != 1, false, 1, "eh_frame_hdr version is invalid");
376 EhFrameHDRValid_ = true;
377 ehFrameHDRElfOffset_ = shdrOffset;
378 ehFrameHDRFdeCount_ = dwFdeCount.GetAppliedValue();
379 ehFrameHDRFdeTableElfOffset_ = dwTable.GetData() - buffer + shdrOffset;
380 ehFrameHDRFdeTableItemSize_ = dwTable.GetSize() + dwTableValue.GetSize();
381 DumpEhFrameHDR();
382
383 if (!dwFdeCount.IsOmit() && dwFdeCount.GetValue() > 0) {
384 return true;
385 } else {
386 HLOGW("fde table not found.\n");
387 }
388 return false;
389 }
390
UpdateSymbols(std::vector<DfxSymbol> & symbolsTable,const std::string & elfPath)391 void UpdateSymbols(std::vector<DfxSymbol> &symbolsTable, const std::string &elfPath)
392 {
393 symbols_.clear();
394 HLOGD("%zu symbols loadded from symbolsTable.", symbolsTable.size());
395
396 symbols_.swap(symbolsTable);
397
398 AdjustSymbols();
399 HLOGD("%zu symbols loadded from elf '%s'.", symbols_.size(), elfPath.c_str());
400 for (auto& symbol: symbols_) {
401 HLOGD("symbol %s", symbol.ToDebugString().c_str());
402 }
403 if (buildId_.empty()) {
404 HLOGD("buildId not found from elf '%s'.", elfPath.c_str());
405 // don't failed. some time the lib have not got the build id
406 // buildId not found from elf '/system/bin/ld-musl-arm.so.1'.
407 }
408 }
409
AddSymbols(std::vector<DfxSymbol> & symbolsTable,std::shared_ptr<DfxElf> elf,const std::string & filePath)410 void AddSymbols(std::vector<DfxSymbol>& symbolsTable, std::shared_ptr<DfxElf> elf, const std::string& filePath)
411 {
412 // use elfFile_ to get symbolsTable
413 DfxSymbols::ParseSymbols(symbolsTable, elf, filePath);
414 DfxSymbols::AddSymbolsByPlt(symbolsTable, elf, filePath);
415 }
416
LoadElfSymbols(std::shared_ptr<DfxMap> map,std::string elfPath)417 bool LoadElfSymbols(std::shared_ptr<DfxMap> map, std::string elfPath)
418 {
419 #ifdef HIPERF_DEBUG_TIME
420 const auto startTime = steady_clock::now();
421 #endif
422 if (elfFile_ == nullptr) {
423 if (StringEndsWith(elfPath, ".hap") && map != nullptr) {
424 if (!map->IsMapExec()) {
425 HLOGW("map is not exec, no need parse elf.");
426 return false;
427 }
428 elfFile_ = DfxElf::CreateFromHap(elfPath, map->prevMap, map->offset);
429 map->elf = elfFile_;
430 } else {
431 elfFile_ = std::make_shared<DfxElf>(elfPath);
432 }
433 }
434 CHECK_TRUE(elfFile_ == nullptr, false, 1, "Failed to create elf file for %s.", elfPath.c_str());
435 HLOGD("loaded elf %s", elfPath.c_str());
436 if (!elfFile_->IsValid()) {
437 HLOGD("parser elf file failed.");
438 return false;
439 }
440
441 textExecVaddr_ = elfFile_->GetStartVaddr();
442 textExecVaddrFileOffset_ = elfFile_->GetStartOffset();
443 HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
444 textExecVaddrFileOffset_);
445
446 // we prepare two table here
447 // only one we will push in to symbols_
448 // or both drop if build id is not same
449 std::string buildIdFound = elfFile_->GetBuildId();
450 std::vector<DfxSymbol> symbolsTable;
451 AddSymbols(symbolsTable, elfFile_, filePath_);
452 if (UpdateBuildIdIfMatch(buildIdFound)) {
453 UpdateSymbols(symbolsTable, elfPath);
454 } else {
455 HLOGW("symbols will not update for '%s' because buildId is not match.",
456 elfPath.c_str());
457 // this mean failed . we don't goon for this.
458 return false;
459 }
460
461 #ifdef HIPERF_DEBUG_TIME
462 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
463 if (usedTime.count() != 0) {
464 HLOGV("cost %0.3f ms to load symbols '%s'",
465 usedTime.count() / static_cast<double>(milliseconds::duration::period::den),
466 elfPath.c_str());
467 }
468 #endif
469 return true;
470 }
471
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapPageOffset) const472 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
473 uint64_t mapPageOffset) const override
474 {
475 /*
476 00200000-002c5000 r--p 00000000 08:02 46400311
477 002c5000-00490000 r-xp 000c5000 08:02 4640031
478
479 [14] .text PROGBITS 00000000002c5000 000c5000
480
481 if ip is 0x46e6ab
482 1. find the map range is 002c5000-00490000
483 2. ip - map start(002c5000) = map section offset
484 3. map section offset + map page offset(000c5000) = elf file offset
485 4. elf file offset - exec file offset(000c5000)
486 = ip offset (ip always in exec file offset)
487 5. ip offset + exec begin vaddr(2c5000) = virtual ip in elf
488 */
489 uint64_t vaddr = ip - mapStart + mapPageOffset - textExecVaddrFileOffset_ + textExecVaddr_;
490 HLOGM(" ip :0x%016" PRIx64 " -> elf offset :0x%016" PRIx64 " -> vaddr :0x%016" PRIx64 " ",
491 ip, ip - mapStart + mapPageOffset, vaddr);
492 HLOGM("(minExecAddrFileOffset_ is 0x%" PRIx64 " textExecVaddr_ is 0x%" PRIx64 ")",
493 textExecVaddrFileOffset_, textExecVaddr_);
494 return vaddr;
495 }
496 };
497
498 class KernelSymbols : public ElfFileSymbols {
499 public:
KernelSymbols(const std::string & symbolFilePath)500 explicit KernelSymbols(const std::string &symbolFilePath)
501 : ElfFileSymbols(symbolFilePath, SYMBOL_KERNEL_FILE)
502 {
503 }
504
KernelSymbols(const std::string & symbolFilePath,const SymbolsFileType symbolsFileType)505 KernelSymbols(const std::string &symbolFilePath,
506 const SymbolsFileType symbolsFileType)
507 : ElfFileSymbols(symbolFilePath, symbolsFileType)
508 {
509 }
510
511 static constexpr const int KSYM_MIN_TOKENS = 3;
512 static constexpr const int KSYM_DEFAULT_LINE = 35000;
513 static constexpr const int KSYM_DEFAULT_SIZE = 1024 * 1024 * 1; // 1MB
514
ParseKallsymsLine(const std::string & kallsymsPath)515 bool ParseKallsymsLine(const std::string &kallsymsPath)
516 {
517 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
518 const auto startTime = steady_clock::now();
519 std::chrono::microseconds parseLineTime = std::chrono::microseconds::zero();
520 std::chrono::microseconds sscanfTime = std::chrono::microseconds::zero();
521 std::chrono::microseconds newTime = std::chrono::microseconds::zero();
522 std::chrono::microseconds readFileTime = std::chrono::microseconds::zero();
523 #endif
524 size_t lines = 0;
525 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
526 const auto eachFileStartTime = steady_clock::now();
527 #endif
528 std::string kallsym;
529 CHECK_TRUE(!ReadFileToString(kallsymsPath, kallsym, KSYM_DEFAULT_SIZE) || kallsym.empty(), false, 1,
530 "%s load failed.", kallsymsPath.c_str());
531 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
532 // any way we finish the line scan
533 readFileTime += duration_cast<milliseconds>(steady_clock::now() - eachFileStartTime);
534 #endif
535 // reduce the mem alloc
536 symbols_.reserve(KSYM_DEFAULT_LINE);
537
538 char *lineBegin = kallsym.data();
539 char *dataEnd = lineBegin + kallsym.size();
540 while (lineBegin < dataEnd) {
541 char *lineEnd = strchr(lineBegin, '\n');
542 if (lineEnd != nullptr) {
543 *lineEnd = '\0';
544 } else {
545 lineEnd = dataEnd;
546 }
547 size_t lineSize = (lineEnd != nullptr) ? (lineEnd - lineBegin) : (dataEnd - lineBegin);
548
549 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
550 const auto eachLineStartTime = steady_clock::now();
551 #endif
552 lines++;
553 uint64_t addr = 0;
554 char type = '\0';
555
556 char nameRaw[lineSize];
557 char moduleRaw[lineSize];
558 int ret = sscanf_s(lineBegin, "%" PRIx64 " %c %s%s", &addr, &type, sizeof(type),
559 nameRaw, sizeof(nameRaw), moduleRaw, sizeof(moduleRaw));
560
561 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
562 // any way we finish the line scan
563 sscanfTime += duration_cast<milliseconds>(steady_clock::now() - eachLineStartTime);
564 #endif
565 if (ret >= KSYM_MIN_TOKENS) {
566 if (ret == KSYM_MIN_TOKENS) {
567 moduleRaw[0] = '\0';
568 }
569 HLOGM(" 0x%016" PRIx64 " %c '%s' '%s'", addr, type, nameRaw, moduleRaw);
570 } else {
571 HLOGW("unknown line %d: '%s'", ret, lineBegin);
572 lineBegin = lineEnd + 1;
573 continue;
574 }
575 lineBegin = lineEnd + 1;
576 std::string name = nameRaw;
577 std::string module = moduleRaw;
578
579 /*
580 T
581 The symbol is in the text (code) section.
582
583 W
584 The symbol is a weak symbol that has not been specifically
585 tagged as a weak object symbol. When a weak defined symbol is
586 linked with a normal defined symbol, the normal defined symbol
587 is used with no error. When a weak undefined symbol is linked
588 and the symbol is not defined, the value of the weak symbol
589 becomes zero with no error.
590 */
591 if (addr != 0 && strchr("TtWw", type)) {
592 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
593 const auto eachNewSymbolTime = steady_clock::now();
594 #endif
595 // we only need text symbols
596 symbols_.emplace_back(addr, name, module.empty() ? filePath_ : module);
597 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
598 newTime += duration_cast<milliseconds>(steady_clock::now() - eachNewSymbolTime);
599 #endif
600 }
601 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
602 parseLineTime += duration_cast<milliseconds>(steady_clock::now() - eachLineStartTime);
603 #endif
604 }
605 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
606 std::chrono::microseconds usedTime =
607 duration_cast<milliseconds>(steady_clock::now() - startTime);
608 printf("parse kernel symbols use : %0.3f ms\n", usedTime.count() / MS_DURATION);
609 printf("parse line use : %0.3f ms\n", parseLineTime.count() / MS_DURATION);
610 printf("sscanf line use : %0.3f ms\n", sscanfTime.count() / MS_DURATION);
611 printf("new symbols use : %0.3f ms\n", newTime.count() / MS_DURATION);
612 printf("read file use : %0.3f ms\n", readFileTime.count() / MS_DURATION);
613 #endif
614 HLOGD("load %s: %zu line processed(%zu symbols)", kallsymsPath.c_str(), lines, symbols_.size());
615 return true;
616 }
617
618 const std::string KPTR_RESTRICT = "/proc/sys/kernel/kptr_restrict";
619
LoadKernelSyms()620 bool LoadKernelSyms()
621 {
622 if (!IsRoot()) {
623 return false;
624 }
625 HLOGD("try read /proc/kallsyms");
626 if (access("/proc/kallsyms", R_OK) != 0) {
627 printf("No vmlinux path is given, and kallsyms cannot be opened\n");
628 return false;
629 }
630 bool hasChangeKptr = false;
631 std::string oldKptrRestrict = ReadFileToString(KPTR_RESTRICT);
632 if (oldKptrRestrict.front() != '0') {
633 printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n");
634 hasChangeKptr = WriteStringToFile(KPTR_RESTRICT, "0");
635 if (!hasChangeKptr) {
636 printf("/proc/sys/kernel/kptr_restrict write failed and we can't not change it.\n");
637 }
638 }
639
640 // getline end
641 CHECK_TRUE(!ParseKallsymsLine("/proc/kallsyms"), false, 0, "");
642
643 if (hasChangeKptr) {
644 if (!WriteStringToFile(KPTR_RESTRICT, oldKptrRestrict)) {
645 printf("recover /proc/sys/kernel/kptr_restrict fail.\n");
646 }
647 }
648
649 if (symbols_.empty()) {
650 printf("The symbol table addresses in /proc/kallsyms are all 0.\n"
651 "Please check the value of /proc/sys/kernel/kptr_restrict, it "
652 "should be 0.\n"
653 "Or provide a separate vmlinux path.\n");
654
655 if (buildId_.size() != 0) {
656 // but we got the buildid , so we make a dummpy symbols
657 HLOGD("kallsyms not found. but we have the buildid");
658 return true;
659 } else {
660 // we got nothing
661 return false;
662 }
663 } else {
664 AdjustSymbols();
665 HLOGV("%zu symbols_ loadded from kallsyms.\n", symbols_.size());
666 return true;
667 }
668 }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)669 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
670 {
671 symbolsLoaded_ = true;
672 HLOGV("KernelSymbols try read '%s' search paths size %zu, inDeviceRecord %d",
673 symbolFilePath.c_str(), symbolsFileSearchPaths_.size(), onRecording_);
674
675 if (onRecording_) {
676 const auto startTime = std::chrono::steady_clock::now();
677 if (!LoadKernelSyms()) {
678 if (IsRoot()) {
679 printf("parse kalsyms failed.\n");
680 }
681 return false;
682 } else {
683 const auto thisTime = std::chrono::steady_clock::now();
684 const auto usedTimeMsTick =
685 std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime);
686 HLOGV("Load kernel symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count());
687 // load complete
688 return true;
689 }
690
691 // try read
692 HLOGD("try read /sys/kernel/notes");
693 std::string notes = ReadFileToString("/sys/kernel/notes");
694 if (notes.empty()) {
695 printf("notes cannot be opened, unable get buildid\n");
696 return false;
697 } else {
698 HLOGD("kernel notes size: %zu", notes.size());
699 buildId_ = DfxElf::GetBuildId((uint64_t)notes.data(), (uint64_t)notes.size());
700 }
701 } // no search path
702
703 // try vmlinux
704 return ElfFileSymbols::LoadSymbols(nullptr, KERNEL_ELF_NAME);
705 }
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t) const706 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
707 {
708 // ip is vaddr in /proc/kallsyms
709 return ip;
710 }
~KernelSymbols()711 ~KernelSymbols() override {}
712 };
713
714 class KernelThreadSymbols : public KernelSymbols {
715 public:
KernelThreadSymbols(const std::string & symbolFilePath)716 explicit KernelThreadSymbols(const std::string &symbolFilePath)
717 : KernelSymbols(symbolFilePath, SYMBOL_KERNEL_THREAD_FILE)
718 {
719 }
720
LoadKernelSyms()721 bool LoadKernelSyms()
722 {
723 if (!IsRoot()) {
724 return false;
725 }
726 // find real proc path by filePath_
727 std::string procPath;
728 if (filePath_ == SYSMGR_FILE_NAME) {
729 procPath = StringPrintf("/proc/%u/uallsyms", SYSMGR_PID);
730 } else if (filePath_ == DEVHOST_FILE_NAME) {
731 procPath = "/proc/devhost/root/kallsyms";
732 }
733 HLOGD("try read kernel thread symbol file %s in %s", filePath_.c_str(), procPath.c_str());
734 CHECK_TRUE(access(procPath.c_str(), R_OK) != 0, false, LOG_TYPE_PRINTF,
735 "kernel thread symbol file %s cannot be opened\n", filePath_.c_str());
736 bool hasChangeKptr = false;
737 std::string oldKptrRestrict = ReadFileToString(KPTR_RESTRICT);
738 if (oldKptrRestrict.front() != '0') {
739 printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n");
740 hasChangeKptr = WriteStringToFile(KPTR_RESTRICT, "0");
741 if (!hasChangeKptr) {
742 printf("/proc/sys/kernel/kptr_restrict write failed and we can't not change it.\n");
743 }
744 }
745
746 // getline end
747 CHECK_TRUE(!ParseKallsymsLine(procPath), false, 0, "");
748
749 if (hasChangeKptr) {
750 if (!WriteStringToFile(KPTR_RESTRICT, oldKptrRestrict)) {
751 printf("recover /proc/sys/kernel/kptr_restrict fail.\n");
752 }
753 }
754
755 if (symbols_.empty()) {
756 printf("The symbol table addresses in %s are all 0.\n"
757 "Please check the value of /proc/sys/kernel/kptr_restrict, it "
758 "should be 0.\n", filePath_.c_str());
759 return false;
760 } else {
761 AdjustSymbols();
762 HLOGV("%zu symbols_ loadded from %s.\n", symbols_.size(), procPath.c_str());
763 return true;
764 }
765 }
766
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)767 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
768 {
769 symbolsLoaded_ = true;
770 HLOGV("KernelThreadSymbols try read '%s', inDeviceRecord %d",
771 filePath_.c_str(), onRecording_);
772
773 if (onRecording_) {
774 const auto startTime = std::chrono::steady_clock::now();
775 if (!LoadKernelSyms()) {
776 if (IsRoot()) {
777 printf("parse %s failed.\n", filePath_.c_str());
778 }
779 } else {
780 const auto thisTime = std::chrono::steady_clock::now();
781 const auto usedTimeMsTick =
782 std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime);
783 HLOGV("Load kernel thread symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count());
784 // load complete
785 return true;
786 }
787 } // no search path
788
789 // try elf
790 return ElfFileSymbols::LoadSymbols(nullptr, filePath_);
791 }
~KernelThreadSymbols()792 ~KernelThreadSymbols() override {}
793 };
794
795 class KernelModuleSymbols : public ElfFileSymbols {
796 public:
KernelModuleSymbols(const std::string & symbolFilePath)797 explicit KernelModuleSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
798 {
799 HLOGV("create %s", symbolFilePath.c_str());
800 symbolFileType_ = SYMBOL_KERNEL_MODULE_FILE;
801 module_ = symbolFilePath;
802 }
~KernelModuleSymbols()803 ~KernelModuleSymbols() override {};
804
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)805 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
806 {
807 symbolsLoaded_ = true;
808 if (module_ == filePath_ and onRecording_) {
809 // file name still not convert to ko file path
810 // this is in record mode
811 HLOGV("find ko name %s", module_.c_str());
812 for (const std::string &path : kernelModulePaths) {
813 if (access(path.c_str(), R_OK) == 0) {
814 std::string koPath = path + module_ + KERNEL_MODULES_EXT_NAME;
815 HLOGV("found ko in %s", koPath.c_str());
816 if (access(koPath.c_str(), R_OK) == 0) {
817 // create symbol
818 filePath_ = koPath;
819 break; // find next ko
820 }
821 }
822 }
823 LoadBuildId();
824 } else {
825 HLOGV("we have file path, load with %s", filePath_.c_str());
826 return ElfFileSymbols::LoadSymbols(nullptr, filePath_);
827 }
828 return false;
829 }
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t) const830 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
831 {
832 return ip - mapStart;
833 }
834
835 private:
LoadBuildId()836 bool LoadBuildId()
837 {
838 std::string sysFile = "/sys/module/" + module_ + "/notes/.note.gnu.build-id";
839 std::string buildIdRaw = ReadFileToString(sysFile);
840 if (!buildIdRaw.empty()) {
841 buildId_ = DfxElf::GetBuildId((uint64_t)buildIdRaw.data(), (uint64_t)buildIdRaw.size());
842 HLOGD("kerne module %s(%s) build id %s", module_.c_str(), filePath_.c_str(),
843 buildId_.c_str());
844 return buildId_.empty() ? false : true;
845 }
846 return false;
847 }
848
849 const std::vector<std::string> kernelModulePaths = {"/vendor/modules/"};
850 std::string module_ = "";
851 };
852
853 class JavaFileSymbols : public ElfFileSymbols {
854 public:
JavaFileSymbols(const std::string & symbolFilePath)855 explicit JavaFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
856 {
857 symbolFileType_ = SYMBOL_KERNEL_FILE;
858 }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)859 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
860 {
861 symbolsLoaded_ = true;
862 return false;
863 }
~JavaFileSymbols()864 ~JavaFileSymbols() override {}
865
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapPageOffset) const866 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
867 uint64_t mapPageOffset) const override
868 {
869 // this is different with elf
870 // elf use ip - mapStart + mapPageOffset - minExecAddrFileOffset_ + textExecVaddr_
871 return ip - mapStart + mapPageOffset;
872 }
873 };
874
875 class JSFileSymbols : public ElfFileSymbols {
876 public:
JSFileSymbols(const std::string & symbolFilePath)877 explicit JSFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
878 {
879 symbolFileType_ = SYMBOL_KERNEL_FILE;
880 }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)881 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
882 {
883 symbolsLoaded_ = true;
884 return false;
885 }
~JSFileSymbols()886 ~JSFileSymbols() override {}
887 };
888
889 class HapFileSymbols : public ElfFileSymbols {
890 private:
891 #if defined(is_ohos) && is_ohos
892 std::unique_ptr<DfxExtractor> dfxExtractor_;
893 bool hapExtracted_ = false;
894 #endif
895 std::unique_ptr<uint8_t[]> abcDataPtr_ = nullptr;
896 [[maybe_unused]] uintptr_t loadOffSet_ = 0;
897 [[maybe_unused]] size_t abcDataSize_ = 0;
898 [[maybe_unused]] uintptr_t arkExtractorptr_ = 0;
899 bool isHapAbc_ = false;
900 pid_t pid_ = 0;
901 public:
HapFileSymbols(const std::string & symbolFilePath,pid_t pid)902 explicit HapFileSymbols(const std::string &symbolFilePath, pid_t pid)
903 : ElfFileSymbols(symbolFilePath, SYMBOL_HAP_FILE)
904 {
905 pid_ = pid;
906 }
907
IsHapAbc()908 bool IsHapAbc()
909 {
910 #if defined(is_ohos) && is_ohos
911 if (hapExtracted_) {
912 return isHapAbc_;
913 }
914 hapExtracted_ = true;
915 HLOGD("the symbol file is %s, pid is %d.", filePath_.c_str(), pid_);
916 if (IsRoot()) {
917 if (IsApplicationEncryped(pid_)) {
918 HLOGD("no need to parse js symbols");
919 return false;
920 }
921 }
922
923 CHECK_TRUE(StringEndsWith(filePath_, ".hap") && map_->IsMapExec(), false, 1,
924 "map is exec not abc file , the symbol file is:%s", map_->name.c_str());
925
926 if (StringEndsWith(filePath_, ".hap") || StringEndsWith(filePath_, ".hsp")) {
927 dfxExtractor_ = std::make_unique<DfxExtractor>(filePath_);
928 CHECK_TRUE(!dfxExtractor_->GetHapAbcInfo(loadOffSet_, abcDataPtr_, abcDataSize_), false, 1,
929 "failed to call GetHapAbcInfo, the symbol file is:%s", filePath_.c_str());
930 HLOGD("loadOffSet %u", (uint32_t)loadOffSet_);
931 if (abcDataPtr_ != nullptr) {
932 isHapAbc_ = true;
933 HLOGD("symbol file : %s, isAbc: %d", filePath_.c_str(), isHapAbc_);
934 }
935 } else {
936 loadOffSet_ = map_->offset;
937 abcDataSize_ = map_->end - map_->begin;
938 abcDataPtr_ = std::make_unique<uint8_t[]>(abcDataSize_);
939 auto size = DfxMemory::ReadProcMemByPid(pid_, map_->begin, abcDataPtr_.get(), map_->end - map_->begin);
940 if (size != abcDataSize_) {
941 HLOGD("return size is small abcDataPtr : %s, isAbc: %d", abcDataPtr_.get(), isHapAbc_);
942 return false;
943 }
944 isHapAbc_ = true;
945 HLOGD("symbol file name %s loadOffSet %u abcDataSize_ %u",
946 filePath_.c_str(), (uint32_t)loadOffSet_, (uint32_t)abcDataSize_);
947 }
948 auto ret = DfxArk::ArkCreateJsSymbolExtractor(&arkExtractorptr_);
949 if (ret < 0) {
950 arkExtractorptr_ = 0;
951 HLOGE("failed to call ArkCreateJsSymbolExtractor, the symbol file is:%s", filePath_.c_str());
952 }
953 #endif
954 return isHapAbc_;
955 }
956
IsAbc()957 bool IsAbc() override
958 {
959 return isHapAbc_ == true;
960 }
961
SetBoolValue(bool value)962 void SetBoolValue(bool value) override
963 {
964 isHapAbc_ = value;
965 }
966
LoadDebugInfo(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)967 bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
968 {
969 HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str());
970 if (debugInfoLoaded_) {
971 return true;
972 }
973 CHECK_TRUE(!onRecording_, true, 0, "");
974
975 if (!IsHapAbc() && map_->IsMapExec()) {
976 ElfFileSymbols::LoadDebugInfo(map, "");
977 }
978 debugInfoLoaded_ = true;
979 debugInfoLoadResult_ = true;
980 return true;
981 }
982
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)983 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
984 {
985 HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str());
986 CHECK_TRUE(symbolsLoaded_ || !onRecording_, true, 0, "");
987 symbolsLoaded_ = true;
988 if (!IsHapAbc() && map_->IsMapExec()) {
989 ElfFileSymbols::LoadSymbols(map, "");
990 }
991 return true;
992 }
993
GetSymbolWithPcAndMap(uint64_t ip,std::shared_ptr<DfxMap> map)994 DfxSymbol GetSymbolWithPcAndMap(uint64_t ip, std::shared_ptr<DfxMap> map) override
995 {
996 // get cache
997 auto iter = symbolsMap_.find(ip);
998 if (iter != symbolsMap_.end()) {
999 return iter->second;
1000 }
1001 if (map == nullptr) {
1002 return DfxSymbol(ip, "");
1003 }
1004 HLOGD("map ptr:%p, map name:%s", map.get(), map->name.c_str());
1005
1006 #if defined(is_ohos) && is_ohos
1007 if (IsAbc() && needParseJsFunc_) {
1008 JsFunction jsFunc;
1009 std::string module = map->name;
1010 HLOGD("map->name module:%s", module.c_str());
1011 auto ret = DfxArk::ParseArkFrameInfo(static_cast<uintptr_t>(ip), static_cast<uintptr_t>(map->begin),
1012 loadOffSet_, abcDataPtr_.get(), abcDataSize_,
1013 arkExtractorptr_, &jsFunc);
1014 if (ret == -1) {
1015 HLOGD("failed to call ParseArkFrameInfo, the symbol file is : %s", map->name.c_str());
1016 return DfxSymbol(ip, "");
1017 }
1018 this->symbolsMap_.insert(std::make_pair(ip,
1019 DfxSymbol(ip,
1020 jsFunc.codeBegin,
1021 jsFunc.functionName,
1022 jsFunc.ToString(),
1023 map->name)));
1024
1025 DfxSymbol &foundSymbol = symbolsMap_[ip];
1026 if (!foundSymbol.matched_) {
1027 foundSymbol.matched_ = true;
1028 foundSymbol.symbolFileIndex_ = id_;
1029 matchedSymbols_.push_back(&(symbolsMap_[ip]));
1030 }
1031
1032 HLOGD("ip : 0x%" PRIx64 " the symbol file is : %s, function is %s demangle_ : %s", ip,
1033 symbolsMap_[ip].module_.data(), jsFunc.functionName, matchedSymbols_.back()->demangle_.data());
1034 return symbolsMap_[ip];
1035 }
1036 #endif
1037 DfxSymbol symbol(ip, "");
1038 return symbol;
1039 }
1040 };
1041
1042 class UnknowFileSymbols : public SymbolsFile {
1043 public:
UnknowFileSymbols(const std::string & symbolFilePath)1044 explicit UnknowFileSymbols(const std::string &symbolFilePath)
1045 : SymbolsFile(SYMBOL_UNKNOW_FILE, symbolFilePath)
1046 {
1047 }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)1048 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
1049 {
1050 symbolsLoaded_ = true;
1051 return false;
1052 }
~UnknowFileSymbols()1053 ~UnknowFileSymbols() override {}
1054 };
1055
~SymbolsFile()1056 SymbolsFile::~SymbolsFile() {}
1057
CreateSymbolsFile(SymbolsFileType symbolType,const std::string symbolFilePath,pid_t pid)1058 std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(SymbolsFileType symbolType,
1059 const std::string symbolFilePath, pid_t pid)
1060 {
1061 switch (symbolType) {
1062 case SYMBOL_KERNEL_FILE:
1063 return std::make_unique<KernelSymbols>(symbolFilePath.empty() ? KERNEL_MMAP_NAME
1064 : symbolFilePath);
1065 case SYMBOL_KERNEL_MODULE_FILE:
1066 return std::make_unique<KernelModuleSymbols>(symbolFilePath);
1067 case SYMBOL_KERNEL_THREAD_FILE:
1068 return std::make_unique<KernelThreadSymbols>(symbolFilePath);
1069 case SYMBOL_ELF_FILE:
1070 return std::make_unique<ElfFileSymbols>(symbolFilePath);
1071 case SYMBOL_JAVA_FILE:
1072 return std::make_unique<JavaFileSymbols>(symbolFilePath);
1073 case SYMBOL_JS_FILE:
1074 return std::make_unique<JSFileSymbols>(symbolFilePath);
1075 case SYMBOL_HAP_FILE:
1076 return std::make_unique<HapFileSymbols>(symbolFilePath, pid);
1077 default:
1078 return std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, symbolFilePath);
1079 }
1080 }
1081
CreateSymbolsFile(const std::string & symbolFilePath,pid_t pid)1082 std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(const std::string &symbolFilePath, pid_t pid)
1083 {
1084 // we need check file name here
1085 if (symbolFilePath == KERNEL_MMAP_NAME) {
1086 return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_FILE, symbolFilePath);
1087 } else if (symbolFilePath == SYSMGR_FILE_NAME ||
1088 symbolFilePath == DEVHOST_LINUX_FILE_NAME ||
1089 StringStartsWith(symbolFilePath, DEVHOST_LINUX_PREFIX)) {
1090 return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, symbolFilePath);
1091 } else if (StringEndsWith(symbolFilePath, KERNEL_MODULES_EXT_NAME)) {
1092 return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, symbolFilePath);
1093 } else if (IsArkJsFile(symbolFilePath)) {
1094 return SymbolsFile::CreateSymbolsFile(SYMBOL_HAP_FILE, symbolFilePath, pid);
1095 } else {
1096 // default is elf, this may be problematic in the future.
1097 return SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, symbolFilePath);
1098 }
1099 }
1100
AdjustSymbols()1101 void SymbolsFile::AdjustSymbols()
1102 {
1103 if (symbols_.size() <= 0) {
1104 return;
1105 }
1106
1107 // order
1108 sort(symbols_.begin(), symbols_.end(), [](const DfxSymbol& a, const DfxSymbol& b) {
1109 return a.funcVaddr_ < b.funcVaddr_;
1110 });
1111 HLOGV("sort completed");
1112
1113 size_t fullSize = symbols_.size();
1114 size_t erased = 0;
1115
1116 // Check for duplicate vaddr
1117 auto last = std::unique(symbols_.begin(), symbols_.end(), [](const DfxSymbol &a, const DfxSymbol &b) {
1118 return (a.funcVaddr_ == b.funcVaddr_);
1119 });
1120 symbols_.erase(last, symbols_.end());
1121 erased = fullSize - symbols_.size();
1122 HLOGV("uniqued completed");
1123 auto it = symbols_.begin();
1124 while (it != symbols_.end()) {
1125 it->index_ = it - symbols_.begin();
1126 it++;
1127 }
1128 HLOGV("indexed completed");
1129
1130 HLOG_ASSERT(symbols_.size() != 0);
1131
1132 if (textExecVaddrRange_ == maxVaddr) {
1133 textExecVaddrRange_ = symbols_.back().funcVaddr_ - symbols_.front().funcVaddr_;
1134 }
1135
1136 HLOGDDD("%zu symbols after adjust (%zu erased) 0x%016" PRIx64 " - 0x%016" PRIx64
1137 " @0x%016" PRIx64 " ",
1138 symbols_.size(), erased, symbols_.front().funcVaddr_, symbols_.back().funcVaddr_,
1139 textExecVaddrFileOffset_);
1140 }
1141
SortMatchedSymbols()1142 void SymbolsFile::SortMatchedSymbols()
1143 {
1144 if (matchedSymbols_.size() <= 1u) {
1145 return;
1146 }
1147 sort(matchedSymbols_.begin(), matchedSymbols_.end(), [](const DfxSymbol* a, const DfxSymbol* b) {
1148 if (a == nullptr || b == nullptr) {
1149 return true;
1150 }
1151 return a->funcVaddr_ < b->funcVaddr_;
1152 });
1153 }
1154
GetSymbols()1155 const std::vector<DfxSymbol> &SymbolsFile::GetSymbols()
1156 {
1157 return symbols_;
1158 }
1159
GetMatchedSymbols()1160 const std::vector<DfxSymbol *> &SymbolsFile::GetMatchedSymbols()
1161 {
1162 return matchedSymbols_;
1163 }
1164
GetSymbolWithVaddr(uint64_t vaddrInFile)1165 const DfxSymbol SymbolsFile::GetSymbolWithVaddr(uint64_t vaddrInFile)
1166 {
1167 #ifdef HIPERF_DEBUG_TIME
1168 const auto startTime = steady_clock::now();
1169 #endif
1170 DfxSymbol symbol;
1171 // it should be already order from small to large
1172 auto found =
1173 std::upper_bound(symbols_.begin(), symbols_.end(), vaddrInFile, DfxSymbol::ValueLessThen);
1174 /*
1175 if data is { 1, 2, 4, 5, 5, 6 };
1176 upper_bound for each val :
1177 0 < 1 at index 0
1178 1 < 2 at index 1
1179 2 < 4 at index 2
1180 3 < 4 at index 2
1181 4 < 5 at index 3
1182 5 < 6 at index 5
1183 6 < not found
1184 if key symbol vaddr is { 1, 2, 4, 5, 5, 6 };
1185 check ip vaddr for each val :
1186 ip sym
1187 0 not found
1188 1 1
1189 1 1
1190 2 2
1191 3 3
1192 4 4
1193 5 5
1194 6 6
1195 7 7
1196 */
1197 if (found != symbols_.begin()) {
1198 found = std::prev(found);
1199 if (found != symbols_.end()) {
1200 if (found->Contain(vaddrInFile)) {
1201 found->offsetToVaddr_ = vaddrInFile - found->funcVaddr_;
1202 if (!found->matched_) {
1203 found->matched_ = true;
1204 matchedSymbols_.push_back(&(*found));
1205 }
1206 symbol = *found; // copy
1207 HLOGV("found '%s' for vaddr 0x%016" PRIx64 "", symbol.ToString().c_str(), vaddrInFile);
1208 }
1209 }
1210 }
1211
1212 if (!symbol.IsValid()) {
1213 HLOGV("NOT found vaddr 0x%" PRIx64 " in symbole file %s(%zu)", vaddrInFile,
1214 filePath_.c_str(), symbols_.size());
1215 }
1216 symbol.fileVaddr_ = vaddrInFile;
1217 symbol.symbolFileIndex_ = id_;
1218
1219 #ifdef HIPERF_DEBUG_TIME
1220 auto usedTime = duration_cast<milliseconds>(steady_clock::now() - startTime);
1221 if (usedTime > 1ms) {
1222 HLOGW("cost %" PRId64 "ms to search ", usedTime.count());
1223 }
1224 #endif
1225 return symbol;
1226 }
1227
CheckPathReadable(const std::string & path) const1228 bool SymbolsFile::CheckPathReadable(const std::string &path) const
1229 {
1230 if (access(path.c_str(), R_OK) == 0) {
1231 return true;
1232 }
1233 // access does not have the access permission in some scenarios
1234 struct stat st;
1235 if (stat(path.c_str(), &st) == 0) {
1236 return true;
1237 } else {
1238 #if defined(is_ohos) && is_ohos
1239 char errInfo[ERRINFOLEN] = { 0 };
1240 strerror_r(errno, errInfo, ERRINFOLEN);
1241 HLOGM("'%s' is unable read,errno: %d, errmsg: %s", path.c_str(), errno, errInfo);
1242 HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s is unable read errno:%" HILOG_PUBLIC
1243 "d , errInfo: %" HILOG_PUBLIC "s\n",
1244 path.c_str(), errno, errInfo);
1245 #endif
1246 return false;
1247 }
1248 }
1249
setSymbolsFilePath(const std::vector<std::string> & symbolsSearchPaths)1250 bool SymbolsFile::setSymbolsFilePath(const std::vector<std::string> &symbolsSearchPaths)
1251 {
1252 symbolsFileSearchPaths_.clear();
1253 for (auto &symbolsSearchPath : symbolsSearchPaths) {
1254 if (CheckPathReadable(symbolsSearchPath)) {
1255 symbolsFileSearchPaths_.emplace_back(symbolsSearchPath);
1256 HLOGV("'%s' is add to symbolsSearchPath", symbolsSearchPath.c_str());
1257 }
1258 }
1259 return (symbolsFileSearchPaths_.size() > 0);
1260 }
1261
LoadSymbolsFromSaved(const SymbolFileStruct & symbolFileStruct)1262 std::unique_ptr<SymbolsFile> SymbolsFile::LoadSymbolsFromSaved(
1263 const SymbolFileStruct &symbolFileStruct)
1264 {
1265 bool isHapSymbolFile = static_cast<SymbolsFileType>(symbolFileStruct.symbolType_) == SYMBOL_HAP_FILE;
1266 HLOGD("isHapSymbolFile : %d", isHapSymbolFile);
1267 auto symbolsFile = CreateSymbolsFile(symbolFileStruct.filePath_);
1268
1269 // default create elf file. but hap file need special operation.
1270 symbolsFile->filePath_ = symbolFileStruct.filePath_;
1271 symbolsFile->symbolFileType_ = static_cast<SymbolsFileType>(symbolFileStruct.symbolType_);
1272 symbolsFile->textExecVaddr_ = symbolFileStruct.textExecVaddr_;
1273 symbolsFile->textExecVaddrFileOffset_ = symbolFileStruct.textExecVaddrFileOffset_;
1274 symbolsFile->buildId_ = symbolFileStruct.buildId_;
1275 for (auto &symbolStruct : symbolFileStruct.symbolStructs_) {
1276 symbolsFile->symbols_.emplace_back(symbolStruct.vaddr_, symbolStruct.len_,
1277 symbolStruct.symbolName_, symbolFileStruct.filePath_);
1278 }
1279 symbolsFile->AdjustSymbols(); // reorder
1280 if (isHapSymbolFile) {
1281 for (const auto& symbol : symbolsFile->symbols_) {
1282 symbolsFile->symbolsMap_.emplace(symbol.funcVaddr_, symbol);
1283 }
1284 symbolsFile->SetBoolValue(true);
1285 }
1286 symbolsFile->debugInfoLoadResult_ = true;
1287 symbolsFile->symbolsLoaded_ = true; // all ready LoadFrom perf.data
1288 HLOGV("load %zu symbol from SymbolFileStruct for file '%s'", symbolsFile->symbols_.size(),
1289 symbolsFile->filePath_.c_str());
1290 return symbolsFile;
1291 }
1292
SetBoolValue(bool value)1293 void SymbolsFile::SetBoolValue(bool value)
1294 {
1295 }
1296
ExportSymbolToFileFormat(SymbolFileStruct & symbolFileStruct)1297 void SymbolsFile::ExportSymbolToFileFormat(SymbolFileStruct &symbolFileStruct)
1298 {
1299 symbolFileStruct.filePath_ = filePath_;
1300 symbolFileStruct.symbolType_ = symbolFileType_;
1301 symbolFileStruct.textExecVaddr_ = textExecVaddr_;
1302 symbolFileStruct.textExecVaddrFileOffset_ = textExecVaddrFileOffset_;
1303 symbolFileStruct.buildId_ = buildId_;
1304
1305 SortMatchedSymbols();
1306 auto symbols = GetMatchedSymbols();
1307 symbolFileStruct.symbolStructs_.reserve(symbols.size());
1308 for (const auto symbol : symbols) {
1309 auto &symbolStruct = symbolFileStruct.symbolStructs_.emplace_back();
1310 symbolStruct.vaddr_ = symbol->funcVaddr_;
1311 symbolStruct.len_ = symbol->size_;
1312 symbolStruct.symbolName_ = symbol->GetName();
1313 }
1314
1315 HLOGV("export %zu symbol to SymbolFileStruct from %s", symbolFileStruct.symbolStructs_.size(),
1316 filePath_.c_str());
1317 }
1318
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapOffset) const1319 uint64_t SymbolsFile::GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t mapOffset) const
1320 {
1321 // no convert
1322 return ip;
1323 }
1324
AddSymbol(DfxSymbol symbol)1325 void SymbolsFile::AddSymbol(DfxSymbol symbol)
1326 {
1327 symbolsLoaded_ = true;
1328 symbols_.emplace_back(symbol);
1329 }
1330 } // namespace HiPerf
1331 } // namespace Developtools
1332 } // namespace OHOS
1333