summaryrefslogtreecommitdiff
path: root/simpleperf
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2024-02-22 16:19:35 -0800
committerYabin Cui <yabinc@google.com>2024-02-27 12:57:10 -0800
commit57a99140e0d61aa262c95cbe9f0cf15b0ce1e28c (patch)
treed5c86a2f7cd37ed07d79cb3eb0ec1c6a5dc4f7c0 /simpleperf
parentdf6bc1eb89f0f3482cb0a43bc60d23ec6599746f (diff)
downloadextras-57a99140e0d61aa262c95cbe9f0cf15b0ce1e28c.tar.gz
simpleperf: refactor CallChainReportBuilder
Decouple CallChainReportBuilder functions into modifier classes for improved extensibility. Bug: 325429554 Test: run simpleperf_unit_test Change-Id: I6a8a5b40f7459597adfeb106e77145983ac2bde1
Diffstat (limited to 'simpleperf')
-rw-r--r--simpleperf/report_utils.cpp256
-rw-r--r--simpleperf/report_utils.h29
2 files changed, 172 insertions, 113 deletions
diff --git a/simpleperf/report_utils.cpp b/simpleperf/report_utils.cpp
index ecbc6aa8..5478d3dd 100644
--- a/simpleperf/report_utils.cpp
+++ b/simpleperf/report_utils.cpp
@@ -189,6 +189,137 @@ 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_;
+};
+
CallChainReportBuilder::CallChainReportBuilder(ThreadTree& thread_tree)
: thread_tree_(thread_tree) {
const char* env_name = "REMOVE_R8_SYNTHESIZED_FRAME";
@@ -202,13 +333,32 @@ 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 retrace_->AddProguardMappingFile(mapping_file);
+ return static_cast<JavaMethodDeobfuscater*>(java_method_deobfuscater_.get())
+ ->AddProguardMappingFile(mapping_file);
}
std::vector<CallChainReportEntry> CallChainReportBuilder::Build(const ThreadEntry* thread,
@@ -239,17 +389,14 @@ 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 (convert_jit_frame_) {
- ConvertJITFrame(result);
+ if (jit_frame_converter_) {
+ jit_frame_converter_->Modify(result);
}
- if (retrace_) {
- DeObfuscateJavaMethods(result);
+ if (java_method_deobfuscater_) {
+ java_method_deobfuscater_->Modify(result);
}
return result;
}
@@ -292,91 +439,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..78c037df 100644
--- a/simpleperf/report_utils.h
+++ b/simpleperf/report_utils.h
@@ -91,39 +91,36 @@ 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);
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_;
};
struct ThreadReport {