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 "Runtime" 16 17#include "virtual_runtime.h" 18 19#include <cinttypes> 20#include <iostream> 21#include <sstream> 22#include <unistd.h> 23#if !is_mingw 24#include <sys/mman.h> 25#endif 26 27#include "dfx_map.h" 28#include "hiperf_hilog.h" 29#include "register.h" 30#include "spe_decoder.h" 31#include "symbols_file.h" 32#include "utilities.h" 33 34using namespace std::chrono; 35namespace OHOS { 36namespace Developtools { 37namespace HiPerf { 38namespace { 39// if ip is 0 , 1 both not useful 40const uint64_t BAD_IP_ADDRESS = 2; 41} 42// we unable to access 'swapper' from /proc/0/ 43VirtualRuntime::VirtualRuntime(bool onDevice) 44{ 45 UpdateThread(0, 0, "swapper"); 46} 47 48VirtualRuntime::~VirtualRuntime() 49{ 50 if (savedCmdLines_.is_open()) { 51 savedCmdLines_.close(); 52 } 53} 54 55std::string VirtualRuntime::ReadFromSavedCmdLines(pid_t tid) 56{ 57 if (!savedCmdLines_.is_open()) { 58 savedCmdLines_.open(SAVED_CMDLINES, std::ios::in); 59 } 60 if (!savedCmdLines_.is_open()) { 61 return EMPTY_STRING; 62 } 63 savedCmdLines_.seekg(0, std::ios::beg); 64 std::string line; 65 std::string threadid = std::to_string(tid); 66 while (getline(savedCmdLines_, line)) { 67 if (line.find(threadid) != std::string::npos) { 68 constexpr size_t sizeLimit {2}; 69 std::vector<std::string> linesToken = StringSplit(line, " "); 70 if (linesToken.size() < sizeLimit) { 71 return EMPTY_STRING; 72 } 73 if (threadid != linesToken[0]) { 74 continue; 75 } 76 return linesToken[1]; 77 } 78 } 79 return EMPTY_STRING; 80} 81 82std::string VirtualRuntime::ReadThreadName(pid_t tid, bool isThread) 83{ 84 std::string comm = ""; 85 if (tid == SYSMGR_PID) { 86 comm = SYSMGR_NAME; 87 } else if (tid == devhostPid_) { 88 comm = DEVHOST_FILE_NAME; 89 } else if (isThread) { 90 comm = ReadFileToString(StringPrintf("/proc/%d/comm", tid)).c_str(); 91 } else { 92 comm = ReadFileToString(StringPrintf("/proc/%d/cmdline", tid)).c_str(); 93 } 94 if (comm == EMPTY_STRING) { 95 comm = ReadFromSavedCmdLines(tid); 96 } 97 comm.erase(std::remove(comm.begin(), comm.end(), '\r'), comm.end()); 98 comm.erase(std::remove(comm.begin(), comm.end(), '\n'), comm.end()); 99 return comm; 100} 101 102VirtualThread &VirtualRuntime::UpdateThread(pid_t pid, pid_t tid, const std::string name) 103{ 104#ifdef HIPERF_DEBUG_TIME 105 const auto startTime = steady_clock::now(); 106#endif 107 VirtualThread &thread = GetThread(pid, tid, name); 108 if (!name.empty()) { 109 thread.name_ = name; 110 } 111#ifdef HIPERF_DEBUG_TIME 112 updateThreadTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime); 113#endif 114 return thread; 115} 116 117VirtualThread &VirtualRuntime::CreateThread(pid_t pid, pid_t tid, const std::string name) 118{ 119 // make a new one 120 if (pid == tid) { 121 userSpaceThreadMap_.emplace(std::piecewise_construct, std::forward_as_tuple(tid), 122 std::forward_as_tuple(pid, symbolsFiles_)); 123 } else { 124 // for thread we need give it process info( for same mmap) 125 userSpaceThreadMap_.emplace( 126 std::piecewise_construct, std::forward_as_tuple(tid), 127 std::forward_as_tuple(pid, tid, GetThread(pid, pid), symbolsFiles_)); 128 } 129 VirtualThread &thread = userSpaceThreadMap_.at(tid); 130 if (recordCallBack_) { 131 if (pid == tid && !IsKernelThread(pid)) { 132#ifdef HIPERF_DEBUG_TIME 133 const auto startTime = steady_clock::now(); 134#endif 135 thread.ParseMap(); 136#ifdef HIPERF_DEBUG_TIME 137 threadParseMapsTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime); 138#endif 139 } 140#ifdef HIPERF_DEBUG_TIME 141 const auto startCreateMmapTime = steady_clock::now(); 142#endif 143 thread.name_ = name; 144 if (thread.name_.empty()) { 145 thread.name_ = ReadThreadName(tid, pid != tid); 146 } 147 HLOGD("create a new thread record for %u:%u:%s with %zu dso", pid, tid, 148 thread.name_.c_str(), thread.GetMaps().size()); 149 // we need make a PerfRecordComm 150 auto commRecord = std::make_unique<PerfRecordComm>(IsKernelThread(pid), pid, tid, thread.name_); 151 recordCallBack_(std::move(commRecord)); 152 // only work for pid 153 if (pid == tid) { 154 if (isHM_) { 155 thread.FixHMBundleMap(); 156 } 157 std::shared_ptr<DfxMap> prevMap = nullptr; 158 for (auto &map : thread.GetMaps()) { 159 // so in hap is load before start perf record 160 // dynamic load library should be treat in the same way 161 bool updateNormalSymbol = true; 162 if (map->name.find(".hap") != std::string::npos && (map->prots & PROT_EXEC)) { 163 map->prevMap = prevMap; 164 updateNormalSymbol = !UpdateHapSymbols(map); 165 HLOGD("UpdateHapSymbols"); 166 } 167 auto mmapRecord = 168 std::make_unique<PerfRecordMmap2>(false, thread.pid_, thread.tid_, map); 169 HLOGD("make PerfRecordMmap2 %d:%d:%s:%s(0x%" PRIx64 "-0x%" PRIx64 ")@%" PRIx64 " ", 170 thread.pid_, thread.tid_, thread.name_.c_str(), map->name.c_str(), 171 map->begin, map->end, map->offset); 172 recordCallBack_(std::move(mmapRecord)); 173 if (updateNormalSymbol) { 174 UpdateSymbols(map, pid); 175 } 176 prevMap = map; 177 } 178 } 179 HLOGV("thread created"); 180#ifdef HIPERF_DEBUG_TIME 181 threadCreateMmapTimes_ += 182 duration_cast<microseconds>(steady_clock::now() - startCreateMmapTime); 183#endif 184 } 185 return thread; 186} 187 188bool VirtualRuntime::UpdateHapSymbols(std::shared_ptr<DfxMap> map) 189{ 190 if (map == nullptr) { 191 return false; 192 } 193 HLOGV("hap name:%s", map->name.c_str()); 194 // found it by name 195 auto symbolsFile = SymbolsFile::CreateSymbolsFile(map->name); 196 CHECK_TRUE(symbolsFile == nullptr, false, 1, 197 "Failed to load CreateSymbolsFile for exec section in hap(%s)", map->name.c_str()); 198 symbolsFile->SetMapsInfo(map); 199 // update maps name if load debuginfo successfully 200 CHECK_TRUE(!symbolsFile->LoadDebugInfo(map), false, 1, 201 "Failed to load debuginfo for exec section in hap(%s)", map->name.c_str()); 202 203 if (!loadSymboleWhenNeeded_) { // todo misspelling 204 symbolsFile->LoadSymbols(map); 205 } 206 symbolsFiles_.emplace_back(std::move(symbolsFile)); 207 return true; 208} 209 210VirtualThread &VirtualRuntime::GetThread(pid_t pid, pid_t tid, const std::string name) 211{ 212 if (userSpaceThreadMap_.find(pid) == userSpaceThreadMap_.end()) { 213 // no pid found 214 // create process first 215 CreateThread(pid, pid); 216 } 217 218 auto it = userSpaceThreadMap_.find(tid); 219 if (it == userSpaceThreadMap_.end()) { 220 // we also need thread 221 return CreateThread(pid, tid, name); 222 } else { 223 return it->second; 224 } 225} 226 227std::shared_ptr<DfxMap> VirtualRuntime::UpdateThreadMaps(pid_t pid, pid_t tid, const std::string filename, 228 uint64_t begin, uint64_t len, uint64_t offset, uint32_t prot) 229{ 230 VirtualThread &thread = GetThread(pid, tid); 231 std::shared_ptr<DfxMap> map = thread.CreateMapItem(filename, begin, len, offset, prot); 232 if (isHM_) { 233 thread.FixHMBundleMap(); 234 } 235 return map; 236} 237 238void VirtualRuntime::UpdateKernelModulesSpaceMaps() 239{ 240 // found the kernel modules 241 std::vector<DfxMap> koMaps; 242 std::ifstream ifs("/proc/modules", std::ifstream::in); 243 if (!ifs.is_open()) { 244 perror("kernel modules read failed(/proc/modules)\n"); 245 return; 246 } 247 std::string line; 248 while (getline(ifs, line)) { 249 uint64_t addr = 0; 250 uint64_t size = 0; 251 char module[line.size()]; 252 /* 253 name size load map 254 hi_mipi_rx 53248 0 - Live 0xbf109000 (O) 255 hi3516cv500_hdmi 237568 0 - Live 0xbf0bb000 (O) 256 hifb 143360 0 - Live 0xbf089000 (O) 257 hi3516cv500_vo_dev 98304 0 - Live 0xbf070000 (O) 258 hi3516cv500_tde 110592 0 - Live 0xbf04a000 (O) 259 hi3516cv500_sys 36864 0 - Live 0xbf03a000 (O) 260 hi3516cv500_base 20480 5 261 hi_mipi_rx,hi3516cv500_hdmi,hifb,hi3516cv500_vo_dev,hi3516cv500_tde,hi3516cv500_sys, 262 hi3516cv500_base,sys_config,hi_proc,hi_irq,Live 0xbf000000 (O) 263 */ 264 int ret = sscanf_s(line.c_str(), "%s%" PRIu64 "%*u%*s%*s 0x%" PRIx64 "", module, 265 sizeof(module), &size, &addr, sizeof(addr)); 266 constexpr int numSlices {3}; 267 if (ret == numSlices) { 268 auto &map = koMaps.emplace_back(addr, addr + size, 0, "", std::string(module)); 269 HLOGV("add ko map %s", map.ToString().c_str()); 270 } else { 271 HLOGE("unknown line %d: '%s'", ret, line.c_str()); 272 } 273 } 274 275 if (std::all_of(koMaps.begin(), koMaps.end(), 276 [](const DfxMap &item) { return item.begin == 0; })) { 277 koMaps.clear(); 278 HLOGW("no addr found in /proc/modules. remove all the ko"); 279 } 280 if (recordCallBack_) { 281 for (const auto &map : koMaps) { 282 auto record = std::make_unique<PerfRecordMmap>(true, 0, 0, map.begin, 283 map.end - map.begin, 0, map.name); 284 recordCallBack_(std::move(record)); 285 } 286 } 287 std::move(koMaps.begin(), koMaps.end(), std::back_inserter(kernelSpaceMemMaps_)); 288} 289 290void VirtualRuntime::UpdateKernelSpaceMaps() 291{ 292 // add kernel first 293 auto &map = kernelSpaceMemMaps_.emplace_back(0, std::numeric_limits<uint64_t>::max(), 0, "", KERNEL_MMAP_NAME); 294 if (recordCallBack_) { 295 auto record = std::make_unique<PerfRecordMmap>(true, 0, 0, map.begin, 296 map.end - map.begin, 0, map.name); 297 recordCallBack_(std::move(record)); 298 } 299} 300 301void VirtualRuntime::UpdateKernelModulesSymbols() 302{ 303 HLOGD("load ko symbol and build id"); 304 for (auto &map : kernelSpaceMemMaps_) { 305 if (map.name == KERNEL_MMAP_NAME) { 306 continue; 307 } 308 auto kernelModuleFile = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_MODULE_FILE, map.name); 309 if (symbolsPaths_.size() > 0) { 310 kernelModuleFile->setSymbolsFilePath(symbolsPaths_); // also load from search path 311 } 312 kernelModuleFile->LoadSymbols(); 313 symbolsFiles_.emplace_back(std::move(kernelModuleFile)); 314 } 315} 316 317void VirtualRuntime::UpdateKernelSymbols() 318{ 319 HLOGD("create a kernel mmap record"); 320 // found kernel source 321 auto kernelFile = SymbolsFile::CreateSymbolsFile(KERNEL_MMAP_NAME); 322 // set symbol path If it exists 323 if (symbolsPaths_.size() > 0) { 324 kernelFile->setSymbolsFilePath(symbolsPaths_); // also load from search path 325 } 326 if (!IsRoot()) { 327 HLOGD("user mode do not load kernel syms"); 328 printf("Hiperf is not running as root mode. Do not need load kernel syms\n"); 329 } 330 if (kernelFile->LoadSymbols()) { 331 auto record = std::make_unique<PerfRecordMmap>( 332 true, 0, 0, kernelFile->textExecVaddr_, kernelFile->textExecVaddrRange_, 333 kernelFile->textExecVaddrFileOffset_, KERNEL_MMAP_NAME); 334 335 if (recordCallBack_) { 336 recordCallBack_(std::move(record)); 337 } 338 symbolsFiles_.emplace_back(std::move(kernelFile)); 339 } else { 340 HLOGW("kernel symbol not found.\n"); 341 } 342} 343 344void VirtualRuntime::UpdatekernelMap(uint64_t begin, uint64_t end, uint64_t offset, 345 std::string filename) 346{ 347 HLOGV("update kernel map name:'%s' 0x%" PRIx64 " - 0x%" PRIx64 "@0x%08" PRIx64 "", 348 filename.c_str(), begin, end, offset); 349 350 HLOG_ASSERT(!filename.empty()); 351 auto it = find(kernelSpaceMemMaps_.begin(), kernelSpaceMemMaps_.end(), filename); 352 if (it == kernelSpaceMemMaps_.end()) { 353 kernelSpaceMemMaps_.emplace_back(begin, end, offset, "", filename); 354 } else { 355 it->begin = begin; 356 it->end = end; 357 it->offset = offset; 358 it->name = filename; 359 } 360} 361 362void VirtualRuntime::DedupFromRecord(PerfRecordSample *recordSample) 363{ 364 CHECK_TRUE(recordSample == nullptr, NO_RETVAL, 0, ""); 365 u64 nr = recordSample->data_.nr; 366 if (nr == 0) { 367 collectSymbolCallBack_(recordSample); 368 return; 369 } 370 u32 pid = recordSample->data_.pid; 371 u64 *ips = recordSample->data_.ips; 372 StackId stackId; 373 stackId.value = 0; 374 auto entry = processStackMap_.find(pid); 375 std::shared_ptr<UniqueStackTable> table = nullptr; 376 if (entry != processStackMap_.end()) { 377 table = entry->second; 378 } else { 379 table = std::make_shared<UniqueStackTable>(pid); 380 processStackMap_[pid] = table; 381 } 382 CHECK_TRUE(table == nullptr, NO_RETVAL, 0, ""); 383 while (table->PutIpsInTable(&stackId, ips, nr) == 0) { 384 // try expand hashtable if collison can not resolved 385 if (!table->Resize()) { 386 HLOGW("Hashtable size limit, ip compress failed!"); 387 collectSymbolCallBack_(recordSample); 388 return; 389 } 390 } 391 // callstack dedup success 392 recordSample->stackId_.value = stackId.value; 393 recordSample->header.size -= (sizeof(u64) * nr - sizeof(stackId)); 394 recordSample->data_.nr = 0; 395 recordSample->data_.ips = nullptr; 396 recordSample->removeStack_ = true; 397} 398 399void VirtualRuntime::CollectDedupSymbol(kSymbolsHits &kernelSymbolsHits, 400 uSymbolsHits &userSymbolsHits) 401{ 402 Node *node = nullptr; 403 Node *head = nullptr; 404 u32 pid; 405 for (const auto &tableEntry : processStackMap_) { 406 const auto &table = tableEntry.second; 407 if (table == nullptr) { 408 continue; 409 } 410 pid = table->GetPid(); 411 head = table->GetHeadNode(); 412 const auto &idxes = table->GetUsedIndexes(); 413 for (const auto idx : idxes) { 414 node = head + idx; 415 if (node == nullptr) { 416 continue; 417 } 418 if (node->value != 0) { 419 if (node->section.inKernel) { 420 uint64_t ip = node->section.ip | KERNEL_PREFIX; 421 if (ip == PERF_CONTEXT_KERNEL || ip == PERF_CONTEXT_USER) { 422 continue; 423 } 424 kernelSymbolsHits.insert(ip); 425 } else { 426 userSymbolsHits[pid].insert(node->section.ip); 427 } 428 } else { 429 HLOGD("node value error 0x%x", idx); 430 } 431 } 432 } 433} 434 435void VirtualRuntime::UpdateFromRecord(PerfEventRecord &record) 436{ 437#ifdef HIPERF_DEBUG_TIME 438 const auto startTime = steady_clock::now(); 439#endif 440 if (record.GetType() == PERF_RECORD_SAMPLE) { 441 auto recordSample = static_cast<PerfRecordSample *>(&record); 442 UpdateFromRecord(*recordSample); 443#ifdef HIPERF_DEBUG_TIME 444 processSampleRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime); 445#endif 446 } else if (record.GetType() == PERF_RECORD_MMAP) { 447 auto recordMmap = static_cast<PerfRecordMmap *>(&record); 448 UpdateFromRecord(*recordMmap); 449#ifdef HIPERF_DEBUG_TIME 450 processMmapRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime); 451#endif 452 } else if (record.GetType() == PERF_RECORD_MMAP2) { 453 auto recordMmap2 = static_cast<PerfRecordMmap2 *>(&record); 454 UpdateFromRecord(*recordMmap2); 455#ifdef HIPERF_DEBUG_TIME 456 processMmap2RecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime); 457#endif 458 } else if (record.GetType() == PERF_RECORD_COMM) { 459 auto recordComm = static_cast<PerfRecordComm *>(&record); 460 UpdateFromRecord(*recordComm); 461#ifdef HIPERF_DEBUG_TIME 462 processCommRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime); 463#endif 464 } else if (record.GetType() == PERF_RECORD_AUXTRACE) { 465 auto recordAuxTrace = static_cast<PerfRecordAuxtrace *>(&record); 466 UpdateFromRecord(*recordAuxTrace); 467#ifdef HIPERF_DEBUG_TIME 468 processCommRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime); 469#endif 470 } else { 471 HLOGW("skip record type %d", record.GetType()); 472 } 473} 474 475void VirtualRuntime::MakeCallFrame(DfxSymbol &symbol, DfxFrame &callFrame) 476{ 477 callFrame.funcOffset = symbol.funcVaddr_; 478 callFrame.mapOffset = symbol.offsetToVaddr_; 479 callFrame.symbolFileIndex = symbol.symbolFileIndex_; 480 callFrame.funcName = symbol.GetName(); 481 if (callFrame.funcName.empty()) { 482 HLOGD("callFrame.funcName:%s, GetName:%s\n", callFrame.funcName.c_str(), symbol.GetName().data()); 483 } 484 485 callFrame.index = static_cast<size_t>(symbol.index_); 486 callFrame.mapName = symbol.module_.empty() ? symbol.comm_ : symbol.module_; 487 HLOG_ASSERT_MESSAGE(!callFrame.funcName.empty(), "%s", symbol.ToDebugString().c_str()); 488} 489 490void VirtualRuntime::SymbolicCallFrame(PerfRecordSample &recordSample, uint64_t ip, 491 pid_t serverPid, perf_callchain_context context) 492{ 493 pid_t pid = static_cast<pid_t>(recordSample.data_.pid); 494 pid_t tid = static_cast<pid_t>(recordSample.data_.tid); 495 if (serverPid != pid) { 496 pid = tid = serverPid; 497 } 498 auto symbol = GetSymbol(ip, pid, tid, context); 499 MakeCallFrame(symbol, recordSample.callFrames_.emplace_back(ip, 0)); 500 HLOGV(" (%zu)unwind symbol: %*s%s", recordSample.callFrames_.size(), 501 static_cast<int>(recordSample.callFrames_.size()), "", 502 recordSample.callFrames_.back().ToSymbolString().c_str()); 503} 504 505bool VirtualRuntime::RecoverCallStack(PerfRecordSample &recordSample) 506{ 507 auto StackTable = processStackMap_.find(recordSample.data_.pid); 508 CHECK_TRUE(StackTable == processStackMap_.end(), false, 1, "not found %" PRIu32 " pid", recordSample.data_.pid); 509 recordSample.ips_.clear(); 510 if (StackTable->second != nullptr) { 511 StackTable->second->GetIpsByStackId(recordSample.stackId_, recordSample.ips_); 512 } 513 recordSample.RecoverCallStack(); 514 return true; 515} 516 517void VirtualRuntime::SymbolicRecord(PerfRecordSample &recordSample) 518{ 519#ifdef HIPERF_DEBUG_TIME 520 const auto startTime = steady_clock::now(); 521#endif 522 // Symbolic the Call Stack 523 recordSample.callFrames_.clear(); 524 perf_callchain_context context = PERF_CONTEXT_MAX; 525 pid_t serverPid; 526 if (recordSample.data_.nr == 0) { 527 serverPid = recordSample.GetServerPidof(0); 528 SymbolicCallFrame(recordSample, recordSample.data_.ip, serverPid, PERF_CONTEXT_MAX); 529 } 530 for (u64 i = 0; i < recordSample.data_.nr; i++) { 531 uint64_t ip = recordSample.data_.ips[i]; 532 if (ip >= PERF_CONTEXT_MAX) { 533 std::string contextName = UpdatePerfContext(ip, context); 534 HLOGV("context switch to %s", contextName.c_str()); 535 continue; 536 } else if (ip < BAD_IP_ADDRESS) { 537 // ip 0 or 1 or less than 0 538 continue; 539 } 540 serverPid = recordSample.GetServerPidof(i); 541 SymbolicCallFrame(recordSample, ip, serverPid, context); 542 } 543#ifdef HIPERF_DEBUG_TIME 544 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime); 545 if (usedTime.count() != 0) { 546 HLOGV("cost %0.3f ms to symbolic ", usedTime.count() / MS_DURATION); 547 } 548 symbolicRecordTimes_ += usedTime; 549#endif 550} 551 552void VirtualRuntime::NeedDropKernelCallChain(PerfRecordSample &sample) 553{ 554 // only do this in record mode. 555 if (recordCallBack_ == nullptr || needkernelCallChain_ || 556 !sample.inKernel() || sample.data_.nr == 0) { 557 return; 558 } 559 560 u64 skip = 0; 561 u64 skipPid = 0; 562 u64 *ips = sample.data_.ips; 563 for (; skip < sample.data_.nr; skip++) { 564 if (ips[skip] == PERF_CONTEXT_KERNEL) { 565 skipPid++; 566 } 567 if (ips[skip] == PERF_CONTEXT_USER) { 568 break; 569 } 570 } 571 sample.skipKernel_ = skip; 572 sample.data_.nr -= skip; 573 sample.header.size -= sizeof(u64) * skip; 574 if (sample.data_.server_nr > 0) { 575 sample.skipPid_ = skipPid; 576 sample.data_.server_nr -= skipPid; 577 sample.header.size -= sizeof(u64) * skipPid; 578 } 579} 580 581void VirtualRuntime::UnwindFromRecord(PerfRecordSample &recordSample) 582{ 583#if defined(is_ohos) && is_ohos 584#ifdef HIPERF_DEBUG_TIME 585 const auto startTime = steady_clock::now(); 586#endif 587 HLOGV("unwind record (time:%llu)", recordSample.data_.time); 588 // if we have userstack ? 589 if (recordSample.data_.stack_size > 0) { 590 pid_t serverPid = recordSample.GetUstackServerPid(); 591 pid_t pid = static_cast<pid_t>(recordSample.data_.pid); 592 pid_t tid = static_cast<pid_t>(recordSample.data_.tid); 593 if (serverPid != pid) { 594 pid = tid = serverPid; 595 } 596 auto &thread = UpdateThread(pid, tid); 597 callstack_.UnwindCallStack(thread, recordSample.data_.user_abi == PERF_SAMPLE_REGS_ABI_32, 598 recordSample.data_.user_regs, recordSample.data_.reg_nr, 599 recordSample.data_.stack_data, recordSample.data_.dyn_size, 600 recordSample.callFrames_); 601#ifdef HIPERF_DEBUG_TIME 602 unwindCallStackTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime); 603#endif 604 size_t oldSize = recordSample.callFrames_.size(); 605 HLOGV("unwind %zu", recordSample.callFrames_.size()); 606 callstack_.ExpandCallStack(thread.tid_, recordSample.callFrames_, callstackMergeLevel_); 607 HLOGV("expand %zu (+%zu)", recordSample.callFrames_.size(), 608 recordSample.callFrames_.size() - oldSize); 609 610 recordSample.ReplaceWithCallStack(oldSize); 611 } 612 613#ifdef HIPERF_DEBUG_TIME 614 unwindFromRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime); 615#endif 616 617 NeedDropKernelCallChain(recordSample); 618 // we will not do this in non record mode. 619 if (dedupStack_ && recordCallBack_ != nullptr) { 620 DedupFromRecord(&recordSample); 621 } 622#endif 623 624 // we will not do this in record mode 625 if (recordCallBack_ == nullptr) { 626 if (dedupStack_ && recordSample.stackId_.section.id > 0 && recordSample.data_.nr == 0) { 627 RecoverCallStack(recordSample); 628 } 629 // find the symbols , reabuild frame info 630 SymbolicRecord(recordSample); 631 } 632} 633 634void VirtualRuntime::SetCollectSymbolCallBack(CollectSymbolCallBack collectSymbolCallBack) 635{ 636 collectSymbolCallBack_ = collectSymbolCallBack; 637} 638 639void VirtualRuntime::UpdateFromRecord(PerfRecordSample &recordSample) 640{ 641 UpdateThread(recordSample.data_.pid, recordSample.data_.tid); 642 if (recordSample.data_.server_nr) { 643 // update all server threads 644 for (size_t i = 0; i < recordSample.data_.server_nr; i++) { 645 pid_t pid = static_cast<pid_t>(recordSample.data_.server_pids[i]); 646 UpdateThread(pid, pid); 647 } 648 } 649 // unwind 650 if (disableUnwind_) { 651 return; 652 } else { 653 UnwindFromRecord(recordSample); 654 } 655} 656 657void VirtualRuntime::UpdateFromRecord(PerfRecordMmap &recordMmap) 658{ 659 HLOGV(" MMAP: size %d pid %u tid %u", recordMmap.header.size, recordMmap.data_.pid, 660 recordMmap.data_.tid); 661 HLOGV(" MMAP: %s dso '%s' (0x%llx-0x%llx)@0x%llx", recordMmap.inKernel() ? "kernel" : "user", 662 recordMmap.data_.filename, recordMmap.data_.addr, 663 recordMmap.data_.addr + recordMmap.data_.len, recordMmap.data_.pgoff); 664 // kernel mmap 665 // don't overwrite the vailed mmap , so we also check the recordMmap.data_.len 666 if (IsKernelThread(recordMmap.data_.pid)) { 667 UpdateKernelThreadMap(recordMmap.data_.pid, recordMmap.data_.addr, 668 recordMmap.data_.len, recordMmap.data_.filename); 669 } else if (recordMmap.inKernel()) { 670 UpdatekernelMap(recordMmap.data_.addr, recordMmap.data_.addr + recordMmap.data_.len, 671 recordMmap.data_.pgoff, recordMmap.data_.filename); 672 } else { 673 NeedAdaptSandboxPath(recordMmap.data_.filename, recordMmap.data_.pid, recordMmap.header.size); 674 FixHMBundleMmap(recordMmap.data_.filename, recordMmap.data_.pid, recordMmap.header.size); 675 auto map = UpdateThreadMaps(recordMmap.data_.pid, recordMmap.data_.tid, recordMmap.data_.filename, 676 recordMmap.data_.addr, recordMmap.data_.len, recordMmap.data_.pgoff); 677 UpdateSymbols(map, recordMmap.data_.pid); 678 } 679} 680 681bool VirtualRuntime::CheckValidSandBoxMmap(PerfRecordMmap2 &recordMmap2) 682{ 683 static std::shared_ptr<DfxMap> prevMap; 684 if ((recordMmap2.data_.prot & PROT_EXEC) != 0) { 685 // fake first segment, when second segment come. 686 auto symFile = SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, recordMmap2.data_.filename); 687 CHECK_TRUE(symFile == nullptr, false, 1, "CheckValidSandBoxMmap Failed to create symbolFile!"); 688 689 std::shared_ptr<DfxMap> curMap; 690 if (strstr(recordMmap2.data_.filename, ".hap") != nullptr) { 691 curMap = std::make_shared<DfxMap>( 692 recordMmap2.data_.addr, 693 recordMmap2.data_.addr + recordMmap2.data_.len, 694 recordMmap2.data_.pgoff, 695 "", // prot 696 recordMmap2.data_.filename 697 ); 698 curMap->prevMap = prevMap; 699 } 700 701 CHECK_TRUE(!symFile->LoadDebugInfo(curMap), false, 1, "CheckValidSandBoxMmap Failed to load debuginfo!"); 702 703 if (!loadSymboleWhenNeeded_) { 704 symFile->LoadSymbols(curMap); 705 } 706 707 if (strstr(recordMmap2.data_.filename, ".hap") == nullptr) { 708 auto elfLoadInfoMap = symFile->GetPtLoads(); 709 u64 begin = recordMmap2.data_.addr - elfLoadInfoMap[0].mmapLen; 710 u64 len = elfLoadInfoMap[0].mmapLen; 711 u64 pgoff = elfLoadInfoMap[0].offset & (~(elfLoadInfoMap[0].align >= 1 ? elfLoadInfoMap[0].align - 1 : 0)); 712 std::unique_ptr<PerfRecordMmap2> mmap2FirstSeg = 713 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid, 714 begin, len, pgoff, 0, 0, 0, PROT_READ, 0, std::string(recordMmap2.data_.filename)); 715 UpdateThreadMaps(mmap2FirstSeg->data_.pid, mmap2FirstSeg->data_.tid, mmap2FirstSeg->data_.filename, 716 mmap2FirstSeg->data_.addr, mmap2FirstSeg->data_.len, mmap2FirstSeg->data_.pgoff); 717 recordCallBack_(std::move(mmap2FirstSeg)); 718 } else { 719 auto elfLoadInfoMap = symFile->GetPtLoads(); 720 u64 begin = recordMmap2.data_.addr - elfLoadInfoMap[0].mmapLen; 721 u64 len = elfLoadInfoMap[0].mmapLen; 722 u64 pgoff = elfLoadInfoMap[0].offset & 723 (~(elfLoadInfoMap[0].align >= 1 ? elfLoadInfoMap[0].align - 1 : 0)); 724 std::unique_ptr<PerfRecordMmap2> mmap2FirstSeg = 725 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid, 726 begin, len, pgoff, 0, 0, 0, PROT_READ, 0, curMap->name); 727 UpdateThreadMaps(mmap2FirstSeg->data_.pid, mmap2FirstSeg->data_.tid, curMap->name, 728 mmap2FirstSeg->data_.addr, mmap2FirstSeg->data_.len, mmap2FirstSeg->data_.pgoff); 729 recordCallBack_(std::move(mmap2FirstSeg)); 730 731 std::unique_ptr<PerfRecordMmap2> mmap2SecondSegment = 732 std::make_unique<PerfRecordMmap2>(recordMmap2.inKernel(), recordMmap2.data_.pid, recordMmap2.data_.tid, 733 recordMmap2.data_.addr, 734 recordMmap2.data_.len, 735 recordMmap2.data_.pgoff - prevMap->offset, // minus load offset of hap 736 0, 0, 0, recordMmap2.data_.prot, 0, curMap->name); 737 UpdateThreadMaps(mmap2SecondSegment->data_.pid, mmap2SecondSegment->data_.tid, curMap->name, 738 mmap2SecondSegment->data_.addr, mmap2SecondSegment->data_.len, mmap2SecondSegment->data_.pgoff); 739 recordCallBack_(std::move(mmap2SecondSegment)); 740 recordMmap2.discard_ = true; 741 } 742 symbolsFiles_.emplace_back(std::move(symFile)); 743 return true; 744 } else if (recordMmap2.data_.pgoff == 0) { 745 recordMmap2.discard_ = true; 746 } 747 748 if (strstr(recordMmap2.data_.filename, ".hap") != nullptr) { 749 prevMap = std::make_shared<DfxMap>( 750 recordMmap2.data_.addr, 751 recordMmap2.data_.addr + recordMmap2.data_.len, 752 recordMmap2.data_.pgoff, 753 "", // prot 754 recordMmap2.data_.filename 755 ); 756 HLOGD("CheckValidSandBoxMmap Update prev map!"); 757 } 758 return !recordMmap2.discard_; 759} 760 761void VirtualRuntime::UpdateFromRecord(PerfRecordMmap2 &recordMmap2) 762{ 763 if (!OHOS::HiviewDFX::DfxMaps::IsLegalMapItem(recordMmap2.data_.filename)) { 764 return; 765 } 766 767 HLOGV(" MMAP2: size %d pid %u tid %u", recordMmap2.header.size, recordMmap2.data_.pid, 768 recordMmap2.data_.tid); 769 HLOGV(" MMAP2: %s dso '%s' (0x%llx-0x%llx)@0x%llx prot:%u", recordMmap2.inKernel() ? "kernel" : "user", 770 recordMmap2.data_.filename, recordMmap2.data_.addr, 771 recordMmap2.data_.addr + recordMmap2.data_.len, recordMmap2.data_.pgoff, recordMmap2.data_.prot); 772 773 if (recordCallBack_) { 774 if (NeedAdaptSandboxPath(recordMmap2.data_.filename, recordMmap2.data_.pid, recordMmap2.header.size)) { 775 FixHMBundleMmap(recordMmap2.data_.filename, recordMmap2.data_.pid, recordMmap2.header.size); 776 CHECK_TRUE(!CheckValidSandBoxMmap(recordMmap2), NO_RETVAL, 0, ""); 777 } 778 } 779 auto map = UpdateThreadMaps(recordMmap2.data_.pid, recordMmap2.data_.tid, recordMmap2.data_.filename, 780 recordMmap2.data_.addr, recordMmap2.data_.len, recordMmap2.data_.pgoff, 781 recordMmap2.data_.prot); 782 UpdateSymbols(map, recordMmap2.data_.pid); 783} 784 785void VirtualRuntime::UpdateFromRecord(PerfRecordComm &recordComm) 786{ 787 recordComm.DumpLog(__FUNCTION__); 788 UpdateThread(recordComm.data_.pid, recordComm.data_.tid, recordComm.data_.comm); 789} 790 791void VirtualRuntime::UpdateFromRecord(PerfRecordAuxtrace &recordAuxTrace) 792{ 793 if (recordCallBack_ == nullptr) { 794#if defined(is_ohos) && is_ohos 795 recordAuxTrace.DumpLog(__FUNCTION__); 796 SpeDecoder *decoder = SpeDecoderDataNew(recordAuxTrace.rawData_, recordAuxTrace.data_.size); 797 CHECK_TRUE(decoder == nullptr, NO_RETVAL, 0, ""); 798 std::vector<SpeRecord> records; 799 while (true) { 800 int ret = SpeDecode(decoder); 801 if (ret <= 0) { 802 break; 803 } 804 struct SpeRecord record = SpeRecord(decoder->record); 805 records.emplace_back(record); 806 } 807 std::vector<ReportItemAuxRawData> auxRawData; 808 for (auto rec: records) { 809 u64 pc = 0; 810 if (rec.from_ip) { 811 pc = rec.from_ip; 812 } else if (rec.to_ip) { 813 pc = rec.to_ip; 814 } else { 815 continue; 816 } 817 DfxSymbol symbol = GetSymbol(pc, recordAuxTrace.data_.reserved__, recordAuxTrace.data_.tid); 818 HLOGV("pc 0x%llx symbol %s", pc, symbol.ToDebugString().c_str()); 819 struct ReportItemAuxRawData reportItem = {rec.type, 0.0f, 1, symbol.comm_.data(), pc, 820 symbol.module_.data(), symbol.GetName().data(), 821 symbol.fileVaddr_}; 822 auxRawData.emplace_back(reportItem); 823 HLOGV("type %u, from_ip: 0x%llx, to_ip: 0x%llx, timestamp: %llu, virt_addr: 0x%llx, phys_addr: 0x%llx", 824 rec.type, rec.from_ip, rec.to_ip, rec.timestamp, rec.virt_addr, rec.phys_addr); 825 } 826 AddReportItems(auxRawData); 827 SpeDecoderFree(decoder); 828#endif 829 } 830} 831 832void VirtualRuntime::SymbolSpeRecord(PerfRecordAuxtrace &recordAuxTrace) 833{ 834#if defined(is_ohos) && is_ohos 835 recordAuxTrace.DumpLog(__FUNCTION__); 836 SpeDecoder *decoder = SpeDecoderDataNew(recordAuxTrace.rawData_, recordAuxTrace.data_.size); 837 CHECK_TRUE(decoder == nullptr, NO_RETVAL, 0, ""); 838 while (true) { 839 int ret = SpeDecode(decoder); 840 if (ret <= 0) { 841 break; 842 } 843 struct SpeRecord record = SpeRecord(decoder->record); 844 u64 pc = 0; 845 if (record.from_ip) { 846 pc = record.from_ip; 847 } else if (record.to_ip) { 848 pc = record.to_ip; 849 } else { 850 continue; 851 } 852 853 DfxSymbol symbol = GetSymbol(pc, recordAuxTrace.data_.reserved__, recordAuxTrace.data_.tid); 854 HLOGV("pc 0x%llx symbol %s", pc, symbol.ToDebugString().c_str()); 855 } 856 SpeDecoderFree(decoder); 857#endif 858} 859 860void VirtualRuntime::SetRecordMode(RecordCallBack recordCallBack) 861{ 862 recordCallBack_ = recordCallBack; 863} 864 865void VirtualRuntime::UpdateSymbols(std::shared_ptr<DfxMap> map, pid_t pid) 866{ 867 CHECK_TRUE(map == nullptr || map->symbolFileIndex != -1, NO_RETVAL, 0, ""); 868 HLOGD("try to find symbols for file: %s", map->name.c_str()); 869 for (size_t i = 0; i < symbolsFiles_.size(); ++i) { 870 if (symbolsFiles_[i]->filePath_ == map->name) { 871 map->symbolFileIndex = static_cast<int32_t>(i); 872 HLOGV("already have '%s', symbol index:%zu", map->name.c_str(), i); 873 return; 874 } 875 } 876 877#ifdef HIPERF_DEBUG_TIME 878 const auto startTime = steady_clock::now(); 879#endif 880 /** 881 * map[] map.name = SymbolsFile.filePath_ prot SymbolsFileType 882 * seg1 /data/storage/el1/bundle/entry.hap r--p ABC 883 * seg2 /data/storage/el1/bundle/entry.hap r-xp ELF 884 * seg3 /data/storage/el1/bundle/entry.hap r--p ABC 885 * seg4 /data/storage/el1/bundle/entry.hap r--p ABC 886 * seg1 /system/app/SceneBoard/SceneBoard.hap r--p ABC 887 * seg2 /system/app/SceneBoard/SceneBoard.hap r--p ABC 888 * seg3 /system/app/SceneBoard/SceneBoard.hap r--p ABC 889 * segN .hap r--p .an/jit/etc 890 * 1.map.name == symbolsFile.filePath_ 891 * 2.map.FileType == symbolsFiles_[map.symbolFileIndex] 892 * 3.cache pc->map->symbolsFiles[map.symbolFileIndex] 893 * 4.must ensure map.mapType assigned with SymbolsFile constructions at the same time. 894 */ 895 auto symbolsFile = SymbolsFile::CreateSymbolsFile(map->name, pid); 896 symbolsFile->SetMapsInfo(map); 897 if (enableDebugInfoSymbolic_ && symbolsFile->symbolFileType_ == SymbolsFileType::SYMBOL_ELF_FILE) { 898 symbolsFile->EnableMiniDebugInfo(); 899 } 900 // set symbol path If it exists 901 if (symbolsPaths_.size() > 0) { 902 // also load from search path 903 symbolsFile->setSymbolsFilePath(symbolsPaths_); 904 } 905 if (loadSymboleWhenNeeded_) { 906 // load it when we need it 907 symbolsFiles_.emplace_back(std::move(symbolsFile)); 908 } else if (symbolsFile->LoadSymbols()) { 909 symbolsFiles_.emplace_back(std::move(symbolsFile)); 910 } else { 911 HLOGW("symbols file for '%s' not found.", map->name.c_str()); 912 } 913#ifdef HIPERF_DEBUG_TIME 914 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime); 915 if (usedTime.count() != 0) { 916 HLOGV("cost %0.3f ms to load '%s'", usedTime.count() / MS_DURATION, map->name.c_str()); 917 } 918 updateSymbolsTimes_ += usedTime; 919#endif 920} 921 922const DfxSymbol VirtualRuntime::GetKernelSymbol(uint64_t ip, const std::vector<DfxMap> &memMaps, 923 const VirtualThread &thread) 924{ 925 DfxSymbol vaddrSymbol(ip, thread.name_); 926 for (auto &map : memMaps) { 927 if (ip > map.begin && ip < map.end) { 928 HLOGM("found addr 0x%" PRIx64 " in kernel map 0x%" PRIx64 " - 0x%" PRIx64 " from %s", 929 ip, map.begin, map.end, map.name.c_str()); 930 vaddrSymbol.module_ = map.name; 931 // found symbols by file name 932 for (auto &symbolsFile : symbolsFiles_) { 933 if (symbolsFile->filePath_ == map.name) { 934 vaddrSymbol.symbolFileIndex_ = symbolsFile->id_; 935 vaddrSymbol.fileVaddr_ = 936 symbolsFile->GetVaddrInSymbols(ip, map.begin, map.offset); 937 perf_callchain_context context = PERF_CONTEXT_KERNEL; 938 if (GetSymbolCache(vaddrSymbol.fileVaddr_, vaddrSymbol, context)) { 939 return vaddrSymbol; 940 } 941 HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 942 " at '%s'", 943 vaddrSymbol.fileVaddr_, ip, map.name.c_str()); 944 if (!symbolsFile->SymbolsLoaded()) { 945 symbolsFile->LoadSymbols(); 946 } 947 DfxSymbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_); 948 foundSymbols.taskVaddr_ = ip; 949 if (!foundSymbols.IsValid()) { 950 HLOGW("addr 0x%" PRIx64 " vaddr 0x%" PRIx64 " NOT found in symbol file %s", 951 ip, vaddrSymbol.fileVaddr_, map.name.c_str()); 952 return vaddrSymbol; 953 } else { 954 return foundSymbols; 955 } 956 } 957 } 958 HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip, 959 map.name.c_str()); 960 } else { 961 HLOGM("addr 0x%" PRIx64 " not in map 0x%" PRIx64 " - 0x%" PRIx64 " from %s", ip, 962 map.begin, map.end, map.name.c_str()); 963 } 964 } 965 return vaddrSymbol; 966} 967 968const DfxSymbol VirtualRuntime::GetKernelThreadSymbol(uint64_t ip, const VirtualThread &thread) 969{ 970 DfxSymbol vaddrSymbol(ip, thread.name_); 971 int64_t mapIndex = thread.FindMapIndexByAddr(ip); 972 if (mapIndex < 0) { 973 HLOGV("not found in any map"); 974 return vaddrSymbol; 975 } 976 977 auto map = thread.GetMaps()[mapIndex]; 978 CHECK_TRUE(map == nullptr, vaddrSymbol, 0, ""); 979 HLOGM("found addr 0x%" PRIx64 " in kthread map 0x%" PRIx64 " - 0x%" PRIx64 " from %s", 980 ip, map->begin, map->end, map->name.c_str()); 981 // found symbols by file name 982 for (auto &symbolsFile : symbolsFiles_) { 983 if (symbolsFile->filePath_ == map->name) { 984 vaddrSymbol.symbolFileIndex_ = symbolsFile->id_; 985 vaddrSymbol.module_ = map->name; 986 vaddrSymbol.fileVaddr_ = 987 symbolsFile->GetVaddrInSymbols(ip, map->begin, map->offset); 988 perf_callchain_context context = PERF_CONTEXT_MAX; 989 if (GetSymbolCache(vaddrSymbol.fileVaddr_, vaddrSymbol, context)) { 990 return vaddrSymbol; 991 } 992 HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'", 993 vaddrSymbol.fileVaddr_, ip, map->name.c_str()); 994 if (!symbolsFile->SymbolsLoaded()) { 995 symbolsFile->LoadDebugInfo(); 996 symbolsFile->LoadSymbols(map); 997 } 998 auto foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_); 999 foundSymbols.taskVaddr_ = ip; 1000 if (!foundSymbols.IsValid()) { 1001 HLOGW("addr 0x%" PRIx64 " vaddr 0x%" PRIx64 " NOT found in symbol file %s", 1002 ip, vaddrSymbol.fileVaddr_, map->name.c_str()); 1003 return vaddrSymbol; 1004 } else { 1005 return foundSymbols; 1006 } 1007 } 1008 } 1009 HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip, 1010 map->name.c_str()); 1011 return vaddrSymbol; 1012} 1013 1014const DfxSymbol VirtualRuntime::GetUserSymbol(uint64_t ip, const VirtualThread &thread) 1015{ 1016 DfxSymbol vaddrSymbol(ip, thread.name_); 1017 int64_t mapIndex = thread.FindMapIndexByAddr(ip); 1018 if (mapIndex >= 0) { 1019 auto map = thread.GetMaps()[mapIndex]; 1020 SymbolsFile *symbolsFile = thread.FindSymbolsFileByMap(map); 1021 if (symbolsFile != nullptr) { 1022 vaddrSymbol.symbolFileIndex_ = symbolsFile->id_; 1023 vaddrSymbol.module_ = map->name; 1024 vaddrSymbol.fileVaddr_ = symbolsFile->GetVaddrInSymbols(ip, map->begin, map->offset); 1025 perf_callchain_context context = PERF_CONTEXT_USER; 1026 if (GetSymbolCache(vaddrSymbol.fileVaddr_, vaddrSymbol, context)) { 1027 return vaddrSymbol; 1028 } 1029 HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64 " at '%s'", 1030 vaddrSymbol.fileVaddr_, ip, map->name.c_str()); 1031 if (!symbolsFile->SymbolsLoaded()) { 1032 symbolsFile->LoadDebugInfo(map); 1033 symbolsFile->LoadSymbols(map); 1034 } 1035 DfxSymbol foundSymbols; 1036 if (!symbolsFile->IsAbc()) { 1037 foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_); 1038 } else { 1039 HLOGD("symbolsFile:%s is ABC :%d", symbolsFile->filePath_.c_str(), symbolsFile->IsAbc()); 1040 foundSymbols = symbolsFile->GetSymbolWithPcAndMap(ip, map); 1041 } 1042 1043 if (foundSymbols.IsValid()) { 1044 return foundSymbols; 1045 } else { 1046 HLOGW("addr 0x%" PRIx64 " vaddr 0x%" PRIx64 " NOT found in symbol file %s", ip, 1047 vaddrSymbol.fileVaddr_, map->name.c_str()); 1048 if (symbolsFile->IsAbc()) { 1049 symbolsFile->symbolsMap_.insert(std::make_pair(ip, vaddrSymbol)); 1050 } 1051 return vaddrSymbol; 1052 } 1053 } else { 1054 HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip, map->name.c_str()); 1055 } 1056 } else { 1057 HLOGV("not found in any map"); 1058 } 1059 return vaddrSymbol; 1060} 1061 1062bool VirtualRuntime::GetSymbolCache(uint64_t fileVaddr, DfxSymbol &symbol, 1063 const perf_callchain_context &context) 1064{ 1065 if (context == PERF_CONTEXT_MAX and kThreadSymbolCache_.count(fileVaddr)) { 1066 CHECK_TRUE(kThreadSymbolCache_.find(symbol.fileVaddr_) == kThreadSymbolCache_.end(), false, 0, ""); 1067 symbol = kThreadSymbolCache_[symbol.fileVaddr_]; 1068 symbol.hit_++; 1069 HLOGV("hit kernel thread cache 0x%" PRIx64 " %d", fileVaddr, symbol.hit_); 1070 return true; 1071 } else if (context != PERF_CONTEXT_USER and kernelSymbolCache_.count(fileVaddr)) { 1072 CHECK_TRUE(kernelSymbolCache_.find(symbol.fileVaddr_) == kernelSymbolCache_.end(), false, 0, ""); 1073 symbol = kernelSymbolCache_[symbol.fileVaddr_]; 1074 symbol.hit_++; 1075 HLOGV("hit kernel cache 0x%" PRIx64 " %d", fileVaddr, symbol.hit_); 1076 return true; 1077 } else if (userSymbolCache_.count(symbol.fileVaddr_) != 0) { 1078 const DfxSymbol &cachedSymbol = userSymbolCache_[symbol.fileVaddr_]; 1079 // must be the same file 1080 if (cachedSymbol.module_ != symbol.module_) { 1081 return false; 1082 } 1083 symbol = cachedSymbol; 1084 symbol.hit_++; 1085 HLOGV("hit user cache 0x%" PRIx64 " %d %s", fileVaddr, symbol.hit_, 1086 symbol.ToDebugString().c_str()); 1087 return true; 1088 } else { 1089 HLOGM("cache miss k %zu u %zu kt %zu", kernelSymbolCache_.size(), 1090 userSymbolCache_.size(), kThreadSymbolCache_.size()); 1091 } 1092 return false; 1093} 1094 1095DfxSymbol VirtualRuntime::GetSymbol(uint64_t ip, pid_t pid, pid_t tid, const perf_callchain_context &context) 1096{ 1097 HLOGV("try find tid %u ip 0x%" PRIx64 " in %zu symbolsFiles\n", tid, ip, symbolsFiles_.size()); 1098 DfxSymbol symbol; 1099 1100 if (IsKernelThread(pid)) { 1101 VirtualThread &kthread = GetThread(pid, tid); 1102 HLOGM("try found addr in kernel thread %u with %zu maps", pid, 1103 kthread.GetMaps().size()); 1104 symbol = GetKernelThreadSymbol(ip, kthread); 1105 HLOGM("add addr to kernel thread cache 0x%" PRIx64 " cache size %zu", ip, 1106 kThreadSymbolCache_.size()); 1107 kThreadSymbolCache_[symbol.fileVaddr_] = symbol; 1108 if (symbol.IsValid()) { 1109 return symbol; 1110 } 1111 } 1112 1113 if (context == PERF_CONTEXT_USER or (context == PERF_CONTEXT_MAX and !symbol.IsValid())) { 1114 // check userspace memmap 1115 symbol = GetUserSymbol(ip, GetThread(pid, tid)); 1116 if (userSymbolCache_.find(symbol.fileVaddr_) == userSymbolCache_.end()) { 1117 userSymbolCache_.reserve(USER_SYMBOL_CACHE_LIMIT); 1118 } 1119 userSymbolCache_[symbol.fileVaddr_] = symbol; 1120 HLOGV("cache ip 0x%" PRIx64 " to %s", ip, 1121 userSymbolCache_[symbol.fileVaddr_].ToDebugString().c_str()); 1122 } 1123 1124 if (context == PERF_CONTEXT_KERNEL or (context == PERF_CONTEXT_MAX and !symbol.IsValid())) { 1125 // check kernelspace 1126 HLOGM("try found addr in kernelspace %zu maps", kernelSpaceMemMaps_.size()); 1127 symbol = GetKernelSymbol(ip, kernelSpaceMemMaps_, GetThread(pid, tid)); 1128 HLOGM("add addr to kernel cache 0x%" PRIx64 " cache size %zu", ip, 1129 kernelSymbolCache_.size()); 1130 kernelSymbolCache_[symbol.fileVaddr_] = symbol; 1131 } 1132 return symbol; 1133} 1134 1135bool VirtualRuntime::SetSymbolsPaths(const std::vector<std::string> &symbolsPaths) 1136{ 1137 std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_UNKNOW_FILE); 1138 CHECK_TRUE(symbolsFile == nullptr, false, 0, ""); 1139 // we need check if the path is accessible 1140 bool accessible = symbolsFile->setSymbolsFilePath(symbolsPaths); 1141 if (accessible) { 1142 symbolsPaths_ = symbolsPaths; 1143 } else { 1144 if (!symbolsPaths.empty()) { 1145 HLOGE("some symbols path unable access"); 1146 } 1147 } 1148 return accessible; 1149} 1150 1151void VirtualRuntime::UpdateFromPerfData(const std::vector<SymbolFileStruct> &symbolFileStructs) 1152{ 1153 // review: if we need move to some other place ? 1154 HLOG_ASSERT_MESSAGE(symbolsFiles_.size() == 0, " symbolsFiles_ size is %zu", 1155 symbolsFiles_.size()); 1156 for (const auto &symbolFileStruct : symbolFileStructs) { 1157 HLOGV("symbolFileStruct.filePath_:'%s'", symbolFileStruct.filePath_.c_str()); 1158 HLOGV("symbolFileStruct.buildId_:'%s'", symbolFileStruct.buildId_.c_str()); 1159 HLOGV("process symbols file:'%s':'%s'", symbolFileStruct.filePath_.c_str(), 1160 symbolFileStruct.buildId_.c_str()); 1161 1162 // load from symbolFileStruct (perf.data) 1163 std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::LoadSymbolsFromSaved(symbolFileStruct); 1164 if (symbolsFile == nullptr) { 1165 continue; 1166 } 1167 // reaload from sybol path If it exists 1168 if (symbolsPaths_.size() > 0) { 1169 HLOGV("try again with symbolsPaths setup"); 1170 symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path 1171 // use give us path ,we must reload it. 1172 symbolsFile->LoadSymbols(); 1173 } 1174 symbolsFile->id_ = static_cast<int32_t>(symbolsFiles_.size()); 1175 symbolsFiles_.emplace_back(std::move(symbolsFile)); 1176 } 1177} 1178 1179void VirtualRuntime::ImportUniqueStackNodes(const std::vector<UniStackTableInfo>& uniStackTableInfos) 1180{ 1181 for (const UniStackTableInfo& item : uniStackTableInfos) { 1182 auto stackTable = std::make_shared<UniqueStackTable>(item.pid, item.tableSize); 1183 for (const UniStackNode& node : item.nodes) { 1184 stackTable->ImportNode(node.index, node.node); 1185 } 1186 processStackMap_[item.pid] = std::move(stackTable); 1187 } 1188} 1189 1190/* 1191 ARM functions 1192 The table below lists the symbols exported by the vDSO. 1193 1194 symbol version 1195 ──────────────────────────────────────────────────────────── 1196 __vdso_gettimeofday LINUX_2.6 (exported since Linux 4.1) 1197 __vdso_clock_gettime LINUX_2.6 (exported since Linux 4.1) 1198 1199 Additionally, the ARM port has a code page full of utility 1200 functions. Since it's just a raw page of code, there is no ELF 1201 information for doing symbol lookups or versioning. It does 1202 provide support for different versions though. 1203 1204 For information on this code page, it's best to refer to the 1205 kernel documentation as it's extremely detailed and covers 1206 everything you need to know: 1207 Documentation/arm/kernel_user_helpers.txt. 1208 1209 aarch64 functions 1210 The table below lists the symbols exported by the vDSO. 1211 1212 symbol version 1213 ────────────────────────────────────── 1214 __kernel_rt_sigreturn LINUX_2.6.39 1215 __kernel_gettimeofday LINUX_2.6.39 1216 __kernel_clock_gettime LINUX_2.6.39 1217 __kernel_clock_getres LINUX_2.6.39 1218*/ 1219void VirtualRuntime::LoadVdso() 1220{ 1221#if !is_mingw 1222 VirtualThread myThread(getpid(), symbolsFiles_); 1223 myThread.ParseMap(); 1224 for (const auto &map : myThread.GetMaps()) { 1225 if (map->IsVdsoMap()) { 1226 std::string memory(map->end - map->begin, '\0'); 1227 std::copy(reinterpret_cast<char *>((map->begin)), reinterpret_cast<char *>((map->end)), 1228 &memory[0]); 1229 std::string tempPath("/data/local/tmp/"); 1230 std::string tempFileName = tempPath + map->name; 1231 if (!WriteStringToFile(tempFileName, memory)) { 1232 printf("vdso temp file create fail at %s\n", tempFileName.c_str()); 1233 } else { 1234 HLOGD("vdso temp file create at %s:%zu", tempFileName.c_str(), memory.size()); 1235 auto symbolsFile = SymbolsFile::CreateSymbolsFile(map->name); 1236 symbolsFile->setSymbolsFilePath(tempPath); // also load from search path 1237 symbolsFiles_.emplace_back(std::move(symbolsFile)); 1238 return; 1239 } 1240 } 1241 } 1242 HLOGD("no vdso found"); 1243#endif 1244} 1245 1246void VirtualRuntime::UpdateServiceSpaceMaps() 1247{ 1248 VirtualThread &kthread = GetThread(SYSMGR_PID, SYSMGR_PID); 1249 kthread.ParseServiceMap(SYSMGR_FILE_NAME); 1250 if (recordCallBack_) { 1251 for (const auto &map : kthread.GetMaps()) { 1252 auto record = 1253 std::make_unique<PerfRecordMmap>(true, SYSMGR_PID, SYSMGR_PID, 1254 map->begin, map->end - map->begin, 1255 0, SYSMGR_FILE_NAME); 1256 recordCallBack_(std::move(record)); 1257 } 1258 } 1259} 1260 1261void VirtualRuntime::UpdateServiceSymbols() 1262{ 1263 HLOGD("try to update kernel thread symbols for kernel service"); 1264 std::string fileName = SYSMGR_FILE_NAME; 1265 auto symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, fileName); 1266 1267 HLOGD("add kernel service symbol file: %s", fileName.c_str()); 1268 if (symbolsFile->LoadSymbols()) { 1269 symbolsFiles_.emplace_back(std::move(symbolsFile)); 1270 } else { 1271 HLOGW("symbols file for '%s' not found.", fileName.c_str()); 1272 } 1273} 1274 1275void VirtualRuntime::UpdateKernelThreadMap(pid_t pid, uint64_t begin, uint64_t len, 1276 std::string filename) 1277{ 1278 HLOGV("update kernel thread map pid %u name:'%s'", pid, filename.c_str()); 1279 1280 VirtualThread &thread = GetThread(pid, pid); 1281 thread.CreateMapItem(filename, begin, len, 0u); 1282} 1283 1284void VirtualRuntime::UpdateDevhostSpaceMaps() 1285{ 1286 VirtualThread &kthread = GetThread(devhostPid_, devhostPid_); 1287 kthread.ParseDevhostMap(devhostPid_); 1288 if (recordCallBack_) { 1289 for (const auto &map : kthread.GetMaps()) { 1290 auto record = 1291 std::make_unique<PerfRecordMmap>(false, devhostPid_, devhostPid_, 1292 map->begin, map->end - map->begin, 1293 0, map->name); 1294 recordCallBack_(std::move(record)); 1295 } 1296 } 1297} 1298 1299void VirtualRuntime::UpdateDevhostSymbols() 1300{ 1301 HLOGD("try to update kernel thread symbols for devhost"); 1302 auto kallsyms = SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, DEVHOST_FILE_NAME); 1303 // file name of devhost.ko 1304 std::map<std::string_view, std::unique_ptr<SymbolsFile>> koMaps; 1305 koMaps[DEVHOST_FILE_NAME] = 1306 SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, DEVHOST_LINUX_FILE_NAME); 1307 1308 if (kallsyms->LoadSymbols()) { 1309 for (auto &symbol : kallsyms->GetSymbols()) { 1310 if (koMaps.find(symbol.module_) == koMaps.end()) { 1311 std::string filename = std::string(symbol.module_); 1312 // [devhost] to /liblinux/devhost.ko 1313 filename.erase(filename.begin()); 1314 filename.erase(filename.end() - 1); 1315 filename = DEVHOST_LINUX_PREFIX + filename + KERNEL_MODULES_EXT_NAME; 1316 koMaps[symbol.module_] = 1317 SymbolsFile::CreateSymbolsFile(SYMBOL_KERNEL_THREAD_FILE, filename); 1318 } 1319 if (koMaps[symbol.module_] == nullptr) { 1320 continue; 1321 } 1322 koMaps[symbol.module_]->AddSymbol(std::move(symbol)); 1323 } 1324 1325 HLOGD("devhost loaded %zu symbolfiles", koMaps.size()); 1326 for (auto &it : koMaps) { 1327 if (it.second == nullptr) { 1328 continue; 1329 } 1330 HLOGD("Load %zu symbols to %s", it.second->GetSymbols().size(), 1331 it.second->filePath_.c_str()); 1332 symbolsFiles_.emplace_back(std::move(it.second)); 1333 } 1334 } else { 1335 HLOGW("symbols file for devhost parse failed."); 1336 } 1337 1338 // update normal symbole files 1339 VirtualThread &kthread = GetThread(devhostPid_, devhostPid_); 1340 for (const auto &map : kthread.GetMaps()) { 1341 UpdateSymbols(map, devhostPid_); 1342 } 1343} 1344 1345void VirtualRuntime::FixHMBundleMmap(char *filename, int pid, u16 &headerSize) 1346{ 1347 if (!isHM_) { 1348 return; 1349 } 1350 // fix bundle path in mmap 1351 std::string newFilename = filename; 1352 VirtualThread &thread = GetThread(pid, pid); 1353 if (NeedAdaptHMBundlePath(newFilename, thread.name_)) { 1354 size_t oldSize = strlen(filename); 1355 (void)memset_s(filename, KILO, '\0', KILO); 1356 if (strncpy_s(filename, KILO, newFilename.c_str(), newFilename.size()) != 0) { 1357 HLOGD("strncpy_s recordMmap2 failed!"); 1358 } 1359 headerSize += newFilename.size() - oldSize; 1360 } 1361} 1362 1363void VirtualRuntime::SetDevhostPid(pid_t devhost) 1364{ 1365 HLOGD("Set devhost pid: %d", devhost); 1366 devhostPid_ = devhost; 1367} 1368 1369bool VirtualRuntime::IsKernelThread(pid_t pid) 1370{ 1371 if (!isHM_) { 1372 return false; 1373 } 1374 return pid == SYSMGR_PID || pid == devhostPid_; 1375} 1376 1377} // namespace HiPerf 1378} // namespace Developtools 1379} // namespace OHOS 1380