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: