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