summaryrefslogtreecommitdiff
path: root/simpleperf
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2024-02-26 11:33:07 -0800
committerYabin Cui <yabinc@google.com>2024-02-27 13:27:52 -0800
commitcd5d5348b183751cd736b20cac92a4de613bf3a2 (patch)
tree0ff4b45aa32b8b6642269029d9f0b44a93bf519b /simpleperf
parent57a99140e0d61aa262c95cbe9f0cf15b0ce1e28c (diff)
downloadextras-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.cpp9
-rw-r--r--simpleperf/RegEx.cpp9
-rw-r--r--simpleperf/RegEx.h3
-rw-r--r--simpleperf/report_lib_interface.cpp56
-rw-r--r--simpleperf/report_utils.cpp38
-rw-r--r--simpleperf/report_utils.h3
-rw-r--r--simpleperf/report_utils_test.cpp32
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 &current_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 &current_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() {