From 4428eecacde38a656b8dee7cc325d2acf1b47c35 Mon Sep 17 00:00:00 2001 From: leiguangyu Date: Thu, 28 Nov 2024 16:33:51 +0800 Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E9=80=89=E6=8B=A9=E5=9B=9E?= =?UTF-8?q?=E6=A0=88=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I0b5e99a0ab4d123a761714fbeaf205fd199f1df3 Signed-off-by: leiguangyu --- include/perf_events.h | 9 ++ include/perf_file_reader.h | 1 + include/subcommand_record.h | 5 +- include/subcommand_report.h | 1 + include/utilities.h | 1 + src/perf_events.cpp | 131 ++++++++++++++++-- src/perf_file_reader.cpp | 16 +++ src/subcommand_record.cpp | 23 ++- src/subcommand_report.cpp | 27 +++- src/utilities.cpp | 9 ++ .../common/native/subcommand_record_test.cpp | 32 +++++ 11 files changed, 229 insertions(+), 26 deletions(-) diff --git a/include/perf_events.h b/include/perf_events.h index b5eaa3d..badd5f3 100644 --- a/include/perf_events.h +++ b/include/perf_events.h @@ -385,6 +385,7 @@ public: NONE, FP, DWARF, + AUTO, }; void SetSampleStackType(SampleStackType type); void SetDwarfSampleStackSize(uint32_t stackSize); @@ -527,6 +528,7 @@ public: void SetHM(bool isHM); void SetConfig(std::map &speOptMaps); + void SetMainPids(const std::vector selectPids); private: size_t recordEventCount_ = 0; // only for debug time #ifdef HIPERF_DEBUG_TIME @@ -564,6 +566,9 @@ private: void WaitRecordThread(); bool HaveTargetsExit(const std::chrono::steady_clock::time_point &startTime); void ExitReadRecordBufThread(); + void AddFpStackType(perf_event_attr &attr); + void AddDwarfStackType(perf_event_attr &attr); + void GetEventAttrMap(); enum EventSpaceType { UNKNOW = 0, @@ -617,6 +622,7 @@ private: std::string configName; perf_event_attr attr = {}; std::vector fdItems; + SampleStackType stackType; }; struct EventGroupItem { std::vector eventItems; @@ -641,6 +647,9 @@ private: SampleStackType sampleStackType_ = SampleStackType::NONE; uint32_t dwarfSampleStackSize_ = MAX_SAMPLE_STACK_SIZE; + std::vector mainPids_; + std::map idAttrMap_; + std::map pidStackTypeMap_; // read records from the ring buffer singleton void ReadRecordFromBuffer(); diff --git a/include/perf_file_reader.h b/include/perf_file_reader.h index 6f9b11b..c19ae84 100644 --- a/include/perf_file_reader.h +++ b/include/perf_file_reader.h @@ -75,6 +75,7 @@ private: bool ReadIdsForAttr(const perf_file_attr &attr, std::vector *ids); const perf_event_attr *GetDefaultAttr(); + const perf_event_attr *GetAttrById(uint64_t sampleId); const std::string fileName_; uint64_t dataSectionSize_; diff --git a/include/subcommand_record.h b/include/subcommand_record.h index a693e25..441f041 100644 --- a/include/subcommand_record.h +++ b/include/subcommand_record.h @@ -128,6 +128,8 @@ public: " fp: frame pointer\n" " dwarf: DWARF's CFI - Call Frame Information\n" " 'dwarf,size' set sample stack size, size should be in 8~65528 and 8 byte aligned. \n" + " auto: Automatically select call stack recording for each thread, fp/dwarf\n" + " Conflicts with the -a option.\n" " as the method to collect the information used to show the call stacks.\n" " --kernel-callchain\n" " collect kernel callchain, must used with -s fp/dwarf simultaneously.\n" @@ -258,8 +260,7 @@ private: bool CheckSelectCpuPidOption(); bool GetOptionFrequencyAndPeriod(std::vector &args); - bool isCallStackDwarf_ = false; - bool isCallStackFp_ = false; + PerfEvents::SampleStackType stackType_ = PerfEvents::SampleStackType::NONE; uint32_t callStackDwarfSize_ = MAX_SAMPLE_STACK_SIZE; uint64_t branchSampleType_ = 0; uint64_t dataSizeLimit_ = 0; diff --git a/include/subcommand_report.h b/include/subcommand_report.h index df29ea4..158f68a 100644 --- a/include/subcommand_report.h +++ b/include/subcommand_report.h @@ -123,6 +123,7 @@ private: bool LoadPerfData(); void ProcessFeaturesData(); void LoadEventConfigData(); + std::vector MergeAutoAttr(const std::vector &attrIds); void LoadAttrSection(); void LoadEventDesc(); void ProcessSymbolsData(); diff --git a/include/utilities.h b/include/utilities.h index 2feae56..f308a91 100644 --- a/include/utilities.h +++ b/include/utilities.h @@ -364,6 +364,7 @@ const std::string GetUserType(); bool GetDeveloperMode(); bool IsArkJsFile(const std::string& filepath); std::string GetProcessName(int pid); +std::string GetThreadName(pid_t tid); bool NeedAdaptSandboxPath(char *filename, int pid, u16 &headerSize); bool NeedAdaptHMBundlePath(std::string& filename, const std::string& threadname); } // namespace HiPerf diff --git a/src/perf_events.cpp b/src/perf_events.cpp index 1650af4..fe48dbe 100644 --- a/src/perf_events.cpp +++ b/src/perf_events.cpp @@ -409,6 +409,25 @@ void PerfEvents::SetConfig(std::map &speO config2_ |= speOptMaps["min_latency"] & 0xfff; } +void PerfEvents::SetMainPids(std::vector selectPids) +{ + mainPids_ = selectPids; +} + +void PerfEvents::AddFpStackType(perf_event_attr &attr) +{ + attr.sample_type |= PERF_SAMPLE_CALLCHAIN; +} + +void PerfEvents::AddDwarfStackType(perf_event_attr &attr) +{ + attr.sample_type |= PERF_SAMPLE_CALLCHAIN | + PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER; + attr.exclude_callchain_user = 1; + attr.sample_regs_user = GetSupportedRegMask(GetDeviceArch()); + attr.sample_stack_user = dwarfSampleStackSize_; +} + bool PerfEvents::AddEvent(perf_type_id type, __u64 config, bool excludeUser, bool excludeKernel, bool followGroup) { @@ -482,16 +501,14 @@ bool PerfEvents::AddEvent(perf_type_id type, __u64 config, bool excludeUser, boo eventItem.attr.mmap_data = 1; } + eventItem.attr.sample_type = SAMPLE_TYPE; + eventItem.stackType = SampleStackType::NONE; if (sampleStackType_ == SampleStackType::DWARF) { - eventItem.attr.sample_type = SAMPLE_TYPE | PERF_SAMPLE_CALLCHAIN | - PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER; - eventItem.attr.exclude_callchain_user = 1; - eventItem.attr.sample_regs_user = GetSupportedRegMask(GetDeviceArch()); - eventItem.attr.sample_stack_user = dwarfSampleStackSize_; - } else if (sampleStackType_ == SampleStackType::FP) { - eventItem.attr.sample_type = SAMPLE_TYPE | PERF_SAMPLE_CALLCHAIN; - } else { - eventItem.attr.sample_type = SAMPLE_TYPE; + AddDwarfStackType(eventItem.attr); + eventItem.stackType = SampleStackType::DWARF; + } else if ((sampleStackType_ == SampleStackType::FP) || (sampleStackType_ == SampleStackType::AUTO)) { + AddFpStackType(eventItem.attr); + eventItem.stackType = SampleStackType::FP; } if (isHM_) { @@ -513,6 +530,27 @@ bool PerfEvents::AddEvent(perf_type_id type, __u64 config, bool excludeUser, boo excludeUser ? "excludeUser" : "", excludeKernel ? "excludeKernel" : "", followGroup ? "" : "group leader"); + if (sampleStackType_ == SampleStackType::AUTO) { + // always new item + EventItem &eventItemAuto = eventGroupItem.eventItems.emplace_back(); + eventItemAuto.typeName = GetTypeName(type); + eventItemAuto.configName = eventItem.configName; + if (type == PERF_TYPE_TRACEPOINT) { + eventItemAuto.configName = GetTraceConfigName(config); + } else { + eventItemAuto.configName = GetStaticConfigName(type, config); + } + if (memcpy_s(&eventItemAuto.attr, sizeof(perf_event_attr), &eventItem.attr, sizeof(perf_event_attr)) != EOK) { + HLOGE("memcpy_s failed in PerfEvents::AddEvent"); + return false; + } + AddDwarfStackType(eventItemAuto.attr); + eventItemAuto.stackType = SampleStackType::DWARF; + HLOGV("Add Event: '%s':'%s' %s %s %s", eventItem.typeName.c_str(), eventItem.configName.c_str(), + excludeUser ? "excludeUser" : "", excludeKernel ? "excludeKernel" : "", + followGroup ? "" : "group leader"); + } + return true; } @@ -566,6 +604,10 @@ bool PerfEvents::PrepareTracking(void) // 1. prepare cpu pid CHECK_TRUE(!PrepareFdEvents(), false, 1, "PrepareFdEvents() failed"); + if (sampleStackType_ == SampleStackType::AUTO) { + GetEventAttrMap(); + } + // 2. create events CHECK_TRUE(!CreateFdEvents(), false, 1, "CreateFdEvents() failed"); @@ -1019,6 +1061,20 @@ bool PerfEvents::PrepareFdEvents(void) return true; } +void PerfEvents::GetEventAttrMap() +{ + for (size_t ipid = 0; ipid < pids_.size(); ipid++) { + pid_t pid = pids_[ipid]; + std::string threadName = GetThreadName(pid); + if (std::find(mainPids_.begin(), mainPids_.end(), pid) != mainPids_.end() || + StringStartsWith(threadName, "OS_FFRT")) { + pidStackTypeMap_[pid] = SampleStackType::DWARF; + } else { + pidStackTypeMap_[pid] = SampleStackType::FP; + } + } +} + bool PerfEvents::CreateFdEvents(void) { // must be some events , or will failed @@ -1082,8 +1138,19 @@ bool PerfEvents::CreateFdEvents(void) // one fd event group must match same cpu and same pid config (event can be // different) // clang-format off - UniqueFd fd = Open(eventItem.attr, pids_[ipid], cpus_[icpu], - groupFdCache[icpu][ipid], 0); + UniqueFd fd; + if (sampleStackType_ == SampleStackType::AUTO) { + if (eventItem.stackType == pidStackTypeMap_[pids_[ipid]]) { + fd = Open(eventItem.attr, pids_[ipid], cpus_[icpu], + groupFdCache[icpu][ipid], 0); + } else { + HLOGE("pid %d not support this stacktype: %d.\n", pids_[ipid], eventItem.stackType); + continue; + } + } else { + fd = Open(eventItem.attr, pids_[ipid], cpus_[icpu], + groupFdCache[icpu][ipid], 0); + } // clang-format on if (fd < 0) { if (errno == ESRCH) { @@ -1115,7 +1182,9 @@ bool PerfEvents::CreateFdEvents(void) fdItem.cpu = cpus_[icpu]; fdItem.pid = pids_[ipid]; fdNumber++; - + if (sampleStackType_ == SampleStackType::AUTO) { + idAttrMap_[fdItem.GetPrefId()] = &eventItem.attr; + } // if sampling, mmap ring buffer bool createMmapSucc = true; if (recordCallBack_) { @@ -1562,6 +1631,16 @@ void PerfEvents::MoveRecordToBuf(MmapFd &mmap, bool &isAuxEvent, u64 &auxOffset, HLOGD("BUFFER_CRITICAL_LEVEL: lost sample record"); goto RETURN; } + if (sampleStackType_ == SampleStackType::AUTO) { + constexpr size_t idPos = sizeof(perf_event_header); + uint64_t sampleId = 0; + GetRecordFieldFromMmap(mmap, &sampleId, mmap.mmapPage->data_tail + idPos, sizeof(sampleId)); + if (idAttrMap_.find(sampleId) != idAttrMap_.end()) { + mmap.attr = idAttrMap_[sampleId]; + } else { + goto RETURN; + } + } if (CutStackAndMove(mmap)) { return; } @@ -1624,7 +1703,19 @@ void PerfEvents::ReadRecordFromBuf() const auto readingStartTime_ = steady_clock::now(); #endif #if !HIDEBUG_SKIP_CALLBACK - recordCallBack_(PerfEventRecordFactory::GetPerfEventRecord(*type, p, *attr)); + if (sampleStackType_ == SampleStackType::AUTO) { + constexpr size_t idPos = sizeof(perf_event_header); + uint64_t sampleId = 0; + if (memcpy_s(&sampleId, sizeof(sampleId), p + idPos, sizeof(sampleId)) != 0) { + HLOGE("memcpy_s %p to %" PRIu64 " failed. size %zd", p + idPos, sampleId, sizeof(sampleId)); + } + if (idAttrMap_.find(sampleId) != idAttrMap_.end()) { + const perf_event_attr *attrPtr = idAttrMap_[sampleId]; + recordCallBack_(PerfEventRecordFactory::GetPerfEventRecord(*type, p, *attrPtr)); + } + } else { + recordCallBack_(PerfEventRecordFactory::GetPerfEventRecord(*type, p, *attr)); + } #endif recordEventCount_++; #ifdef HIPERF_DEBUG_TIME @@ -1643,7 +1734,19 @@ void PerfEvents::ReadRecordFromBuf() const auto readingStartTime_ = steady_clock::now(); #endif #if !HIDEBUG_SKIP_CALLBACK - recordCallBack_(PerfEventRecordFactory::GetPerfEventRecord(*type, p, *attr)); + if (sampleStackType_ == SampleStackType::AUTO) { + constexpr size_t idPos = sizeof(perf_event_header); + uint64_t sampleId = 0; + if (memcpy_s(&sampleId, sizeof(sampleId), p + idPos, sizeof(sampleId)) != 0) { + HLOGE("memcpy_s %p to %" PRIu64 " failed. size %zd", p + idPos, sampleId, sizeof(sampleId)); + } + if (idAttrMap_.find(sampleId) != idAttrMap_.end()) { + const perf_event_attr *attrPtr = idAttrMap_[sampleId]; + recordCallBack_(PerfEventRecordFactory::GetPerfEventRecord(*type, p, *attrPtr)); + } + } else { + recordCallBack_(PerfEventRecordFactory::GetPerfEventRecord(*type, p, *attr)); + } #endif recordEventCount_++; #ifdef HIPERF_DEBUG_TIME diff --git a/src/perf_file_reader.cpp b/src/perf_file_reader.cpp index a053ee2..9f97133 100644 --- a/src/perf_file_reader.cpp +++ b/src/perf_file_reader.cpp @@ -237,6 +237,13 @@ const perf_event_attr *PerfFileReader::GetDefaultAttr() return &(vecAttr_[0].attr); } +const perf_event_attr *PerfFileReader::GetAttrById(uint64_t sampleId) +{ + uint64_t attrIndex = mapId2Attr_[sampleId]; + CHECK_TRUE(vecAttr_.size() < attrIndex + 1, nullptr, 0, ""); + return &(vecAttr_[attrIndex].attr); +} + bool PerfFileReader::ReadRecord(ProcessRecordCB &callback) { #ifdef HIPERF_DEBUG_TIME @@ -277,6 +284,15 @@ bool PerfFileReader::ReadRecord(ProcessRecordCB &callback) Read(buf + header->size, auxtrace->size); } } + if (header->type == PERF_RECORD_SAMPLE) { + constexpr size_t idPos = sizeof(perf_event_header); + uint64_t sampleId = 0; + if (memcpy_s(&sampleId, sizeof(sampleId), buf + idPos, sizeof(sampleId)) != 0) { + HLOGE("memcpy_s %p to %" PRIu64 " failed. size: %zd", buf + idPos, sampleId, sizeof(sampleId)); + } + attr = GetAttrById(sampleId); + CHECK_TRUE(attr == nullptr, false, 1, "attr is null"); + } uint8_t *data = buf; PerfEventRecord& record = PerfEventRecordFactory::GetPerfEventRecord( static_cast(header->type), data, *attr); diff --git a/src/subcommand_record.cpp b/src/subcommand_record.cpp index 773d2b6..03e4bf1 100644 --- a/src/subcommand_record.cpp +++ b/src/subcommand_record.cpp @@ -519,6 +519,9 @@ bool SubCommandRecord::CheckTargetProcessOptions() if (targetSystemWide_) { hasTarget = true; } + if (stackType_ == PerfEvents::SampleStackType::AUTO) { + CHECK_TRUE(hasTarget, false, LOG_TYPE_PRINTF, "auto options conflict with -a, please check usage\n"); + } if (!selectPids_.empty() || !selectTids_.empty()) { CHECK_TRUE(hasTarget, false, LOG_TYPE_PRINTF, "-p/-t %s options conflict, please check usage\n", VectorToString(selectPids_).c_str()); @@ -636,8 +639,8 @@ bool SubCommandRecord::ParseCallStackOption(const std::vector &call printf("Invalid -s value %s.\n", VectorToString(callStackType).c_str()); return false; } - isCallStackFp_ = true; - } else if (callStackType[0] == "dwarf") { + stackType_ = PerfEvents::SampleStackType::FP; + } else if (callStackType[0] == "dwarf" || callStackType[0] == "auto") { if (callStackType.size() > MAX_DWARF_CALL_CHAIN) { printf("Invalid -s value %s.\n", VectorToString(callStackType).c_str()); return false; @@ -665,7 +668,12 @@ bool SubCommandRecord::ParseCallStackOption(const std::vector &call return false; } } - isCallStackDwarf_ = true; + if (callStackType[0] == "auto") { + stackType_ = PerfEvents::SampleStackType::AUTO; + perfEvents_.SetMainPids(selectPids_); + } else { + stackType_ = PerfEvents::SampleStackType::DWARF; + } SymbolsFile::needParseJsFunc_ = true; // only in record and dwarf mode need to parse } else { printf("Invalid -s value '%s'.\n", callStackType.at(0).c_str()); @@ -834,12 +842,13 @@ bool SubCommandRecord::PreparePerfEvent() perfEvents_.SetTimeOut(timeStopSec_); perfEvents_.SetVerboseReport(verboseReport_); perfEvents_.SetMmapPages(mmapPages_); - if (isCallStackFp_) { - perfEvents_.SetSampleStackType(PerfEvents::SampleStackType::FP); - } else if (isCallStackDwarf_) { - perfEvents_.SetSampleStackType(PerfEvents::SampleStackType::DWARF); + if (stackType_ == PerfEvents::SampleStackType::DWARF || stackType_ == PerfEvents::SampleStackType::AUTO) { + perfEvents_.SetSampleStackType(stackType_); perfEvents_.SetDwarfSampleStackSize(callStackDwarfSize_); } + if (stackType_ == PerfEvents::SampleStackType::FP) { + perfEvents_.SetSampleStackType(stackType_); + } if (!perfEvents_.SetBranchSampleType(branchSampleType_)) { printf("branch sample %s is not supported\n", VectorToString(vecBranchFilters_).c_str()); HLOGE("Fail to SetBranchSampleType %" PRIx64 "", branchSampleType_); diff --git a/src/subcommand_report.cpp b/src/subcommand_report.cpp index 5ae0ef8..a3db109 100644 --- a/src/subcommand_report.cpp +++ b/src/subcommand_report.cpp @@ -365,8 +365,9 @@ void SubCommandReport::LoadEventDesc() const PerfFileSectionEventDesc §ionEventdesc = *static_cast(featureSection); HLOGV("Event descriptions: %zu", sectionEventdesc.eventDesces_.size()); - for (size_t i = 0; i < sectionEventdesc.eventDesces_.size(); i++) { - const AttrWithId &fileAttr = sectionEventdesc.eventDesces_[i]; + std::vector attrIds = MergeAutoAttr(sectionEventdesc.eventDesces_); + for (size_t i = 0; i < attrIds.size(); i++) { + const AttrWithId &fileAttr = attrIds[i]; HLOGV("event name[%zu]: %s ids: %s", i, fileAttr.name.c_str(), VectorToString(fileAttr.ids).c_str()); @@ -399,9 +400,29 @@ void SubCommandReport::LoadEventDesc() } } +std::vector SubCommandReport::MergeAutoAttr(const std::vector &attrIds) +{ + std::unordered_map> nameIdsMap; + std::unordered_map nameAttrMap; + std::vector result; + for (const auto &fileAttr: attrIds) { + nameIdsMap[fileAttr.name].insert(nameIdsMap[fileAttr.name].end(), fileAttr.ids.begin(), fileAttr.ids.end()); + } + + for (const auto &fileAttr: attrIds) { + if (nameAttrMap.find(fileAttr.name) == nameAttrMap.end()) { + nameAttrMap[fileAttr.name] = fileAttr; + nameAttrMap[fileAttr.name].ids = nameIdsMap[fileAttr.name]; + result.emplace_back(nameAttrMap[fileAttr.name]); + } + } + + return result; +} + void SubCommandReport::LoadAttrSection() { - std::vector attrIds = recordFileReader_->GetAttrSection(); + std::vector attrIds = MergeAutoAttr(recordFileReader_->GetAttrSection()); for (size_t i = 0; i < attrIds.size(); ++i) { const AttrWithId &fileAttr = attrIds[i]; std::string name = PerfEvents::GetStaticConfigName( diff --git a/src/utilities.cpp b/src/utilities.cpp index 197f513..815580a 100644 --- a/src/utilities.cpp +++ b/src/utilities.cpp @@ -777,6 +777,15 @@ std::string GetProcessName(int pid) #endif } +std::string GetThreadName(pid_t tid) +{ + std::string comm = ""; + comm = ReadFileToString(StringPrintf("/proc/%d/comm", tid)).c_str(); + comm.erase(std::remove(comm.begin(), comm.end(), '\r'), comm.end()); + comm.erase(std::remove(comm.begin(), comm.end(), '\n'), comm.end()); + return comm; +} + bool NeedAdaptSandboxPath(char *filename, int pid, u16 &headerSize) { std::string oldFilename = filename; diff --git a/test/unittest/common/native/subcommand_record_test.cpp b/test/unittest/common/native/subcommand_record_test.cpp index e1ecfe4..6a59a51 100644 --- a/test/unittest/common/native/subcommand_record_test.cpp +++ b/test/unittest/common/native/subcommand_record_test.cpp @@ -749,6 +749,38 @@ HWTEST_F(SubCommandRecordTest, CallStackDwarfSizeInputMoreErr, TestSize.Level1) TestRecordCommand("-d 2 -s dwarf,16,32 ", false); } +HWTEST_F(SubCommandRecordTest, CallStackAuto, TestSize.Level1) +{ + ForkAndRunTest("-d 2 --call-stack auto "); + TearDown(); + SetUp(); + ForkAndRunTest("-d 2 -s auto "); +} + +HWTEST_F(SubCommandRecordTest, CallStackAutoSizeMin, TestSize.Level1) +{ + ForkAndRunTest("-d 2 --call-stack auto,8 "); + TearDown(); + SetUp(); + ForkAndRunTest("-d 2 -s auto,8 "); +} + +HWTEST_F(SubCommandRecordTest, CallStackAutoSizeMaxErr, TestSize.Level1) +{ + TestRecordCommand("-d 2 --call-stack auto,65529 ", false); + TearDown(); + SetUp(); + TestRecordCommand("-d 2 -s auto,65529 ", false); +} + +HWTEST_F(SubCommandRecordTest, CallStackAutoConflict, TestSize.Level1) +{ + TestRecordCommand("-d 2 --call-stack auto -a ", false, false); + TearDown(); + SetUp(); + TestRecordCommand("-d 2 -s auto -a ", false, false); +} + HWTEST_F(SubCommandRecordTest, CallStackUsageErr, TestSize.Level1) { TestRecordCommand("-d 2 -s abc --call-stack bcd", false); -- Gitee