* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define HILOG_TAG "Symbols"
#include "symbols_file.h"
#include <algorithm>
#include <chrono>
#include <cxxabi.h>
#include <cstdlib>
#include <fcntl.h>
#include <fstream>
#include <string_view>
#include <type_traits>
#if defined(is_mingw) && is_mingw
#include <memoryapi.h>
#else
#include <sys/mman.h>
#include <sys/stat.h>
#endif
#include <unistd.h>
#include "dfx_ark.h"
#include "dfx_extractor_utils.h"
#include "dfx_jsvm.h"
#include "dfx_symbols.h"
#include "dwarf_encoding.h"
#include "elf_factory.h"
#include "hiperf_hilog.h"
#include "unwinder_config.h"
#include "utilities.h"
#include "ipc_utilities.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
using namespace std::chrono;
bool SymbolsFile::onRecording_ = true;
bool SymbolsFile::needJsvm_ = false;
uint32_t SymbolsFile::offsetNum_ = 0;
const std::string SymbolsFile::GetBuildId() const
{
return buildId_;
}
bool SymbolsFile::UpdateBuildIdIfMatch(const std::string &buildId)
{
here we have two case
1 buildId_ is empty
a) always return match
2 buildId_ is not empty
a) really check if the same one
*/
if (buildId_.empty()) {
if (buildId.empty()) {
HLOGD("build id is empty.");
} else {
buildId_ = buildId;
HLOGD("new buildId %s", buildId_.c_str());
}
return true;
}
HLOGV("expected buildid: %s vs %s", buildId_.c_str(), buildId.c_str());
if (buildId_ != buildId) {
HLOGW("id not match");
return false;
}
HLOGD("id match");
return true;
}
std::string SymbolsFile::SearchReadableFile(const std::vector<std::string> &searchPaths,
const std::string &filePath) const
{
if (filePath.empty()) {
HLOGW("filePath is empty, nothing to found");
return filePath;
}
for (auto searchPath : searchPaths) {
if (searchPath.empty()) {
HLOGW("searchPath is empty.");
continue;
}
if (searchPath.back() != PATH_SEPARATOR) {
searchPath += PATH_SEPARATOR;
}
std::string PossibleFilePath = searchPath + filePath;
if (CheckPathReadable(PossibleFilePath)) {
return PossibleFilePath;
}
HLOGW("have not found '%s' in search paths %s", filePath.c_str(), searchPath.c_str());
}
return EMPTY_STRING;
}
const std::string SymbolsFile::FindSymbolFile(
const std::vector<std::string> &symbolsFileSearchPaths, std::string symboleFilePath) const
{
this function do 2 things:
find by name:
1 find dso path
2 find search path
a) search path + dso path
b) search path + dso name
show we should return filePath_ as default ?
*/
if (symboleFilePath.empty()) {
symboleFilePath = filePath_;
HLOGD("use default filename: %s ", symboleFilePath.c_str());
}
symboleFilePath = PlatformPathConvert(symboleFilePath);
std::string foundPath;
if (symbolsFileSearchPaths.size() != 0) {
foundPath = SearchReadableFile(symbolsFileSearchPaths, symboleFilePath);
if (foundPath.empty()) {
HLOGV("try base name for: %s split with %s", symboleFilePath.c_str(),
PATH_SEPARATOR_STR.c_str());
auto pathSplit = StringSplit(symboleFilePath, PATH_SEPARATOR_STR);
if (pathSplit.size() > 1) {
HLOGV("base name is: %s ", pathSplit.back().c_str());
foundPath = SearchReadableFile(symbolsFileSearchPaths, pathSplit.back());
}
}
}
if (foundPath.empty() && onRecording_) {
if (CheckPathReadable(symboleFilePath)) {
HLOGD("find %s in current work dir", symboleFilePath.c_str());
return symboleFilePath;
}
}
return foundPath;
}
class ElfFileSymbols : public SymbolsFile {
public:
explicit ElfFileSymbols(const std::string &symbolFilePath,
const SymbolsFileType symbolsFileType = SYMBOL_ELF_FILE)
: SymbolsFile(symbolsFileType, symbolFilePath)
{
}
virtual ~ElfFileSymbols()
{
}
DfxSymbol GetSymbolWithPcAndMap(const uint64_t pc, std::shared_ptr<DfxMap> map) override
{
const DfxSymbol symbol;
return symbol;
}
bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
symbolsLoaded_ = true;
std::string findPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
if (findPath.empty() && elfFile_ == nullptr) {
HLOGW("elf found failed (belong to %s)", filePath_.c_str());
return false;
}
if (LoadElfSymbols(map, findPath)) {
return true;
} else {
HLOGW("elf open failed with '%s'", findPath.c_str());
return false;
}
return false;
}
void EnableMiniDebugInfo() override
{
UnwinderConfig::SetEnableMiniDebugInfo(true);
}
std::shared_ptr<DfxElf> GetElfFile() override
{
return elfFile_;
}
const std::unordered_map<uint64_t, ElfLoadInfo> GetPtLoads() override
{
CHECK_TRUE(elfFile_ != nullptr, info_, 0, "");
return elfFile_->GetPtLoads();
}
void ReleaseDebugInfo() override
{
if (map_ != nullptr) {
map_->elf = nullptr;
if (map_->prevMap != nullptr) {
map_->prevMap->elf = nullptr;
}
}
elfFile_.reset();
debugInfoLoaded_ = false;
debugInfoLoadResult_ = false;
SymbolsFile::ReleaseDebugInfo();
}
protected:
bool CreateElfFile(std::shared_ptr<DfxMap> map, std::string &elfPath)
{
if (elfFile_ == nullptr) {
if (StringEndsWith(elfPath, ".hap") && StringStartsWith(elfPath, "/proc")) {
if (map == nullptr) {
HLOGW("map should not be nullptr.");
return false;
}
if (!map->IsMapExec()) {
HLOGW("map is not exec, no need parse elf.");
return false;
}
CompressHapElfFactory elfFactory(elfPath, map->prevMap);
elfFile_ = elfFactory.Create();
map->offset -= map->prevMap->offset;
HLOGD("try create elf from hap");
} else {
RegularElfFactory elfFactory(elfPath);
elfFile_ = elfFactory.Create();
}
}
CHECK_TRUE(elfFile_ != nullptr, false, 1, "Failed to create elf file for %s.", elfPath.c_str());
CHECK_TRUE(elfFile_->IsValid(), false, 1, "parser elf file failed.");
return true;
}
bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
if (debugInfoLoadResult_) {
return true;
} else if (debugInfoLoaded_) {
return debugInfoLoadResult_;
}
debugInfoLoaded_ = true;
std::string elfPath = FindSymbolFile(symbolsFileSearchPaths_, symbolFilePath);
if (elfPath.empty()) {
HLOGW("elf found failed (belong to %s)", filePath_.c_str());
return false;
}
if (!CreateElfFile(map, elfPath)) {
return false;
}
HLOGD("loaded elf %s", elfPath.c_str());
if (StringEndsWith(elfPath, ".hap")) {
filePath_ = elfPath + "!" + elfFile_->GetElfName();
HLOGD("update path for so in hap %s.", filePath_.c_str());
if (map == nullptr) {
HLOGW("map should not be nullptr.");
return false;
}
map->name = filePath_;
map->elf = elfFile_;
map->prevMap->name = filePath_;
map->prevMap->elf = elfFile_;
}
textExecVaddr_ = elfFile_->GetStartVaddr();
textExecVaddrFileOffset_ = elfFile_->GetStartOffset();
HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
textExecVaddrFileOffset_);
#ifndef __arm__
ShdrInfo shinfo;
if (elfFile_->GetSectionInfo(shinfo, ".eh_frame_hdr")) {
auto mmapPtr = elfFile_->GetMmapPtr();
CHECK_TRUE(mmapPtr != nullptr, false, 1, "mmapPtr should not be nullptr.");
LoadEhFrameHDR(mmapPtr + shinfo.offset, shinfo.size, shinfo.offset);
}
#endif
HLOGD("LoadDebugInfo success!");
debugInfoLoadResult_ = true;
return true;
}
private:
bool EhFrameHDRValid_ {false};
uint64_t ehFrameHDRElfOffset_ {0};
uint64_t ehFrameHDRFdeCount_ {0};
uint64_t ehFrameHDRFdeTableItemSize_ {0};
uint64_t ehFrameHDRFdeTableElfOffset_ {0};
std::shared_ptr<DfxElf> elfFile_ = nullptr;
std::unordered_map<uint64_t, ElfLoadInfo> info_;
bool GetSectionInfo(const std::string &name, uint64_t §ionVaddr, uint64_t §ionSize,
uint64_t §ionFileOffset) const override
{
struct ShdrInfo shdrInfo;
if (elfFile_ != nullptr && elfFile_->GetSectionInfo(shdrInfo, name)) {
sectionVaddr = shdrInfo.addr;
sectionSize = shdrInfo.size;
sectionFileOffset = shdrInfo.offset;
HLOGM("Get Section '%s' %" PRIx64 " - %" PRIx64 "", name.c_str(), sectionVaddr, sectionSize);
return true;
}
HLOGW("Section '%s' not found", name.c_str());
return false;
}
#ifndef __arm__
bool GetHDRSectionInfo(uint64_t &ehFrameHdrElfOffset, uint64_t &fdeTableElfOffset,
uint64_t &fdeTableSize) override
{
CHECK_TRUE(elfFile_ != nullptr, false, 1, "elfFile_ is nullptr");
ShdrInfo shinfo;
if (!elfFile_->GetSectionInfo(shinfo, ".eh_frame_hdr")) {
return false;
}
ehFrameHDRElfOffset_ = shinfo.offset;
if (EhFrameHDRValid_) {
ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
fdeTableSize = ehFrameHDRFdeCount_;
return true;
}
auto mmapPtr = elfFile_->GetMmapPtr();
if (mmapPtr == nullptr) {
HLOGE("mmapPtr should not be nullptr.");
return false;
}
if (!LoadEhFrameHDR(mmapPtr + shinfo.offset, elfFile_->GetMmapSize(), shinfo.offset)) {
HLOGW("Failed to load eh_frame_hdr");
return false;
}
ehFrameHdrElfOffset = ehFrameHDRElfOffset_;
fdeTableElfOffset = ehFrameHDRFdeTableElfOffset_;
fdeTableSize = ehFrameHDRFdeCount_;
return true;
}
#endif
void DumpEhFrameHDR()
{
HLOGD(" ehFrameHDRElfOffset_: 0x%" PRIx64 "", ehFrameHDRElfOffset_);
HLOGD(" ehFrameHDRFdeCount_: 0x%" PRIx64 "", ehFrameHDRFdeCount_);
HLOGD(" ehFrameHDRFdeTableElfOffset_: 0x%" PRIx64 "", ehFrameHDRFdeTableElfOffset_);
HLOGD(" ehFrameHDRFdeTableItemSize_: 0x%" PRIx64 "", ehFrameHDRFdeTableItemSize_);
}
bool LoadEhFrameHDR(const unsigned char *buffer, size_t bufferSize, uint64_t shdrOffset)
{
CHECK_TRUE(bufferSize >= sizeof(eh_frame_hdr), false, 0, "");
const eh_frame_hdr *ehFrameHdr = reinterpret_cast<const eh_frame_hdr *>(buffer);
CHECK_TRUE(ehFrameHdr != nullptr, false, 0, "");
const uint8_t *dataPtr = ehFrameHdr->encode_data;
DwarfEncoding dwEhFramePtr(ehFrameHdr->eh_frame_ptr_enc, dataPtr);
DwarfEncoding dwFdeCount(ehFrameHdr->fde_count_enc, dataPtr);
DwarfEncoding dwTable(ehFrameHdr->table_enc, dataPtr);
DwarfEncoding dwTableValue(ehFrameHdr->table_enc, dataPtr);
HLOGD("eh_frame_hdr:");
if (HexDump(ehFrameHdr, BITS_OF_FOUR_BYTE, bufferSize) == false) {
HLOGW("HexDump failed.");
}
unsigned char version = ehFrameHdr->version;
HLOGD(" version: %02x:%s", version, (version == 1) ? "valid" : "invalid");
HLOGD(" eh_frame_ptr_enc: %s", dwEhFramePtr.ToString().c_str());
HLOGD(" fde_count_enc: %s", dwFdeCount.ToString().c_str());
HLOGD(" table_enc: %s", dwTable.ToString().c_str());
HLOGD(" table_value_enc: %s", dwTableValue.ToString().c_str());
HLOGD(" table_item_size: %zd", dwTable.GetSize() + dwTableValue.GetSize());
HLOGD(" table_offset_in_hdr: %zu", dwTable.GetData() - buffer);
CHECK_TRUE(version == 1, false, 1, "eh_frame_hdr version is invalid");
EhFrameHDRValid_ = true;
ehFrameHDRElfOffset_ = shdrOffset;
ehFrameHDRFdeCount_ = dwFdeCount.GetAppliedValue();
ehFrameHDRFdeTableElfOffset_ = dwTable.GetData() - buffer + shdrOffset;
ehFrameHDRFdeTableItemSize_ = dwTable.GetSize() + dwTableValue.GetSize();
DumpEhFrameHDR();
if (!dwFdeCount.IsOmit() && dwFdeCount.GetValue() > 0) {
return true;
}
HLOGW("fde table not found.\n");
return false;
}
void UpdateSymbols(std::vector<DfxSymbol> &symbolsTable, const std::string &elfPath)
{
symbols_.clear();
HLOGD("%zu symbols loadded from symbolsTable.", symbolsTable.size());
symbols_.swap(symbolsTable);
AdjustSymbols();
HLOGD("%zu symbols loadded from elf '%s'.", symbols_.size(), elfPath.c_str());
for (auto& symbol: symbols_) {
HLOGD("symbol %s", symbol.ToDebugString().c_str());
}
if (buildId_.empty()) {
HLOGD("buildId not found from elf '%s'.", elfPath.c_str());
}
}
void AddSymbols(std::vector<DfxSymbol>& symbolsTable, std::shared_ptr<DfxElf> elf, const std::string& filePath)
{
DfxSymbols::ParseSymbols(symbolsTable, elf, filePath);
DfxSymbols::AddSymbolsByPlt(symbolsTable, elf, filePath);
}
void GetTextExecVaddr()
{
#if defined(is_ohos) && is_ohos
textExecVaddr_ = elfFile_->GetStartVaddr();
textExecVaddrFileOffset_ = elfFile_->GetStartOffset();
#else
if (textExecVaddr_ == maxVaddr) {
textExecVaddr_ = elfFile_->GetStartVaddr();
textExecVaddrFileOffset_ = elfFile_->GetStartOffset();
}
#endif
HLOGD("textExecVaddr_ 0x%016" PRIx64 " file offset 0x%016" PRIx64 "", textExecVaddr_,
textExecVaddrFileOffset_);
}
bool LoadElfSymbols(std::shared_ptr<DfxMap> map, std::string elfPath)
{
#ifdef HIPERF_DEBUG_TIME
const auto startTime = steady_clock::now();
#endif
if (elfFile_ == nullptr) {
if (StringEndsWith(elfPath, ".hap") && StringStartsWith(elfPath, "/proc") && map != nullptr) {
if (!map->IsMapExec()) {
HLOGW("map is not exec, no need parse elf.");
return false;
}
CompressHapElfFactory elfFactory(elfPath, map->prevMap);
elfFile_ = elfFactory.Create();
map->offset -= map->prevMap->offset;
map->elf = elfFile_;
} else {
RegularElfFactory elfFactory(elfPath);
elfFile_ = elfFactory.Create();
}
}
CHECK_TRUE(elfFile_ != nullptr, false, 1, "Failed to create elf file for %s.", elfPath.c_str());
HLOGD("loaded elf %s", elfPath.c_str());
if (!elfFile_->IsValid()) {
HLOGD("parser elf file failed.");
return false;
}
GetTextExecVaddr();
std::string buildIdFound = elfFile_->GetBuildId();
std::vector<DfxSymbol> symbolsTable;
AddSymbols(symbolsTable, elfFile_, filePath_);
if (UpdateBuildIdIfMatch(buildIdFound)) {
UpdateSymbols(symbolsTable, elfPath);
} else {
HLOGW("symbols will not update for '%s' because buildId is not match.",
elfPath.c_str());
return false;
}
#ifdef HIPERF_DEBUG_TIME
auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
if (usedTime.count() != 0) {
HLOGV("cost %0.3f ms to load symbols '%s'",
usedTime.count() / static_cast<double>(milliseconds::duration::period::den),
elfPath.c_str());
}
#endif
return true;
}
uint64_t GetVaddrInSymbols(const uint64_t ip, const uint64_t mapStart,
const uint64_t mapPageOffset) const override
{
00200000-002c5000 r--p 00000000 08:02 46400311
002c5000-00490000 r-xp 000c5000 08:02 4640031
[14] .text PROGBITS 00000000002c5000 000c5000
if ip is 0x46e6ab
1. find the map range is 002c5000-00490000
2. ip - map start(002c5000) = map section offset
3. map section offset + map page offset(000c5000) = elf file offset
4. elf file offset - exec file offset(000c5000)
= ip offset (ip always in exec file offset)
5. ip offset + exec begin vaddr(2c5000) = virtual ip in elf
*/
uint64_t vaddr = ip - mapStart + mapPageOffset - textExecVaddrFileOffset_ + textExecVaddr_;
HLOGM(" ip :0x%016" PRIx64 " -> elf offset :0x%016" PRIx64 " -> vaddr :0x%016" PRIx64 " ",
ip, ip - mapStart + mapPageOffset, vaddr);
HLOGM("(minExecAddrFileOffset_ is 0x%" PRIx64 " textExecVaddr_ is 0x%" PRIx64 ")",
textExecVaddrFileOffset_, textExecVaddr_);
return vaddr;
}
};
class KernelSymbols : public ElfFileSymbols {
public:
explicit KernelSymbols(const std::string &symbolFilePath)
: ElfFileSymbols(symbolFilePath, SYMBOL_KERNEL_FILE)
{
}
KernelSymbols(const std::string &symbolFilePath,
const SymbolsFileType symbolsFileType)
: ElfFileSymbols(symbolFilePath, symbolsFileType)
{
}
static constexpr const int KSYM_MIN_TOKENS = 3;
static constexpr const int KSYM_DEFAULT_LINE = 35000;
static constexpr const int KSYM_DEFAULT_SIZE = 1024 * 1024 * 1;
struct ParseTime {
std::chrono::microseconds parseLineTime = std::chrono::microseconds::zero();
std::chrono::microseconds sscanfTime = std::chrono::microseconds::zero();
std::chrono::microseconds newTime = std::chrono::microseconds::zero();
std::chrono::microseconds readFileTime = std::chrono::microseconds::zero();
};
void ParseKsymsLine(char **lineBegin, char **dataEnd, size_t &lines, ParseTime &parseTime)
{
char *lineEnd = strchr(*lineBegin, '\n');
if (lineEnd != nullptr) {
*lineEnd = '\0';
} else {
lineEnd = *dataEnd;
}
size_t lineSize = (lineEnd != nullptr) ? (lineEnd - *lineBegin) : (*dataEnd - *lineBegin);
const auto eachLineStartTime = std::chrono::steady_clock::now();
lines++;
uint64_t addr = 0;
char type = '\0';
char nameRaw[lineSize];
char moduleRaw[lineSize];
int ret = sscanf_s(*lineBegin, "%" PRIx64 " %c %s%s", &addr, &type, sizeof(type),
nameRaw, (unsigned)(sizeof(nameRaw) - 1),
moduleRaw, (unsigned)(sizeof(moduleRaw) - 1));
parseTime.sscanfTime += duration_cast<milliseconds>(steady_clock::now() - eachLineStartTime);
if (ret >= KSYM_MIN_TOKENS) {
if (ret == KSYM_MIN_TOKENS) {
moduleRaw[0] = '\0';
}
HLOGM(" 0x%016" PRIx64 " %c '%s' '%s'", addr, type, nameRaw, moduleRaw);
} else {
HLOGW("unknown line %d: '%s'", ret, *lineBegin);
*lineBegin = lineEnd + 1;
return;
}
*lineBegin = lineEnd + 1;
T
The symbol is in the text (code) section.
W
The symbol is a weak symbol that has not been specifically
tagged as a weak object symbol. When a weak defined symbol is
linked with a normal defined symbol, the normal defined symbol
is used with no error. When a weak undefined symbol is linked
and the symbol is not defined, the value of the weak symbol
becomes zero with no error.
*/
if (addr != 0 && strchr("TtWw", type)) {
const auto eachNewSymbolTime = steady_clock::now();
std::string name = nameRaw;
std::string module = moduleRaw;
symbols_.emplace_back(addr, name, module.empty() ? filePath_ : module);
parseTime.newTime += duration_cast<milliseconds>(steady_clock::now() - eachNewSymbolTime);
}
parseTime.parseLineTime += duration_cast<milliseconds>(steady_clock::now() - eachLineStartTime);
}
void ParseKsymsLine(char **lineBegin, char **dataEnd, size_t &lines)
{
char *lineEnd = strchr(*lineBegin, '\n');
if (lineEnd != nullptr) {
*lineEnd = '\0';
} else {
lineEnd = *dataEnd;
}
size_t lineSize = (lineEnd != nullptr) ? (lineEnd - *lineBegin) : (*dataEnd - *lineBegin);
lines++;
uint64_t addr = 0;
char type = '\0';
char nameRaw[lineSize];
char moduleRaw[lineSize];
int ret = sscanf_s(*lineBegin, "%" PRIx64 " %c %s%s", &addr, &type, sizeof(type),
nameRaw, (unsigned)(sizeof(nameRaw) - 1),
moduleRaw, (unsigned)(sizeof(moduleRaw) - 1));
if (ret >= KSYM_MIN_TOKENS) {
if (ret == KSYM_MIN_TOKENS) {
moduleRaw[0] = '\0';
}
HLOGM(" 0x%016" PRIx64 " %c '%s' '%s'", addr, type, nameRaw, moduleRaw);
} else {
HLOGW("unknown line %d: '%s'", ret, *lineBegin);
*lineBegin = lineEnd + 1;
return;
}
*lineBegin = lineEnd + 1;
T
The symbol is in the text (code) section.
W
The symbol is a weak symbol that has not been specifically
tagged as a weak object symbol. When a weak defined symbol is
linked with a normal defined symbol, the normal defined symbol
is used with no error. When a weak undefined symbol is linked
and the symbol is not defined, the value of the weak symbol
becomes zero with no error.
*/
if (addr != 0 && strchr("TtWw", type)) {
std::string name = nameRaw;
std::string module = moduleRaw;
symbols_.emplace_back(addr, name, module.empty() ? filePath_ : module);
}
}
bool ParseKallsymsLine(const std::string &kallsymsPath)
{
#ifdef HIPERF_DEBUG_SYMBOLS_TIME
const auto startTime = steady_clock::now();
ParseTime parseTime;
parseTime.parseLineTime = std::chrono::microseconds::zero();
parseTime.sscanfTime = std::chrono::microseconds::zero();
parseTime.newTime = std::chrono::microseconds::zero();
parseTime.readFileTime = std::chrono::microseconds::zero();
#endif
size_t lines = 0;
#ifdef HIPERF_DEBUG_SYMBOLS_TIME
const auto eachFileStartTime = steady_clock::now();
#endif
std::string kallsym;
CHECK_TRUE(ReadFileToString(kallsymsPath, kallsym, KSYM_DEFAULT_SIZE) && !kallsym.empty(), false, 1,
"%s load failed.", kallsymsPath.c_str());
#ifdef HIPERF_DEBUG_SYMBOLS_TIME
readFileTime += duration_cast<milliseconds>(steady_clock::now() - eachFileStartTime);
#endif
symbols_.reserve(KSYM_DEFAULT_LINE);
char *lineBegin = kallsym.data();
char *dataEnd = lineBegin + kallsym.size();
while (lineBegin < dataEnd) {
#ifdef HIPERF_DEBUG_SYMBOLS_TIME
ParseKsymsLine(&lineBegin, &dataEnd, lines, parseTime);
#else
ParseKsymsLine(&lineBegin, &dataEnd, lines);
#endif
}
#ifdef HIPERF_DEBUG_SYMBOLS_TIME
std::chrono::microseconds usedTime =
duration_cast<milliseconds>(steady_clock::now() - startTime);
printf("parse kernel symbols use : %0.3f ms\n", usedTime.count() / MS_DURATION);
printf("parse line use : %0.3f ms\n", parseTime.parseLineTime.count() / MS_DURATION);
printf("sscanf line use : %0.3f ms\n", parseTime.sscanfTime.count() / MS_DURATION);
printf("new symbols use : %0.3f ms\n", parseTime.newTime.count() / MS_DURATION);
printf("read file use : %0.3f ms\n", parseTime.readFileTime.count() / MS_DURATION);
#endif
HLOGD("load %s: %zu line processed(%zu symbols)", kallsymsPath.c_str(), lines, symbols_.size());
return true;
}
const std::string KPTR_RESTRICT = "/proc/sys/kernel/kptr_restrict";
bool LoadKernelSyms()
{
if (!IsRoot()) {
return false;
}
HLOGD("try read /proc/kallsyms");
if (access("/proc/kallsyms", R_OK) != 0) {
printf("No vmlinux path is given, and kallsyms cannot be opened\n");
return false;
}
bool hasChangeKptr = false;
std::string oldKptrRestrict = ReadFileToString(KPTR_RESTRICT);
if (oldKptrRestrict.front() != '0') {
printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n");
hasChangeKptr = WriteStringToFile(KPTR_RESTRICT, "0");
if (!hasChangeKptr) {
printf("/proc/sys/kernel/kptr_restrict write failed and we can't not change it.\n");
}
}
CHECK_TRUE(ParseKallsymsLine("/proc/kallsyms"), false, 0, "");
if (hasChangeKptr) {
if (!WriteStringToFile(KPTR_RESTRICT, oldKptrRestrict)) {
printf("recover /proc/sys/kernel/kptr_restrict fail.\n");
}
}
if (symbols_.empty()) {
printf("The symbol table addresses in /proc/kallsyms are all 0.\n"
"Please check the value of /proc/sys/kernel/kptr_restrict, it "
"should be 0.\n"
"Or provide a separate vmlinux path.\n");
if (buildId_.size() != 0) {
HLOGD("kallsyms not found. but we have the buildid");
return true;
} else {
return false;
}
} else {
AdjustSymbols();
HLOGV("%zu symbols_ loadded from kallsyms.\n", symbols_.size());
return true;
}
}
bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
symbolsLoaded_ = true;
HLOGV("KernelSymbols try read '%s' search paths size %zu, inDeviceRecord %d",
symbolFilePath.c_str(), symbolsFileSearchPaths_.size(), onRecording_);
if (onRecording_) {
const auto startTime = std::chrono::steady_clock::now();
if (!LoadKernelSyms()) {
if (IsRoot()) {
printf("parse kalsyms failed.\n");
}
return false;
} else {
const auto thisTime = std::chrono::steady_clock::now();
const auto usedTimeMsTick =
std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime);
HLOGV("Load kernel symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count());
return true;
}
}
return ElfFileSymbols::LoadSymbols(nullptr, KERNEL_ELF_NAME);
}
uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
{
return ip;
}
~KernelSymbols() override {}
};
class KernelThreadSymbols : public KernelSymbols {
public:
explicit KernelThreadSymbols(const std::string &symbolFilePath)
: KernelSymbols(symbolFilePath, SYMBOL_KERNEL_THREAD_FILE)
{
}
bool LoadKernelSyms()
{
if (!IsRoot()) {
return false;
}
std::string procPath;
if (filePath_ == SYSMGR_FILE_NAME) {
procPath = StringPrintf("/proc/%u/uallsyms", SYSMGR_PID);
} else if (filePath_ == DEVHOST_FILE_NAME) {
procPath = "/proc/devhost/root/kallsyms";
}
HLOGD("try read kernel thread symbol file %s in %s", filePath_.c_str(), procPath.c_str());
CHECK_TRUE(access(procPath.c_str(), R_OK) == 0, false, LOG_TYPE_PRINTF,
"kernel thread symbol file %s cannot be opened\n", filePath_.c_str());
bool hasChangeKptr = false;
std::string oldKptrRestrict = ReadFileToString(KPTR_RESTRICT);
if (oldKptrRestrict.front() != '0') {
printf("/proc/sys/kernel/kptr_restrict is NOT 0, will try set it to 0.\n");
hasChangeKptr = WriteStringToFile(KPTR_RESTRICT, "0");
if (!hasChangeKptr) {
printf("/proc/sys/kernel/kptr_restrict write failed and we can't not change it.\n");
}
}
CHECK_TRUE(ParseKallsymsLine(procPath), false, 0, "");
if (hasChangeKptr) {
if (!WriteStringToFile(KPTR_RESTRICT, oldKptrRestrict)) {
printf("recover /proc/sys/kernel/kptr_restrict fail.\n");
}
}
if (symbols_.empty()) {
printf("The symbol table addresses in %s are all 0.\n"
"Please check the value of /proc/sys/kernel/kptr_restrict, it "
"should be 0.\n", filePath_.c_str());
return false;
}
AdjustSymbols();
HLOGV("%zu symbols_ loadded from %s.\n", symbols_.size(), procPath.c_str());
return true;
}
bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
symbolsLoaded_ = true;
HLOGV("KernelThreadSymbols try read '%s', inDeviceRecord %d",
filePath_.c_str(), onRecording_);
if (onRecording_) {
const auto startTime = std::chrono::steady_clock::now();
if (!LoadKernelSyms()) {
if (IsRoot()) {
printf("parse %s failed.\n", filePath_.c_str());
}
} else {
const auto thisTime = std::chrono::steady_clock::now();
const auto usedTimeMsTick =
std::chrono::duration_cast<std::chrono::milliseconds>(thisTime - startTime);
HLOGV("Load kernel thread symbols (total %" PRId64 " ms)\n", (int64_t)usedTimeMsTick.count());
return true;
}
}
return ElfFileSymbols::LoadSymbols(nullptr, filePath_);
}
DfxSymbol GetSymbolWithPcAndMap(uint64_t ip, std::shared_ptr<DfxMap> map) override
{
DfxSymbol symbol;
auto found =
std::upper_bound(symbols_.begin(), symbols_.end(), ip, DfxSymbol::ValueLessThen);
if (found == symbols_.begin()) {
if (!symbol.IsValid()) {
HLOGV("NOT found vaddr 0x%" PRIx64 " in symbole file %s(%zu)", ip,
filePath_.c_str(), symbols_.size());
}
symbol.fileVaddr_ = ip;
symbol.symbolFileIndex_ = id_;
return symbol;
}
found = std::prev(found);
if (found != symbols_.end() && found->Contain(ip)) {
found->offsetToVaddr_ = ip - found->funcVaddr_;
CacheMatchedSymbol(ip, *found);
symbol = *found;
HLOGV("found '%s' for vaddr 0x%016" PRIx64 "", symbol.ToString().c_str(), ip);
}
if (!symbol.IsValid()) {
HLOGV("NOT found vaddr 0x%" PRIx64 " in symbole file %s(%zu)", ip,
filePath_.c_str(), symbols_.size());
}
symbol.fileVaddr_ = ip;
symbol.symbolFileIndex_ = id_;
return symbol;
}
void CacheMatchedSymbol(uint64_t ip, DfxSymbol& matched)
{
if (matched.matched_) {
return;
}
matched.matched_ = true;
DfxSymbol matchedSymbol = matched;
matchedSymbol.funcVaddr_ += SymbolsFile::offsetNum_;
auto insertResult = this->symbolsMap_.emplace(ip, matchedSymbol);
if (insertResult.second) {
matchedSymbols_.push_back(&(insertResult.first->second));
}
}
bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
if (onRecording_) {
if (IsHM()) {
debugInfoLoaded_ = true;
debugInfoLoadResult_ = true;
return true;
}
}
return ElfFileSymbols::LoadDebugInfo(map, symbolFilePath);
}
~KernelThreadSymbols() override {}
};
class KernelModuleSymbols : public ElfFileSymbols {
public:
explicit KernelModuleSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
{
HLOGV("create %s", symbolFilePath.c_str());
symbolFileType_ = SYMBOL_KERNEL_MODULE_FILE;
module_ = symbolFilePath;
}
~KernelModuleSymbols() override {};
bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
symbolsLoaded_ = true;
if (module_ == filePath_ && onRecording_) {
HLOGV("find ko name %s", module_.c_str());
for (const std::string &path : kernelModulePaths) {
if (access(path.c_str(), R_OK) == 0) {
std::string koPath = path + module_ + KERNEL_MODULES_EXT_NAME;
HLOGV("found ko in %s", koPath.c_str());
if (access(koPath.c_str(), R_OK) == 0) {
filePath_ = koPath;
break;
}
}
}
LoadBuildId();
} else {
HLOGV("we have file path, load with %s", filePath_.c_str());
return ElfFileSymbols::LoadSymbols(nullptr, filePath_);
}
return false;
}
uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t) const override
{
return ip - mapStart;
}
private:
bool LoadBuildId()
{
std::string sysFile = "/sys/module/" + module_ + "/notes/.note.gnu.build-id";
std::string buildIdRaw = ReadFileToString(sysFile);
if (!buildIdRaw.empty()) {
buildId_ = DfxElf::GetBuildId((uint64_t)buildIdRaw.data(), (uint64_t)buildIdRaw.size());
HLOGD("kerne module %s(%s) build id %s", module_.c_str(), filePath_.c_str(),
buildId_.c_str());
return buildId_.empty() ? false : true;
}
return false;
}
const std::vector<std::string> kernelModulePaths = {"/vendor/modules/"};
std::string module_ = "";
};
class JavaFileSymbols : public ElfFileSymbols {
public:
explicit JavaFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
{
symbolFileType_ = SYMBOL_KERNEL_FILE;
}
bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
symbolsLoaded_ = true;
return false;
}
~JavaFileSymbols() override {}
uint64_t GetVaddrInSymbols(uint64_t ip, uint64_t mapStart,
uint64_t mapPageOffset) const override
{
return ip - mapStart + mapPageOffset;
}
};
class JSFileSymbols : public ElfFileSymbols {
public:
explicit JSFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
{
symbolFileType_ = SYMBOL_KERNEL_FILE;
}
bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
symbolsLoaded_ = true;
return false;
}
~JSFileSymbols() override {}
};
class HapFileSymbols : public ElfFileSymbols {
private:
#if defined(is_ohos) && is_ohos
std::unique_ptr<DfxExtractor> dfxExtractor_;
bool hapExtracted_ = false;
#endif
std::unique_ptr<uint8_t[]> abcDataPtr_ = nullptr;
[[maybe_unused]] uintptr_t loadOffSet_ = 0;
[[maybe_unused]] size_t abcDataSize_ = 0;
[[maybe_unused]] uintptr_t arkExtractorPtr_ = 0;
bool isHapAbc_ = false;
pid_t pid_ = 0;
public:
explicit HapFileSymbols(const std::string &symbolFilePath, pid_t pid)
: ElfFileSymbols(symbolFilePath, SYMBOL_HAP_FILE)
{
pid_ = pid;
}
~HapFileSymbols() override
{
#if defined(is_ohos) && is_ohos
abcDataPtr_ = nullptr;
if (arkExtractorPtr_ != 0) {
DfxArk::Instance().ArkDestoryJsSymbolExtractor(arkExtractorPtr_);
arkExtractorPtr_ = 0;
}
#endif
}
bool IsHapAbc()
{
#if defined(is_ohos) && is_ohos
if (hapExtracted_) {
return isHapAbc_;
}
hapExtracted_ = true;
HLOGD("the symbol file is %s, pid is %d.", filePath_.c_str(), pid_);
if (IsApplicationEncryped(pid_)) {
HLOGD("no need to parse js symbols");
return false;
}
CHECK_TRUE(!StringEndsWith(filePath_, ".hap") || !map_->IsMapExec(), false, 1,
"map is exec not abc file , the symbol file is:%s", map_->name.c_str());
if (StringEndsWith(filePath_, ".hap") || StringEndsWith(filePath_, ".hsp") ||
StringEndsWith(filePath_, ".hqf") || StringEndsWith(filePath_, ".abc")) {
isHapAbc_ = true;
} else {
loadOffSet_ = map_->offset;
abcDataSize_ = map_->end - map_->begin;
abcDataPtr_ = std::make_unique<uint8_t[]>(abcDataSize_);
auto size = DfxMemory::ReadProcMemByPid(pid_, map_->begin, abcDataPtr_.get(), map_->end - map_->begin);
if (size != abcDataSize_) {
HLOGD("return size is small abcDataPtr : %s, isAbc: %d", abcDataPtr_.get(), isHapAbc_);
return false;
}
isHapAbc_ = true;
HLOGD("symbol file name %s loadOffSet %u abcDataSize_ %u",
filePath_.c_str(), (uint32_t)loadOffSet_, (uint32_t)abcDataSize_);
}
auto ret = DfxArk::Instance().ArkCreateJsSymbolExtractor(&arkExtractorPtr_);
if (ret < 0) {
arkExtractorPtr_ = 0;
HLOGE("failed to call ArkCreateJsSymbolExtractor, the symbol file is:%s", filePath_.c_str());
}
#endif
return isHapAbc_;
}
bool IsAbc() override
{
return isHapAbc_ == true;
}
void SetBoolValue(bool value) override
{
isHapAbc_ = value;
}
bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
if (map == nullptr) {
HLOGD("map is null, symbolFilePath: %s", symbolFilePath.c_str());
return false;
}
HLOGD("map name:%s", map->name.c_str());
if (debugInfoLoaded_) {
return true;
}
CHECK_TRUE(onRecording_, true, 0, "");
if (!IsHapAbc() && map_->IsMapExec()) {
ElfFileSymbols::LoadDebugInfo(map, "");
}
debugInfoLoaded_ = true;
debugInfoLoadResult_ = true;
return true;
}
bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
if (map == nullptr) {
HLOGD("map is null, symbolFilePath: %s", symbolFilePath.c_str());
return false;
}
HLOGD("map name:%s", map->name.c_str());
CHECK_TRUE(!symbolsLoaded_ && onRecording_, true, 0, "");
symbolsLoaded_ = true;
if (!IsHapAbc() && map_->IsMapExec()) {
ElfFileSymbols::LoadSymbols(map, "");
}
return true;
}
DfxSymbol GetSymbolWithPcAndMap(const uint64_t ip, std::shared_ptr<DfxMap> map) override
{
auto iter = symbolsMap_.find(ip);
if (iter != symbolsMap_.end()) {
return iter->second;
}
if (map == nullptr) {
return DfxSymbol(ip, "");
}
HLOGD("map name:%s", map->name.c_str());
#if defined(is_ohos) && is_ohos
if (IsAbc()) {
JsFunction jsFunc;
if (StringEndsWith(map->name, ".hap") || StringEndsWith(map->name, ".hsp") ||
StringEndsWith(map->name, ".hqf") || StringEndsWith(map->name, ".abc")) {
auto ret = DfxArk::Instance().ParseArkFileInfo(static_cast<uintptr_t>(ip),
static_cast<uintptr_t>(map->begin), static_cast<uintptr_t>(map->offset),
filePath_.c_str(), arkExtractorPtr_, &jsFunc, false);
if (ret == -1) {
HLOGD("failed to call ParseArkFileInfo, the symbol file is : %s", map->name.c_str());
return DfxSymbol(ip, "");
}
} else {
auto ret = DfxArk::Instance().ParseArkFrameInfo(static_cast<uintptr_t>(ip),
static_cast<uintptr_t>(map->begin), loadOffSet_, abcDataPtr_.get(),
abcDataSize_, arkExtractorPtr_, &jsFunc);
if (ret == -1) {
HLOGD("failed to call ParseArkFrameInfo, the symbol file is : %s", map->name.c_str());
return DfxSymbol(ip, "");
}
}
DfxSymbol newSymbol(ip, jsFunc.codeBegin, jsFunc.functionName, jsFunc.ToString(), map->name);
newSymbol.matched_ = true;
newSymbol.symbolFileIndex_ = id_;
auto insertResult = symbolsMap_.emplace(ip, newSymbol);
DfxSymbol &foundSymbol = insertResult.first->second;
if (insertResult.second) {
matchedSymbols_.push_back(&foundSymbol);
}
HLOGD("ip : 0x%" PRIx64 " the symbol file is : %s, function is %s demangle_ : %s", ip,
foundSymbol.module_.data(), jsFunc.functionName, foundSymbol.demangle_.data());
return foundSymbol;
}
#endif
return DfxSymbol(ip, "");
}
};
class JsvmV8FileSymbols : public ElfFileSymbols {
private:
bool jsvmExtracted_ = false;
[[maybe_unused]] bool isJsvm_ = false;
[[maybe_unused]] uintptr_t jsvmExtractorPtr_ = 0;
pid_t pid_ = 0;
std::string GetDemangle(const JsvmFunction& jsvmFunction)
{
std::string result(jsvmFunction.functionName);
if (strlen(jsvmFunction.url) > 0) {
result += ":[" + std::string(jsvmFunction.url) + ":" + std::to_string(jsvmFunction.line) +
":" + std::to_string(jsvmFunction.column) + "]";
}
return result;
}
public:
explicit JsvmV8FileSymbols(const std::string &symbolFilePath, pid_t pid)
: ElfFileSymbols(symbolFilePath, SYMBOL_JSVM_V8_FILE)
{
pid_ = pid;
}
~JsvmV8FileSymbols() override
{
#if defined(is_ohos) && is_ohos
if (jsvmExtractorPtr_ != 0) {
DfxJsvm::Instance().JsvmDestroyJsSymbolExtractor(jsvmExtractorPtr_);
jsvmExtractorPtr_ = 0;
}
#endif
}
bool IsJsvm() override
{
#if defined(is_ohos) && is_ohos
if (!needJsvm_) {
return true;
}
if (jsvmExtracted_) {
return isJsvm_;
}
jsvmExtracted_ = true;
if (jsvmExtractorPtr_ == 0 && IsJsvmV8File(filePath_)) {
auto ret = DfxJsvm::Instance().JsvmCreateJsSymbolExtractor(&jsvmExtractorPtr_, pid_);
if (ret < 0) {
jsvmExtractorPtr_ = 0;
isJsvm_ = false;
HLOGE("failed to call JsvmCreateJsSymbolExtractor, the symbol file is:%s", filePath_.c_str());
} else {
isJsvm_ = true;
}
}
return isJsvm_;
#endif
return true;
}
void SetBoolValue(bool value) override
{
jsvmExtracted_ = value;
}
bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
if (map == nullptr) {
HLOGD("map is null, symbolFilePath: %s", symbolFilePath.c_str());
return false;
}
HLOGD("map name:%s", map->name.c_str());
if (debugInfoLoaded_) {
return true;
}
CHECK_TRUE(onRecording_, true, 0, "");
debugInfoLoaded_ = true;
debugInfoLoadResult_ = true;
return true;
}
bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
if (map == nullptr) {
HLOGD("map is null, symbolFilePath: %s", symbolFilePath.c_str());
return false;
}
HLOGD("map name:%s", map->name.c_str());
CHECK_TRUE(!symbolsLoaded_ && onRecording_, true, 0, "");
symbolsLoaded_ = true;
return true;
}
DfxSymbol GetSymbolWithPcAndMap(const uint64_t ip, std::shared_ptr<DfxMap> map) override
{
auto iter = symbolsMap_.find(ip);
if (iter != symbolsMap_.end()) {
return iter->second;
}
if (map == nullptr) {
return DfxSymbol(ip, "");
}
HLOGD("map name:%s", map->name.c_str());
#if defined(is_ohos) && is_ohos
if (IsJsvm() && needJsvm_) {
JsvmFunction jsvmFunction;
std::string module = map->name;
HLOGD("map->name module:%s", module.c_str());
auto ret = DfxJsvm::Instance().ParseJsvmFrameInfo(static_cast<uintptr_t>(ip),
jsvmExtractorPtr_, &jsvmFunction);
if (ret == -1) {
HLOGD("failed to call ParseJsvmFrameInfo, the symbol file is : %s", map->name.c_str());
return DfxSymbol(ip, "");
}
std::string demangle = GetDemangle(jsvmFunction);
DfxSymbol symbol = DfxSymbol(ip, 0, jsvmFunction.functionName, demangle, map->name);
symbol.matched_ = true;
symbol.symbolFileIndex_ = id_;
auto insertResult = symbolsMap_.emplace(ip, symbol);
DfxSymbol &foundSymbol = insertResult.first->second;
if (insertResult.second) {
matchedSymbols_.push_back(&foundSymbol);
}
HLOGD("ip : 0x%" PRIx64 " the symbol file is : %s, function is %s demangle_ : %s", ip,
foundSymbol.module_.data(), jsvmFunction.functionName, foundSymbol.demangle_.data());
return foundSymbol;
}
#endif
DfxSymbol symbol(ip, "");
return symbol;
}
};
class ArkwebV8FileSymbols : public ElfFileSymbols {
private:
bool arkwebExtracted_ = false;
[[maybe_unused]] bool isArkweb_ = false;
[[maybe_unused]] uintptr_t arkwebExtractorPtr_ = 0;
pid_t pid_ = 0;
std::string GetDemangle(const JsvmFunction& jsvmFunction)
{
std::string result(jsvmFunction.functionName);
if (strlen(jsvmFunction.url) > 0) {
result += ":[" + std::string(jsvmFunction.url) + ":" + std::to_string(jsvmFunction.line) +
":" + std::to_string(jsvmFunction.column) + "]";
}
return result;
}
public:
explicit ArkwebV8FileSymbols(const std::string &symbolFilePath, pid_t pid)
: ElfFileSymbols(symbolFilePath, SYMBOL_ARKWEB_V8_FILE)
{
pid_ = pid;
}
~ArkwebV8FileSymbols() override
{
#if defined(is_ohos) && is_ohos
if (arkwebExtractorPtr_ != 0) {
DfxJsvm::Instance().JsvmDestroyJsSymbolExtractor(arkwebExtractorPtr_);
arkwebExtractorPtr_ = 0;
}
#endif
}
bool IsArkweb() override
{
#if defined(is_ohos) && is_ohos
if (arkwebExtracted_) {
return isArkweb_;
}
arkwebExtracted_ = true;
if (arkwebExtractorPtr_ == 0 && IsArkwebV8File(filePath_)) {
auto ret = DfxJsvm::Instance().JsvmCreateJsSymbolExtractor(&arkwebExtractorPtr_, pid_);
if (ret < 0) {
arkwebExtractorPtr_ = 0;
isArkweb_ = false;
HLOGE("failed to call JsvmCreateJsSymbolExtractor, the symbol file is:%s", filePath_.c_str());
} else {
isArkweb_ = true;
}
}
return isArkweb_;
#endif
return true;
}
void SetBoolValue(bool value) override
{
arkwebExtracted_ = value;
}
bool LoadDebugInfo(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
if (map == nullptr) {
HLOGD("map is null, symbolFilePath: %s", symbolFilePath.c_str());
return false;
}
HLOGD("map name:%s", map->name.c_str());
if (debugInfoLoaded_) {
return true;
}
CHECK_TRUE(onRecording_, true, 0, "");
debugInfoLoaded_ = true;
debugInfoLoadResult_ = true;
return true;
}
bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
if (map == nullptr) {
HLOGD("map is null, symbolFilePath: %s", symbolFilePath.c_str());
return false;
}
HLOGD("map name:%s", map->name.c_str());
CHECK_TRUE(!symbolsLoaded_ && onRecording_, true, 0, "");
symbolsLoaded_ = true;
return true;
}
DfxSymbol GetSymbolWithPcAndMap(const uint64_t ip, std::shared_ptr<DfxMap> map) override
{
auto iter = symbolsMap_.find(ip);
if (iter != symbolsMap_.end()) {
return iter->second;
}
if (map == nullptr) {
return DfxSymbol(ip, "");
}
HLOGD("map name:%s", map->name.c_str());
#if defined(is_ohos) && is_ohos
if (IsArkweb()) {
JsvmFunction jsvmFunction;
std::string module = map->name;
HLOGD("map->name module:%s", module.c_str());
auto ret = DfxJsvm::Instance().ParseJsvmFrameInfo(static_cast<uintptr_t>(ip),
arkwebExtractorPtr_, &jsvmFunction);
if (ret == -1) {
HLOGD("failed to call ParseJsvmFrameInfo, the symbol file is : %s", map->name.c_str());
return DfxSymbol(ip, "");
}
std::string demangle = GetDemangle(jsvmFunction);
DfxSymbol symbol = DfxSymbol(ip, 0, jsvmFunction.functionName, demangle, map->name);
symbol.matched_ = true;
symbol.symbolFileIndex_ = id_;
auto insertResult = symbolsMap_.emplace(ip, symbol);
DfxSymbol &foundSymbol = insertResult.first->second;
if (insertResult.second) {
matchedSymbols_.push_back(&foundSymbol);
}
HLOGD("ip : 0x%" PRIx64 " the symbol file is : %s, function is %s demangle_ : %s", ip,
foundSymbol.module_.data(), jsvmFunction.functionName, foundSymbol.demangle_.data());
return foundSymbol;
}
#endif
DfxSymbol symbol(ip, "");
return symbol;
}
};
class UnknowFileSymbols : public SymbolsFile {
public:
explicit UnknowFileSymbols(const std::string &symbolFilePath)
: SymbolsFile(SYMBOL_UNKNOW_FILE, symbolFilePath)
{
}
bool LoadSymbols(std::shared_ptr<DfxMap> map, const std::string &symbolFilePath) override
{
symbolsLoaded_ = true;
return false;
}
~UnknowFileSymbols() override {}
};
class CJFileSymbols : public ElfFileSymbols {
public:
explicit CJFileSymbols(const std::string &symbolFilePath) : ElfFileSymbols(symbolFilePath)
{
symbolFileType_ = SYMBOL_CJ_FILE;
}
~CJFileSymbols() override {}
};
SymbolsFile::~SymbolsFile() {}
std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(SymbolsFileType symbolType,
const std::string symbolFilePath, pid_t pid)
{
switch (symbolType) {
case SYMBOL_KERNEL_FILE:
return std::make_unique<KernelSymbols>(symbolFilePath.empty() ? KERNEL_MMAP_NAME
: symbolFilePath);
case SYMBOL_KERNEL_MODULE_FILE:
return std::make_unique<KernelModuleSymbols>(symbolFilePath);
case SYMBOL_KERNEL_THREAD_FILE:
return std::make_unique<KernelThreadSymbols>(symbolFilePath);
case SYMBOL_ELF_FILE:
return std::make_unique<ElfFileSymbols>(symbolFilePath);
case SYMBOL_JAVA_FILE:
return std::make_unique<JavaFileSymbols>(symbolFilePath);
case SYMBOL_JS_FILE:
return std::make_unique<JSFileSymbols>(symbolFilePath);
case SYMBOL_HAP_FILE:
return std::make_unique<HapFileSymbols>(symbolFilePath, pid);
case SYMBOL_JSVM_V8_FILE:
return std::make_unique<JsvmV8FileSymbols>(symbolFilePath, pid);
case SYMBOL_ARKWEB_V8_FILE:
return std::make_unique<ArkwebV8FileSymbols>(symbolFilePath, pid);
case SYMBOL_CJ_FILE:
return std::make_unique<CJFileSymbols>(symbolFilePath);
default:
return std::make_unique<SymbolsFile>(SYMBOL_UNKNOW_FILE, symbolFilePath);
}
}
static bool IsCJFile(const std::string& filepath)
{
if (!StringEndsWith(filepath, ".so")) {
return false;
}
if (filepath.find("/data/storage") == std::string::npos &&
!StringStartsWith(filepath, "/system/lib64/platformsdk/cjsdk")) {
return false;
}
RegularElfFactory elfFactory(filepath);
std::shared_ptr<DfxElf> elfFile_ = elfFactory.Create();
ShdrInfo shinfo;
if (elfFile_->GetSectionInfo(shinfo, ".cjmetadata")) {
return true;
}
return false;
}
std::unique_ptr<SymbolsFile> SymbolsFile::CreateSymbolsFile(const std::string &symbolFilePath, pid_t pid)
{
if (symbolFilePath == KERNEL_MMAP_NAME) {
return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_FILE, symbolFilePath);
} else if (symbolFilePath == SYSMGR_FILE_NAME ||
symbolFilePath == DEVHOST_LINUX_FILE_NAME ||
StringStartsWith(symbolFilePath, DEVHOST_LINUX_PREFIX)) {
return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, symbolFilePath);
} else if (StringEndsWith(symbolFilePath, KERNEL_MODULES_EXT_NAME)) {
return SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, symbolFilePath);
} else if (IsArkJsFile(symbolFilePath)) {
return SymbolsFile::CreateSymbolsFile(SYMBOL_HAP_FILE, symbolFilePath, pid);
} else if (IsJsvmV8File(symbolFilePath)) {
return SymbolsFile::CreateSymbolsFile(SYMBOL_JSVM_V8_FILE, symbolFilePath, pid);
} else if (IsArkwebV8File(symbolFilePath)) {
return SymbolsFile::CreateSymbolsFile(SYMBOL_ARKWEB_V8_FILE, symbolFilePath, pid);
} else if (IsCJFile(symbolFilePath)) {
return SymbolsFile::CreateSymbolsFile(SYMBOL_CJ_FILE, symbolFilePath, pid);
} else {
return SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, symbolFilePath);
}
}
void SymbolsFile::AdjustSymbols()
{
if (symbols_.size() <= 0) {
return;
}
sort(symbols_.begin(), symbols_.end(), [](const DfxSymbol& a, const DfxSymbol& b) {
return a.funcVaddr_ < b.funcVaddr_;
});
HLOGV("sort completed");
size_t fullSize = symbols_.size();
size_t erased = 0;
auto last = std::unique(symbols_.begin(), symbols_.end(), [](const DfxSymbol &a, const DfxSymbol &b) {
return (a.funcVaddr_ == b.funcVaddr_);
});
symbols_.erase(last, symbols_.end());
erased = fullSize - symbols_.size();
HLOGV("uniqued completed");
auto it = symbols_.begin();
while (it != symbols_.end()) {
it->index_ = it - symbols_.begin();
it++;
}
HLOGV("indexed completed");
HLOG_ASSERT(symbols_.size() != 0);
if (textExecVaddrRange_ == maxVaddr) {
textExecVaddrRange_ = symbols_.back().funcVaddr_ - symbols_.front().funcVaddr_;
}
HLOGDDD("%zu symbols after adjust (%zu erased) 0x%016" PRIx64 " - 0x%016" PRIx64
" @0x%016" PRIx64 " ",
symbols_.size(), erased, symbols_.front().funcVaddr_, symbols_.back().funcVaddr_,
textExecVaddrFileOffset_);
}
void SymbolsFile::SortMatchedSymbols()
{
if (matchedSymbols_.size() <= 1u) {
return;
}
sort(matchedSymbols_.begin(), matchedSymbols_.end(), [](const DfxSymbol* a, const DfxSymbol* b) {
if (a == nullptr || b == nullptr) {
return true;
}
return a->funcVaddr_ < b->funcVaddr_;
});
}
const std::vector<DfxSymbol> &SymbolsFile::GetSymbols()
{
return symbols_;
}
const std::vector<DfxSymbol *> &SymbolsFile::GetMatchedSymbols()
{
return matchedSymbols_;
}
const DfxSymbol SymbolsFile::GetSymbolWithVaddr(uint64_t vaddrInFile)
{
#ifdef HIPERF_DEBUG_TIME
const auto startTime = steady_clock::now();
#endif
DfxSymbol symbol;
auto found =
std::upper_bound(symbols_.begin(), symbols_.end(), vaddrInFile, DfxSymbol::ValueLessThen);
if data is { 1, 2, 4, 5, 5, 6 };
upper_bound for each val :
0 < 1 at index 0
1 < 2 at index 1
2 < 4 at index 2
3 < 4 at index 2
4 < 5 at index 3
5 < 6 at index 5
6 < not found
if key symbol vaddr is { 1, 2, 4, 5, 5, 6 };
check ip vaddr for each val :
ip sym
0 not found
1 1
1 1
2 2
3 3
4 4
5 5
6 6
7 7
*/
if (found != symbols_.begin()) {
found = std::prev(found);
if (found != symbols_.end()) {
if (found->Contain(vaddrInFile)) {
found->offsetToVaddr_ = vaddrInFile - found->funcVaddr_;
if (!found->matched_) {
found->matched_ = true;
matchedSymbols_.push_back(&(*found));
}
symbol = *found;
HLOGV("found '%s' for vaddr 0x%016" PRIx64 "", symbol.ToString().c_str(), vaddrInFile);
}
}
}
if (!symbol.IsValid()) {
HLOGV("NOT found vaddr 0x%" PRIx64 " in symbole file %s(%zu)", vaddrInFile,
filePath_.c_str(), symbols_.size());
}
symbol.fileVaddr_ = vaddrInFile;
symbol.symbolFileIndex_ = id_;
#ifdef HIPERF_DEBUG_TIME
auto usedTime = duration_cast<milliseconds>(steady_clock::now() - startTime);
if (usedTime > 1ms) {
HLOGW("cost %" PRId64 "ms to search ", usedTime.count());
}
#endif
return symbol;
}
bool SymbolsFile::CheckPathReadable(const std::string &path) const
{
if (access(path.c_str(), R_OK) == 0) {
return true;
}
struct stat st;
if (stat(path.c_str(), &st) == 0) {
return true;
} else {
#if defined(is_ohos) && is_ohos
char errInfo[ERRINFOLEN] = { 0 };
strerror_r(errno, errInfo, ERRINFOLEN);
HLOGM("'%s' is unable read,errno: %d, errmsg: %s", path.c_str(), errno, errInfo);
#endif
return false;
}
}
bool SymbolsFile::setSymbolsFilePath(const std::vector<std::string> &symbolsSearchPaths)
{
symbolsFileSearchPaths_.clear();
for (auto &symbolsSearchPath : symbolsSearchPaths) {
if (CheckPathReadable(symbolsSearchPath)) {
symbolsFileSearchPaths_.emplace_back(symbolsSearchPath);
HLOGV("'%s' is add to symbolsSearchPath", symbolsSearchPath.c_str());
}
}
return (symbolsFileSearchPaths_.size() > 0);
}
std::unique_ptr<SymbolsFile> SymbolsFile::LoadSymbolsFromSaved(
const SymbolFileStruct &symbolFileStruct)
{
bool isHapSymbolFile = (static_cast<SymbolsFileType>(symbolFileStruct.symbolType_) == SYMBOL_HAP_FILE);
HLOGD("isHapSymbolFile : %d", isHapSymbolFile);
bool isJsvmV8SymbolFile = (static_cast<SymbolsFileType>(symbolFileStruct.symbolType_) == SYMBOL_JSVM_V8_FILE);
HLOGD("isJsvmV8SymbolFile : %d", isJsvmV8SymbolFile);
bool isArkwebV8SymbolFile = (static_cast<SymbolsFileType>(symbolFileStruct.symbolType_) == SYMBOL_ARKWEB_V8_FILE);
HLOGD("isArkwebV8SymbolFile : %d", isArkwebV8SymbolFile);
auto symbolsFile = CreateSymbolsFile(symbolFileStruct.filePath_);
symbolsFile->filePath_ = symbolFileStruct.filePath_;
symbolsFile->symbolFileType_ = static_cast<SymbolsFileType>(symbolFileStruct.symbolType_);
symbolsFile->textExecVaddr_ = symbolFileStruct.textExecVaddr_;
symbolsFile->textExecVaddrFileOffset_ = symbolFileStruct.textExecVaddrFileOffset_;
symbolsFile->buildId_ = symbolFileStruct.buildId_;
for (auto &symbolStruct : symbolFileStruct.symbolStructs_) {
symbolsFile->symbols_.emplace_back(symbolStruct.vaddr_, symbolStruct.len_,
symbolStruct.symbolName_, symbolFileStruct.filePath_);
}
symbolsFile->AdjustSymbols();
if (isHapSymbolFile || isJsvmV8SymbolFile || isArkwebV8SymbolFile) {
symbolsFile->symbolsMap_.reserve(symbolsFile->symbols_.size());
for (const auto& symbol : symbolsFile->symbols_) {
symbolsFile->symbolsMap_.emplace(symbol.funcVaddr_, symbol);
}
symbolsFile->SetBoolValue(true);
}
symbolsFile->debugInfoLoadResult_ = true;
symbolsFile->symbolsLoaded_ = true;
HLOGV("load %zu symbol from SymbolFileStruct for file '%s'", symbolsFile->symbols_.size(),
symbolsFile->filePath_.c_str());
return symbolsFile;
}
void SymbolsFile::SetBoolValue(bool value)
{
}
void SymbolsFile::ExportSymbolToFileFormat(SymbolFileStruct &symbolFileStruct)
{
symbolFileStruct.filePath_ = filePath_;
symbolFileStruct.symbolType_ = symbolFileType_;
symbolFileStruct.textExecVaddr_ = textExecVaddr_;
symbolFileStruct.textExecVaddrFileOffset_ = textExecVaddrFileOffset_;
symbolFileStruct.buildId_ = buildId_;
SortMatchedSymbols();
auto symbols = GetMatchedSymbols();
symbolFileStruct.symbolStructs_.reserve(symbols.size());
for (const auto symbol : symbols) {
auto &symbolStruct = symbolFileStruct.symbolStructs_.emplace_back();
symbolStruct.vaddr_ = symbol->funcVaddr_;
symbolStruct.len_ = symbol->size_;
symbolStruct.symbolName_ = symbol->GetName();
}
HLOGV("export %zu symbol to SymbolFileStruct from %s", symbolFileStruct.symbolStructs_.size(),
filePath_.c_str());
}
uint64_t SymbolsFile::GetVaddrInSymbols(uint64_t ip, uint64_t mapStart, uint64_t mapOffset) const
{
return ip;
}
uint64_t SymbolsFile::GetVaddrByLoadBase(uint64_t ip, uint64_t loadBase) const
{
return ip - loadBase;
}
void SymbolsFile::AddSymbol(DfxSymbol symbol)
{
symbolsLoaded_ = true;
symbols_.emplace_back(symbol);
}
}
}
}