Add deobfuscation to trace_to_text profile and tp shell.
Test: manual
Bug: 175381133
Change-Id: Ica07fadb3d52b67859ed87ca5e392434d6033a1e
diff --git a/Android.bp b/Android.bp
index 7345d41..92b7a3d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8791,6 +8791,7 @@
":perfetto_include_perfetto_ext_trace_processor_export_json",
":perfetto_include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
":perfetto_include_perfetto_ext_traced_sys_stats_counters",
+ ":perfetto_include_perfetto_profiling_deobfuscator",
":perfetto_include_perfetto_protozero_protozero",
":perfetto_include_perfetto_trace_processor_basic_types",
":perfetto_include_perfetto_trace_processor_storage",
@@ -8825,6 +8826,7 @@
":perfetto_protos_perfetto_trace_system_info_zero_gen",
":perfetto_protos_perfetto_trace_track_event_zero_gen",
":perfetto_src_base_base",
+ ":perfetto_src_profiling_deobfuscator",
":perfetto_src_profiling_symbolizer_symbolize_database",
":perfetto_src_profiling_symbolizer_symbolizer",
":perfetto_src_protozero_protozero",
diff --git a/BUILD b/BUILD
index cd969e4..8e63d57 100644
--- a/BUILD
+++ b/BUILD
@@ -3255,10 +3255,12 @@
":include_perfetto_ext_trace_processor_export_json",
":include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
":include_perfetto_ext_traced_sys_stats_counters",
+ ":include_perfetto_profiling_deobfuscator",
":include_perfetto_protozero_protozero",
":include_perfetto_trace_processor_basic_types",
":include_perfetto_trace_processor_storage",
":include_perfetto_trace_processor_trace_processor",
+ ":src_profiling_deobfuscator",
":src_profiling_symbolizer_symbolize_database",
":src_profiling_symbolizer_symbolizer",
":src_trace_processor_analysis_analysis",
@@ -3415,6 +3417,7 @@
":protos_perfetto_trace_system_info_zero",
":protos_perfetto_trace_track_event_zero",
":protos_third_party_pprof_zero",
+ ":protozero",
] + PERFETTO_CONFIG.deps.zlib,
linkstatic = True,
)
diff --git a/include/perfetto/profiling/deobfuscator.h b/include/perfetto/profiling/deobfuscator.h
index 06300a8..2d9ca8b 100644
--- a/include/perfetto/profiling/deobfuscator.h
+++ b/include/perfetto/profiling/deobfuscator.h
@@ -17,14 +17,17 @@
#ifndef INCLUDE_PERFETTO_PROFILING_DEOBFUSCATOR_H_
#define INCLUDE_PERFETTO_PROFILING_DEOBFUSCATOR_H_
+#include <functional>
#include <map>
#include <string>
+#include <utility>
+#include <vector>
namespace perfetto {
namespace profiling {
struct ObfuscatedClass {
- ObfuscatedClass(std::string d) : deobfuscated_name(std::move(d)) {}
+ explicit ObfuscatedClass(std::string d) : deobfuscated_name(std::move(d)) {}
ObfuscatedClass(std::string d,
std::map<std::string, std::string> f,
std::map<std::string, std::string> m)
@@ -42,6 +45,7 @@
// A return value of false means this line failed to parse. This leaves the
// parser in an undefined state and it should no longer be used.
bool AddLine(std::string line);
+ bool AddLines(std::string contents);
std::map<std::string, ObfuscatedClass> ConsumeMapping() {
return std::move(mapping_);
@@ -52,6 +56,22 @@
ObfuscatedClass* current_class_ = nullptr;
};
+struct ProguardMap {
+ std::string package;
+ std::string filename;
+};
+
+void MakeDeobfuscationPackets(
+ const std::string& package_name,
+ const std::map<std::string, profiling::ObfuscatedClass>& mapping,
+ std::function<void(const std::string&)> callback);
+
+std::vector<ProguardMap> GetPerfettoProguardMapPath();
+
+bool ReadProguardMapsToDeobfuscationPackets(
+ const std::vector<ProguardMap>& maps,
+ std::function<void(std::string)> fn);
+
} // namespace profiling
} // namespace perfetto
diff --git a/src/profiling/BUILD.gn b/src/profiling/BUILD.gn
index 5a929b6..64cbbd2 100644
--- a/src/profiling/BUILD.gn
+++ b/src/profiling/BUILD.gn
@@ -20,6 +20,9 @@
deps = [
"../../gn:default_deps",
"../../include/perfetto/ext/base:base",
+ "../../protos/perfetto/trace:zero",
+ "../../protos/perfetto/trace/profiling:zero",
+ "../../src/protozero:protozero",
]
public_deps = [ "../../include/perfetto/profiling:deobfuscator" ]
}
diff --git a/src/profiling/deobfuscator.cc b/src/profiling/deobfuscator.cc
index cea630a..f8096a1 100644
--- a/src/profiling/deobfuscator.cc
+++ b/src/profiling/deobfuscator.cc
@@ -15,9 +15,15 @@
*/
#include "perfetto/profiling/deobfuscator.h"
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/string_splitter.h"
#include "perfetto/ext/base/optional.h"
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "protos/perfetto/trace/profiling/deobfuscation.pbzero.h"
+#include "protos/perfetto/trace/trace.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
namespace perfetto {
namespace profiling {
@@ -183,5 +189,96 @@
return true;
}
+bool ProguardParser::AddLines(std::string contents) {
+ for (base::StringSplitter lines(std::move(contents), '\n'); lines.Next();) {
+ if (!AddLine(lines.cur_token()))
+ return false;
+ }
+ return true;
+}
+
+void MakeDeobfuscationPackets(
+ const std::string& package_name,
+ const std::map<std::string, profiling::ObfuscatedClass>& mapping,
+ std::function<void(const std::string&)> callback) {
+ protozero::HeapBuffered<perfetto::protos::pbzero::Trace> trace;
+ auto* packet = trace->add_packet();
+ // TODO(fmayer): Add handling for package name and version code here so we
+ // can support multiple dumps in the same trace.
+ auto* proto_mapping = packet->set_deobfuscation_mapping();
+ proto_mapping->set_package_name(package_name);
+ for (const auto& p : mapping) {
+ const std::string& obfuscated_class_name = p.first;
+ const profiling::ObfuscatedClass& cls = p.second;
+
+ auto* proto_class = proto_mapping->add_obfuscated_classes();
+ proto_class->set_obfuscated_name(obfuscated_class_name);
+ proto_class->set_deobfuscated_name(cls.deobfuscated_name);
+ for (const auto& field_p : cls.deobfuscated_fields) {
+ const std::string& obfuscated_field_name = field_p.first;
+ const std::string& deobfuscated_field_name = field_p.second;
+ auto* proto_member = proto_class->add_obfuscated_members();
+ proto_member->set_obfuscated_name(obfuscated_field_name);
+ proto_member->set_deobfuscated_name(deobfuscated_field_name);
+ }
+ for (const auto& field_p : cls.deobfuscated_methods) {
+ const std::string& obfuscated_method_name = field_p.first;
+ const std::string& deobfuscated_method_name = field_p.second;
+ auto* proto_member = proto_class->add_obfuscated_methods();
+ proto_member->set_obfuscated_name(obfuscated_method_name);
+ proto_member->set_deobfuscated_name(deobfuscated_method_name);
+ }
+ }
+ callback(trace.SerializeAsString());
+}
+
+bool ReadProguardMapsToDeobfuscationPackets(
+ const std::vector<ProguardMap>& maps,
+ std::function<void(std::string)> fn) {
+ for (const ProguardMap& map : maps) {
+ const char* filename = map.filename.c_str();
+ base::ScopedFstream f(fopen(filename, "r"));
+ if (!f) {
+ PERFETTO_ELOG("Failed to open %s", filename);
+ return false;
+ }
+ profiling::ProguardParser parser;
+ std::string contents;
+ PERFETTO_CHECK(base::ReadFileStream(*f, &contents));
+ if (!parser.AddLines(std::move(contents))) {
+ PERFETTO_ELOG("Failed to parse %s", filename);
+ return false;
+ }
+ std::map<std::string, profiling::ObfuscatedClass> obfuscation_map =
+ parser.ConsumeMapping();
+
+ // TODO(fmayer): right now, we don't use the profile we are given. We can
+ // filter the output to only contain the classes actually seen in the
+ // profile.
+ MakeDeobfuscationPackets(map.package, obfuscation_map, fn);
+ }
+ return true;
+}
+
+std::vector<ProguardMap> GetPerfettoProguardMapPath() {
+ const char* env = getenv("PERFETTO_PROGUARD_MAP");
+ if (env == nullptr)
+ return {};
+ std::vector<ProguardMap> res;
+ for (base::StringSplitter sp(std::string(env), ':'); sp.Next();) {
+ std::string token(sp.cur_token(), sp.cur_token_size());
+ size_t eq = token.find('=');
+ if (eq == std::string::npos) {
+ PERFETTO_ELOG(
+ "Invalid PERFETTO_PROGUARD_MAP. "
+ "Expected format packagename=filename[:packagename=filename...], "
+ "e.g. com.example.package1=foo.txt:com.example.package2=bar.txt.");
+ return {};
+ }
+ res.emplace_back(ProguardMap{token.substr(0, eq), token.substr(eq + 1)});
+ }
+ return res; // for Wreturn-std-move-in-c++11.
+}
+
} // namespace profiling
} // namespace perfetto
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index b6b0ae8..51a21ff 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -350,6 +350,7 @@
":lib",
"../../gn:default_deps",
"../../gn:protobuf_full",
+ "../../src/profiling:deobfuscator",
"../../src/profiling/symbolizer",
"../../src/profiling/symbolizer:symbolize_database",
"../base",
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index d96c8ee..fcf53bb 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -36,6 +36,7 @@
#include "perfetto/ext/base/string_splitter.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/version.h"
+#include "perfetto/profiling/deobfuscator.h"
#include "perfetto/trace_processor/read_trace.h"
#include "perfetto/trace_processor/trace_processor.h"
#include "src/trace_processor/metrics/chrome/all_chrome_metrics.descriptor.h"
@@ -953,6 +954,21 @@
});
g_tp->NotifyEndOfFile();
}
+
+ auto maybe_map = profiling::GetPerfettoProguardMapPath();
+ if (!maybe_map.empty()) {
+ profiling::ReadProguardMapsToDeobfuscationPackets(
+ maybe_map, [](const std::string& trace_proto) {
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[trace_proto.size()]);
+ memcpy(buf.get(), trace_proto.data(), trace_proto.size());
+ auto status = g_tp->Parse(std::move(buf), trace_proto.size());
+ if (!status.ok()) {
+ PERFETTO_DFATAL_OR_ELOG("Failed to parse: %s",
+ status.message().c_str());
+ return;
+ }
+ });
+ }
return util::OkStatus();
}
diff --git a/tools/trace_to_text/deobfuscate_profile.cc b/tools/trace_to_text/deobfuscate_profile.cc
index f21c913..dfdb67b 100644
--- a/tools/trace_to_text/deobfuscate_profile.cc
+++ b/tools/trace_to_text/deobfuscate_profile.cc
@@ -28,51 +28,22 @@
namespace perfetto {
namespace trace_to_text {
-namespace {
-
-bool ParseFile(profiling::ProguardParser* p, FILE* f) {
- std::string contents;
- PERFETTO_CHECK(base::ReadFileStream(f, &contents));
- for (base::StringSplitter lines(std::move(contents), '\n'); lines.Next();) {
- if (!p->AddLine(lines.cur_token()))
- return false;
- }
- return true;
-}
-
-} // namespace
int DeobfuscateProfile(std::istream* input, std::ostream* output) {
base::ignore_result(input);
base::ignore_result(output);
- auto maybe_map = GetPerfettoProguardMapPath();
- if (!maybe_map) {
+ auto maybe_map = profiling::GetPerfettoProguardMapPath();
+ if (maybe_map.empty()) {
PERFETTO_ELOG("No PERFETTO_PROGUARD_MAP specified.");
return 1;
}
- for (const ProguardMap& map : *maybe_map) {
- const char* filename = map.filename.c_str();
- base::ScopedFstream f(fopen(filename, "r"));
- if (!f) {
- PERFETTO_ELOG("Failed to open %s", filename);
- return 1;
- }
- profiling::ProguardParser parser;
- if (!ParseFile(&parser, *f)) {
- PERFETTO_ELOG("Failed to parse %s", filename);
- return 1;
- }
- std::map<std::string, profiling::ObfuscatedClass> obfuscation_map =
- parser.ConsumeMapping();
-
- // TODO(fmayer): right now, we don't use the profile we are given. We can
- // filter the output to only contain the classes actually seen in the
- // profile.
- MakeDeobfuscationPackets(map.package, obfuscation_map,
- [output](const std::string& packet_proto) {
- WriteTracePacket(packet_proto, output);
- });
+ if (!profiling::ReadProguardMapsToDeobfuscationPackets(
+ maybe_map, [output](const std::string& trace_proto) {
+ *output << trace_proto;
+ })) {
+ return 1;
}
+
return 0;
}
diff --git a/tools/trace_to_text/trace_to_profile.cc b/tools/trace_to_text/trace_to_profile.cc
index d624fe9..ce0f6d8 100644
--- a/tools/trace_to_text/trace_to_profile.cc
+++ b/tools/trace_to_text/trace_to_profile.cc
@@ -57,16 +57,21 @@
getenv("PERFETTO_SYMBOLIZER_MODE"));
if (!symbolizer)
return;
- profiling::SymbolizeDatabase(
- tp, symbolizer.get(), [tp](const std::string& trace_proto) {
- std::unique_ptr<uint8_t[]> buf(new uint8_t[trace_proto.size()]);
- memcpy(buf.get(), trace_proto.data(), trace_proto.size());
- auto status = tp->Parse(std::move(buf), trace_proto.size());
- if (!status.ok()) {
- PERFETTO_DFATAL_OR_ELOG("Failed to parse: %s",
- status.message().c_str());
- return;
- }
+ profiling::SymbolizeDatabase(tp, symbolizer.get(),
+ [tp](const std::string& trace_proto) {
+ IngestTraceOrDie(tp, trace_proto);
+ });
+ tp->NotifyEndOfFile();
+}
+
+void MaybeDeobfuscate(trace_processor::TraceProcessor* tp) {
+ auto maybe_map = profiling::GetPerfettoProguardMapPath();
+ if (maybe_map.empty()) {
+ return;
+ }
+ profiling::ReadProguardMapsToDeobfuscationPackets(
+ maybe_map, [tp](const std::string& trace_proto) {
+ IngestTraceOrDie(tp, trace_proto);
});
tp->NotifyEndOfFile();
}
@@ -87,6 +92,7 @@
tp->NotifyEndOfFile();
MaybeSymbolize(tp.get());
+ MaybeDeobfuscate(tp.get());
TraceToPprof(tp.get(), &profiles, pid, timestamps);
if (profiles.empty()) {
diff --git a/tools/trace_to_text/utils.cc b/tools/trace_to_text/utils.cc
index a916471..dcacd5e 100644
--- a/tools/trace_to_text/utils.cc
+++ b/tools/trace_to_text/utils.cc
@@ -25,7 +25,9 @@
#include <utility>
#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/string_splitter.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "perfetto/trace_processor/trace_processor.h"
@@ -51,16 +53,6 @@
} // namespace
-void WriteTracePacket(const std::string& str, std::ostream* output) {
- constexpr char kPreamble =
- MakeTagLengthDelimited(protos::pbzero::Trace::kPacketFieldNumber);
- uint8_t length_field[10];
- uint8_t* end = WriteVarInt(str.size(), length_field);
- *output << kPreamble;
- *output << std::string(length_field, end);
- *output << str;
-}
-
void ForEachPacketBlobInTrace(
std::istream* input,
const std::function<void(std::unique_ptr<char[]>, size_t)>& f) {
@@ -106,25 +98,6 @@
}
}
-base::Optional<std::vector<ProguardMap>> GetPerfettoProguardMapPath() {
- const char* env = getenv("PERFETTO_PROGUARD_MAP");
- if (env == nullptr)
- return base::nullopt;
- std::vector<ProguardMap> res;
- for (base::StringSplitter sp(std::string(env), ':'); sp.Next();) {
- std::string token(sp.cur_token(), sp.cur_token_size());
- size_t eq = token.find('=');
- if (eq == std::string::npos) {
- PERFETTO_ELOG(
- "Invalid PERFETTO_PROGUARD_MAP. "
- "Expected format packagename=filename[:packagename=filename...], "
- "e.g. com.example.package1=foo.txt:com.example.package2=bar.txt.");
- return base::nullopt;
- }
- res.emplace_back(ProguardMap{token.substr(0, eq), token.substr(eq + 1)});
- }
- return std::move(res); // for Wreturn-std-move-in-c++11.
-}
bool ReadTrace(trace_processor::TraceProcessor* tp, std::istream* input) {
// 1MB chunk size seems the best tradeoff on a MacBook Pro 2013 - i7 2.8 GHz.
@@ -165,38 +138,14 @@
return true;
}
-void MakeDeobfuscationPackets(
- const std::string& package_name,
- const std::map<std::string, profiling::ObfuscatedClass>& mapping,
- std::function<void(const std::string&)> callback) {
- protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket> packet;
- // TODO(fmayer): Add handling for package name and version code here so we
- // can support multiple dumps in the same trace.
- auto* proto_mapping = packet->set_deobfuscation_mapping();
- proto_mapping->set_package_name(package_name);
- for (const auto& p : mapping) {
- const std::string& obfuscated_class_name = p.first;
- const profiling::ObfuscatedClass& cls = p.second;
-
- auto* proto_class = proto_mapping->add_obfuscated_classes();
- proto_class->set_obfuscated_name(obfuscated_class_name);
- proto_class->set_deobfuscated_name(cls.deobfuscated_name);
- for (const auto& field_p : cls.deobfuscated_fields) {
- const std::string& obfuscated_field_name = field_p.first;
- const std::string& deobfuscated_field_name = field_p.second;
- auto* proto_member = proto_class->add_obfuscated_members();
- proto_member->set_obfuscated_name(obfuscated_field_name);
- proto_member->set_deobfuscated_name(deobfuscated_field_name);
- }
- for (const auto& field_p : cls.deobfuscated_methods) {
- const std::string& obfuscated_method_name = field_p.first;
- const std::string& deobfuscated_method_name = field_p.second;
- auto* proto_member = proto_class->add_obfuscated_methods();
- proto_member->set_obfuscated_name(obfuscated_method_name);
- proto_member->set_deobfuscated_name(deobfuscated_method_name);
- }
+void IngestTraceOrDie(trace_processor::TraceProcessor* tp,
+ const std::string& trace_proto) {
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[trace_proto.size()]);
+ memcpy(buf.get(), trace_proto.data(), trace_proto.size());
+ auto status = tp->Parse(std::move(buf), trace_proto.size());
+ if (!status.ok()) {
+ PERFETTO_DFATAL_OR_ELOG("Failed to parse: %s", status.message().c_str());
}
- callback(packet.SerializeAsString());
}
TraceWriter::TraceWriter(std::ostream* output) : output_(output) {}
diff --git a/tools/trace_to_text/utils.h b/tools/trace_to_text/utils.h
index 2faeb94..1ac1d6d 100644
--- a/tools/trace_to_text/utils.h
+++ b/tools/trace_to_text/utils.h
@@ -45,11 +45,6 @@
namespace trace_to_text {
-struct ProguardMap {
- std::string package;
- std::string filename;
-};
-
// When running in Web Assembly, fflush() is a no-op and the stdio buffering
// sends progress updates to JS only when a write ends with \n.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
@@ -62,19 +57,10 @@
std::istream* input,
const std::function<void(std::unique_ptr<char[]>, size_t)>&);
-base::Optional<std::vector<ProguardMap>> GetPerfettoProguardMapPath();
bool ReadTrace(trace_processor::TraceProcessor* tp, std::istream* input);
-
-void WriteTracePacket(const std::string& str, std::ostream* output);
-
-// Generate ObfuscationMapping protos for all obfuscated java names in the
-// database.
-// Wrap them in proto-encoded TracePackets messages and call callback.
-void MakeDeobfuscationPackets(
- const std::string& package_name,
- const std::map<std::string, profiling::ObfuscatedClass>& mapping,
- std::function<void(const std::string&)> callback);
+void IngestTraceOrDie(trace_processor::TraceProcessor* tp,
+ const std::string& trace_proto);
class TraceWriter {
public: