diff --git a/include/subcommand_record.h b/include/subcommand_record.h index 5f265f955de7c3a3dd467c122757b37da4710c39..422c1256a7f1002e7d68787239b6137df42acab5 100644 --- a/include/subcommand_record.h +++ b/include/subcommand_record.h @@ -361,9 +361,17 @@ private: bool isNeedSetPerfHarden_ = false; bool isSpe_ = false; + const bool isRoot_ = IsRoot(); + uint32_t offset_ = 0; + uint32_t devhostPid_ = UINT32_MAX; + // callback to process record bool ProcessRecord(PerfEventRecord& record); bool SaveRecord(const PerfEventRecord& record); + uint32_t GetOffsetNum(); + void UpdateDevHostMaps(PerfEventRecord& record); + void UpdateDevHostCallChains(PerfEventRecord& record); + void UpdateDevHostMapsAndIPs(PerfEventRecord& record); // file format like as 0,1-3,4-6,7,8 uint32_t GetCountFromFile(const std::string &fileName); diff --git a/include/symbols_file.h b/include/symbols_file.h index b4f98bef15585171613cfc200f9ea2baf72c3fb9..11e223131a6b5c79346279d274111095d5df5401 100644 --- a/include/symbols_file.h +++ b/include/symbols_file.h @@ -207,6 +207,7 @@ public: std::vector symbols_ {}; std::vector matchedSymbols_ {}; std::map symbolsMap_; + static uint32_t offsetNum_; virtual DfxSymbol GetSymbolWithPcAndMap(uint64_t pc, std::shared_ptr map) { return DfxSymbol(); diff --git a/src/subcommand_record.cpp b/src/subcommand_record.cpp index aa24d52cf75321c1b03664b9b8125b68970098d8..f147db6918cea06d1dfa827486242a0d1dca0b66 100644 --- a/src/subcommand_record.cpp +++ b/src/subcommand_record.cpp @@ -21,6 +21,8 @@ #include #include #include +#include + #if defined(CONFIG_HAS_SYSPARA) #include #endif @@ -1143,7 +1145,9 @@ void SubCommandRecord::PrepareKernelMaps() // prepare from kernel and ko virtualRuntime_.SetNeedKernelCallChain(!callChainUserOnly_); virtualRuntime_.UpdateKernelSpaceMaps(); - virtualRuntime_.UpdateKernelModulesSpaceMaps(); + if (isRoot_) { + virtualRuntime_.UpdateKernelModulesSpaceMaps(); + } if (isHM_) { virtualRuntime_.UpdateServiceSpaceMaps(); } @@ -1157,6 +1161,9 @@ void SubCommandRecord::PrepareKernelMaps() bool SubCommandRecord::PrepareVirtualRuntime() { auto saveRecord = [this](PerfEventRecord& record) -> bool { + if (!isRoot_) { + UpdateDevHostMaps(record); + } return this->SaveRecord(record); }; virtualRuntime_.SetRecordMode(saveRecord); @@ -1608,6 +1615,12 @@ HiperfError SubCommandRecord::OnSubCommand(std::vector& args) } } + if (!isRoot_) { + offset_ = GetOffsetNum(); + SymbolsFile::offsetNum_ = offset_; + HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] get offset success"); + } + if (!CheckTargetPids()) { HIPERF_HILOGE(MODULE_DEFAULT, "[OnSubCommand] CheckTargetPids failed"); if (controlCmd_ == CONTROL_CMD_PREPARE) { @@ -1775,6 +1788,15 @@ void SubCommandRecord::RemoveVdsoTmpFile() } } +void SubCommandRecord::UpdateDevHostMapsAndIPs(PerfEventRecord& record) +{ + if (isRoot_) { + return; + } + UpdateDevHostCallChains(record); + UpdateDevHostMaps(record); +} + bool SubCommandRecord::ProcessRecord(PerfEventRecord& record) { CHECK_TRUE(record.GetName() != nullptr, false, 1, "record is null"); @@ -1818,6 +1840,7 @@ bool SubCommandRecord::ProcessRecord(PerfEventRecord& record) #ifdef HIPERF_DEBUG_TIME prcessRecordTimes_ += duration_cast(steady_clock::now() - startTime); #endif + UpdateDevHostMapsAndIPs(record); return SaveRecord(record); #endif } @@ -2177,24 +2200,32 @@ void SubCommandRecord::CollectSymbol(PerfRecordSample *sample) } else { userSymbolsHits_[sample->data_.pid].insert(sample->data_.ip); } - } else { - for (u64 i = 0; i < sample->data_.nr; i++) { - if (sample->data_.ips[i] >= PERF_CONTEXT_MAX) { - if (sample->data_.ips[i] == PERF_CONTEXT_KERNEL) { - context = PERF_CONTEXT_KERNEL; - } else { - context = PERF_CONTEXT_USER; - } + return; + } + + for (u64 i = 0; i < sample->data_.nr; i++) { + if (sample->data_.ips[i] >= PERF_CONTEXT_MAX) { + if (sample->data_.ips[i] == PERF_CONTEXT_KERNEL) { + context = PERF_CONTEXT_KERNEL; } else { - serverPid = sample->GetServerPidof(i); - if (virtualRuntime_.IsKernelThread(serverPid)) { - kernelThreadSymbolsHits_[serverPid].insert(sample->data_.ips[i]); - } else if (context == PERF_CONTEXT_KERNEL) { - kernelSymbolsHits_.insert(sample->data_.ips[i]); - } else { - userSymbolsHits_[sample->data_.pid].insert(sample->data_.ips[i]); - } + context = PERF_CONTEXT_USER; } + continue; + } + + serverPid = sample->GetServerPidof(i); + if (!isRoot_ && serverPid == devhostPid_) { + // in func UpdateDevHostCallChains add offset_ to ips, need sub offset_ when symboling + if (sample->data_.ips[i] > offset_) { + sample->data_.ips[i] -= offset_; + } + } + if (virtualRuntime_.IsKernelThread(serverPid)) { + kernelThreadSymbolsHits_[serverPid].insert(sample->data_.ips[i]); + } else if (context == PERF_CONTEXT_KERNEL) { + kernelSymbolsHits_.insert(sample->data_.ips[i]); + } else { + userSymbolsHits_[sample->data_.pid].insert(sample->data_.ips[i]); } } } @@ -2356,6 +2387,7 @@ void SubCommandRecord::SetHM() std::string cmdline = GetProcessName(pid); if (cmdline == "/bin/" + DEVHOST_FILE_NAME) { virtualRuntime_.SetDevhostPid(pid); + devhostPid_ = static_cast(pid); break; } } @@ -2457,6 +2489,62 @@ void SubCommandRecord::SetCheckRecordCallback(CheckRecordCallBack callback) checkCallback_ = callback; #endif } + +uint32_t SubCommandRecord::GetOffsetNum() +{ + uint32_t result = 0; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dis(0, UINT32_MAX); + result = dis(gen); + + std::ifstream randomFile("/dev/random", std::ios::binary); + do { + if (!randomFile.is_open()) { + break; + } + randomFile.read(reinterpret_cast(&result), sizeof(result)); + } while (0); + randomFile.close(); + return result; +} + +void SubCommandRecord::UpdateDevHostMaps(PerfEventRecord& record) +{ + if (record.GetType() == PERF_RECORD_MMAP) { + auto recordMmap = static_cast(&record); + if (recordMmap->data_.pid == devhostPid_) { + recordMmap->data_.addr += offset_; + } + } else if (record.GetType() == PERF_RECORD_MMAP2) { + auto recordMmap2 = static_cast(&record); + if (recordMmap2->data_.pid == devhostPid_) { + recordMmap2->data_.addr += offset_; + } + } +} + +void SubCommandRecord::UpdateDevHostCallChains(PerfEventRecord& record) +{ + if (record.GetType() == PERF_RECORD_SAMPLE) { + pid_t serverPid; + const uint64_t BAD_IP_ADDRESS = 2; + auto sample = static_cast(&record); + serverPid = sample->GetServerPidof(0); + if (serverPid == devhostPid_) { + sample->data_.ip += offset_; + } + for (u64 i = 0; i < sample->data_.nr; i++) { + if (sample->data_.ips[i] >= PERF_CONTEXT_MAX || sample->data_.ips[i] < BAD_IP_ADDRESS) { + continue; + } + serverPid = sample->GetServerPidof(i); + if (serverPid == devhostPid_) { + sample->data_.ips[i] += offset_; + } + } + } +} } // namespace HiPerf } // namespace Developtools } // namespace OHOS diff --git a/src/symbols_file.cpp b/src/symbols_file.cpp index 95070d9ee796878910664567996d7129b272196a..3b68cab0e35f899c5bc2289a36a705dcc388bc53 100644 --- a/src/symbols_file.cpp +++ b/src/symbols_file.cpp @@ -50,6 +50,7 @@ namespace Developtools { namespace HiPerf { bool SymbolsFile::onRecording_ = true; bool SymbolsFile::needParseJsFunc_ = false; +uint32_t SymbolsFile::offsetNum_ = 0; const std::string SymbolsFile::GetBuildId() const { @@ -793,6 +794,50 @@ public: // try elf return ElfFileSymbols::LoadSymbols(nullptr, filePath_); } + + DfxSymbol GetSymbolWithPcAndMap(uint64_t ip, std::shared_ptr map) override + { + DfxSymbol symbol; + // it should be already order from small to large + 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()) { + if (found->Contain(ip)) { + found->offsetToVaddr_ = ip - found->funcVaddr_; + if (!found->matched_) { + found->matched_ = true; + DfxSymbol matchedSymbol = *found; + // in func UpdateDevHostCallChains add offset to ips, need add offset when saveing + matchedSymbol.funcVaddr_ += SymbolsFile::offsetNum_; + this->symbolsMap_.emplace(ip, matchedSymbol); + matchedSymbols_.push_back(&(symbolsMap_[ip])); + } + symbol = *found; // copy + 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; + } + ~KernelThreadSymbols() override {} }; diff --git a/src/virtual_runtime.cpp b/src/virtual_runtime.cpp index 6128a1717417ec6cf59d210ee8ec9e2e90b43ae2..d21128882a04104923712723c753c505a544a977 100644 --- a/src/virtual_runtime.cpp +++ b/src/virtual_runtime.cpp @@ -1043,7 +1043,13 @@ const DfxSymbol VirtualRuntime::GetKernelThreadSymbol(uint64_t ip, const Virtual symbolsFile->LoadDebugInfo(); symbolsFile->LoadSymbols(map); } - auto foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_); + DfxSymbol foundSymbols; + if (thread.pid_ == devhostPid_ && recordCallBack_ != nullptr) { + foundSymbols = symbolsFile->GetSymbolWithPcAndMap(vaddrSymbol.fileVaddr_, map); + } else { + foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_); + } + foundSymbols.taskVaddr_ = ip; if (!foundSymbols.IsValid()) { HLOGW("addr 0x%" PRIx64 " vaddr 0x%" PRIx64 " NOT found in symbol file %s", diff --git a/test/unittest/common/native/subcommand_record_test.cpp b/test/unittest/common/native/subcommand_record_test.cpp index db55df8f42ed43d8da21ace4f238d3dbe4bb0ae0..9b753b45aca8fd62e61576c337106e592e5a830c 100644 --- a/test/unittest/common/native/subcommand_record_test.cpp +++ b/test/unittest/common/native/subcommand_record_test.cpp @@ -2449,6 +2449,101 @@ HWTEST_F(SubCommandRecordTest, OutPutFileName, TestSize.Level2) EXPECT_EQ(CheckTraceCommandOutput("hiperf record -d 3 -a -o /data/log/hiperflog/perf.data", {"Invalid output file path, permission denied"}), true); } + +/** + * @tc.name: TestOnSubCommand_OutPutFileName + * @tc.type: FUNC + */ +HWTEST_F(SubCommandRecordTest, GetOffsetNum, TestSize.Level1) +{ + SubCommandRecord cmd; + uint32_t offset = cmd.GetOffsetNum(); + EXPECT_GT(offset, 0); +} + +/** + * @tc.name: TestOnSubCommand_OutPutFileName + * @tc.type: FUNC + */ +HWTEST_F(SubCommandRecordTest, UpdateDevHostMaps1, TestSize.Level1) +{ + constexpr uint32_t pid = 70; + constexpr uint32_t tid = 70; + constexpr uint32_t addr = 111; + constexpr uint64_t len = 1000; + constexpr uint64_t pgoff = 0; + PerfRecordMmap recordIn {true, pid, tid, addr, + len, pgoff, "testdatammap"}; + SubCommandRecord cmd; + cmd.devhostPid_ = pid; + cmd.offset_ = cmd.GetOffsetNum(); + cmd.UpdateDevHostMaps(recordIn); + EXPECT_EQ(recordIn.data_.addr, cmd.offset_ + addr); +} + +/** + * @tc.name: TestOnSubCommand_OutPutFileName + * @tc.type: FUNC + */ +HWTEST_F(SubCommandRecordTest, UpdateDevHostMaps2, TestSize.Level1) +{ + constexpr uint32_t devhostPid = 71; + constexpr uint32_t pid = 70; + constexpr uint32_t tid = 70; + constexpr uint32_t addr = 111; + constexpr uint64_t len = 1000; + constexpr uint64_t pgoff = 0; + PerfRecordMmap recordIn {true, pid, tid, addr, + len, pgoff, "testdatammap"}; + SubCommandRecord cmd; + cmd.devhostPid_ = devhostPid; + cmd.offset_ = cmd.GetOffsetNum(); + cmd.UpdateDevHostMaps(recordIn); + EXPECT_EQ(recordIn.data_.addr, addr); +} + +/** + * @tc.name: TestOnSubCommand_OutPutFileName + * @tc.type: FUNC + */ +HWTEST_F(SubCommandRecordTest, UpdateDevHostMaps3, TestSize.Level1) +{ + constexpr uint32_t pid = 70; + constexpr uint32_t tid = 70; + constexpr uint32_t addr = 111; + constexpr uint64_t len = 1000; + constexpr uint64_t pgoff = 0; + constexpr uint64_t testNum = 1; + PerfRecordMmap2 recordIn {true, pid, tid, addr, len, pgoff, + testNum, testNum, testNum, testNum, testNum, "testdatammap2"}; + SubCommandRecord cmd; + cmd.devhostPid_ = pid; + cmd.offset_ = cmd.GetOffsetNum(); + cmd.UpdateDevHostMaps(recordIn); + EXPECT_EQ(recordIn.data_.addr, cmd.offset_ + addr); +} + +/** + * @tc.name: TestOnSubCommand_OutPutFileName + * @tc.type: FUNC + */ +HWTEST_F(SubCommandRecordTest, UpdateDevHostMaps4, TestSize.Level1) +{ + constexpr uint32_t devhostPid = 71; + constexpr uint32_t pid = 70; + constexpr uint32_t tid = 70; + constexpr uint32_t addr = 111; + constexpr uint64_t len = 1000; + constexpr uint64_t pgoff = 0; + constexpr uint64_t testNum = 1; + PerfRecordMmap2 recordIn {true, pid, tid, addr, len, pgoff, + testNum, testNum, testNum, testNum, testNum, "testdatammap2"}; + SubCommandRecord cmd; + cmd.devhostPid_ = devhostPid; + cmd.offset_ = cmd.GetOffsetNum(); + cmd.UpdateDevHostMaps(recordIn); + EXPECT_EQ(recordIn.data_.addr, addr); +} } // namespace HiPerf } // namespace Developtools } // namespace OHOS