* 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 "RuntimeThread"
#include "virtual_thread.h"
#include <cinttypes>
#include <iostream>
#include <sstream>
#if !is_mingw
#include <sys/mman.h>
#endif
#include "string_util.h"
#include "symbols_file.h"
#include "utilities.h"
#include "virtual_runtime.h"
#include "stack_map_query.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
static constexpr int MMAP_PROT_CHARS = 4;
static constexpr int MAP_PROT_EXEC_INDEX = 2;
#ifdef DEBUG_TIME
bool VirtualThread::IsSorted() const
{
if (memMapsIndexs_.empty()) {
return true;
}
for (std::size_t index = 1; index < memMaps_.size(); ++index) {
if (memMaps_[memMapsIndexs_[index - 1]]->end > memMaps_[memMapsIndexs_[index]]->begin) {
std::cout << "memMaps_ order error:\n"
<< " " << memMaps_[memMapsIndexs_[index - 1]]->begin << "-"
<< memMaps_[memMapsIndexs_[index - 1]]->end
<< " " << memMaps_[memMapsIndexs_[index]]->begin << "-"
<< memMaps_[memMapsIndexs_[index]]->end;
return false;
}
}
return true;
}
#endif
int64_t VirtualThread::FindMapIndexByAddr(uint64_t addr) const
{
HLOGM("try found vaddr 0x%" PRIx64 "in maps %zu", addr, memMaps_.size());
const int64_t illegal = -1;
auto r = OHOS::Developtools::StackCommon::LookupMapByAddrIndexed(memMaps_, memMapsIndexs_, addr);
if (!r.ok) {
return illegal;
}
if (r.sortedPosition > 0) {
memMaps_[r.mapVectorIndex]->prevMap = memMaps_[memMapsIndexs_[r.sortedPosition - 1]];
}
return r.mapVectorIndex;
}
std::shared_ptr<DfxMap> VirtualThread::FindMapByAddr(uint64_t addr) const
{
if (memMaps_.empty()) {
return nullptr;
}
int64_t idx = FindMapIndexByAddr(addr);
if (idx < 0) {
return nullptr;
}
return memMaps_[static_cast<size_t>(idx)];
}
std::shared_ptr<DfxMap> VirtualThread::FindMapByFileInfo(const std::string name, uint64_t offset) const
{
auto map = OHOS::Developtools::StackCommon::FindMapByFileOffset(memMaps_, name, offset);
if (map != nullptr) {
HLOGMMM("found fileoffset 0x%" PRIx64 " in map (0x%" PRIx64 " - 0x%" PRIx64
" pageoffset 0x%" PRIx64 ") from %s",
offset, map->begin, map->end, map->offset, map->name.c_str());
} else {
HLOGM("NOT found offset 0x%" PRIx64 " in maps %zu ", offset, memMaps_.size());
}
return map;
}
SymbolsFile *VirtualThread::FindSymbolsFileByMap(std::shared_ptr<DfxMap> map) const
{
if (map == nullptr) {
return nullptr;
}
if (map->symbolFileIndex != -1) {
if (symbolsFiles_[map->symbolFileIndex]->LoadDebugInfo(map)) {
return symbolsFiles_[map->symbolFileIndex].get();
}
} else {
for (size_t i = 0; i < symbolsFiles_.size(); ++i) {
if (symbolsFiles_[i]->filePath_ == map->name) {
HLOGD("found symbol for map '%s'", map->name.c_str());
map->symbolFileIndex = static_cast<int32_t>(i);
if (symbolsFiles_[i]->LoadDebugInfo(map)) {
return symbolsFiles_[i].get();
}
}
}
}
#ifdef DEBUG_MISS_SYMBOL
if (find(missedSymbolFile_.begin(), missedSymbolFile_.end(), map->name) ==
missedSymbolFile_.end()) {
missedSymbolFile_.emplace_back(map->name);
HLOGW("NOT found symbol for map '%s'", map->name.c_str());
for (const auto &file : symbolsFiles_) {
HLOGW(" we have '%s'", file->filePath_.c_str());
}
}
#endif
return nullptr;
}
void VirtualThread::ReportVaddrMapMiss(uint64_t vaddr) const
{
#ifdef HIPERF_DEBUG
if (DebugLogger::logDisabled_) {
return;
}
if (DebugLogger::GetInstance()->GetLogLevel() <= LEVEL_VERBOSE) {
if (missedRuntimeVaddr_.find(vaddr) == missedRuntimeVaddr_.end()) {
missedRuntimeVaddr_.insert(vaddr);
HLOGV("vaddr %" PRIx64 " not found in any map", vaddr);
for (auto &map : memMaps_) {
if (map == nullptr) {
return;
}
HLOGV("map %s ", map->ToString().c_str());
}
}
}
#endif
}
bool VirtualThread::ReadRoMemory(uint64_t vaddr, uint8_t *data, const size_t size) const
{
uint64_t pageIndex = vaddr >> 12;
uint64_t memMapIndex = -1;
const int64_t exceptRet = -1;
const uint64_t illegal = -1;
auto pageFile = vaddr4kPageCache_.find(pageIndex);
if (pageFile != vaddr4kPageCache_.end()) {
memMapIndex = pageFile->second;
} else {
int64_t retIndex = FindMapIndexByAddr(vaddr);
memMapIndex = static_cast<uint64_t>(retIndex);
if (retIndex != exceptRet && memMapIndex < memMaps_.size()) {
const_cast<VirtualThread *>(this)->vaddr4kPageCache_[pageIndex] = memMapIndex;
}
}
if (memMapIndex != illegal) {
auto map = memMaps_[memMapIndex];
if (map != nullptr) {
if (map->elf == nullptr) {
SymbolsFile* symFile = FindSymbolsFileByMap(map);
if (symFile == nullptr) {
return false;
}
map->elf = symFile->GetElfFile();
}
if (map->elf != nullptr) {
uint64_t foff = vaddr - map->begin + map->offset - map->elf->GetBaseOffset();
if (map->elf->Read(foff, data, size)) {
return true;
} else {
return false;
}
} else {
HLOGW("find addr %" PRIx64 "in map but not loaded symbole %s", vaddr, map->name.c_str());
}
}
} else {
HLOGV("not found in any map");
}
return false;
}
#if defined(is_mingw) && is_mingw
void VirtualThread::ParseMap()
{
return;
}
#else
void VirtualThread::ParseMap()
{
if (!(OHOS::HiviewDFX::DfxMaps::Create(pid_, memMaps_, memMapsIndexs_))) {
HLOGE("VirtualThread Failed to Parse Map.");
}
FixContainerMap();
SortMemMaps();
}
#endif
void VirtualThread::FixHMBundleMap()
{
for (auto &map : memMaps_) {
NeedAdaptHMBundlePath(map->name, name_);
}
}
void VirtualThread::FixContainerMap()
{
if (!isContainerProcess_) {
return;
}
std::string containerRoot = StringPrintf("/proc/%d/root", pid_);
for (auto &map : memMaps_) {
if (!StringStartsWith(map->name, containerRoot)) {
map->name = containerRoot + map->name;
}
}
}
constexpr const int MMAP_LINE_TOKEN_INDEX_FLAG = 1;
constexpr const int MMAP_LINE_TOKEN_INDEX_OFFSET = 2;
constexpr const int MMAP_LINE_TOKEN_INDEX_NAME = 5;
constexpr const int MMAP_LINE_MAX_TOKEN = 6;
void VirtualThread::ParseServiceMap(const std::string &filename)
{
std::string mapPath = StringPrintf("/proc/%d/maps", pid_);
std::string mapContent = ReadFileToString(mapPath);
uint64_t begin = 0;
uint64_t end = 0;
if (mapContent.size() == 0) {
HLOGW("Parse %s failed, content empty", mapPath.c_str());
return;
}
std::istringstream s(mapContent);
std::string line;
while (std::getline(s, line)) {
std::vector<std::string> mapTokens = StringSplit(line, " ");
if (mapTokens.size() == MMAP_LINE_MAX_TOKEN &&
mapTokens[MMAP_LINE_TOKEN_INDEX_NAME].find(name_) != std::string::npos) {
HLOGM("map line: %s", line.c_str());
constexpr int mmapAddrRangeToken = 2;
std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
if (addrRanges.size() < mmapAddrRangeToken) {
continue;
}
if (!StringToUint64(addrRanges[0], begin, NUMBER_FORMAT_HEX_BASE)) {
HLOGE("StringToUint64 fail %s", addrRanges[0].c_str());
}
if (!StringToUint64(addrRanges[1], end, NUMBER_FORMAT_HEX_BASE)) {
HLOGE("StringToUint64 fail %s", addrRanges[1].c_str());
}
break;
}
}
CreateMapItem(filename, begin, end - begin, 0);
}
void VirtualThread::ParseDevhostMapEachLine(std::string &filename, std::istringstream &iStringstream,
std::string &line)
{
std::vector<std::string> mapTokens = StringSplit(line, " ");
if (mapTokens.size() < MMAP_LINE_MAX_TOKEN) {
return;
}
HLOGM("map line: %s", line.c_str());
constexpr const int mmapAddrRangeToken = 2;
std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
if (addrRanges.size() != mmapAddrRangeToken) {
return;
}
uint64_t begin = 0;
uint64_t end = 0;
uint64_t offset = 0;
if (!StringToUint64(addrRanges[0], begin, NUMBER_FORMAT_HEX_BASE) ||
!StringToUint64(addrRanges[1], end, NUMBER_FORMAT_HEX_BASE) ||
!StringToUint64(mapTokens[MMAP_LINE_TOKEN_INDEX_OFFSET], offset, NUMBER_FORMAT_HEX_BASE)) {
return;
}
if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG].size() != MMAP_PROT_CHARS ||
mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_PROT_EXEC_INDEX] != 'x') {
return;
}
const std::string anonPrefix = "[anon:[";
const std::string anonPostfix = "]]";
filename = mapTokens[MMAP_LINE_TOKEN_INDEX_NAME];
if (filename == "[shmm]") {
return;
}
if (filename.find(anonPrefix) != std::string::npos) {
if (filename.size() <= anonPrefix.size() + anonPostfix.size()) {
return;
}
filename = filename.substr(anonPrefix.size(),
filename.size() - anonPrefix.size() -
anonPostfix.size());
filename = "/" + filename;
} else if (filename.find(DEVHOST_LINUX_FILE_NAME) != std::string::npos) {
filename = DEVHOST_LINUX_FILE_NAME;
}
CreateMapItem(filename, begin, end - begin, offset);
}
void VirtualThread::ParseDevhostMap(const pid_t devhost)
{
std::string mapPath = StringPrintf("/proc/%d/maps", devhost);
std::string mapContent = ReadFileToString(mapPath);
std::string filename;
if (mapContent.size() > 0) {
std::istringstream s(mapContent);
std::string line;
while (std::getline(s, line)) {
ParseDevhostMapEachLine(filename, s, line);
}
}
SortMemMaps();
}
void VirtualThread::SortMemMaps()
{
OHOS::Developtools::StackCommon::SortMapIndicesByEndAscending(memMaps_, memMapsIndexs_);
}
std::shared_ptr<DfxMap> VirtualThread::CreateMapItem(const std::string &filename, uint64_t const begin,
const uint64_t len, const uint64_t offset,
const uint32_t prot)
{
std::string adaptedFilename = filename;
if (isContainerProcess_) {
std::string containerRoot = StringPrintf("/proc/%d/root", pid_);
if (!StringStartsWith(adaptedFilename, containerRoot)) {
adaptedFilename = containerRoot + adaptedFilename;
}
}
if (!OHOS::HiviewDFX::DfxMaps::IsLegalMapItem(adaptedFilename)) {
return nullptr;
}
std::shared_ptr<DfxMap> map = memMaps_.emplace_back(std::make_shared<DfxMap>(begin, begin + len, offset,
prot, adaptedFilename));
memMapsIndexs_.emplace_back(memMaps_.size() >= 1 ? memMaps_.size() - 1 : 0);
if (map->name.find("libadlt") != std::string::npos && EndsWith(map->name, ".so")) {
if (!getLoadBaseFlag && offset == 0 && ((prot & PROT_EXEC) == 0)) {
HLOGD("Get adltloadbase 0x%" PRIx64, map->begin);
adltLoadBase = map->begin;
getLoadBaseFlag = true;
}
if (getLoadBaseFlag) {
map->SetAdltLoadBase(adltLoadBase);
}
}
HLOGD(" %u:%u create a new map(total %zu) at '%s' (0x%" PRIx64 "-0x%" PRIx64 ")@0x%" PRIx64 " ",
pid_, tid_, memMaps_.size(), map->name.c_str(), map->begin, map->end, map->offset);
SortMemMaps();
return map;
}
}
}
}