summaryrefslogtreecommitdiff
path: root/simpleperf
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2024-02-28 19:56:49 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2024-02-28 19:56:49 +0000
commite48ae55565ce54973ab488496ea78e4ad38363ee (patch)
treeaf57c9c6c362c36188131564222717ee3f29839e /simpleperf
parente3ff5614104ce2717da117580bad1d743c542add (diff)
parentcd5d5348b183751cd736b20cac92a4de613bf3a2 (diff)
downloadextras-e48ae55565ce54973ab488496ea78e4ad38363ee.tar.gz
Merge changes I2b3cf4b5,I6a8a5b40 into main
* changes: simpleperf: support removing methods based on method name regex simpleperf: refactor CallChainReportBuilder
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.cpp290
-rw-r--r--simpleperf/report_utils.h32
-rw-r--r--simpleperf/report_utils_test.cpp32
7 files changed, 294 insertions, 137 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 ecbc6aa8..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 {
@@ -189,6 +190,160 @@ static bool IsArtEntry(const CallChainReportEntry& entry, bool* is_jni_trampolin
return false;
};
+CallChainReportModifier::~CallChainReportModifier() {}
+
+// Remove art frames.
+class ArtFrameRemover : public CallChainReportModifier {
+ public:
+ void Modify(std::vector<CallChainReportEntry>& callchain) override {
+ auto it =
+ std::remove_if(callchain.begin(), callchain.end(), [](const CallChainReportEntry& entry) {
+ return entry.execution_type == CallChainExecutionType::ART_METHOD;
+ });
+ callchain.erase(it, callchain.end());
+ }
+};
+
+// Convert JIT methods to their corresponding interpreted Java methods.
+class JITFrameConverter : public CallChainReportModifier {
+ public:
+ JITFrameConverter(const ThreadTree& thread_tree) : thread_tree_(thread_tree) {}
+
+ void Modify(std::vector<CallChainReportEntry>& callchain) override {
+ CollectJavaMethods();
+ for (size_t i = 0; i < callchain.size();) {
+ auto& entry = callchain[i];
+ if (entry.execution_type == CallChainExecutionType::JIT_JVM_METHOD) {
+ // This is a JIT java method, merge it with the interpreted java method having the same
+ // name if possible. Otherwise, merge it with other JIT java methods having the same name
+ // by assigning a common dso_name.
+ if (auto it = java_method_map_.find(std::string(entry.symbol->FunctionName()));
+ it != java_method_map_.end()) {
+ entry.dso = it->second.dso;
+ entry.symbol = it->second.symbol;
+ // Not enough info to map an offset in a JIT method to an offset in a dex file. So just
+ // use the symbol_addr.
+ entry.vaddr_in_file = entry.symbol->addr;
+
+ // ART may call from an interpreted Java method into its corresponding JIT method. To
+ // avoid showing the method calling itself, remove the JIT frame.
+ if (i + 1 < callchain.size() && callchain[i + 1].dso == entry.dso &&
+ callchain[i + 1].symbol == entry.symbol) {
+ callchain.erase(callchain.begin() + i);
+ continue;
+ }
+
+ } else if (!JITDebugReader::IsPathInJITSymFile(entry.dso->Path())) {
+ // Old JITSymFiles use names like "TemporaryFile-XXXXXX". So give them a better name.
+ entry.dso_name = "[JIT cache]";
+ }
+ }
+ i++;
+ }
+ }
+
+ private:
+ struct JavaMethod {
+ Dso* dso;
+ const Symbol* symbol;
+ JavaMethod(Dso* dso, const Symbol* symbol) : dso(dso), symbol(symbol) {}
+ };
+
+ void CollectJavaMethods() {
+ if (!java_method_initialized_) {
+ java_method_initialized_ = true;
+ for (Dso* dso : thread_tree_.GetAllDsos()) {
+ if (dso->type() == DSO_DEX_FILE) {
+ dso->LoadSymbols();
+ for (auto& symbol : dso->GetSymbols()) {
+ java_method_map_.emplace(symbol.Name(), JavaMethod(dso, &symbol));
+ }
+ }
+ }
+ }
+ }
+
+ const ThreadTree& thread_tree_;
+ bool java_method_initialized_ = false;
+ std::unordered_map<std::string, JavaMethod> java_method_map_;
+};
+
+// Use proguard mapping.txt to de-obfuscate minified symbols.
+class JavaMethodDeobfuscater : public CallChainReportModifier {
+ public:
+ JavaMethodDeobfuscater(bool remove_r8_synthesized_frame)
+ : remove_r8_synthesized_frame_(remove_r8_synthesized_frame) {}
+
+ bool AddProguardMappingFile(std::string_view mapping_file) {
+ return retrace_.AddProguardMappingFile(mapping_file);
+ }
+
+ void Modify(std::vector<CallChainReportEntry>& callchain) override {
+ for (size_t i = 0; i < callchain.size();) {
+ auto& entry = callchain[i];
+ if (!IsJavaEntry(entry)) {
+ i++;
+ continue;
+ }
+ std::string_view name = entry.symbol->FunctionName();
+ std::string original_name;
+ bool synthesized;
+ if (retrace_.DeObfuscateJavaMethods(name, &original_name, &synthesized)) {
+ if (synthesized && remove_r8_synthesized_frame_) {
+ callchain.erase(callchain.begin() + i);
+ continue;
+ }
+ entry.symbol->SetDemangledName(original_name);
+ }
+ i++;
+ }
+ }
+
+ private:
+ bool IsJavaEntry(const CallChainReportEntry& entry) {
+ static const char* COMPILED_JAVA_FILE_SUFFIXES[] = {".odex", ".oat", ".dex"};
+ if (entry.execution_type == CallChainExecutionType::JIT_JVM_METHOD ||
+ entry.execution_type == CallChainExecutionType::INTERPRETED_JVM_METHOD) {
+ return true;
+ }
+ if (entry.execution_type == CallChainExecutionType::NATIVE_METHOD) {
+ const std::string& path = entry.dso->Path();
+ for (const char* suffix : COMPILED_JAVA_FILE_SUFFIXES) {
+ if (android::base::EndsWith(path, suffix)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ const bool remove_r8_synthesized_frame_;
+ 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";
@@ -202,13 +357,39 @@ CallChainReportBuilder::CallChainReportBuilder(ThreadTree& thread_tree)
remove_r8_synthesized_frame_ = true;
}
}
+ SetRemoveArtFrame(true);
+ SetConvertJITFrame(true);
+}
+
+void CallChainReportBuilder::SetRemoveArtFrame(bool enable) {
+ if (enable) {
+ art_frame_remover_.reset(new ArtFrameRemover);
+ } else {
+ art_frame_remover_.reset(nullptr);
+ }
+}
+
+void CallChainReportBuilder::SetConvertJITFrame(bool enable) {
+ if (enable) {
+ jit_frame_converter_.reset(new JITFrameConverter(thread_tree_));
+ } else {
+ jit_frame_converter_.reset(nullptr);
+ }
}
bool CallChainReportBuilder::AddProguardMappingFile(std::string_view mapping_file) {
- if (!retrace_) {
- retrace_.reset(new ProguardMappingRetrace);
+ if (!java_method_deobfuscater_) {
+ java_method_deobfuscater_.reset(new JavaMethodDeobfuscater(remove_r8_synthesized_frame_));
+ }
+ 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 retrace_->AddProguardMappingFile(mapping_file);
+ return static_cast<MethodNameFilter&>(*method_name_filter_).RemoveMethod(method_name_regex);
}
std::vector<CallChainReportEntry> CallChainReportBuilder::Build(const ThreadEntry* thread,
@@ -239,17 +420,17 @@ std::vector<CallChainReportEntry> CallChainReportBuilder::Build(const ThreadEntr
entry.execution_type = execution_type;
}
MarkArtFrame(result);
- if (remove_art_frame_) {
- auto it = std::remove_if(result.begin(), result.end(), [](const CallChainReportEntry& entry) {
- return entry.execution_type == CallChainExecutionType::ART_METHOD;
- });
- result.erase(it, result.end());
+ if (art_frame_remover_) {
+ art_frame_remover_->Modify(result);
+ }
+ if (jit_frame_converter_) {
+ jit_frame_converter_->Modify(result);
}
- if (convert_jit_frame_) {
- ConvertJITFrame(result);
+ if (java_method_deobfuscater_) {
+ java_method_deobfuscater_->Modify(result);
}
- if (retrace_) {
- DeObfuscateJavaMethods(result);
+ if (method_name_filter_) {
+ method_name_filter_->Modify(result);
}
return result;
}
@@ -292,91 +473,6 @@ void CallChainReportBuilder::MarkArtFrame(std::vector<CallChainReportEntry>& cal
}
}
-void CallChainReportBuilder::ConvertJITFrame(std::vector<CallChainReportEntry>& callchain) {
- CollectJavaMethods();
- for (size_t i = 0; i < callchain.size();) {
- auto& entry = callchain[i];
- if (entry.execution_type == CallChainExecutionType::JIT_JVM_METHOD) {
- // This is a JIT java method, merge it with the interpreted java method having the same
- // name if possible. Otherwise, merge it with other JIT java methods having the same name
- // by assigning a common dso_name.
- if (auto it = java_method_map_.find(std::string(entry.symbol->FunctionName()));
- it != java_method_map_.end()) {
- entry.dso = it->second.dso;
- entry.symbol = it->second.symbol;
- // Not enough info to map an offset in a JIT method to an offset in a dex file. So just
- // use the symbol_addr.
- entry.vaddr_in_file = entry.symbol->addr;
-
- // ART may call from an interpreted Java method into its corresponding JIT method. To
- // avoid showing the method calling itself, remove the JIT frame.
- if (i + 1 < callchain.size() && callchain[i + 1].dso == entry.dso &&
- callchain[i + 1].symbol == entry.symbol) {
- callchain.erase(callchain.begin() + i);
- continue;
- }
-
- } else if (!JITDebugReader::IsPathInJITSymFile(entry.dso->Path())) {
- // Old JITSymFiles use names like "TemporaryFile-XXXXXX". So give them a better name.
- entry.dso_name = "[JIT cache]";
- }
- }
- i++;
- }
-}
-
-void CallChainReportBuilder::CollectJavaMethods() {
- if (!java_method_initialized_) {
- java_method_initialized_ = true;
- for (Dso* dso : thread_tree_.GetAllDsos()) {
- if (dso->type() == DSO_DEX_FILE) {
- dso->LoadSymbols();
- for (auto& symbol : dso->GetSymbols()) {
- java_method_map_.emplace(symbol.Name(), JavaMethod(dso, &symbol));
- }
- }
- }
- }
-}
-
-static bool IsJavaEntry(const CallChainReportEntry& entry) {
- static const char* COMPILED_JAVA_FILE_SUFFIXES[] = {".odex", ".oat", ".dex"};
- if (entry.execution_type == CallChainExecutionType::JIT_JVM_METHOD ||
- entry.execution_type == CallChainExecutionType::INTERPRETED_JVM_METHOD) {
- return true;
- }
- if (entry.execution_type == CallChainExecutionType::NATIVE_METHOD) {
- const std::string& path = entry.dso->Path();
- for (const char* suffix : COMPILED_JAVA_FILE_SUFFIXES) {
- if (android::base::EndsWith(path, suffix)) {
- return true;
- }
- }
- }
- return false;
-}
-
-void CallChainReportBuilder::DeObfuscateJavaMethods(std::vector<CallChainReportEntry>& callchain) {
- for (size_t i = 0; i < callchain.size();) {
- auto& entry = callchain[i];
- if (!IsJavaEntry(entry)) {
- i++;
- continue;
- }
- std::string_view name = entry.symbol->FunctionName();
- std::string original_name;
- bool synthesized;
- if (retrace_->DeObfuscateJavaMethods(name, &original_name, &synthesized)) {
- if (synthesized && remove_r8_synthesized_frame_) {
- callchain.erase(callchain.begin() + i);
- continue;
- }
- entry.symbol->SetDemangledName(original_name);
- }
- i++;
- }
-}
-
bool ThreadReportBuilder::AggregateThreads(const std::vector<std::string>& thread_name_regex) {
size_t i = thread_regs_.size();
thread_regs_.resize(i + thread_name_regex.size());
diff --git a/simpleperf/report_utils.h b/simpleperf/report_utils.h
index fa42bba6..631e5fad 100644
--- a/simpleperf/report_utils.h
+++ b/simpleperf/report_utils.h
@@ -91,39 +91,39 @@ struct CallChainReportEntry {
CallChainExecutionType execution_type = CallChainExecutionType::NATIVE_METHOD;
};
+// a base class for modifying callchain reports
+class CallChainReportModifier {
+ public:
+ virtual ~CallChainReportModifier();
+
+ virtual void Modify(std::vector<CallChainReportEntry>& callchain) = 0;
+};
+
class CallChainReportBuilder {
public:
CallChainReportBuilder(ThreadTree& thread_tree);
// If true, remove interpreter frames both before and after a Java frame.
// Default is true.
- void SetRemoveArtFrame(bool enable) { remove_art_frame_ = enable; }
+ void SetRemoveArtFrame(bool enable);
// If true, convert a JIT method into its corresponding interpreted Java method. So they can be
// merged in reports like flamegraph. Default is true.
- void SetConvertJITFrame(bool enable) { convert_jit_frame_ = enable; }
+ 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);
private:
- struct JavaMethod {
- Dso* dso;
- const Symbol* symbol;
- JavaMethod(Dso* dso, const Symbol* symbol) : dso(dso), symbol(symbol) {}
- };
-
void MarkArtFrame(std::vector<CallChainReportEntry>& callchain);
- void ConvertJITFrame(std::vector<CallChainReportEntry>& callchain);
- void CollectJavaMethods();
- void DeObfuscateJavaMethods(std::vector<CallChainReportEntry>& callchain);
ThreadTree& thread_tree_;
- bool remove_art_frame_ = true;
bool remove_r8_synthesized_frame_ = false;
- bool convert_jit_frame_ = true;
- bool java_method_initialized_ = false;
- std::unordered_map<std::string, JavaMethod> java_method_map_;
- std::unique_ptr<ProguardMappingRetrace> retrace_;
+ 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() {