Allow to produce DeobfuscationMapping from proguard map.
Bug: 143611277
Change-Id: Ia3f65830b7483b48baed4e835b0fee39f5538ce7
diff --git a/Android.bp b/Android.bp
index b7a3a33..85f661e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5192,6 +5192,7 @@
filegroup {
name: "perfetto_tools_trace_to_text_common",
srcs: [
+ "tools/trace_to_text/deobfuscate_profile.cc",
"tools/trace_to_text/main.cc",
"tools/trace_to_text/symbolize_profile.cc",
"tools/trace_to_text/trace_to_json.cc",
@@ -5729,6 +5730,7 @@
":perfetto_include_perfetto_base_base",
":perfetto_include_perfetto_ext_base_base",
":perfetto_include_perfetto_ext_traced_sys_stats_counters",
+ ":perfetto_include_perfetto_profiling_deobfuscator",
":perfetto_include_perfetto_profiling_symbolizer",
":perfetto_include_perfetto_protozero_protozero",
":perfetto_include_perfetto_trace_processor_basic_types",
@@ -5787,6 +5789,7 @@
":perfetto_protos_perfetto_trace_track_event_zero_gen",
":perfetto_protos_third_party_pprof_lite_gen",
":perfetto_src_base_base",
+ ":perfetto_src_profiling_deobfuscator",
":perfetto_src_protozero_protozero",
":perfetto_src_trace_processor_common",
":perfetto_src_trace_processor_db_lib",
diff --git a/BUILD b/BUILD
index 57831e6..c7db1e7 100644
--- a/BUILD
+++ b/BUILD
@@ -330,6 +330,14 @@
],
)
+# GN target: //include/perfetto/profiling:deobfuscator
+filegroup(
+ name = "include_perfetto_profiling_deobfuscator",
+ srcs = [
+ "include/perfetto/profiling/deobfuscator.h",
+ ],
+)
+
# GN target: //include/perfetto/profiling:symbolizer
filegroup(
name = "include_perfetto_profiling_symbolizer",
@@ -524,6 +532,14 @@
],
)
+# GN target: //src/profiling:deobfuscator
+filegroup(
+ name = "src_profiling_deobfuscator",
+ srcs = [
+ "src/profiling/deobfuscator.cc",
+ ],
+)
+
# GN target: //src/protozero:protozero
filegroup(
name = "src_protozero_protozero",
@@ -1090,6 +1106,8 @@
filegroup(
name = "tools_trace_to_text_common",
srcs = [
+ "tools/trace_to_text/deobfuscate_profile.cc",
+ "tools/trace_to_text/deobfuscate_profile.h",
"tools/trace_to_text/main.cc",
"tools/trace_to_text/symbolize_profile.cc",
"tools/trace_to_text/symbolize_profile.h",
@@ -2552,6 +2570,7 @@
name = "libpprofbuilder",
srcs = [
":src_base_base",
+ ":src_profiling_deobfuscator",
":src_protozero_protozero",
":src_trace_processor_common",
":src_trace_processor_db_lib",
@@ -2570,6 +2589,7 @@
":include_perfetto_ext_base_base",
":include_perfetto_ext_trace_processor_export_json",
":include_perfetto_ext_traced_sys_stats_counters",
+ ":include_perfetto_profiling_deobfuscator",
":include_perfetto_profiling_symbolizer",
":include_perfetto_protozero_protozero",
":include_perfetto_trace_processor_basic_types",
@@ -2648,12 +2668,14 @@
":include_perfetto_ext_base_base",
":include_perfetto_ext_trace_processor_export_json",
":include_perfetto_ext_traced_sys_stats_counters",
+ ":include_perfetto_profiling_deobfuscator",
":include_perfetto_profiling_symbolizer",
":include_perfetto_protozero_protozero",
":include_perfetto_trace_processor_basic_types",
":include_perfetto_trace_processor_storage",
":include_perfetto_trace_processor_trace_processor",
":src_base_base",
+ ":src_profiling_deobfuscator",
":src_protozero_protozero",
":src_trace_processor_common",
":src_trace_processor_db_lib",
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index abfe920..7faa5d4 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -3045,7 +3045,7 @@
message DeobfuscationMapping {
optional string package_name = 1;
optional int64 version_code = 2;
- repeated ObfuscatedClass obfusucated_classes = 3;
+ repeated ObfuscatedClass obfuscated_classes = 3;
}
message HeapGraphRoot {
@@ -3554,7 +3554,7 @@
// TracePacket(s).
//
// Next reserved id: 13 (up to 15).
-// Next id: 64.
+// Next id: 65.
message TracePacket {
// The timestamp of the TracePacket.
// By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on
@@ -3608,6 +3608,7 @@
// Only used in profile packets.
ProfiledFrameSymbols profiled_frame_symbols = 55;
ModuleSymbols module_symbols = 61;
+ DeobfuscationMapping deobfuscation_mapping = 64;
// Only used by TrackEvent.
TrackDescriptor track_descriptor = 60;
diff --git a/protos/perfetto/trace/profiling/heap_graph.proto b/protos/perfetto/trace/profiling/heap_graph.proto
index 5601b9b..933d2d0 100644
--- a/protos/perfetto/trace/profiling/heap_graph.proto
+++ b/protos/perfetto/trace/profiling/heap_graph.proto
@@ -38,7 +38,7 @@
message DeobfuscationMapping {
optional string package_name = 1;
optional int64 version_code = 2;
- repeated ObfuscatedClass obfusucated_classes = 3;
+ repeated ObfuscatedClass obfuscated_classes = 3;
}
message HeapGraphRoot {
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index b23c98b..95e9eef 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -58,7 +58,7 @@
// TracePacket(s).
//
// Next reserved id: 13 (up to 15).
-// Next id: 64.
+// Next id: 65.
message TracePacket {
// The timestamp of the TracePacket.
// By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on
@@ -112,6 +112,7 @@
// Only used in profile packets.
ProfiledFrameSymbols profiled_frame_symbols = 55;
ModuleSymbols module_symbols = 61;
+ DeobfuscationMapping deobfuscation_mapping = 64;
// Only used by TrackEvent.
TrackDescriptor track_descriptor = 60;
diff --git a/src/profiling/deobfuscator.cc b/src/profiling/deobfuscator.cc
index 8306ded..a8b7652 100644
--- a/src/profiling/deobfuscator.cc
+++ b/src/profiling/deobfuscator.cc
@@ -117,21 +117,31 @@
std::tie(obfuscated_name, deobfuscated_name) = *opt_pair;
auto p = mapping_.emplace(std::move(obfuscated_name),
std::move(deobfuscated_name));
- if (!p.second)
+ if (!p.second) {
+ PERFETTO_ELOG("Duplicate class.");
return false;
+ }
current_class_ = &p.first->second;
} else {
- // TODO(fmayer): Teach this to properly parse methods.
std::string obfuscated_name;
std::string deobfuscated_name;
auto opt_pair = ParseMember(std::move(line));
if (!opt_pair)
return false;
std::tie(obfuscated_name, deobfuscated_name) = *opt_pair;
- auto p = current_class_->deobfuscated_fields.emplace(
- std::move(obfuscated_name), std::move(deobfuscated_name));
- if (!p.second)
+ // TODO(fmayer): Teach this to properly parse methods.
+ if (deobfuscated_name.find("(") != std::string::npos) {
+ // Skip functions, as they will trigger the "Duplicate member" below.
+ return true;
+ }
+ auto p = current_class_->deobfuscated_fields.emplace(obfuscated_name,
+ deobfuscated_name);
+ if (!p.second && p.first->second != deobfuscated_name) {
+ PERFETTO_ELOG("Member redefinition: %s.%s",
+ current_class_->deobfuscated_name.c_str(),
+ deobfuscated_name.c_str());
return false;
+ }
}
return true;
}
diff --git a/src/profiling/deobfuscator_unittest.cc b/src/profiling/deobfuscator_unittest.cc
index 0b7833e..0d9619a 100644
--- a/src/profiling/deobfuscator_unittest.cc
+++ b/src/profiling/deobfuscator_unittest.cc
@@ -79,7 +79,7 @@
ASSERT_TRUE(p.AddLine(
"android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
ASSERT_FALSE(p.AddLine(
- "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
+ "android.arch.core.executor.ArchTaskExecutor2 -> android.arch.a.a.a:"));
}
TEST(ProguardParserTest, DuplicateField) {
@@ -89,7 +89,7 @@
ASSERT_TRUE(
p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate -> b"));
ASSERT_FALSE(
- p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate -> b"));
+ p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate2 -> b"));
}
} // namespace
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index 5ddea68..d7ae08f 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -39,6 +39,8 @@
source_set("utils") {
deps = [
":symbolizer",
+ "../../include/perfetto/profiling:deobfuscator",
+ "../../src/profiling:deobfuscator",
]
public_deps = [
"../../gn:default_deps",
@@ -128,6 +130,8 @@
":pprofbuilder",
":symbolizer",
":utils",
+ "../../include/perfetto/profiling:deobfuscator",
+ "../../src/profiling:deobfuscator",
]
public_deps = [
"../../gn:default_deps",
@@ -143,6 +147,8 @@
"../../src/trace_processor:lib",
]
sources = [
+ "deobfuscate_profile.cc",
+ "deobfuscate_profile.h",
"main.cc",
"symbolize_profile.cc",
"symbolize_profile.h",
diff --git a/tools/trace_to_text/deobfuscate_profile.cc b/tools/trace_to_text/deobfuscate_profile.cc
new file mode 100644
index 0000000..1d1af52
--- /dev/null
+++ b/tools/trace_to_text/deobfuscate_profile.cc
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/profiling/deobfuscator.h"
+#include "protos/perfetto/trace/trace_packet.pb.h"
+#include "tools/trace_to_text/deobfuscate_profile.h"
+#include "tools/trace_to_text/utils.h"
+
+namespace perfetto {
+namespace trace_to_text {
+namespace {
+
+bool ParseFile(profiling::ProguardParser* p, FILE* f) {
+ std::vector<std::string> lines;
+ size_t n = 0;
+ char* line = nullptr;
+ ssize_t rd = 0;
+ bool success = true;
+ do {
+ rd = getline(&line, &n, f);
+ // Do not read empty line that terminates the output.
+ if (rd > 1) {
+ // Remove newline character.
+ PERFETTO_DCHECK(line[rd - 1] == '\n');
+ line[rd - 1] = '\0';
+ success = p->AddLine(line);
+ }
+ } while (rd > 1 && success);
+ free(line);
+ return success;
+}
+} // namespace
+
+int DeobfuscateProfile(std::istream* input, std::ostream* output) {
+ base::ignore_result(input);
+ base::ignore_result(output);
+ auto maybe_map = GetPerfettoProguardMapPath();
+ if (!maybe_map) {
+ PERFETTO_ELOG("No PERFETTO_PROGUARD_MAP specified.");
+ return 1;
+ }
+ base::ScopedFstream f(fopen(maybe_map->c_str(), "r"));
+ if (!f) {
+ PERFETTO_ELOG("Failed to open %s", maybe_map->c_str());
+ return 1;
+ }
+ profiling::ProguardParser parser;
+ if (!ParseFile(&parser, *f)) {
+ PERFETTO_ELOG("Failed to parse %s", maybe_map->c_str());
+ return 1;
+ }
+ std::map<std::string, profiling::ObfuscatedClass> obfuscation_map =
+ parser.ConsumeMapping();
+
+ trace_processor::Config config;
+ std::unique_ptr<trace_processor::TraceProcessor> tp =
+ trace_processor::TraceProcessor::CreateInstance(config);
+
+ if (!ReadTrace(tp.get(), input))
+ PERFETTO_FATAL("Failed to read trace.");
+
+ tp->NotifyEndOfFile();
+ DeobfuscateDatabase(tp.get(), obfuscation_map,
+ [output](const perfetto::protos::TracePacket& packet) {
+ WriteTracePacket(packet.SerializeAsString(), output);
+ });
+ return 0;
+}
+
+} // namespace trace_to_text
+} // namespace perfetto
diff --git a/tools/trace_to_text/deobfuscate_profile.h b/tools/trace_to_text/deobfuscate_profile.h
new file mode 100644
index 0000000..d5808c6
--- /dev/null
+++ b/tools/trace_to_text/deobfuscate_profile.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TOOLS_TRACE_TO_TEXT_DEOBFUSCATE_PROFILE_H_
+#define TOOLS_TRACE_TO_TEXT_DEOBFUSCATE_PROFILE_H_
+
+#include <iostream>
+
+namespace perfetto {
+namespace trace_to_text {
+
+int DeobfuscateProfile(std::istream* input, std::ostream* output);
+
+}
+} // namespace perfetto
+
+#endif // TOOLS_TRACE_TO_TEXT_DEOBFUSCATE_PROFILE_H_
diff --git a/tools/trace_to_text/main.cc b/tools/trace_to_text/main.cc
index 223fc64..efcc053 100644
--- a/tools/trace_to_text/main.cc
+++ b/tools/trace_to_text/main.cc
@@ -21,6 +21,7 @@
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/string_utils.h"
+#include "tools/trace_to_text/deobfuscate_profile.h"
#include "tools/trace_to_text/symbolize_profile.h"
#include "tools/trace_to_text/trace_to_json.h"
#include "tools/trace_to_text/trace_to_profile.h"
@@ -176,6 +177,8 @@
if (format == "symbolize")
return SymbolizeProfile(input_stream, output_stream);
+ if (format == "deobfuscate")
+ return DeobfuscateProfile(input_stream, output_stream);
return Usage(argv[0]);
}
diff --git a/tools/trace_to_text/symbolize_profile.cc b/tools/trace_to_text/symbolize_profile.cc
index 1d2812d..4ae59fb 100644
--- a/tools/trace_to_text/symbolize_profile.cc
+++ b/tools/trace_to_text/symbolize_profile.cc
@@ -31,22 +31,6 @@
namespace perfetto {
namespace trace_to_text {
-namespace {
-
-using ::protozero::proto_utils::kMessageLengthFieldSize;
-using ::protozero::proto_utils::MakeTagLengthDelimited;
-using ::protozero::proto_utils::WriteVarInt;
-
-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;
-}
-}
// Ingest profile, and emit a symbolization table for each sequence. This can
// be prepended to the profile to attach the symbol information.
diff --git a/tools/trace_to_text/utils.cc b/tools/trace_to_text/utils.cc
index 5055b98..a523e2a 100644
--- a/tools/trace_to_text/utils.cc
+++ b/tools/trace_to_text/utils.cc
@@ -21,6 +21,7 @@
#include <memory>
#include <ostream>
+#include <set>
#include <utility>
#include "perfetto/base/logging.h"
@@ -29,6 +30,7 @@
#include "protos/perfetto/trace/ftrace/ftrace_stats.pb.h"
#include "protos/perfetto/trace/trace.pb.h"
+#include "protos/perfetto/trace/trace.pbzero.h"
#include "protos/perfetto/trace/trace_packet.pb.h"
namespace perfetto {
@@ -92,8 +94,51 @@
return res;
}
+std::map<std::string, std::set<std::string>> GetHeapGraphClasses(
+ trace_processor::TraceProcessor* tp) {
+ std::map<std::string, std::set<std::string>> res;
+ Iterator it = tp->ExecuteQuery("select type_name from heap_graph_object");
+ while (it.Next())
+ res.emplace(it.Get(0).string_value, std::set<std::string>());
+
+ PERFETTO_CHECK(it.Status().ok());
+
+ it = tp->ExecuteQuery("select field_name from heap_graph_reference");
+ while (it.Next()) {
+ std::string field_name = it.Get(0).string_value;
+ if (field_name.size() == 0)
+ continue;
+ size_t n = field_name.rfind(".");
+ if (n == std::string::npos || n == field_name.size() - 1) {
+ PERFETTO_ELOG("Invalid field name: %s", field_name.c_str());
+ continue;
+ }
+ std::string class_name = field_name;
+ class_name.resize(n);
+ field_name = field_name.substr(n + 1);
+ res[class_name].emplace(field_name);
+ }
+
+ PERFETTO_CHECK(it.Status().ok());
+ return res;
+}
+
+using ::protozero::proto_utils::kMessageLengthFieldSize;
+using ::protozero::proto_utils::MakeTagLengthDelimited;
+using ::protozero::proto_utils::WriteVarInt;
+
} // 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) {
@@ -165,6 +210,14 @@
return roots;
}
+base::Optional<std::string> GetPerfettoProguardMapPath() {
+ base::Optional<std::string> proguard_map;
+ const char* env = getenv("PERFETTO_PROGUARD_MAP");
+ if (env != nullptr)
+ proguard_map = env;
+ return proguard_map;
+}
+
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.
constexpr size_t kChunkSize = 1024 * 1024;
@@ -238,6 +291,46 @@
}
}
+void DeobfuscateDatabase(
+ trace_processor::TraceProcessor* tp,
+ const std::map<std::string, profiling::ObfuscatedClass>& mapping,
+ std::function<void(perfetto::protos::TracePacket)> callback) {
+ std::map<std::string, std::set<std::string>> classes =
+ GetHeapGraphClasses(tp);
+ perfetto::protos::TracePacket packet;
+ // TODO(fmayer): Add handling for package name and version code here so we
+ // can support multiple dumps in the same trace.
+ perfetto::protos::DeobfuscationMapping* proto_mapping =
+ packet.mutable_deobfuscation_mapping();
+ for (const auto& p : classes) {
+ const std::string& obfuscated_class_name = p.first;
+ const std::set<std::string>& obfuscated_field_names = p.second;
+ auto it = mapping.find(obfuscated_class_name);
+ if (it == mapping.end()) {
+ // This can happen for non-obfuscated class names. Do not log.
+ continue;
+ }
+ const profiling::ObfuscatedClass& cls = it->second;
+ perfetto::protos::ObfuscatedClass* 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 std::string& obfuscated_field_name : obfuscated_field_names) {
+ auto field_it = cls.deobfuscated_fields.find(obfuscated_field_name);
+ if (field_it != cls.deobfuscated_fields.end()) {
+ perfetto::protos::ObfuscatedMember* proto_member =
+ proto_class->add_obfuscated_members();
+ proto_member->set_obfuscated_name(obfuscated_field_name);
+ proto_member->set_deobfuscated_name(field_it->second);
+ } else {
+ PERFETTO_ELOG("%s.%s not found", obfuscated_class_name.c_str(),
+ obfuscated_field_name.c_str());
+ }
+ }
+ }
+ callback(packet);
+}
+
TraceWriter::TraceWriter(std::ostream* output) : output_(output) {}
TraceWriter::~TraceWriter() = default;
diff --git a/tools/trace_to_text/utils.h b/tools/trace_to_text/utils.h
index f4004c3..9cc7c1f 100644
--- a/tools/trace_to_text/utils.h
+++ b/tools/trace_to_text/utils.h
@@ -29,7 +29,9 @@
#include <zlib.h>
#include "perfetto/base/build_config.h"
+#include "perfetto/ext/base/optional.h"
#include "perfetto/ext/base/paged_memory.h"
+#include "perfetto/profiling/deobfuscator.h"
#include "perfetto/profiling/symbolizer.h"
#include "perfetto/trace_processor/trace_processor.h"
@@ -58,14 +60,27 @@
const std::function<void(const protos::TracePacket&)>&);
std::vector<std::string> GetPerfettoBinaryPath();
+base::Optional<std::string> GetPerfettoProguardMapPath();
bool ReadTrace(trace_processor::TraceProcessor* tp, std::istream* input);
+void WriteTracePacket(const std::string& str, std::ostream* output);
+
+// Generate ModuleSymbol protos for all unsymbolized frames in the database.
+// Wrap them in TracePackets and call callback.
void SymbolizeDatabase(
trace_processor::TraceProcessor* tp,
Symbolizer* symbolizer,
std::function<void(perfetto::protos::TracePacket)> callback);
+// Generate ObfuscationMapping protos for all obfuscated java names in the
+// database.
+// Wrap them in TracePackets and call callback.
+void DeobfuscateDatabase(
+ trace_processor::TraceProcessor* tp,
+ const std::map<std::string, profiling::ObfuscatedClass>& mapping,
+ std::function<void(perfetto::protos::TracePacket)> callback);
+
class TraceWriter {
public:
TraceWriter(std::ostream* output);