1/* 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15#define HILOG_TAG "RuntimeThread" 16 17#include "virtual_thread.h" 18 19#include <cinttypes> 20#include <iostream> 21#include <sstream> 22#if !is_mingw 23#include <sys/mman.h> 24#endif 25 26#include "symbols_file.h" 27#include "utilities.h" 28#include "virtual_runtime.h" 29namespace OHOS { 30namespace Developtools { 31namespace HiPerf { 32 33static constexpr int MMAP_PROT_CHARS = 4; 34static constexpr int MAP_PROT_EXEC_INDEX = 2; 35 36#ifdef DEBUG_TIME 37 38bool VirtualThread::IsSorted() const 39{ 40 for (std::size_t index = 1; index < memMaps_.size(); ++index) { 41 if (memMaps_[memMapsIndexs_[index - 1]]->end > memMaps_[memMapsIndexs_[index]]->begin) { 42 std::cout << "memMaps_ order error:\n" 43 << " " << memMaps_[memMapsIndexs_[index - 1]]->begin << "-" 44 << memMaps_[memMapsIndexs_[index - 1]]->end 45 << " " << memMaps_[memMapsIndexs_[index]]->begin << "-" 46 << memMaps_[memMapsIndexs_[index]]->end; 47 return false; 48 } 49 } 50 return true; 51} 52#endif 53 54int64_t VirtualThread::FindMapIndexByAddr(uint64_t addr) const 55{ 56 HLOGM("try found vaddr 0x%" PRIx64 "in maps %zu", addr, memMaps_.size()); 57 const int64_t illegal = -1; 58 if (memMaps_.size() == 0) { 59 return illegal; 60 } 61 if (memMaps_[memMapsIndexs_[0]]->begin > addr) { 62 return illegal; 63 } 64 if (memMaps_[memMapsIndexs_[memMapsIndexs_.size() >= 1 ? memMapsIndexs_.size() - 1 : 0]]->end <= addr) { 65 return illegal; 66 } 67 constexpr int divisorNum {2}; 68 std::size_t left {0}; 69 std::size_t right {memMapsIndexs_.size()}; 70 std::size_t mid = (right - left) / divisorNum + left; 71 while (left < right) { 72 if (addr < memMaps_[memMapsIndexs_[mid]]->end) { 73 right = mid; 74 mid = (right - left) / divisorNum + left; 75 continue; 76 } 77 if (addr >= memMaps_[memMapsIndexs_[mid]]->end) { 78 left = mid + 1; 79 mid = (right - left) / divisorNum + left; 80 continue; 81 } 82 } 83 if (addr >= memMaps_[memMapsIndexs_[left]]->begin && addr < memMaps_[memMapsIndexs_[left]]->end) { 84 if (left > 0) { 85 memMaps_[memMapsIndexs_[left]]->prevMap = memMaps_[memMapsIndexs_[left - 1]]; 86 } 87 return static_cast<int64_t>(memMapsIndexs_[left]); 88 } 89 return illegal; 90} 91 92std::shared_ptr<DfxMap> VirtualThread::FindMapByAddr(uint64_t addr) const 93{ 94 HLOGM("try found vaddr 0x%" PRIx64 "in maps %zu", addr, memMaps_.size()); 95 if (memMaps_.size() == 0) { 96 return nullptr; 97 } 98 if (memMaps_[memMapsIndexs_[0]]->begin > addr) { 99 return nullptr; 100 } 101 if (memMaps_[memMapsIndexs_[memMapsIndexs_.size() >= 1 ? memMapsIndexs_.size() - 1 : 0]]->end <= addr) { 102 return nullptr; 103 } 104 constexpr int divisorNum {2}; 105 std::size_t left {0}; 106 std::size_t right {memMapsIndexs_.size()}; 107 std::size_t mid = (right - left) / divisorNum + left; 108 while (left < right) { 109 if (addr < memMaps_[memMapsIndexs_[mid]]->end) { 110 right = mid; 111 mid = (right - left) / divisorNum + left; 112 continue; 113 } 114 if (addr >= memMaps_[memMapsIndexs_[mid]]->end) { 115 left = mid + 1; 116 mid = (right - left) / divisorNum + left; 117 continue; 118 } 119 } 120 if (addr >= memMaps_[memMapsIndexs_[left]]->begin && addr < memMaps_[memMapsIndexs_[left]]->end) { 121 if (left > 0) { 122 memMaps_[memMapsIndexs_[left]]->prevMap = memMaps_[memMapsIndexs_[left - 1]]; 123 } 124 return memMaps_[memMapsIndexs_[left]]; 125 } 126 return nullptr; 127} 128 129std::shared_ptr<DfxMap> VirtualThread::FindMapByFileInfo(const std::string name, uint64_t offset) const 130{ 131 for (auto &map : memMaps_) { 132 if (name != map->name) { 133 continue; 134 } 135 // check begin and length 136 if (offset >= map->offset && (offset - map->offset) < (map->end - map->begin)) { 137 HLOGMMM("found fileoffset 0x%" PRIx64 " in map (0x%" PRIx64 " - 0x%" PRIx64 138 " pageoffset 0x%" PRIx64 ") from %s", 139 offset, map->begin, map->end, map->offset, map->name.c_str()); 140 return map; 141 } 142 } 143 HLOGM("NOT found offset 0x%" PRIx64 " in maps %zu ", offset, memMaps_.size()); 144 return nullptr; 145} 146 147SymbolsFile *VirtualThread::FindSymbolsFileByMap(std::shared_ptr<DfxMap> map) const 148{ 149 if (map == nullptr) { 150 return nullptr; 151 } 152 if (map->symbolFileIndex != -1) { 153 // no need further operation 154 if (symbolsFiles_[map->symbolFileIndex]->LoadDebugInfo(map)) { 155 return symbolsFiles_[map->symbolFileIndex].get(); 156 } 157 } else { 158 // add it to cache 159 for (size_t i = 0; i < symbolsFiles_.size(); ++i) { 160 if (symbolsFiles_[i]->filePath_ == map->name) { 161 HLOGD("found symbol for map '%s'", map->name.c_str()); 162 if (symbolsFiles_[i]->LoadDebugInfo(map)) { 163 HLOGD("found symbol for map '%s'", map->name.c_str()); 164 map->symbolFileIndex = static_cast<int32_t>(i); 165 return symbolsFiles_[i].get(); 166 } 167 } 168 } 169 } 170 171#ifdef DEBUG_MISS_SYMBOL 172 if (find(missedSymbolFile_.begin(), missedSymbolFile_.end(), inMap.name) == 173 missedSymbolFile_.end()) { 174 missedSymbolFile_.emplace_back(inMap.name); 175 HLOGW("NOT found symbol for map '%s'", inMap.name.c_str()); 176 for (const auto &file : symbolsFiles_) { 177 HLOGW(" we have '%s'", file->filePath_.c_str()); 178 } 179 } 180#endif 181 return nullptr; 182} 183void VirtualThread::ReportVaddrMapMiss(uint64_t vaddr) const 184{ 185#ifdef HIPERF_DEBUG 186 if (DebugLogger::logDisabled_) { 187 return; 188 } 189 if (DebugLogger::GetInstance()->GetLogLevel() <= LEVEL_VERBOSE) { 190 if (missedRuntimeVaddr_.find(vaddr) == missedRuntimeVaddr_.end()) { 191 missedRuntimeVaddr_.insert(vaddr); 192 HLOGV("vaddr %" PRIx64 " not found in any map", vaddr); 193 for (auto &map : memMaps_) { 194 if (map == nullptr) { 195 return; 196 } 197 HLOGV("map %s ", map->ToString().c_str()); 198 } 199 } 200 } 201#endif 202} 203 204bool VirtualThread::ReadRoMemory(uint64_t vaddr, uint8_t *data, size_t size) const 205{ 206 uint64_t pageIndex = vaddr >> 12; 207 uint64_t memMapIndex = -1; 208 const int64_t exceptRet = -1; 209 const uint64_t illegal = -1; 210 auto pageFile = vaddr4kPageCache_.find(pageIndex); 211 if (pageFile != vaddr4kPageCache_.end()) { 212 memMapIndex = pageFile->second; 213 } else { 214 int64_t retIndex = FindMapIndexByAddr(vaddr); 215 memMapIndex = static_cast<uint64_t>(retIndex); 216 // add to 4k page cache table 217 if (retIndex != exceptRet && memMapIndex < memMaps_.size()) { 218 const_cast<VirtualThread *>(this)->vaddr4kPageCache_[pageIndex] = memMapIndex; 219 } 220 } 221 if (memMapIndex != illegal) { 222 auto map = memMaps_[memMapIndex]; 223 if (map != nullptr) { 224 if (map->elf == nullptr) { 225 SymbolsFile* symFile = FindSymbolsFileByMap(map); 226 if (symFile == nullptr) { 227 return false; 228 } 229 map->elf = symFile->GetElfFile(); 230 } 231 if (map->elf != nullptr) { 232 // default base offset is zero 233 uint64_t foff = vaddr - map->begin + map->offset - map->elf->GetBaseOffset(); 234 if (map->elf->Read(foff, data, size)) { 235 return true; 236 } else { 237 return false; 238 } 239 } else { 240 HLOGW("find addr %" PRIx64 "in map but not loaded symbole %s", vaddr, map->name.c_str()); 241 } 242 } 243 } else { 244 HLOGV("not found in any map"); 245 } 246 return false; 247} 248 249#if defined(is_mingw) && is_mingw 250void VirtualThread::ParseMap() 251{ 252 // only linux support read maps in runtime 253 return; 254} 255#else 256void VirtualThread::ParseMap() 257{ 258 if (!(OHOS::HiviewDFX::DfxMaps::Create(pid_, memMaps_, memMapsIndexs_))) { 259 HLOGE("VirtualThread Failed to Parse Map."); 260 } 261 SortMemMaps(); 262} 263#endif 264 265void VirtualThread::FixHMBundleMap() 266{ 267 // fix bundle path in map 268 for (auto &map : memMaps_) { 269 NeedAdaptHMBundlePath(map->name, name_); 270 } 271} 272 273constexpr const int MMAP_LINE_TOKEN_INDEX_FLAG = 1; 274constexpr const int MMAP_LINE_TOKEN_INDEX_OFFSET = 2; 275constexpr const int MMAP_LINE_TOKEN_INDEX_NAME = 5; 276constexpr const int MMAP_LINE_MAX_TOKEN = 6; 277void VirtualThread::ParseServiceMap(const std::string &filename) 278{ 279 std::string mapPath = StringPrintf("/proc/%d/maps", pid_); 280 std::string mapContent = ReadFileToString(mapPath); 281 uint64_t begin = 0; 282 uint64_t end = 0; 283 if (mapContent.size() == 0) { 284 HLOGW("Parse %s failed, content empty", mapPath.c_str()); 285 return; 286 } 287 std::istringstream s(mapContent); 288 std::string line; 289 while (std::getline(s, line)) { 290 std::vector<std::string> mapTokens = StringSplit(line, " "); 291 if (mapTokens.size() == MMAP_LINE_MAX_TOKEN && 292 mapTokens[MMAP_LINE_TOKEN_INDEX_NAME] == name_) { 293 HLOGM("map line: %s", line.c_str()); 294 std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-"); 295 begin = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE); 296 end = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE); 297 break; 298 } 299 } 300 CreateMapItem(filename, begin, end - begin, 0); 301} 302 303void VirtualThread::ParseDevhostMap(pid_t devhost) 304{ 305 std::string mapPath = StringPrintf("/proc/%d/maps", devhost); 306 std::string mapContent = ReadFileToString(mapPath); 307 std::string filename; 308 if (mapContent.size() > 0) { 309 std::istringstream s(mapContent); 310 std::string line; 311 while (std::getline(s, line)) { 312 // 2fe40000-311e1000 r-xp 00000000 00:01 217 /lib/libdh-linux.so.5.10.97-oh 313 // 0 1 2 3 4 5 314 std::vector<std::string> mapTokens = StringSplit(line, " "); 315 if (mapTokens.size() < MMAP_LINE_MAX_TOKEN) { 316 continue; 317 } 318 HLOGM("map line: %s", line.c_str()); 319 320 // 2fe40000-311e1000 321 constexpr const int mmapAddrRangeToken = 2; 322 std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-"); 323 if (addrRanges.size() != mmapAddrRangeToken) { 324 continue; 325 } 326 uint64_t begin, end, offset; 327 // 2fe40000 / 311e1000 328 try { 329 begin = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE); 330 end = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE); 331 offset = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_OFFSET], 332 nullptr, NUMBER_FORMAT_HEX_BASE); 333 } catch (...) { 334 continue; 335 } 336 337 // --x- 338 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG].size() != MMAP_PROT_CHARS || 339 mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_PROT_EXEC_INDEX] != 'x') { 340 continue; 341 } 342 343 const std::string anonPrefix = "[anon:["; 344 const std::string anonPostfix = "]]"; 345 filename = mapTokens[MMAP_LINE_TOKEN_INDEX_NAME]; 346 if (filename == "[shmm]") { 347 continue; 348 } 349 if (filename.find(anonPrefix) != std::string::npos) { 350 // '[anon:[liblinux/devhost.ko]]' to '/liblinux/devhost.ko' 351 filename = filename.substr(anonPrefix.size(), 352 filename.size() - anonPrefix.size() - 353 anonPostfix.size()); 354 filename = "/" + filename; 355 } else if (filename.find(DEVHOST_LINUX_FILE_NAME) != std::string::npos) { 356 // '/lib/libdh-linux.so.5.10.97-oh' to '/lib/libdh-linux.so' 357 filename = DEVHOST_LINUX_FILE_NAME; 358 } 359 CreateMapItem(filename, begin, end - begin, offset); 360 } 361 } 362 SortMemMaps(); 363} 364 365void VirtualThread::SortMemMaps() 366{ 367 for (int currPos = 1; currPos < static_cast<int>(memMaps_.size()); ++currPos) { 368 int targetPos = currPos - 1; 369 while (targetPos >= 0 and memMaps_[memMapsIndexs_[currPos]]->end < memMaps_[memMapsIndexs_[targetPos]]->end) { 370 --targetPos; 371 } 372 if (targetPos < currPos - 1) { 373 auto target = memMapsIndexs_[currPos]; 374 for (int k = currPos - 1; k > targetPos; --k) { 375 memMapsIndexs_[k + 1] = memMapsIndexs_[k]; 376 } 377 memMapsIndexs_[targetPos + 1] = target; 378 } 379 } 380} 381 382std::shared_ptr<DfxMap> VirtualThread::CreateMapItem(const std::string filename, uint64_t begin, uint64_t len, 383 uint64_t offset, uint32_t prot) 384{ 385 if (!OHOS::HiviewDFX::DfxMaps::IsLegalMapItem(filename)) { 386 return nullptr; // skip some memmap 387 } 388 std::shared_ptr<DfxMap> map = memMaps_.emplace_back(std::make_shared<DfxMap>(begin, begin + len, offset, 389 prot, filename)); 390 memMapsIndexs_.emplace_back(memMaps_.size() >= 1 ? memMaps_.size() - 1 : 0); 391 HLOGD(" %u:%u create a new map(total %zu) at '%s' (0x%" PRIx64 "-0x%" PRIx64 ")@0x%" PRIx64 " ", 392 pid_, tid_, memMaps_.size(), map->name.c_str(), map->begin, map->end, map->offset); 393 SortMemMaps(); 394 return map; 395} 396} // namespace HiPerf 397} // namespace Developtools 398} // namespace OHOS 399