diff options
author | Yabin Cui <yabinc@google.com> | 2024-02-26 11:33:07 -0800 |
---|---|---|
committer | Yabin Cui <yabinc@google.com> | 2024-02-27 13:27:52 -0800 |
commit | cd5d5348b183751cd736b20cac92a4de613bf3a2 (patch) | |
tree | 0ff4b45aa32b8b6642269029d9f0b44a93bf519b /simpleperf | |
parent | 57a99140e0d61aa262c95cbe9f0cf15b0ce1e28c (diff) | |
download | extras-cd5d5348b183751cd736b20cac92a4de613bf3a2.tar.gz |
simpleperf: support removing methods based on method name regex
In CallChainReportBuilder, support removing methods with name
containing the given regular expression. Export this function
in report_lib_interface.cpp.
Bug: 325429554
Test: run simpleperf_unit_test
Change-Id: I2b3cf4b536d44f865628de3ff2ab5db94b382949
Diffstat (limited to 'simpleperf')
-rw-r--r-- | simpleperf/RecordFilter.cpp | 9 | ||||
-rw-r--r-- | simpleperf/RegEx.cpp | 9 | ||||
-rw-r--r-- | simpleperf/RegEx.h | 3 | ||||
-rw-r--r-- | simpleperf/report_lib_interface.cpp | 56 | ||||
-rw-r--r-- | simpleperf/report_utils.cpp | 38 | ||||
-rw-r--r-- | simpleperf/report_utils.h | 3 | ||||
-rw-r--r-- | simpleperf/report_utils_test.cpp | 32 |
7 files changed, 124 insertions, 26 deletions
diff --git a/simpleperf/RecordFilter.cpp b/simpleperf/RecordFilter.cpp index 1c8f44e9..d6d25154 100644 --- a/simpleperf/RecordFilter.cpp +++ b/simpleperf/RecordFilter.cpp @@ -79,15 +79,6 @@ class TidFilter : public RecordFilterCondition { std::set<pid_t> exclude_tids_; }; -static bool SearchInRegs(std::string_view s, const std::vector<std::unique_ptr<RegEx>>& regs) { - for (auto& reg : regs) { - if (reg->Search(s)) { - return true; - } - } - return false; -} - class ProcessNameFilter : public RecordFilterCondition { public: ProcessNameFilter(const ThreadTree& thread_tree) : thread_tree_(thread_tree) {} diff --git a/simpleperf/RegEx.cpp b/simpleperf/RegEx.cpp index 97bb45bb..6de8440a 100644 --- a/simpleperf/RegEx.cpp +++ b/simpleperf/RegEx.cpp @@ -77,4 +77,13 @@ std::unique_ptr<RegEx> RegEx::Create(std::string_view pattern) { } } +bool SearchInRegs(std::string_view s, const std::vector<std::unique_ptr<RegEx>>& regs) { + for (auto& reg : regs) { + if (reg->Search(s)) { + return true; + } + } + return false; +} + } // namespace simpleperf diff --git a/simpleperf/RegEx.h b/simpleperf/RegEx.h index c1e4f511..6e75afff 100644 --- a/simpleperf/RegEx.h +++ b/simpleperf/RegEx.h @@ -20,6 +20,7 @@ #include <optional> #include <string> #include <string_view> +#include <vector> namespace simpleperf { @@ -51,4 +52,6 @@ class RegEx { std::string pattern_; }; +bool SearchInRegs(std::string_view s, const std::vector<std::unique_ptr<RegEx>>& regs); + } // namespace simpleperf diff --git a/simpleperf/report_lib_interface.cpp b/simpleperf/report_lib_interface.cpp index 09a39f0a..e59dd3a3 100644 --- a/simpleperf/report_lib_interface.cpp +++ b/simpleperf/report_lib_interface.cpp @@ -193,6 +193,9 @@ class ReportLib { bool remove_art_frame = !show; callchain_report_builder_.SetRemoveArtFrame(remove_art_frame); } + bool RemoveMethod(const char* method_name_regex) { + return callchain_report_builder_.RemoveMethod(method_name_regex); + } void MergeJavaMethods(bool merge) { callchain_report_builder_.SetConvertJITFrame(merge); } bool AddProguardMappingFile(const char* mapping_file) { return callchain_report_builder_.AddProguardMappingFile(mapping_file); @@ -212,11 +215,12 @@ class ReportLib { FeatureSection* GetFeatureSection(const char* feature_name); private: + std::unique_ptr<SampleRecord> GetNextSampleRecord(); void ProcessSampleRecord(std::unique_ptr<Record> r); void ProcessSwitchRecord(std::unique_ptr<Record> r); void AddSampleRecordToQueue(SampleRecord* r); - void SetCurrentSample(const SampleRecord& r); - const EventInfo* FindEventOfCurrentSample(); + bool SetCurrentSample(std::unique_ptr<SampleRecord> sample_record); + const EventInfo& FindEvent(const SampleRecord& r); void CreateEvents(); bool OpenRecordFileIfNecessary(); @@ -357,9 +361,20 @@ Sample* ReportLib::GetNextSample() { if (!OpenRecordFileIfNecessary()) { return nullptr; } - if (!sample_record_queue_.empty()) { - sample_record_queue_.pop(); + + while (true) { + std::unique_ptr<SampleRecord> r = GetNextSampleRecord(); + if (!r) { + break; + } + if (SetCurrentSample(std::move(r))) { + return ¤t_sample_; + } } + return nullptr; +} + +std::unique_ptr<SampleRecord> ReportLib::GetNextSampleRecord() { while (sample_record_queue_.empty()) { std::unique_ptr<Record> record; if (!record_file_reader_->ReadRecord(record) || record == nullptr) { @@ -380,8 +395,9 @@ Sample* ReportLib::GetNextSample() { } } } - SetCurrentSample(*sample_record_queue_.front()); - return ¤t_sample_; + std::unique_ptr<SampleRecord> result = std::move(sample_record_queue_.front()); + sample_record_queue_.pop(); + return result; } void ReportLib::ProcessSampleRecord(std::unique_ptr<Record> r) { @@ -453,7 +469,8 @@ void ReportLib::AddSampleRecordToQueue(SampleRecord* r) { } } -void ReportLib::SetCurrentSample(const SampleRecord& r) { +bool ReportLib::SetCurrentSample(std::unique_ptr<SampleRecord> sample_record) { + const SampleRecord& r = *sample_record; current_mappings_.clear(); callchain_entries_.clear(); current_sample_.ip = r.ip_data.ip; @@ -471,6 +488,10 @@ void ReportLib::SetCurrentSample(const SampleRecord& r) { std::vector<uint64_t> ips = r.GetCallChain(&kernel_ip_count); std::vector<CallChainReportEntry> report_entries = callchain_report_builder_.Build(current_thread_, ips, kernel_ip_count); + if (report_entries.empty()) { + // Skip samples with callchain fully removed by RemoveMethod(). + return false; + } for (const auto& report_entry : report_entries) { callchain_entries_.resize(callchain_entries_.size() + 1); @@ -491,29 +512,29 @@ void ReportLib::SetCurrentSample(const SampleRecord& r) { current_symbol_ = &(callchain_entries_[0].symbol); current_callchain_.nr = callchain_entries_.size() - 1; current_callchain_.entries = &callchain_entries_[1]; - const EventInfo* event = FindEventOfCurrentSample(); - current_event_.name = event->name.c_str(); - current_event_.tracing_data_format = event->tracing_info.data_format; + const EventInfo& event = FindEvent(r); + current_event_.name = event.name.c_str(); + current_event_.tracing_data_format = event.tracing_info.data_format; if (current_event_.tracing_data_format.size > 0u && (r.sample_type & PERF_SAMPLE_RAW)) { CHECK_GE(r.raw_data.size, current_event_.tracing_data_format.size); current_tracing_data_ = r.raw_data.data; } else { current_tracing_data_ = nullptr; } + return true; } -const EventInfo* ReportLib::FindEventOfCurrentSample() { +const EventInfo& ReportLib::FindEvent(const SampleRecord& r) { if (events_.empty()) { CreateEvents(); } if (trace_offcpu_.mode == TraceOffCpuMode::MIXED_ON_OFF_CPU) { // To mix on-cpu and off-cpu samples, pretend they are from the same event type. // Otherwise, some report scripts may split them. - return &events_[0]; + return events_[0]; } - SampleRecord* r = sample_record_queue_.front().get(); - size_t attr_index = record_file_reader_->GetAttrIndexOfRecord(r); - return &events_[attr_index]; + size_t attr_index = record_file_reader_->GetAttrIndexOfRecord(&r); + return events_[attr_index]; } void ReportLib::CreateEvents() { @@ -611,6 +632,7 @@ bool SetRecordFile(ReportLib* report_lib, const char* record_file) EXPORT; bool SetKallsymsFile(ReportLib* report_lib, const char* kallsyms_file) EXPORT; void ShowIpForUnknownSymbol(ReportLib* report_lib) EXPORT; void ShowArtFrames(ReportLib* report_lib, bool show) EXPORT; +bool RemoveMethod(ReportLib* report_lib, const char* method_name_regex) EXPORT; void MergeJavaMethods(ReportLib* report_lib, bool merge) EXPORT; bool AddProguardMappingFile(ReportLib* report_lib, const char* mapping_file) EXPORT; const char* GetSupportedTraceOffCpuModes(ReportLib* report_lib) EXPORT; @@ -658,6 +680,10 @@ void ShowArtFrames(ReportLib* report_lib, bool show) { return report_lib->ShowArtFrames(show); } +bool RemoveMethod(ReportLib* report_lib, const char* method_name_regex) { + return report_lib->RemoveMethod(method_name_regex); +} + void MergeJavaMethods(ReportLib* report_lib, bool merge) { return report_lib->MergeJavaMethods(merge); } diff --git a/simpleperf/report_utils.cpp b/simpleperf/report_utils.cpp index 5478d3dd..5c3ce6f9 100644 --- a/simpleperf/report_utils.cpp +++ b/simpleperf/report_utils.cpp @@ -23,6 +23,7 @@ #include <android-base/strings.h> #include "JITDebugReader.h" +#include "RegEx.h" #include "utils.h" namespace simpleperf { @@ -320,6 +321,29 @@ class JavaMethodDeobfuscater : public CallChainReportModifier { ProguardMappingRetrace retrace_; }; +// Use regex to filter method names. +class MethodNameFilter : public CallChainReportModifier { + public: + bool RemoveMethod(std::string_view method_name_regex) { + if (auto regex = RegEx::Create(method_name_regex); regex != nullptr) { + exclude_names_.emplace_back(std::move(regex)); + return true; + } + return false; + } + + void Modify(std::vector<CallChainReportEntry>& callchain) override { + auto it = std::remove_if(callchain.begin(), callchain.end(), + [this](const CallChainReportEntry& entry) { + return SearchInRegs(entry.symbol->DemangledName(), exclude_names_); + }); + callchain.erase(it, callchain.end()); + } + + private: + std::vector<std::unique_ptr<RegEx>> exclude_names_; +}; + CallChainReportBuilder::CallChainReportBuilder(ThreadTree& thread_tree) : thread_tree_(thread_tree) { const char* env_name = "REMOVE_R8_SYNTHESIZED_FRAME"; @@ -357,8 +381,15 @@ bool CallChainReportBuilder::AddProguardMappingFile(std::string_view mapping_fil if (!java_method_deobfuscater_) { java_method_deobfuscater_.reset(new JavaMethodDeobfuscater(remove_r8_synthesized_frame_)); } - return static_cast<JavaMethodDeobfuscater*>(java_method_deobfuscater_.get()) - ->AddProguardMappingFile(mapping_file); + return static_cast<JavaMethodDeobfuscater&>(*java_method_deobfuscater_) + .AddProguardMappingFile(mapping_file); +} + +bool CallChainReportBuilder::RemoveMethod(std::string_view method_name_regex) { + if (!method_name_filter_) { + method_name_filter_.reset(new MethodNameFilter); + } + return static_cast<MethodNameFilter&>(*method_name_filter_).RemoveMethod(method_name_regex); } std::vector<CallChainReportEntry> CallChainReportBuilder::Build(const ThreadEntry* thread, @@ -398,6 +429,9 @@ std::vector<CallChainReportEntry> CallChainReportBuilder::Build(const ThreadEntr if (java_method_deobfuscater_) { java_method_deobfuscater_->Modify(result); } + if (method_name_filter_) { + method_name_filter_->Modify(result); + } return result; } diff --git a/simpleperf/report_utils.h b/simpleperf/report_utils.h index 78c037df..631e5fad 100644 --- a/simpleperf/report_utils.h +++ b/simpleperf/report_utils.h @@ -110,6 +110,8 @@ class CallChainReportBuilder { void SetConvertJITFrame(bool enable); // Add proguard mapping.txt to de-obfuscate minified symbols. bool AddProguardMappingFile(std::string_view mapping_file); + // Remove methods with name containing the given regular expression. + bool RemoveMethod(std::string_view method_name_regex); std::vector<CallChainReportEntry> Build(const ThreadEntry* thread, const std::vector<uint64_t>& ips, size_t kernel_ip_count); @@ -121,6 +123,7 @@ class CallChainReportBuilder { std::unique_ptr<CallChainReportModifier> art_frame_remover_; std::unique_ptr<CallChainReportModifier> jit_frame_converter_; std::unique_ptr<CallChainReportModifier> java_method_deobfuscater_; + std::unique_ptr<CallChainReportModifier> method_name_filter_; }; struct ThreadReport { diff --git a/simpleperf/report_utils_test.cpp b/simpleperf/report_utils_test.cpp index 55771218..ad4b9df3 100644 --- a/simpleperf/report_utils_test.cpp +++ b/simpleperf/report_utils_test.cpp @@ -540,6 +540,38 @@ TEST_F(CallChainReportBuilderTest, convert_jit_frame_for_jit_method_with_signatu ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD); } +TEST_F(CallChainReportBuilderTest, remove_method_name) { + // Test excluding method names. + CallChainReportBuilder builder(thread_tree); + builder.SetRemoveArtFrame(false); + builder.RemoveMethod("art_"); + std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0); + ASSERT_EQ(entries.size(), 2); + ASSERT_EQ(entries[0].ip, 0x2000); + ASSERT_STREQ(entries[0].symbol->Name(), "java_method1"); + ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path); + ASSERT_EQ(entries[0].vaddr_in_file, 0); + ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD); + ASSERT_EQ(entries[1].ip, 0x3000); + ASSERT_STREQ(entries[1].symbol->Name(), "java_method2"); + ASSERT_EQ(entries[1].dso->Path(), fake_dex_file_path); + ASSERT_EQ(entries[1].vaddr_in_file, 0x100); + ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD); + + builder.RemoveMethod("java_method2"); + entries = builder.Build(thread, fake_ips, 0); + ASSERT_EQ(entries.size(), 1); + ASSERT_EQ(entries[0].ip, 0x2000); + ASSERT_STREQ(entries[0].symbol->Name(), "java_method1"); + ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path); + ASSERT_EQ(entries[0].vaddr_in_file, 0); + ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD); + + builder.RemoveMethod("java_method1"); + entries = builder.Build(thread, fake_ips, 0); + ASSERT_EQ(entries.size(), 0); +} + class ThreadReportBuilderTest : public testing::Test { protected: virtual void SetUp() { |