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_jsvm.h"
40 #include "dfx_symbols.h"
41 #include "dwarf_encoding.h"
42 #include "elf_factory.h"
43 #include "hiperf_hilog.h"
44 #include "unwinder_config.h"
45 #include "utilities.h"
46 #include "ipc_utilities.h"
47
48 namespace OHOS {
49 namespace Developtools {
50 namespace HiPerf {
51 bool SymbolsFile::onRecording_ = true;
52 bool SymbolsFile::needParseJsFunc_ = false;
53 uint32_t SymbolsFile::offsetNum_ = 0;
54
GetBuildId() const55 const std::string SymbolsFile::GetBuildId() const
56 {
57 return buildId_;
58 }
59
UpdateBuildIdIfMatch(const std::string & buildId)60 bool SymbolsFile::UpdateBuildIdIfMatch(const std::string &buildId)
61 {
62 /*
63 here we have two case
64 1 buildId_ is empty
65 a) always return match
66 2 buildId_ is not empty
67 a) really check if the same one
68 */
69
70 if (buildId_.empty()) {
71 // we have new empty build
72 if (buildId.empty()) {
73 // both empty , no build id provided
74 HLOGD("build id is empty.");
75 } else {
76 buildId_ = buildId;
77 HLOGD("new buildId %s", buildId_.c_str());
78 }
79 return true;
80 }
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 }
90 HLOGD("id match");
91 return true;
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(const uint64_t pc,std::shared_ptr<DfxMap> map)177 DfxSymbol GetSymbolWithPcAndMap(const 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 }
225 debugInfoLoaded_ = true;
226 std::string elfPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
227 if (elfPath.empty()) {
228 HLOGW("elf found failed (belong to %s)", filePath_.c_str());
229 return false;
230 }
231 if (elfFile_ == nullptr) {
232 if (StringEndsWith(elfPath, ".hap") && StringStartsWith(elfPath, "/proc")) {
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 CompressHapElfFactory elfFactory(elfPath, map->prevMap);
242 elfFile_ = elfFactory.Create();
243 map->offset -= map->prevMap->offset;
244 HLOGD("try create elf from hap");
245 } else {
246 RegularElfFactory elfFactory(elfPath);
247 elfFile_ = elfFactory.Create();
248 }
249 }
250
251 CHECK_TRUE(elfFile_ != nullptr, false, 1, "Failed to create elf file for %s.", elfPath.c_str());
252
253 CHECK_TRUE(elfFile_->IsValid(), false, 1, "parser elf file failed.");
254
255 HLOGD("loaded elf %s", elfPath.c_str());
256 // update path for so in hap
257 if (StringEndsWith(elfPath, ".hap")) {
258 filePath_ = elfPath + "!" + elfFile_->GetElfName();
259 HLOGD("update path for so in hap %s.", filePath_.c_str());
260 if (map == nullptr) {
261 HLOGW("map should not be nullptr.");
262 return false;
263 }
264 map->name = filePath_;
265 map->elf = elfFile_;
266 map->prevMap->name = filePath_;
267 map->prevMap->elf = elfFile_;
268 }
269
270 textExecVaddr_ = elfFile_->GetStartVaddr();
271 textExecVaddrFileOffset_ = elfFile_->GetStartOffset();
272
273 HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
274 textExecVaddrFileOffset_);
275
276 #ifndef __arm__
277 ShdrInfo shinfo;
278 if (elfFile_->GetSectionInfo(shinfo, ".eh_frame_hdr")) {
279 auto mmapPtr = elfFile_->GetMmapPtr();
280 CHECK_TRUE(mmapPtr != nullptr, false, 1, "mmapPtr should not be nullptr.");
281 LoadEhFrameHDR(mmapPtr + shinfo.offset, shinfo.size, shinfo.offset);
282 }
283 #endif
284
285 HLOGD("LoadDebugInfo success!");
286 debugInfoLoadResult_ = true;
287 return true;
288 }
289
290 private:
291 bool EhFrameHDRValid_ {false};
292 uint64_t ehFrameHDRElfOffset_ {0};
293 uint64_t ehFrameHDRFdeCount_ {0};
294 uint64_t ehFrameHDRFdeTableItemSize_ {0};
295 uint64_t ehFrameHDRFdeTableElfOffset_ {0};
296 std::shared_ptr<DfxElf> elfFile_ = nullptr;
297 std::unordered_map<uint64_t, ElfLoadInfo> info_;
298
GetSectionInfo(const std::string & name,uint64_t & sectionVaddr,uint64_t & sectionSize,uint64_t & sectionFileOffset) const299 bool GetSectionInfo(const std::string &name, uint64_t §ionVaddr, uint64_t §ionSize,
300 uint64_t §ionFileOffset) const override
301 {
302 struct ShdrInfo shdrInfo;
303 if (elfFile_ != nullptr && elfFile_->GetSectionInfo(shdrInfo, name)) {
304 sectionVaddr = shdrInfo.addr;
305 sectionSize = shdrInfo.size;
306 sectionFileOffset = shdrInfo.offset;
307 HLOGM("Get Section '%s' %" PRIx64 " - %" PRIx64 "", name.c_str(), sectionVaddr, sectionSize);
308 return true;
309 }
310 HLOGW("Section '%s' not found", name.c_str());
311 return false;
312 }
313
314 #ifndef __arm__
GetHDRSectionInfo(uint64_t & ehFrameHdrElfOffset,uint64_t & fdeTableElfOffset,uint64_t & fdeTableSize)315 bool GetHDRSectionInfo(uint64_t &ehFrameHdrElfOffset, uint64_t &fdeTableElfOffset,
316 uint64_t &fdeTableSize) override
317 {
318 CHECK_TRUE(elfFile_ != nullptr, false, 1, "elfFile_ is nullptr");
319 ShdrInfo shinfo;
320 if (!elfFile_->GetSectionInfo(shinfo, ".eh_frame_hdr")) {
321 return false;
322 }
323
324 ehFrameHDRElfOffset_ = shinfo.offset;
325 if (EhFrameHDRValid_) {
326 ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
327 fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
328 fdeTableSize = ehFrameHDRFdeCount_;
329 return true;
330 }
331 auto mmapPtr = elfFile_->GetMmapPtr();
332 if (mmapPtr == nullptr) {
333 HLOGE("mmapPtr should not be nullptr.");
334 return false;
335 }
336 if (!LoadEhFrameHDR(mmapPtr + shinfo.offset, elfFile_->GetMmapSize(), shinfo.offset)) {
337 HLOGW("Failed to load eh_frame_hdr");
338 return false;
339 }
340
341 ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
342 fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
343 fdeTableSize = ehFrameHDRFdeCount_;
344 return true;
345 }
346 #endif
347
DumpEhFrameHDR()348 void DumpEhFrameHDR()
349 {
350 HLOGD(" ehFrameHDRElfOffset_: 0x%" PRIx64 "", ehFrameHDRElfOffset_);
351 HLOGD(" ehFrameHDRFdeCount_: 0x%" PRIx64 "", ehFrameHDRFdeCount_);
352 HLOGD(" ehFrameHDRFdeTableElfOffset_: 0x%" PRIx64 "", ehFrameHDRFdeTableElfOffset_);
353 HLOGD(" ehFrameHDRFdeTableItemSize_: 0x%" PRIx64 "", ehFrameHDRFdeTableItemSize_);
354 }
355
LoadEhFrameHDR(const unsigned char * buffer,size_t bufferSize,uint64_t shdrOffset)356 bool LoadEhFrameHDR(const unsigned char *buffer, size_t bufferSize, uint64_t shdrOffset)
357 {
358 const eh_frame_hdr *ehFrameHdr = reinterpret_cast<const eh_frame_hdr *>(buffer);
359 CHECK_TRUE(ehFrameHdr != nullptr, false, 0, "");
360 const uint8_t *dataPtr = ehFrameHdr->encode_data;
361 DwarfEncoding dwEhFramePtr(ehFrameHdr->eh_frame_ptr_enc, dataPtr);
362 DwarfEncoding dwFdeCount(ehFrameHdr->fde_count_enc, dataPtr);
363 DwarfEncoding dwTable(ehFrameHdr->table_enc, dataPtr);
364 DwarfEncoding dwTableValue(ehFrameHdr->table_enc, dataPtr);
365
366 HLOGD("eh_frame_hdr:");
367 if (HexDump(ehFrameHdr, BITS_OF_FOUR_BYTE, bufferSize) == false) {
368 HLOGW("HexDump failed.");
369 }
370 unsigned char version = ehFrameHdr->version;
371 HLOGD(" version: %02x:%s", version, (version == 1) ? "valid" : "invalid");
372 HLOGD(" eh_frame_ptr_enc: %s", dwEhFramePtr.ToString().c_str());
373 HLOGD(" fde_count_enc: %s", dwFdeCount.ToString().c_str());
374 HLOGD(" table_enc: %s", dwTable.ToString().c_str());
375 HLOGD(" table_value_enc: %s", dwTableValue.ToString().c_str());
376 HLOGD(" table_item_size: %zd", dwTable.GetSize() + dwTableValue.GetSize());
377 HLOGD(" table_offset_in_hdr: %zu", dwTable.GetData() - buffer);
378
379 CHECK_TRUE(version == 1, false, 1, "eh_frame_hdr version is invalid");
380 EhFrameHDRValid_ = true;
381 ehFrameHDRElfOffset_ = shdrOffset;
382 ehFrameHDRFdeCount_ = dwFdeCount.GetAppliedValue();
383 ehFrameHDRFdeTableElfOffset_ = dwTable.GetData() - buffer + shdrOffset;
384 ehFrameHDRFdeTableItemSize_ = dwTable.GetSize() + dwTableValue.GetSize();
385 DumpEhFrameHDR();
386
387 if (!dwFdeCount.IsOmit() && dwFdeCount.GetValue() > 0) {
388 return true;
389 }
390 HLOGW("fde table not found.\n");
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
GetTextExecVaddr()420 void GetTextExecVaddr()
421 {
422 #if defined(is_ohos) && is_ohos
423 textExecVaddr_ = elfFile_->GetStartVaddr();
424 textExecVaddrFileOffset_ = elfFile_->GetStartOffset();
425 #else
426 if (textExecVaddr_ == maxVaddr) {
427 textExecVaddr_ = elfFile_->GetStartVaddr();
428 textExecVaddrFileOffset_ = elfFile_->GetStartOffset();
429 }
430 #endif
431 HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
432 textExecVaddrFileOffset_);
433 }
434
LoadElfSymbols(std::shared_ptr<DfxMap> map,std::string elfPath)435 bool LoadElfSymbols(std::shared_ptr<DfxMap> map, std::string elfPath)
436 {
437 #ifdef HIPERF_DEBUG_TIME
438 const auto startTime = steady_clock::now();
439 #endif
440 if (elfFile_ == nullptr) {
441 if (StringEndsWith(elfPath, ".hap") && StringStartsWith(elfPath, "/proc") && map != nullptr) {
442 if (!map->IsMapExec()) {
443 HLOGW("map is not exec, no need parse elf.");
444 return false;
445 }
446 CompressHapElfFactory elfFactory(elfPath, map->prevMap);
447 elfFile_ = elfFactory.Create();
448 map->offset -= map->prevMap->offset;
449 map->elf = elfFile_;
450 } else {
451 RegularElfFactory elfFactory(elfPath);
452 elfFile_ = elfFactory.Create();
453 }
454 }
455 CHECK_TRUE(elfFile_ != nullptr, false, 1, "Failed to create elf file for %s.", elfPath.c_str());
456 HLOGD("loaded elf %s", elfPath.c_str());
457 if (!elfFile_->IsValid()) {
458 HLOGD("parser elf file failed.");
459 return false;
460 }
461 GetTextExecVaddr();
462
463 // we prepare two table here
464 // only one we will push in to symbols_
465 // or both drop if build id is not same
466 std::string buildIdFound = elfFile_->GetBuildId();
467 std::vector<DfxSymbol> symbolsTable;
468 AddSymbols(symbolsTable, elfFile_, filePath_);
469 if (UpdateBuildIdIfMatch(buildIdFound)) {
470 UpdateSymbols(symbolsTable, elfPath);
471 } else {
472 HLOGW("symbols will not update for '%s' because buildId is not match.",
473 elfPath.c_str());
474 // this mean failed . we don't goon for this.
475 return false;
476 }
477
478 #ifdef HIPERF_DEBUG_TIME
479 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
480 if (usedTime.count() != 0) {
481 HLOGV("cost %0.3f ms to load symbols '%s'",
482 usedTime.count() / static_cast<double>(milliseconds::duration::period::den),
483 elfPath.c_str());
484 }
485 #endif
486 return true;
487 }
488
GetVaddrInSymbols(const uint64_t ip,const uint64_t mapStart,const uint64_t mapPageOffset) const489 uint64_t GetVaddrInSymbols(const uint64_t ip, const uint64_t mapStart,
490 const uint64_t mapPageOffset) const override
491 {
492 /*
493 00200000-002c5000 r--p 00000000 08:02 46400311
494 002c5000-00490000 r-xp 000c5000 08:02 4640031
495
496 [14] .text PROGBITS 00000000002c5000 000c5000
497
498 if ip is 0x46e6ab
499 1. find the map range is 002c5000-00490000
500 2. ip - map start(002c5000) = map section offset
501 3. map section offset + map page offset(000c5000) = elf file offset
502 4. elf file offset - exec file offset(000c5000)
503 = ip offset (ip always in exec file offset)
504 5. ip offset + exec begin vaddr(2c5000) = virtual ip in elf
505 */
506 uint64_t vaddr = ip - mapStart + mapPageOffset - textExecVaddrFileOffset_ + textExecVaddr_;
507 HLOGM(" ip :0x%016" PRIx64 " -> elf offset :0x%016" PRIx64 " -> vaddr :0x%016" PRIx64 " ",
508 ip, ip - mapStart + mapPageOffset, vaddr);
509 HLOGM("(minExecAddrFileOffset_ is 0x%" PRIx64 " textExecVaddr_ is 0x%" PRIx64 ")",
510 textExecVaddrFileOffset_, textExecVaddr_);
511 return vaddr;
512 }
513 };
514
515 class KernelSymbols : public ElfFileSymbols {
516 public:
KernelSymbols(const std::string & symbolFilePath)517 explicit KernelSymbols(const std::string &symbolFilePath)
518 : ElfFileSymbols(symbolFilePath, SYMBOL_KERNEL_FILE)
519 {
520 }
521
KernelSymbols(const std::string & symbolFilePath,const SymbolsFileType symbolsFileType)522 KernelSymbols(const std::string &symbolFilePath,
523 const SymbolsFileType symbolsFileType)
524 : ElfFileSymbols(symbolFilePath, symbolsFileType)
525 {
526 }
527
528 static constexpr const int KSYM_MIN_TOKENS = 3;
529 static constexpr const int KSYM_DEFAULT_LINE = 35000;
530 static constexpr const int KSYM_DEFAULT_SIZE = 1024 * 1024 * 1; // 1MB
531
ParseKallsymsLine(const std::string & kallsymsPath)532 bool ParseKallsymsLine(const std::string &kallsymsPath)
533 {
534 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
535 const auto startTime = steady_clock::now();
536 std::chrono::microseconds parseLineTime = std::chrono::microseconds::zero();
537 std::chrono::microseconds sscanfTime = std::chrono::microseconds::zero();
538 std::chrono::microseconds newTime = std::chrono::microseconds::zero();
539 std::chrono::microseconds readFileTime = std::chrono::microseconds::zero();
540 #endif
541 size_t lines = 0;
542 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
543 const auto eachFileStartTime = steady_clock::now();
544 #endif
545 std::string kallsym;
546 CHECK_TRUE(ReadFileToString(kallsymsPath, kallsym, KSYM_DEFAULT_SIZE) && !kallsym.empty(), false, 1,
547 "%s load failed.", kallsymsPath.c_str());
548 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
549 // any way we finish the line scan
550 readFileTime += duration_cast<milliseconds>(steady_clock::now() - eachFileStartTime);
551 #endif
552 // reduce the mem alloc
553 symbols_.reserve(KSYM_DEFAULT_LINE);
554
555 char *lineBegin = kallsym.data();
556 char *dataEnd = lineBegin + kallsym.size();
557 while (lineBegin < dataEnd) {
558 char *lineEnd = strchr(lineBegin, '\n');
559 if (lineEnd != nullptr) {
560 *lineEnd = '\0';
561 } else {
562 lineEnd = dataEnd;
563 }
564 size_t lineSize = (lineEnd != nullptr) ? (lineEnd - lineBegin) : (dataEnd - lineBegin);
565
566 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
567 const auto eachLineStartTime = steady_clock::now();
568 #endif
569 lines++;
570 uint64_t addr = 0;
571 char type = '\0';
572
573 char nameRaw[lineSize];
574 char moduleRaw[lineSize];
575 int ret = sscanf_s(lineBegin, "%" PRIx64 " %c %s%s", &addr, &type, sizeof(type),
576 nameRaw, sizeof(nameRaw), moduleRaw, sizeof(moduleRaw));
577
578 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
579 // any way we finish the line scan
580 sscanfTime += duration_cast<milliseconds>(steady_clock::now() - eachLineStartTime);
581 #endif
582 if (ret >= KSYM_MIN_TOKENS) {
583 if (ret == KSYM_MIN_TOKENS) {
584 moduleRaw[0] = '\0';
585 }
586 HLOGM(" 0x%016" PRIx64 " %c '%s' '%s'", addr, type, nameRaw, moduleRaw);
587 } else {
588 HLOGW("unknown line %d: '%s'", ret, lineBegin);
589 lineBegin = lineEnd + 1;
590 continue;
591 }
592 lineBegin = lineEnd + 1;
593 std::string name = nameRaw;
594 std::string module = moduleRaw;
595
596 /*
597 T
598 The symbol is in the text (code) section.
599
600 W
601 The symbol is a weak symbol that has not been specifically
602 tagged as a weak object symbol. When a weak defined symbol is
603 linked with a normal defined symbol, the normal defined symbol
604 is used with no error. When a weak undefined symbol is linked
605 and the symbol is not defined, the value of the weak symbol
606 becomes zero with no error.
607 */
608 if (addr != 0 && strchr("TtWw", type)) {
609 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
610 const auto eachNewSymbolTime = steady_clock::now();
611 #endif
612 // we only need text symbols
613 symbols_.emplace_back(addr, name, module.empty() ? filePath_ : module);
614 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
615 newTime += duration_cast<milliseconds>(steady_clock::now() - eachNewSymbolTime);
616 #endif
617 }
618 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
619 parseLineTime += duration_cast<milliseconds>(steady_clock::now() - eachLineStartTime);
620 #endif
621 }
622 #ifdef HIPERF_DEBUG_SYMBOLS_TIME
623 std::chrono::microseconds usedTime =
624 duration_cast<milliseconds>(steady_clock::now() - startTime);
625 printf("parse kernel symbols use : %0.3f ms\n", usedTime.count() / MS_DURATION);
626 printf("parse line use : %0.3f ms\n", parseLineTime.count() / MS_DURATION);
627 printf("sscanf line use : %0.3f ms\n", sscanfTime.count() / MS_DURATION);
628 printf("new symbols use : %0.3f ms\n", newTime.count() / MS_DURATION);
629 printf("read file use : %0.3f ms\n", readFileTime.count() / MS_DURATION);
630 #endif
631 HLOGD("load %s: %zu line processed(%zu symbols)", kallsymsPath.c_str(), lines, symbols_.size());
632 return true;
633 }
634
635 const std::string KPTR_RESTRICT = "/proc/sys/kernel/kptr_restrict";
636
LoadKernelSyms()637 bool LoadKernelSyms()
638 {
639 if (!IsRoot()) {
640 return false;
641 }
642 HLOGD("try read /proc/kallsyms");
643 if (access("/proc/kallsyms", R_OK) != 0) {
644 printf("No vmlinux path is given, and kallsyms cannot be opened\n");
645 return false;
646 }
647 bool hasChangeKptr = false;
648 std::string oldKptrRestrict = ReadFileToString(KPTR_RESTRICT);
649 if (oldKptrRestrict.front() != '0') {
650 printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n");
651 hasChangeKptr = WriteStringToFile(KPTR_RESTRICT, "0");
652 if (!hasChangeKptr) {
653 printf("/proc/sys/kernel/kptr_restrict write failed and we can't not change it.\n");
654 }
655 }
656
657 // getline end
658 CHECK_TRUE(ParseKallsymsLine("/proc/kallsyms"), false, 0, "");
659
660 if (hasChangeKptr) {
661 if (!WriteStringToFile(KPTR_RESTRICT, oldKptrRestrict)) {
662 printf("recover /proc/sys/kernel/kptr_restrict fail.\n");
663 }
664 }
665
666 if (symbols_.empty()) {
667 printf("The symbol table addresses in /proc/kallsyms are all 0.\n"
668 "Please check the value of /proc/sys/kernel/kptr_restrict, it "
669 "should be 0.\n"
670 "Or provide a separate vmlinux path.\n");
671
672 if (buildId_.size() != 0) {
673 // but we got the buildid , so we make a dummpy symbols
674 HLOGD("kallsyms not found. but we have the buildid");
675 return true;
676 } else {
677 // we got nothing
678 return false;
679 }
680 } else {
681 AdjustSymbols();
682 HLOGV("%zu symbols_ loadded from kallsyms.\n", symbols_.size());
683 return true;
684 }
685 }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)686 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
687 {
688 symbolsLoaded_ = true;
689 HLOGV("KernelSymbols try read '%s' search paths size %zu, inDeviceRecord %d",
690 symbolFilePath.c_str(), symbolsFileSearchPaths_.size(), onRecording_);
691
692 if (onRecording_) {
693 const auto startTime = std::chrono::steady_clock::now();
694 if (!LoadKernelSyms()) {
695 if (IsRoot()) {
696 printf("parse kalsyms failed.\n");
697 }
698 return false;
699 } else {
700 const auto thisTime = std::chrono::steady_clock::now();
701 const auto usedTimeMsTick =
702 std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime);
703 HLOGV("Load kernel symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count());
704 // load complete
705 return true;
706 }
707 } // no search path
708
709 // try vmlinux
710 return ElfFileSymbols::LoadSymbols(nullptr, KERNEL_ELF_NAME);
711 }
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t) const712 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
713 {
714 // ip is vaddr in /proc/kallsyms
715 return ip;
716 }
~KernelSymbols()717 ~KernelSymbols() override {}
718 };
719
720 class KernelThreadSymbols : public KernelSymbols {
721 public:
KernelThreadSymbols(const std::string & symbolFilePath)722 explicit KernelThreadSymbols(const std::string &symbolFilePath)
723 : KernelSymbols(symbolFilePath, SYMBOL_KERNEL_THREAD_FILE)
724 {
725 }
726
LoadKernelSyms()727 bool LoadKernelSyms()
728 {
729 if (!IsRoot()) {
730 return false;
731 }
732 // find real proc path by filePath_
733 std::string procPath;
734 if (filePath_ == SYSMGR_FILE_NAME) {
735 procPath = StringPrintf("/proc/%u/uallsyms", SYSMGR_PID);
736 } else if (filePath_ == DEVHOST_FILE_NAME) {
737 procPath = "/proc/devhost/root/kallsyms";
738 }
739 HLOGD("try read kernel thread symbol file %s in %s", filePath_.c_str(), procPath.c_str());
740 CHECK_TRUE(access(procPath.c_str(), R_OK) == 0, false, LOG_TYPE_PRINTF,
741 "kernel thread symbol file %s cannot be opened\n", filePath_.c_str());
742 bool hasChangeKptr = false;
743 std::string oldKptrRestrict = ReadFileToString(KPTR_RESTRICT);
744 if (oldKptrRestrict.front() != '0') {
745 printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n");
746 hasChangeKptr = WriteStringToFile(KPTR_RESTRICT, "0");
747 if (!hasChangeKptr) {
748 printf("/proc/sys/kernel/kptr_restrict write failed and we can't not change it.\n");
749 }
750 }
751
752 // getline end
753 CHECK_TRUE(ParseKallsymsLine(procPath), false, 0, "");
754
755 if (hasChangeKptr) {
756 if (!WriteStringToFile(KPTR_RESTRICT, oldKptrRestrict)) {
757 printf("recover /proc/sys/kernel/kptr_restrict fail.\n");
758 }
759 }
760
761 if (symbols_.empty()) {
762 printf("The symbol table addresses in %s are all 0.\n"
763 "Please check the value of /proc/sys/kernel/kptr_restrict, it "
764 "should be 0.\n", filePath_.c_str());
765 return false;
766 }
767 AdjustSymbols();
768 HLOGV("%zu symbols_ loadded from %s.\n", symbols_.size(), procPath.c_str());
769 return true;
770 }
771
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)772 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
773 {
774 symbolsLoaded_ = true;
775 HLOGV("KernelThreadSymbols try read '%s', inDeviceRecord %d",
776 filePath_.c_str(), onRecording_);
777
778 if (onRecording_) {
779 const auto startTime = std::chrono::steady_clock::now();
780 if (!LoadKernelSyms()) {
781 if (IsRoot()) {
782 printf("parse %s failed.\n", filePath_.c_str());
783 }
784 } else {
785 const auto thisTime = std::chrono::steady_clock::now();
786 const auto usedTimeMsTick =
787 std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime);
788 HLOGV("Load kernel thread symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count());
789 // load complete
790 return true;
791 }
792 } // no search path
793
794 // try elf
795 return ElfFileSymbols::LoadSymbols(nullptr, filePath_);
796 }
797
GetSymbolWithPcAndMap(uint64_t ip,std::shared_ptr<DfxMap> map)798 DfxSymbol GetSymbolWithPcAndMap(uint64_t ip, std::shared_ptr<DfxMap> map) override
799 {
800 DfxSymbol symbol;
801 // it should be already order from small to large
802 auto found =
803 std::upper_bound(symbols_.begin(), symbols_.end(), ip, DfxSymbol::ValueLessThen);
804 if (found == symbols_.begin()) {
805 if (!symbol.IsValid()) {
806 HLOGV("NOT found vaddr 0x%" PRIx64 " in symbole file %s(%zu)", ip,
807 filePath_.c_str(), symbols_.size());
808 }
809 symbol.fileVaddr_ = ip;
810 symbol.symbolFileIndex_ = id_;
811 return symbol;
812 }
813
814 found = std::prev(found);
815 if (found != symbols_.end()) {
816 if (found->Contain(ip)) {
817 found->offsetToVaddr_ = ip - found->funcVaddr_;
818 if (!found->matched_) {
819 found->matched_ = true;
820 DfxSymbol matchedSymbol = *found;
821 // in func UpdateDevHostCallChains add offset to ips, need add offset when saveing
822 matchedSymbol.funcVaddr_ += SymbolsFile::offsetNum_;
823 this->symbolsMap_.emplace(ip, matchedSymbol);
824 matchedSymbols_.push_back(&(symbolsMap_[ip]));
825 }
826 symbol = *found; // copy
827 HLOGV("found '%s' for vaddr 0x%016" PRIx64 "", symbol.ToString().c_str(), ip);
828 }
829 }
830
831 if (!symbol.IsValid()) {
832 HLOGV("NOT found vaddr 0x%" PRIx64 " in symbole file %s(%zu)", ip,
833 filePath_.c_str(), symbols_.size());
834 }
835 symbol.fileVaddr_ = ip;
836 symbol.symbolFileIndex_ = id_;
837
838 return symbol;
839 }
840
~KernelThreadSymbols()841 ~KernelThreadSymbols() override {}
842 };
843
844 class KernelModuleSymbols : public ElfFileSymbols {
845 public:
KernelModuleSymbols(const std::string & symbolFilePath)846 explicit KernelModuleSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
847 {
848 HLOGV("create %s", symbolFilePath.c_str());
849 symbolFileType_ = SYMBOL_KERNEL_MODULE_FILE;
850 module_ = symbolFilePath;
851 }
~KernelModuleSymbols()852 ~KernelModuleSymbols() override {};
853
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)854 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
855 {
856 symbolsLoaded_ = true;
857 if (module_ == filePath_ && onRecording_) {
858 // file name still not convert to ko file path
859 // this is in record mode
860 HLOGV("find ko name %s", module_.c_str());
861 for (const std::string &path : kernelModulePaths) {
862 if (access(path.c_str(), R_OK) == 0) {
863 std::string koPath = path + module_ + KERNEL_MODULES_EXT_NAME;
864 HLOGV("found ko in %s", koPath.c_str());
865 if (access(koPath.c_str(), R_OK) == 0) {
866 // create symbol
867 filePath_ = koPath;
868 break; // find next ko
869 }
870 }
871 }
872 LoadBuildId();
873 } else {
874 HLOGV("we have file path, load with %s", filePath_.c_str());
875 return ElfFileSymbols::LoadSymbols(nullptr, filePath_);
876 }
877 return false;
878 }
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t) const879 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
880 {
881 return ip - mapStart;
882 }
883
884 private:
LoadBuildId()885 bool LoadBuildId()
886 {
887 std::string sysFile = "/sys/module/" + module_ + "/notes/.note.gnu.build-id";
888 std::string buildIdRaw = ReadFileToString(sysFile);
889 if (!buildIdRaw.empty()) {
890 buildId_ = DfxElf::GetBuildId((uint64_t)buildIdRaw.data(), (uint64_t)buildIdRaw.size());
891 HLOGD("kerne module %s(%s) build id %s", module_.c_str(), filePath_.c_str(),
892 buildId_.c_str());
893 return buildId_.empty() ? false : true;
894 }
895 return false;
896 }
897
898 const std::vector<std::string> kernelModulePaths = {"/vendor/modules/"};
899 std::string module_ = "";
900 };
901
902 class JavaFileSymbols : public ElfFileSymbols {
903 public:
JavaFileSymbols(const std::string & symbolFilePath)904 explicit JavaFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
905 {
906 symbolFileType_ = SYMBOL_KERNEL_FILE;
907 }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)908 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
909 {
910 symbolsLoaded_ = true;
911 return false;
912 }
~JavaFileSymbols()913 ~JavaFileSymbols() override {}
914
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapPageOffset) const915 uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
916 uint64_t mapPageOffset) const override
917 {
918 // this is different with elf
919 // elf use ip - mapStart + mapPageOffset - minExecAddrFileOffset_ + textExecVaddr_
920 return ip - mapStart + mapPageOffset;
921 }
922 };
923
924 class JSFileSymbols : public ElfFileSymbols {
925 public:
JSFileSymbols(const std::string & symbolFilePath)926 explicit JSFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
927 {
928 symbolFileType_ = SYMBOL_KERNEL_FILE;
929 }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)930 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
931 {
932 symbolsLoaded_ = true;
933 return false;
934 }
~JSFileSymbols()935 ~JSFileSymbols() override {}
936 };
937
938 class HapFileSymbols : public ElfFileSymbols {
939 private:
940 #if defined(is_ohos) && is_ohos
941 std::unique_ptr<DfxExtractor> dfxExtractor_;
942 bool hapExtracted_ = false;
943 #endif
944 std::unique_ptr<uint8_t[]> abcDataPtr_ = nullptr;
945 [[maybe_unused]] uintptr_t loadOffSet_ = 0;
946 [[maybe_unused]] size_t abcDataSize_ = 0;
947 [[maybe_unused]] uintptr_t arkExtractorptr_ = 0;
948 bool isHapAbc_ = false;
949 pid_t pid_ = 0;
950 public:
HapFileSymbols(const std::string & symbolFilePath,pid_t pid)951 explicit HapFileSymbols(const std::string &symbolFilePath, pid_t pid)
952 : ElfFileSymbols(symbolFilePath, SYMBOL_HAP_FILE)
953 {
954 pid_ = pid;
955 }
956
~HapFileSymbols()957 ~HapFileSymbols() override
958 {
959 #if defined(is_ohos) && is_ohos
960 abcDataPtr_ = nullptr;
961 if (arkExtractorptr_ != 0) {
962 DfxArk::Instance().ArkDestoryJsSymbolExtractor(arkExtractorptr_);
963 arkExtractorptr_ = 0;
964 }
965 #endif
966 }
967
IsHapAbc()968 bool IsHapAbc()
969 {
970 #if defined(is_ohos) && is_ohos
971 if (hapExtracted_) {
972 return isHapAbc_;
973 }
974 hapExtracted_ = true;
975 HLOGD("the symbol file is %s, pid is %d.", filePath_.c_str(), pid_);
976 if (IsRoot()) {
977 if (IsApplicationEncryped(pid_)) {
978 HLOGD("no need to parse js symbols");
979 return false;
980 }
981 }
982
983 CHECK_TRUE(!StringEndsWith(filePath_, ".hap") || !map_->IsMapExec(), false, 1,
984 "map is exec not abc file , the symbol file is:%s", map_->name.c_str());
985
986 if (StringEndsWith(filePath_, ".hap") || StringEndsWith(filePath_, ".hsp") ||
987 StringEndsWith(filePath_, ".hqf")) {
988 dfxExtractor_ = std::make_unique<DfxExtractor>(filePath_);
989 CHECK_TRUE(dfxExtractor_->GetHapAbcInfo(loadOffSet_, abcDataPtr_, abcDataSize_), false, 1,
990 "failed to call GetHapAbcInfo, the symbol file is:%s", filePath_.c_str());
991 HLOGD("loadOffSet %u", (uint32_t)loadOffSet_);
992 if (abcDataPtr_ != nullptr) {
993 isHapAbc_ = true;
994 HLOGD("symbol file : %s, isAbc: %d", filePath_.c_str(), isHapAbc_);
995 }
996 } else {
997 loadOffSet_ = map_->offset;
998 abcDataSize_ = map_->end - map_->begin;
999 abcDataPtr_ = std::make_unique<uint8_t[]>(abcDataSize_);
1000 auto size = DfxMemory::ReadProcMemByPid(pid_, map_->begin, abcDataPtr_.get(), map_->end - map_->begin);
1001 if (size != abcDataSize_) {
1002 HLOGD("return size is small abcDataPtr : %s, isAbc: %d", abcDataPtr_.get(), isHapAbc_);
1003 return false;
1004 }
1005 isHapAbc_ = true;
1006 HLOGD("symbol file name %s loadOffSet %u abcDataSize_ %u",
1007 filePath_.c_str(), (uint32_t)loadOffSet_, (uint32_t)abcDataSize_);
1008 }
1009 auto ret = DfxArk::Instance().ArkCreateJsSymbolExtractor(&arkExtractorptr_);
1010 if (ret < 0) {
1011 arkExtractorptr_ = 0;
1012 HLOGE("failed to call ArkCreateJsSymbolExtractor, the symbol file is:%s", filePath_.c_str());
1013 }
1014 #endif
1015 return isHapAbc_;
1016 }
1017
IsAbc()1018 bool IsAbc() override
1019 {
1020 return isHapAbc_ == true;
1021 }
1022
SetBoolValue(bool value)1023 void SetBoolValue(bool value) override
1024 {
1025 isHapAbc_ = value;
1026 }
1027
LoadDebugInfo(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)1028 bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
1029 {
1030 if (map == nullptr) {
1031 HLOGD("map is null, symbolFilePath: %s", symbolFilePath.c_str());
1032 return false;
1033 }
1034 HLOGD("map name:%s", map->name.c_str());
1035 if (debugInfoLoaded_) {
1036 return true;
1037 }
1038 CHECK_TRUE(onRecording_, true, 0, "");
1039
1040 if (!IsHapAbc() && map_->IsMapExec()) {
1041 ElfFileSymbols::LoadDebugInfo(map, "");
1042 }
1043 debugInfoLoaded_ = true;
1044 debugInfoLoadResult_ = true;
1045 return true;
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 if (map == nullptr) {
1051 HLOGD("map is null, symbolFilePath: %s", symbolFilePath.c_str());
1052 return false;
1053 }
1054 HLOGD("map name:%s", map->name.c_str());
1055 CHECK_TRUE(!symbolsLoaded_ && onRecording_, true, 0, "");
1056 symbolsLoaded_ = true;
1057 if (!IsHapAbc() && map_->IsMapExec()) {
1058 ElfFileSymbols::LoadSymbols(map, "");
1059 }
1060 return true;
1061 }
1062
GetSymbolWithPcAndMap(const uint64_t ip,std::shared_ptr<DfxMap> map)1063 DfxSymbol GetSymbolWithPcAndMap(const uint64_t ip, std::shared_ptr<DfxMap> map) override
1064 {
1065 // get cache
1066 auto iter = symbolsMap_.find(ip);
1067 if (iter != symbolsMap_.end()) {
1068 return iter->second;
1069 }
1070 if (map == nullptr) {
1071 return DfxSymbol(ip, "");
1072 }
1073 HLOGD("map name:%s", map->name.c_str());
1074
1075 #if defined(is_ohos) && is_ohos
1076 if (IsAbc() && needParseJsFunc_) {
1077 JsFunction jsFunc;
1078 std::string module = map->name;
1079 HLOGD("map->name module:%s", module.c_str());
1080 auto ret = DfxArk::Instance().ParseArkFrameInfo(static_cast<uintptr_t>(ip),
1081 static_cast<uintptr_t>(map->begin),
1082 loadOffSet_, abcDataPtr_.get(), abcDataSize_,
1083 arkExtractorptr_, &jsFunc);
1084 if (ret == -1) {
1085 HLOGD("failed to call ParseArkFrameInfo, the symbol file is : %s", map->name.c_str());
1086 return DfxSymbol(ip, "");
1087 }
1088 this->symbolsMap_.insert(std::make_pair(ip,
1089 DfxSymbol(ip,
1090 jsFunc.codeBegin,
1091 jsFunc.functionName,
1092 jsFunc.ToString(),
1093 map->name)));
1094
1095 DfxSymbol &foundSymbol = symbolsMap_[ip];
1096 if (!foundSymbol.matched_) {
1097 foundSymbol.matched_ = true;
1098 foundSymbol.symbolFileIndex_ = id_;
1099 matchedSymbols_.push_back(&(symbolsMap_[ip]));
1100 }
1101
1102 HLOGD("ip : 0x%" PRIx64 " the symbol file is : %s, function is %s demangle_ : %s", ip,
1103 symbolsMap_[ip].module_.data(), jsFunc.functionName, matchedSymbols_.back()->demangle_.data());
1104 return symbolsMap_[ip];
1105 }
1106 #endif
1107 DfxSymbol symbol(ip, "");
1108 return symbol;
1109 }
1110 };
1111
1112 class V8FileSymbols : public ElfFileSymbols {
1113 private:
1114 bool v8Extracted_ = false;
1115 bool isV8_ = false;
1116 [[maybe_unused]] uintptr_t jsvmExtractorptr_ = 0;
1117
1118 pid_t pid_ = 0;
1119
1120 public:
V8FileSymbols(const std::string & symbolFilePath,pid_t pid)1121 explicit V8FileSymbols(const std::string &symbolFilePath, pid_t pid)
1122 : ElfFileSymbols(symbolFilePath, SYMBOL_V8_FILE)
1123 {
1124 pid_ = pid;
1125 }
1126
~V8FileSymbols()1127 ~V8FileSymbols() override
1128 {
1129 #if defined(is_ohos) && is_ohos
1130 if (jsvmExtractorptr_ != 0) {
1131 DfxJsvm::Instance().JsvmDestroyJsSymbolExtractor(jsvmExtractorptr_);
1132 jsvmExtractorptr_ = 0;
1133 }
1134 #endif
1135 }
1136
IsV8()1137 bool IsV8() override
1138 {
1139 #if defined(is_ohos) && is_ohos
1140 if (!needParseJsFunc_) {
1141 return true;
1142 }
1143 if (v8Extracted_) {
1144 return isV8_;
1145 }
1146 v8Extracted_ = true;
1147 if (jsvmExtractorptr_ == 0 && StringStartsWith(filePath_, "[anon:JSVM_JIT")) {
1148 auto ret = DfxJsvm::Instance().JsvmCreateJsSymbolExtractor(&jsvmExtractorptr_, pid_);
1149 if (ret < 0) {
1150 jsvmExtractorptr_ = 0;
1151 isV8_ = false;
1152 HLOGE("failed to call JsvmCreateJsSymbolExtractor, the symbol file is:%s", filePath_.c_str());
1153 } else {
1154 isV8_ = true;
1155 }
1156 }
1157 return isV8_;
1158 #endif
1159 return true;
1160 }
1161
SetBoolValue(bool value)1162 void SetBoolValue(bool value) override
1163 {
1164 v8Extracted_ = value;
1165 }
1166
LoadDebugInfo(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)1167 bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
1168 {
1169 if (map == nullptr) {
1170 HLOGD("map is null, symbolFilePath: %s", symbolFilePath.c_str());
1171 return false;
1172 }
1173 HLOGD("map name:%s", map->name.c_str());
1174 if (debugInfoLoaded_) {
1175 return true;
1176 }
1177 CHECK_TRUE(onRecording_, true, 0, "");
1178
1179 debugInfoLoaded_ = true;
1180 debugInfoLoadResult_ = true;
1181 return true;
1182 }
1183
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)1184 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
1185 {
1186 if (map == nullptr) {
1187 HLOGD("map is null, symbolFilePath: %s", symbolFilePath.c_str());
1188 return false;
1189 }
1190 HLOGD("map name:%s", map->name.c_str());
1191 CHECK_TRUE(!symbolsLoaded_ && onRecording_, true, 0, "");
1192 symbolsLoaded_ = true;
1193 return true;
1194 }
1195
GetSymbolWithPcAndMap(const uint64_t ip,std::shared_ptr<DfxMap> map)1196 DfxSymbol GetSymbolWithPcAndMap(const uint64_t ip, std::shared_ptr<DfxMap> map) override
1197 {
1198 // get cache
1199 auto iter = symbolsMap_.find(ip);
1200 if (iter != symbolsMap_.end()) {
1201 return iter->second;
1202 }
1203 if (map == nullptr) {
1204 return DfxSymbol(ip, "");
1205 }
1206 HLOGD("map name:%s", map->name.c_str());
1207
1208 #if defined(is_ohos) && is_ohos
1209 if (IsV8() && needParseJsFunc_) {
1210 JsvmFunction jsvmFunc;
1211 std::string module = map->name;
1212 HLOGD("map->name module:%s", module.c_str());
1213 auto ret = DfxJsvm::Instance().ParseJsvmFrameInfo(static_cast<uintptr_t>(ip), jsvmExtractorptr_, &jsvmFunc);
1214 if (ret == -1) {
1215 HLOGD("failed to call ParseJsvmFrameInfo, the symbol file is : %s", map->name.c_str());
1216 return DfxSymbol(ip, "");
1217 }
1218 this->symbolsMap_.insert(std::make_pair(ip,
1219 DfxSymbol(ip, 0, jsvmFunc.functionName, "", map->name)));
1220
1221 DfxSymbol &foundSymbol = symbolsMap_[ip];
1222 if (!foundSymbol.matched_) {
1223 foundSymbol.matched_ = true;
1224 foundSymbol.symbolFileIndex_ = id_;
1225 matchedSymbols_.push_back(&(symbolsMap_[ip]));
1226 }
1227
1228 HLOGD("ip : 0x%" PRIx64 " the symbol file is : %s, function is %s demangle_ : %s", ip,
1229 symbolsMap_[ip].module_.data(), jsvmFunc.functionName, matchedSymbols_.back()->demangle_.data());
1230 return symbolsMap_[ip];
1231 }
1232 #endif
1233 DfxSymbol symbol(ip, "");
1234 return symbol;
1235 }
1236 };
1237
1238 class UnknowFileSymbols : public SymbolsFile {
1239 public:
UnknowFileSymbols(const std::string & symbolFilePath)1240 explicit UnknowFileSymbols(const std::string &symbolFilePath)
1241 : SymbolsFile(SYMBOL_UNKNOW_FILE, symbolFilePath)
1242 {
1243 }
LoadSymbols(std::shared_ptr<DfxMap> map,const std::string & symbolFilePath)1244 bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
1245 {
1246 symbolsLoaded_ = true;
1247 return false;
1248 }
~UnknowFileSymbols()1249 ~UnknowFileSymbols() override {}
1250 };
1251
1252 class CJFileSymbols : public ElfFileSymbols {
1253 public:
CJFileSymbols(const std::string & symbolFilePath)1254 explicit CJFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
1255 {
1256 symbolFileType_ = SYMBOL_CJ_FILE;
1257 }
~CJFileSymbols()1258 ~CJFileSymbols() override {}
1259 };
1260
~SymbolsFile()1261 SymbolsFile::~SymbolsFile() {}
1262
CreateSymbolsFile(SymbolsFileType symbolType,const std::string symbolFilePath,pid_t pid)1263 std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(SymbolsFileType symbolType,
1264 const std::string symbolFilePath, pid_t pid)
1265 {
1266 switch (symbolType) {
1267 case SYMBOL_KERNEL_FILE:
1268 return std::make_unique<KernelSymbols>(symbolFilePath.empty() ? KERNEL_MMAP_NAME
1269 : symbolFilePath);
1270 case SYMBOL_KERNEL_MODULE_FILE:
1271 return std::make_unique<KernelModuleSymbols>(symbolFilePath);
1272 case SYMBOL_KERNEL_THREAD_FILE:
1273 return std::make_unique<KernelThreadSymbols>(symbolFilePath);
1274 case SYMBOL_ELF_FILE:
1275 return std::make_unique<ElfFileSymbols>(symbolFilePath);
1276 case SYMBOL_JAVA_FILE:
1277 return std::make_unique<JavaFileSymbols>(symbolFilePath);
1278 case SYMBOL_JS_FILE:
1279 return std::make_unique<JSFileSymbols>(symbolFilePath);
1280 case SYMBOL_HAP_FILE:
1281 return std::make_unique<HapFileSymbols>(symbolFilePath, pid);
1282 case SYMBOL_V8_FILE:
1283 return std::make_unique<V8FileSymbols>(symbolFilePath, pid);
1284 case SYMBOL_CJ_FILE:
1285 return std::make_unique<CJFileSymbols>(symbolFilePath);
1286 default:
1287 return std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, symbolFilePath);
1288 }
1289 }
1290
IsCJFile(const std::string & filepath)1291 static bool IsCJFile(const std::string& filepath)
1292 {
1293 if (!StringEndsWith(filepath, ".so")) {
1294 return false;
1295 }
1296 if (filepath.find("/data/storage") == std::string::npos &&
1297 !StringStartsWith(filepath, "/system/lib64/platformsdk/cjsdk")) {
1298 return false;
1299 }
1300 RegularElfFactory elfFactory(filepath);
1301 std::shared_ptr<DfxElf> elfFile_ = elfFactory.Create();
1302 ShdrInfo shinfo;
1303 if (elfFile_->GetSectionInfo(shinfo, ".cjmetadata")) {
1304 return true;
1305 }
1306 return false;
1307 }
1308
CreateSymbolsFile(const std::string & symbolFilePath,pid_t pid)1309 std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(const std::string &symbolFilePath, pid_t pid)
1310 {
1311 // we need check file name here
1312 if (symbolFilePath == KERNEL_MMAP_NAME) {
1313 return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_FILE, symbolFilePath);
1314 } else if (symbolFilePath == SYSMGR_FILE_NAME ||
1315 symbolFilePath == DEVHOST_LINUX_FILE_NAME ||
1316 StringStartsWith(symbolFilePath, DEVHOST_LINUX_PREFIX)) {
1317 return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, symbolFilePath);
1318 } else if (StringEndsWith(symbolFilePath, KERNEL_MODULES_EXT_NAME)) {
1319 return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, symbolFilePath);
1320 } else if (IsArkJsFile(symbolFilePath)) {
1321 return SymbolsFile::CreateSymbolsFile(SYMBOL_HAP_FILE, symbolFilePath, pid);
1322 } else if (IsV8File(symbolFilePath)) {
1323 return SymbolsFile::CreateSymbolsFile(SYMBOL_V8_FILE, symbolFilePath, pid);
1324 } else if (IsCJFile(symbolFilePath)) {
1325 return SymbolsFile::CreateSymbolsFile(SYMBOL_CJ_FILE, symbolFilePath, pid);
1326 } else {
1327 // default is elf, this may be problematic in the future.
1328 return SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, symbolFilePath);
1329 }
1330 }
1331
AdjustSymbols()1332 void SymbolsFile::AdjustSymbols()
1333 {
1334 if (symbols_.size() <= 0) {
1335 return;
1336 }
1337
1338 // order
1339 sort(symbols_.begin(), symbols_.end(), [](const DfxSymbol& a, const DfxSymbol& b) {
1340 return a.funcVaddr_ < b.funcVaddr_;
1341 });
1342 HLOGV("sort completed");
1343
1344 size_t fullSize = symbols_.size();
1345 size_t erased = 0;
1346
1347 // Check for duplicate vaddr
1348 auto last = std::unique(symbols_.begin(), symbols_.end(), [](const DfxSymbol &a, const DfxSymbol &b) {
1349 return (a.funcVaddr_ == b.funcVaddr_);
1350 });
1351 symbols_.erase(last, symbols_.end());
1352 erased = fullSize - symbols_.size();
1353 HLOGV("uniqued completed");
1354 auto it = symbols_.begin();
1355 while (it != symbols_.end()) {
1356 it->index_ = it - symbols_.begin();
1357 it++;
1358 }
1359 HLOGV("indexed completed");
1360
1361 HLOG_ASSERT(symbols_.size() != 0);
1362
1363 if (textExecVaddrRange_ == maxVaddr) {
1364 textExecVaddrRange_ = symbols_.back().funcVaddr_ - symbols_.front().funcVaddr_;
1365 }
1366
1367 HLOGDDD("%zu symbols after adjust (%zu erased) 0x%016" PRIx64 " - 0x%016" PRIx64
1368 " @0x%016" PRIx64 " ",
1369 symbols_.size(), erased, symbols_.front().funcVaddr_, symbols_.back().funcVaddr_,
1370 textExecVaddrFileOffset_);
1371 }
1372
SortMatchedSymbols()1373 void SymbolsFile::SortMatchedSymbols()
1374 {
1375 if (matchedSymbols_.size() <= 1u) {
1376 return;
1377 }
1378 sort(matchedSymbols_.begin(), matchedSymbols_.end(), [](const DfxSymbol* a, const DfxSymbol* b) {
1379 if (a == nullptr || b == nullptr) {
1380 return true;
1381 }
1382 return a->funcVaddr_ < b->funcVaddr_;
1383 });
1384 }
1385
GetSymbols()1386 const std::vector<DfxSymbol> &SymbolsFile::GetSymbols()
1387 {
1388 return symbols_;
1389 }
1390
GetMatchedSymbols()1391 const std::vector<DfxSymbol *> &SymbolsFile::GetMatchedSymbols()
1392 {
1393 return matchedSymbols_;
1394 }
1395
GetSymbolWithVaddr(uint64_t vaddrInFile)1396 const DfxSymbol SymbolsFile::GetSymbolWithVaddr(uint64_t vaddrInFile)
1397 {
1398 #ifdef HIPERF_DEBUG_TIME
1399 const auto startTime = steady_clock::now();
1400 #endif
1401 DfxSymbol symbol;
1402 // it should be already order from small to large
1403 auto found =
1404 std::upper_bound(symbols_.begin(), symbols_.end(), vaddrInFile, DfxSymbol::ValueLessThen);
1405 /*
1406 if data is { 1, 2, 4, 5, 5, 6 };
1407 upper_bound for each val :
1408 0 < 1 at index 0
1409 1 < 2 at index 1
1410 2 < 4 at index 2
1411 3 < 4 at index 2
1412 4 < 5 at index 3
1413 5 < 6 at index 5
1414 6 < not found
1415 if key symbol vaddr is { 1, 2, 4, 5, 5, 6 };
1416 check ip vaddr for each val :
1417 ip sym
1418 0 not found
1419 1 1
1420 1 1
1421 2 2
1422 3 3
1423 4 4
1424 5 5
1425 6 6
1426 7 7
1427 */
1428 if (found != symbols_.begin()) {
1429 found = std::prev(found);
1430 if (found != symbols_.end()) {
1431 if (found->Contain(vaddrInFile)) {
1432 found->offsetToVaddr_ = vaddrInFile - found->funcVaddr_;
1433 if (!found->matched_) {
1434 found->matched_ = true;
1435 matchedSymbols_.push_back(&(*found));
1436 }
1437 symbol = *found; // copy
1438 HLOGV("found '%s' for vaddr 0x%016" PRIx64 "", symbol.ToString().c_str(), vaddrInFile);
1439 }
1440 }
1441 }
1442
1443 if (!symbol.IsValid()) {
1444 HLOGV("NOT found vaddr 0x%" PRIx64 " in symbole file %s(%zu)", vaddrInFile,
1445 filePath_.c_str(), symbols_.size());
1446 }
1447 symbol.fileVaddr_ = vaddrInFile;
1448 symbol.symbolFileIndex_ = id_;
1449
1450 #ifdef HIPERF_DEBUG_TIME
1451 auto usedTime = duration_cast<milliseconds>(steady_clock::now() - startTime);
1452 if (usedTime > 1ms) {
1453 HLOGW("cost %" PRId64 "ms to search ", usedTime.count());
1454 }
1455 #endif
1456 return symbol;
1457 }
1458
CheckPathReadable(const std::string & path) const1459 bool SymbolsFile::CheckPathReadable(const std::string &path) const
1460 {
1461 if (access(path.c_str(), R_OK) == 0) {
1462 return true;
1463 }
1464 // access does not have the access permission in some scenarios
1465 struct stat st;
1466 if (stat(path.c_str(), &st) == 0) {
1467 return true;
1468 } else {
1469 #if defined(is_ohos) && is_ohos
1470 char errInfo[ERRINFOLEN] = { 0 };
1471 strerror_r(errno, errInfo, ERRINFOLEN);
1472 HLOGM("'%s' is unable read,errno: %d, errmsg: %s", path.c_str(), errno, errInfo);
1473 #endif
1474 return false;
1475 }
1476 }
1477
setSymbolsFilePath(const std::vector<std::string> & symbolsSearchPaths)1478 bool SymbolsFile::setSymbolsFilePath(const std::vector<std::string> &symbolsSearchPaths)
1479 {
1480 symbolsFileSearchPaths_.clear();
1481 for (auto &symbolsSearchPath : symbolsSearchPaths) {
1482 if (CheckPathReadable(symbolsSearchPath)) {
1483 symbolsFileSearchPaths_.emplace_back(symbolsSearchPath);
1484 HLOGV("'%s' is add to symbolsSearchPath", symbolsSearchPath.c_str());
1485 }
1486 }
1487 return (symbolsFileSearchPaths_.size() > 0);
1488 }
1489
LoadSymbolsFromSaved(const SymbolFileStruct & symbolFileStruct)1490 std::unique_ptr<SymbolsFile> SymbolsFile::LoadSymbolsFromSaved(
1491 const SymbolFileStruct &symbolFileStruct)
1492 {
1493 bool isHapSymbolFile = (static_cast<SymbolsFileType>(symbolFileStruct.symbolType_) == SYMBOL_HAP_FILE);
1494 HLOGD("isHapSymbolFile : %d", isHapSymbolFile);
1495 bool isV8SymbolFile = (static_cast<SymbolsFileType>(symbolFileStruct.symbolType_) == SYMBOL_V8_FILE);
1496 HLOGD("isV8SymbolFile : %d", isV8SymbolFile);
1497 auto symbolsFile = CreateSymbolsFile(symbolFileStruct.filePath_);
1498
1499 // default create elf file. but hap file need special operation.
1500 symbolsFile->filePath_ = symbolFileStruct.filePath_;
1501 symbolsFile->symbolFileType_ = static_cast<SymbolsFileType>(symbolFileStruct.symbolType_);
1502 symbolsFile->textExecVaddr_ = symbolFileStruct.textExecVaddr_;
1503 symbolsFile->textExecVaddrFileOffset_ = symbolFileStruct.textExecVaddrFileOffset_;
1504 symbolsFile->buildId_ = symbolFileStruct.buildId_;
1505 for (auto &symbolStruct : symbolFileStruct.symbolStructs_) {
1506 symbolsFile->symbols_.emplace_back(symbolStruct.vaddr_, symbolStruct.len_,
1507 symbolStruct.symbolName_, symbolFileStruct.filePath_);
1508 }
1509 symbolsFile->AdjustSymbols(); // reorder
1510 if (isHapSymbolFile || isV8SymbolFile) {
1511 for (const auto& symbol : symbolsFile->symbols_) {
1512 symbolsFile->symbolsMap_.emplace(symbol.funcVaddr_, symbol);
1513 }
1514 symbolsFile->SetBoolValue(true);
1515 }
1516 symbolsFile->debugInfoLoadResult_ = true;
1517 symbolsFile->symbolsLoaded_ = true; // all ready LoadFrom perf.data
1518 HLOGV("load %zu symbol from SymbolFileStruct for file '%s'", symbolsFile->symbols_.size(),
1519 symbolsFile->filePath_.c_str());
1520 return symbolsFile;
1521 }
1522
SetBoolValue(bool value)1523 void SymbolsFile::SetBoolValue(bool value)
1524 {
1525 }
1526
ExportSymbolToFileFormat(SymbolFileStruct & symbolFileStruct)1527 void SymbolsFile::ExportSymbolToFileFormat(SymbolFileStruct &symbolFileStruct)
1528 {
1529 symbolFileStruct.filePath_ = filePath_;
1530 symbolFileStruct.symbolType_ = symbolFileType_;
1531 symbolFileStruct.textExecVaddr_ = textExecVaddr_;
1532 symbolFileStruct.textExecVaddrFileOffset_ = textExecVaddrFileOffset_;
1533 symbolFileStruct.buildId_ = buildId_;
1534
1535 SortMatchedSymbols();
1536 auto symbols = GetMatchedSymbols();
1537 symbolFileStruct.symbolStructs_.reserve(symbols.size());
1538 for (const auto symbol : symbols) {
1539 auto &symbolStruct = symbolFileStruct.symbolStructs_.emplace_back();
1540 symbolStruct.vaddr_ = symbol->funcVaddr_;
1541 symbolStruct.len_ = symbol->size_;
1542 symbolStruct.symbolName_ = symbol->GetName();
1543 }
1544
1545 HLOGV("export %zu symbol to SymbolFileStruct from %s", symbolFileStruct.symbolStructs_.size(),
1546 filePath_.c_str());
1547 }
1548
GetVaddrInSymbols(uint64_t ip,uint64_t mapStart,uint64_t mapOffset) const1549 uint64_t SymbolsFile::GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t mapOffset) const
1550 {
1551 // no convert
1552 return ip;
1553 }
1554
AddSymbol(DfxSymbol symbol)1555 void SymbolsFile::AddSymbol(DfxSymbol symbol)
1556 {
1557 symbolsLoaded_ = true;
1558 symbols_.emplace_back(symbol);
1559 }
1560 } // namespace HiPerf
1561 } // namespace Developtools
1562 } // namespace OHOS
1563