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