Separate the actual trace to pprof conversion

Bug: b/135648427
Change-Id: I275dc329a40ef9d7a1c9fe8c7b9b4891913ee198
diff --git a/Android.bp b/Android.bp
index 7a30101..81856fe 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3892,6 +3892,7 @@
     "src/trace_processor/virtual_destructors.cc",
     "src/trace_processor/window_operator_table.cc",
     "tools/trace_to_text/main.cc",
+    "tools/trace_to_text/pprof_builder.cc",
     "tools/trace_to_text/proto_full_utils.cc",
     "tools/trace_to_text/trace_to_profile.cc",
     "tools/trace_to_text/trace_to_systrace.cc",
diff --git a/BUILD b/BUILD
index 24967d0..aca32e3 100644
--- a/BUILD
+++ b/BUILD
@@ -120,6 +120,90 @@
     ],
 )
 
+# GN target: //tools/trace_to_text:libpprofbuilder
+cc_library(
+    name = "tools_trace_to_text_libpprofbuilder",
+    srcs = [
+        "src/protozero/message.cc",
+        "src/protozero/message_handle.cc",
+        "src/protozero/proto_decoder.cc",
+        "src/protozero/scattered_heap_buffer.cc",
+        "src/protozero/scattered_stream_null_delegate.cc",
+        "src/protozero/scattered_stream_writer.cc",
+        "tools/trace_to_text/pprof_builder.cc",
+        "tools/trace_to_text/pprof_builder.h",
+        "tools/trace_to_text/utils.cc",
+        "tools/trace_to_text/utils.h",
+    ],
+    hdrs = [
+        "include/perfetto/base/build_config.h",
+        "include/perfetto/base/compiler.h",
+        "include/perfetto/base/export.h",
+        "include/perfetto/base/logging.h",
+        "include/perfetto/base/task_runner.h",
+        "include/perfetto/ext/base/circular_queue.h",
+        "include/perfetto/ext/base/container_annotations.h",
+        "include/perfetto/ext/base/event.h",
+        "include/perfetto/ext/base/file_utils.h",
+        "include/perfetto/ext/base/hash.h",
+        "include/perfetto/ext/base/metatrace.h",
+        "include/perfetto/ext/base/metatrace_events.h",
+        "include/perfetto/ext/base/no_destructor.h",
+        "include/perfetto/ext/base/optional.h",
+        "include/perfetto/ext/base/paged_memory.h",
+        "include/perfetto/ext/base/pipe.h",
+        "include/perfetto/ext/base/scoped_file.h",
+        "include/perfetto/ext/base/small_set.h",
+        "include/perfetto/ext/base/string_splitter.h",
+        "include/perfetto/ext/base/string_utils.h",
+        "include/perfetto/ext/base/string_view.h",
+        "include/perfetto/ext/base/string_writer.h",
+        "include/perfetto/ext/base/temp_file.h",
+        "include/perfetto/ext/base/thread_annotations.h",
+        "include/perfetto/ext/base/thread_checker.h",
+        "include/perfetto/ext/base/thread_task_runner.h",
+        "include/perfetto/ext/base/thread_utils.h",
+        "include/perfetto/ext/base/time.h",
+        "include/perfetto/ext/base/unix_socket.h",
+        "include/perfetto/ext/base/unix_task_runner.h",
+        "include/perfetto/ext/base/utils.h",
+        "include/perfetto/ext/base/watchdog.h",
+        "include/perfetto/ext/base/watchdog_noop.h",
+        "include/perfetto/ext/base/watchdog_posix.h",
+        "include/perfetto/ext/base/weak_ptr.h",
+        "include/perfetto/ext/traced/sys_stats_counters.h",
+        "include/perfetto/protozero/contiguous_memory_range.h",
+        "include/perfetto/protozero/field.h",
+        "include/perfetto/protozero/message.h",
+        "include/perfetto/protozero/message_handle.h",
+        "include/perfetto/protozero/proto_decoder.h",
+        "include/perfetto/protozero/proto_utils.h",
+        "include/perfetto/protozero/scattered_heap_buffer.h",
+        "include/perfetto/protozero/scattered_stream_null_delegate.h",
+        "include/perfetto/protozero/scattered_stream_writer.h",
+    ],
+    deps = [
+        "//third_party/perfetto/protos:common_cc_proto",
+        "//third_party/perfetto/protos:common_zero_cc_proto",
+        "//third_party/perfetto/protos:config_cc_proto",
+        "//third_party/perfetto/protos:protos_third_party_pprof_cc_proto",
+        "//third_party/perfetto/protos:trace_android_cc_proto",
+        "//third_party/perfetto/protos:trace_cc_proto",
+        "//third_party/perfetto/protos:trace_chrome_cc_proto",
+        "//third_party/perfetto/protos:trace_filesystem_cc_proto",
+        "//third_party/perfetto/protos:trace_ftrace_cc_proto",
+        "//third_party/perfetto/protos:trace_gpu_cc_proto",
+        "//third_party/perfetto/protos:trace_interned_data_cc_proto",
+        "//third_party/perfetto/protos:trace_minimal_cc_proto",
+        "//third_party/perfetto/protos:trace_perfetto_cc_proto",
+        "//third_party/perfetto/protos:trace_power_cc_proto",
+        "//third_party/perfetto/protos:trace_profiling_cc_proto",
+        "//third_party/perfetto/protos:trace_ps_cc_proto",
+        "//third_party/perfetto/protos:trace_sys_stats_cc_proto",
+        "//third_party/perfetto/protos:trace_track_event_cc_proto",
+    ],
+)
+
 # GN target: //src/trace_processor:trace_processor
 cc_library(
     name = "trace_processor",
@@ -802,6 +886,8 @@
         "src/trace_processor/window_operator_table.cc",
         "src/trace_processor/window_operator_table.h",
         "tools/trace_to_text/main.cc",
+        "tools/trace_to_text/pprof_builder.cc",
+        "tools/trace_to_text/pprof_builder.h",
         "tools/trace_to_text/proto_full_utils.cc",
         "tools/trace_to_text/proto_full_utils.h",
         "tools/trace_to_text/trace_to_profile.cc",
diff --git a/tools/gen_bazel b/tools/gen_bazel
index 34f9083..010f69d 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -62,6 +62,7 @@
   '//src/protozero:libprotozero',
   '//src/trace_processor:trace_processor',
   '//tools/trace_to_text:trace_to_text_host(//gn/standalone/toolchain:gcc_like_host)',
+  '//tools/trace_to_text:libpprofbuilder',
   '//protos/perfetto/config:merged_config_gen',
   '//protos/perfetto/trace:merged_trace_gen',
   '//protos/perfetto/trace_processor:lite_gen',
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index e337971..2e4c789 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -16,9 +16,50 @@
 import("../../gn/proto_library.gni")
 import("../../gn/wasm.gni")
 
+source_set("utils") {
+  public_deps = [
+    "../../gn:default_deps",
+    "../../include/perfetto/base",
+    "../../include/perfetto/ext/traced:sys_stats_counters",
+    "../../protos/perfetto/trace:lite",
+    "../../protos/perfetto/trace/ftrace:lite",
+  ]
+  sources = [
+    "utils.cc",
+    "utils.h",
+  ]
+}
+
+source_set("pprofbuilder") {
+  deps = [
+    ":utils",
+    "../../gn:default_deps",
+    "../../include/perfetto/base",
+    "../../protos/perfetto/trace:lite",
+    "../../protos/perfetto/trace/profiling:lite",
+    "../../protos/third_party/pprof:lite",
+  ]
+  sources = [
+    "pprof_builder.cc",
+    "pprof_builder.h",
+  ]
+}
+
+if (perfetto_build_standalone) {
+  static_library("libpprofbuilder") {
+    deps = [
+      ":pprofbuilder",
+    ]
+  }
+}
+
 # The core source files that are used both by the "full" version (the host
 # executable) and by the "lite" version (the WASM module for the UI).
 source_set("common") {
+  deps = [
+    ":pprofbuilder",
+    ":utils",
+  ]
   public_deps = [
     "../../gn:default_deps",
     "../../include/perfetto/base",
@@ -26,7 +67,6 @@
     "../../protos/perfetto/trace:lite",
     "../../protos/perfetto/trace/ftrace:lite",
     "../../protos/perfetto/trace/profiling:lite",
-    "../../protos/third_party/pprof:lite",
     "../../src/base",
     "../../src/trace_processor:lib",
   ]
@@ -37,13 +77,9 @@
     "trace_to_systrace.cc",
     "trace_to_systrace.h",
     "trace_to_text.h",
-    "utils.cc",
-    "utils.h",
   ]
   if (perfetto_build_standalone) {
-    deps = [
-      "../../gn/standalone:gen_git_revision",
-    ]
+    deps += [ "../../gn/standalone:gen_git_revision" ]
   }
 }
 
@@ -63,6 +99,7 @@
   testonly = true
   deps = [
     ":common",
+    ":utils",
     "../../gn:default_deps",
     "../../gn:protobuf_full",
   ]
diff --git a/tools/trace_to_text/pprof_builder.cc b/tools/trace_to_text/pprof_builder.cc
new file mode 100644
index 0000000..3123dee
--- /dev/null
+++ b/tools/trace_to_text/pprof_builder.cc
@@ -0,0 +1,297 @@
+/*
+ * 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 "tools/trace_to_text/pprof_builder.h"
+
+#include <cxxabi.h>
+#include <inttypes.h>
+
+#include <algorithm>
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "tools/trace_to_text/utils.h"
+
+#include "perfetto/base/logging.h"
+
+#include "perfetto/trace/profiling/profile_common.pb.h"
+#include "perfetto/trace/profiling/profile_packet.pb.h"
+#include "perfetto/trace/trace.pb.h"
+#include "perfetto/trace/trace_packet.pb.h"
+
+#include "third_party/pprof/profile.pb.h"
+
+namespace perfetto {
+namespace trace_to_text {
+
+namespace {
+
+void MaybeDemangle(std::string* name) {
+  int ignored;
+  char* data = abi::__cxa_demangle(name->c_str(), nullptr, nullptr, &ignored);
+  if (data) {
+    *name = data;
+    free(data);
+  }
+}
+
+using ::perfetto::protos::Callstack;
+using ::perfetto::protos::Frame;
+using ::perfetto::protos::InternedString;
+using ::perfetto::protos::Mapping;
+using ::perfetto::protos::ProfilePacket;
+
+using GLine = ::perftools::profiles::Line;
+using GMapping = ::perftools::profiles::Mapping;
+using GLocation = ::perftools::profiles::Location;
+using GProfile = ::perftools::profiles::Profile;
+using GValueType = ::perftools::profiles::ValueType;
+using GFunction = ::perftools::profiles::Function;
+using GSample = ::perftools::profiles::Sample;
+
+std::string ToHex(const std::string& build_id) {
+  std::string hex_build_id(2 * build_id.size() + 1, ' ');
+  for (size_t i = 0; i < build_id.size(); ++i)
+    snprintf(&(hex_build_id[2 * i]), 3, "%02hhx", build_id[i]);
+  // Remove the trailing nullbyte.
+  hex_build_id.resize(2 * build_id.size());
+  return hex_build_id;
+}
+
+enum Strings : int64_t {
+  kEmpty = 0,
+  kObjects,
+  kAllocObjects,
+  kCount,
+  kSpace,
+  kAllocSpace,
+  kBytes,
+  kIdleSpace,
+};
+
+void DumpProfilePacket(const std::vector<ProfilePacket>& packet_fragments,
+                       std::vector<SerializedProfile>* output) {
+  std::map<uint64_t, std::string> string_lookup;
+  // A profile packet can be split into multiple fragments. We need to iterate
+  // over all of them to reconstruct the original packet.
+  for (const ProfilePacket& packet : packet_fragments) {
+    for (const InternedString& interned_string : packet.strings())
+      string_lookup.emplace(interned_string.iid(), interned_string.str());
+  }
+
+  std::map<uint64_t, const std::vector<uint64_t>> callstack_lookup;
+  for (const ProfilePacket& packet : packet_fragments) {
+    for (const Callstack& callstack : packet.callstacks()) {
+      std::vector<uint64_t> frame_ids(
+          static_cast<size_t>(callstack.frame_ids().size()));
+      std::reverse_copy(callstack.frame_ids().cbegin(),
+                        callstack.frame_ids().cend(), frame_ids.begin());
+      callstack_lookup.emplace(callstack.iid(), std::move(frame_ids));
+    }
+  }
+
+  std::map<std::string, uint64_t> string_table;
+  string_table[""] = kEmpty;
+  string_table["objects"] = kObjects;
+  string_table["alloc_objects"] = kAllocObjects;
+  string_table["count"] = kCount;
+  string_table["space"] = kSpace;
+  string_table["alloc_space"] = kAllocSpace;
+  string_table["bytes"] = kBytes;
+  string_table["idle_space"] = kIdleSpace;
+
+  GProfile profile;
+  GValueType* value_type = profile.add_sample_type();
+  value_type->set_type(kObjects);
+  value_type->set_unit(kCount);
+
+  value_type = profile.add_sample_type();
+  value_type->set_type(kAllocObjects);
+  value_type->set_unit(kCount);
+
+  value_type = profile.add_sample_type();
+  value_type->set_type(kIdleSpace);
+  value_type->set_unit(kBytes);
+
+  value_type = profile.add_sample_type();
+  value_type->set_type(kAllocSpace);
+  value_type->set_unit(kBytes);
+
+  // The last value is the default one selected.
+  value_type = profile.add_sample_type();
+  value_type->set_type(kSpace);
+  value_type->set_unit(kBytes);
+
+  for (const ProfilePacket& packet : packet_fragments) {
+    for (const Mapping& mapping : packet.mappings()) {
+      GMapping* gmapping = profile.add_mapping();
+      gmapping->set_id(mapping.iid());
+      gmapping->set_memory_start(mapping.start());
+      gmapping->set_memory_limit(mapping.end());
+      gmapping->set_file_offset(mapping.offset());
+      std::string filename;
+      for (uint64_t str_id : mapping.path_string_ids()) {
+        auto it = string_lookup.find(str_id);
+        if (it == string_lookup.end()) {
+          PERFETTO_ELOG("Mapping %" PRIu64
+                        " referring to invalid string_id %" PRIu64 ".",
+                        static_cast<uint64_t>(mapping.iid()), str_id);
+          continue;
+        }
+
+        filename += "/" + it->second;
+      }
+
+      decltype(string_table)::iterator it;
+      std::tie(it, std::ignore) =
+          string_table.emplace(filename, string_table.size());
+      gmapping->set_filename(static_cast<int64_t>(it->second));
+
+      auto str_it = string_lookup.find(mapping.build_id());
+      if (str_it != string_lookup.end()) {
+        const std::string& build_id = str_it->second;
+        std::tie(it, std::ignore) =
+            string_table.emplace(ToHex(build_id), string_table.size());
+        gmapping->set_build_id(static_cast<int64_t>(it->second));
+      }
+    }
+  }
+
+  std::set<uint64_t> functions_to_dump;
+  for (const ProfilePacket& packet : packet_fragments) {
+    for (const Frame& frame : packet.frames()) {
+      GLocation* glocation = profile.add_location();
+      glocation->set_id(frame.iid());
+      glocation->set_mapping_id(frame.mapping_id());
+      // TODO(fmayer): This is probably incorrect. Probably should be abs pc.
+      glocation->set_address(frame.rel_pc());
+      GLine* gline = glocation->add_line();
+      gline->set_function_id(frame.function_name_id());
+      functions_to_dump.emplace(frame.function_name_id());
+    }
+  }
+
+  for (uint64_t function_name_id : functions_to_dump) {
+    auto str_it = string_lookup.find(function_name_id);
+    if (str_it == string_lookup.end()) {
+      PERFETTO_ELOG("Function referring to invalid string id %" PRIu64,
+                    function_name_id);
+      continue;
+    }
+    decltype(string_table)::iterator it;
+    std::string function_name = str_it->second;
+    // This assumes both the device that captured the trace and the host
+    // machine use the same mangling scheme. This is a reasonable
+    // assumption as the Itanium ABI is the de-facto standard for mangling.
+    MaybeDemangle(&function_name);
+    std::tie(it, std::ignore) =
+        string_table.emplace(std::move(function_name), string_table.size());
+    GFunction* gfunction = profile.add_function();
+    gfunction->set_id(function_name_id);
+    gfunction->set_name(static_cast<int64_t>(it->second));
+  }
+
+  // We keep the interning table as string -> uint64_t for fast and easy
+  // lookup. When dumping, we need to turn it into a uint64_t -> string
+  // table so we get it sorted by key order.
+  std::map<uint64_t, std::string> inverted_string_table;
+  for (const auto& p : string_table)
+    inverted_string_table[p.second] = p.first;
+  for (const auto& p : inverted_string_table)
+    profile.add_string_table(p.second);
+
+  std::map<uint64_t, std::vector<const ProfilePacket::ProcessHeapSamples*>>
+      heap_samples;
+  for (const ProfilePacket& packet : packet_fragments) {
+    for (const ProfilePacket::ProcessHeapSamples& samples :
+         packet.process_dumps()) {
+      heap_samples[samples.pid()].emplace_back(&samples);
+    }
+  }
+  for (const auto& p : heap_samples) {
+    GProfile cur_profile = profile;
+    uint64_t pid = p.first;
+    for (const ProfilePacket::ProcessHeapSamples* samples : p.second) {
+      if (samples->rejected_concurrent()) {
+        PERFETTO_ELOG("WARNING: The profile for %" PRIu64
+                      " was rejected due to a concurrent profile.",
+                      pid);
+      }
+      if (samples->buffer_overran()) {
+        PERFETTO_ELOG("WARNING: The profile for %" PRIu64
+                      " ended early due to a buffer overrun.",
+                      pid);
+      }
+      if (samples->buffer_corrupted()) {
+        PERFETTO_ELOG("WARNING: The profile for %" PRIu64
+                      " ended early due to a buffer corruption."
+                      " THIS IS ALWAYS A BUG IN HEAPPROFD OR"
+                      " CLIENT MEMORY CORRUPTION.",
+                      pid);
+      }
+
+      for (const ProfilePacket::HeapSample& sample : samples->samples()) {
+        GSample* gsample = cur_profile.add_sample();
+        auto it = callstack_lookup.find(sample.callstack_id());
+        if (it == callstack_lookup.end()) {
+          PERFETTO_ELOG("Callstack referring to invalid callstack id %" PRIu64,
+                        static_cast<uint64_t>(sample.callstack_id()));
+          continue;
+        }
+        for (uint64_t frame_id : it->second)
+          gsample->add_location_id(frame_id);
+        gsample->add_value(
+            static_cast<int64_t>(sample.alloc_count() - sample.free_count()));
+        gsample->add_value(static_cast<int64_t>(sample.alloc_count()));
+        gsample->add_value(static_cast<int64_t>(sample.self_idle()));
+        gsample->add_value(static_cast<int64_t>(sample.self_allocated()));
+        gsample->add_value(static_cast<int64_t>(sample.self_allocated() -
+                                                sample.self_freed()));
+      }
+    }
+    output->push_back({pid, cur_profile.SerializeAsString()});
+  }
+}
+
+}  // namespace
+
+void TraceToPprof(std::istream* input, std::vector<SerializedProfile>* output) {
+  std::vector<ProfilePacket> rolling_profile_packets;
+  ForEachPacketInTrace(input, [output, &rolling_profile_packets](
+                                  const protos::TracePacket& packet) {
+    if (!packet.has_profile_packet())
+      return;
+    rolling_profile_packets.emplace_back(packet.profile_packet());
+    if (!packet.profile_packet().continued()) {
+      for (size_t i = 1; i < rolling_profile_packets.size(); ++i) {
+        // Ensure we are not missing a chunk.
+        PERFETTO_CHECK(rolling_profile_packets[i - 1].index() + 1 ==
+                       rolling_profile_packets[i].index());
+      }
+      DumpProfilePacket(rolling_profile_packets, output);
+      rolling_profile_packets.clear();
+    }
+  });
+
+  if (!rolling_profile_packets.empty()) {
+    PERFETTO_ELOG("WARNING: Truncated heap dump. Not generating profile.");
+  }
+}
+
+}  // namespace trace_to_text
+}  // namespace perfetto
diff --git a/tools/trace_to_text/pprof_builder.h b/tools/trace_to_text/pprof_builder.h
new file mode 100644
index 0000000..b4450d9
--- /dev/null
+++ b/tools/trace_to_text/pprof_builder.h
@@ -0,0 +1,37 @@
+/*
+ * 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_PPROF_BUILDER_H_
+#define TOOLS_TRACE_TO_TEXT_PPROF_BUILDER_H_
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace perfetto {
+namespace trace_to_text {
+
+struct SerializedProfile {
+  uint64_t pid;
+  std::string serialized;
+};
+
+void TraceToPprof(std::istream* input, std::vector<SerializedProfile>* output);
+
+}  // namespace trace_to_text
+}  // namespace perfetto
+
+#endif  // TOOLS_TRACE_TO_TEXT_PPROF_BUILDER_H_
diff --git a/tools/trace_to_text/trace_to_profile.cc b/tools/trace_to_text/trace_to_profile.cc
index 707b611..553584b 100644
--- a/tools/trace_to_text/trace_to_profile.cc
+++ b/tools/trace_to_text/trace_to_profile.cc
@@ -16,44 +16,20 @@
 
 #include "tools/trace_to_text/trace_to_profile.h"
 
-#include <cxxabi.h>
-#include <inttypes.h>
-
-#include <algorithm>
-#include <map>
-#include <set>
+#include <string>
 #include <vector>
 
-#include "tools/trace_to_text/utils.h"
+#include "tools/trace_to_text/pprof_builder.h"
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/temp_file.h"
 #include "perfetto/ext/base/utils.h"
 
-#include "perfetto/trace/profiling/profile_common.pb.h"
-#include "perfetto/trace/profiling/profile_packet.pb.h"
-#include "perfetto/trace/trace.pb.h"
-#include "perfetto/trace/trace_packet.pb.h"
-
-#include "third_party/pprof/profile.pb.h"
-
-namespace perfetto {
-namespace trace_to_text {
-
 namespace {
 
 constexpr const char* kDefaultTmp = "/tmp";
 
-void MaybeDemangle(std::string* name) {
-  int ignored;
-  char* data = abi::__cxa_demangle(name->c_str(), nullptr, nullptr, &ignored);
-  if (data) {
-    *name = data;
-    free(data);
-  }
-}
-
 std::string GetTemp() {
   const char* tmp = getenv("TMPDIR");
   if (tmp == nullptr)
@@ -61,261 +37,32 @@
   return tmp;
 }
 
-using ::perfetto::protos::Callstack;
-using ::perfetto::protos::Frame;
-using ::perfetto::protos::InternedString;
-using ::perfetto::protos::Mapping;
-using ::perfetto::protos::ProfilePacket;
+}  // namespace
 
-using GLine = ::perftools::profiles::Line;
-using GMapping = ::perftools::profiles::Mapping;
-using GLocation = ::perftools::profiles::Location;
-using GProfile = ::perftools::profiles::Profile;
-using GValueType = ::perftools::profiles::ValueType;
-using GFunction = ::perftools::profiles::Function;
-using GSample = ::perftools::profiles::Sample;
+namespace perfetto {
+namespace trace_to_text {
 
-std::string ToHex(const std::string& build_id) {
-  std::string hex_build_id(2 * build_id.size() + 1, ' ');
-  for (size_t i = 0; i < build_id.size(); ++i)
-    snprintf(&(hex_build_id[2 * i]), 3, "%02hhx", build_id[i]);
-  // Remove the trailing nullbyte.
-  hex_build_id.resize(2 * build_id.size());
-  return hex_build_id;
-}
-
-enum Strings : int64_t {
-  kEmpty = 0,
-  kObjects,
-  kAllocObjects,
-  kCount,
-  kSpace,
-  kAllocSpace,
-  kBytes,
-  kIdleSpace,
-};
-
-void DumpProfilePacket(std::vector<ProfilePacket>& packet_fragments,
-                       const std::string& file_prefix) {
-  std::map<uint64_t, std::string> string_lookup;
-  // A profile packet can be split into multiple fragments. We need to iterate
-  // over all of them to reconstruct the original packet.
-  for (const ProfilePacket& packet : packet_fragments) {
-    for (const InternedString& interned_string : packet.strings())
-      string_lookup.emplace(interned_string.iid(), interned_string.str());
+int TraceToProfile(std::istream* input, std::ostream* output) {
+  std::vector<SerializedProfile> profiles;
+  TraceToPprof(input, &profiles);
+  if (profiles.empty()) {
+    return 0;
   }
 
-  std::map<uint64_t, const std::vector<uint64_t>> callstack_lookup;
-  for (const ProfilePacket& packet : packet_fragments) {
-    for (const Callstack& callstack : packet.callstacks()) {
-      std::vector<uint64_t> frame_ids(
-          static_cast<size_t>(callstack.frame_ids().size()));
-      std::reverse_copy(callstack.frame_ids().cbegin(),
-                        callstack.frame_ids().cend(), frame_ids.begin());
-      callstack_lookup.emplace(callstack.iid(), std::move(frame_ids));
-    }
-  }
-
-  std::map<std::string, uint64_t> string_table;
-  string_table[""] = kEmpty;
-  string_table["objects"] = kObjects;
-  string_table["alloc_objects"] = kAllocObjects;
-  string_table["count"] = kCount;
-  string_table["space"] = kSpace;
-  string_table["alloc_space"] = kAllocSpace;
-  string_table["bytes"] = kBytes;
-  string_table["idle_space"] = kIdleSpace;
-
-  GProfile profile;
-  GValueType* value_type = profile.add_sample_type();
-  value_type->set_type(kObjects);
-  value_type->set_unit(kCount);
-
-  value_type = profile.add_sample_type();
-  value_type->set_type(kAllocObjects);
-  value_type->set_unit(kCount);
-
-  value_type = profile.add_sample_type();
-  value_type->set_type(kIdleSpace);
-  value_type->set_unit(kBytes);
-
-  value_type = profile.add_sample_type();
-  value_type->set_type(kAllocSpace);
-  value_type->set_unit(kBytes);
-
-  // The last value is the default one selected.
-  value_type = profile.add_sample_type();
-  value_type->set_type(kSpace);
-  value_type->set_unit(kBytes);
-
-  for (const ProfilePacket& packet : packet_fragments) {
-    for (const Mapping& mapping : packet.mappings()) {
-      GMapping* gmapping = profile.add_mapping();
-      gmapping->set_id(mapping.iid());
-      gmapping->set_memory_start(mapping.start());
-      gmapping->set_memory_limit(mapping.end());
-      gmapping->set_file_offset(mapping.offset());
-      std::string filename;
-      for (uint64_t str_id : mapping.path_string_ids()) {
-        auto it = string_lookup.find(str_id);
-        if (it == string_lookup.end()) {
-          PERFETTO_ELOG("Mapping %" PRIu64
-                        " referring to invalid string_id %" PRIu64 ".",
-                        static_cast<uint64_t>(mapping.iid()), str_id);
-          continue;
-        }
-
-        filename += "/" + it->second;
-      }
-
-      decltype(string_table)::iterator it;
-      std::tie(it, std::ignore) =
-          string_table.emplace(filename, string_table.size());
-      gmapping->set_filename(static_cast<int64_t>(it->second));
-
-      auto str_it = string_lookup.find(mapping.build_id());
-      if (str_it != string_lookup.end()) {
-        const std::string& build_id = str_it->second;
-        std::tie(it, std::ignore) =
-            string_table.emplace(ToHex(build_id), string_table.size());
-        gmapping->set_build_id(static_cast<int64_t>(it->second));
-      }
-    }
-  }
-
-  std::set<uint64_t> functions_to_dump;
-  for (const ProfilePacket& packet : packet_fragments) {
-    for (const Frame& frame : packet.frames()) {
-      GLocation* glocation = profile.add_location();
-      glocation->set_id(frame.iid());
-      glocation->set_mapping_id(frame.mapping_id());
-      // TODO(fmayer): This is probably incorrect. Probably should be abs pc.
-      glocation->set_address(frame.rel_pc());
-      GLine* gline = glocation->add_line();
-      gline->set_function_id(frame.function_name_id());
-      functions_to_dump.emplace(frame.function_name_id());
-    }
-  }
-
-  for (uint64_t function_name_id : functions_to_dump) {
-    auto str_it = string_lookup.find(function_name_id);
-    if (str_it == string_lookup.end()) {
-      PERFETTO_ELOG("Function referring to invalid string id %" PRIu64,
-                    function_name_id);
-      continue;
-    }
-    decltype(string_table)::iterator it;
-    std::string function_name = str_it->second;
-    // This assumes both the device that captured the trace and the host
-    // machine use the same mangling scheme. This is a reasonable
-    // assumption as the Itanium ABI is the de-facto standard for mangling.
-    MaybeDemangle(&function_name);
-    std::tie(it, std::ignore) =
-        string_table.emplace(std::move(function_name), string_table.size());
-    GFunction* gfunction = profile.add_function();
-    gfunction->set_id(function_name_id);
-    gfunction->set_name(static_cast<int64_t>(it->second));
-  }
-
-  // We keep the interning table as string -> uint64_t for fast and easy
-  // lookup. When dumping, we need to turn it into a uint64_t -> string
-  // table so we get it sorted by key order.
-  std::map<uint64_t, std::string> inverted_string_table;
-  for (const auto& p : string_table)
-    inverted_string_table[p.second] = p.first;
-  for (const auto& p : inverted_string_table)
-    profile.add_string_table(p.second);
-
-  std::map<uint64_t, std::vector<const ProfilePacket::ProcessHeapSamples*>>
-      heap_samples;
-  for (const ProfilePacket& packet : packet_fragments) {
-    for (const ProfilePacket::ProcessHeapSamples& samples :
-         packet.process_dumps()) {
-      heap_samples[samples.pid()].emplace_back(&samples);
-    }
-  }
-  for (const auto& p : heap_samples) {
-    GProfile cur_profile = profile;
-    uint64_t pid = p.first;
-    for (const ProfilePacket::ProcessHeapSamples* samples : p.second) {
-      if (samples->rejected_concurrent()) {
-        PERFETTO_ELOG("WARNING: The profile for %" PRIu64
-                      " was rejected due to a concurrent profile.",
-                      pid);
-      }
-      if (samples->buffer_overran()) {
-        PERFETTO_ELOG("WARNING: The profile for %" PRIu64
-                      " ended early due to a buffer overrun.",
-                      pid);
-      }
-      if (samples->buffer_corrupted()) {
-        PERFETTO_ELOG("WARNING: The profile for %" PRIu64
-                      " ended early due to a buffer corruption."
-                      " THIS IS ALWAYS A BUG IN HEAPPROFD OR"
-                      " CLIENT MEMORY CORRUPTION.",
-                      pid);
-      }
-
-      for (const ProfilePacket::HeapSample& sample : samples->samples()) {
-        GSample* gsample = cur_profile.add_sample();
-        auto it = callstack_lookup.find(sample.callstack_id());
-        if (it == callstack_lookup.end()) {
-          PERFETTO_ELOG("Callstack referring to invalid callstack id %" PRIu64,
-                        static_cast<uint64_t>(sample.callstack_id()));
-          continue;
-        }
-        for (uint64_t frame_id : it->second)
-          gsample->add_location_id(frame_id);
-        gsample->add_value(
-            static_cast<int64_t>(sample.alloc_count() - sample.free_count()));
-        gsample->add_value(static_cast<int64_t>(sample.alloc_count()));
-        gsample->add_value(static_cast<int64_t>(sample.self_idle()));
-        gsample->add_value(static_cast<int64_t>(sample.self_allocated()));
-        gsample->add_value(static_cast<int64_t>(sample.self_allocated() -
-                                                sample.self_freed()));
-      }
-    }
-    std::string filename = file_prefix + std::to_string(pid) + ".pb";
+  std::string temp_dir = GetTemp() + "/heap_profile-XXXXXXX";
+  PERFETTO_CHECK(mkdtemp(&temp_dir[0]));
+  size_t itr = 0;
+  for (const auto& profile : profiles) {
+    std::string filename = temp_dir + "/heap_dump." + std::to_string(++itr) +
+                           "." + std::to_string(profile.pid) + ".pb";
     base::ScopedFile fd(base::OpenFile(filename, O_CREAT | O_WRONLY, 0700));
     if (!fd)
       PERFETTO_FATAL("Failed to open %s", filename.c_str());
-    std::string serialized = cur_profile.SerializeAsString();
-    PERFETTO_CHECK(base::WriteAll(*fd, serialized.c_str(), serialized.size()) ==
-                   static_cast<ssize_t>(serialized.size()));
+    PERFETTO_CHECK(base::WriteAll(*fd, profile.serialized.c_str(),
+                                  profile.serialized.size()) ==
+                   static_cast<ssize_t>(profile.serialized.size()));
   }
-}
-
-}  // namespace
-
-int TraceToProfile(std::istream* input, std::ostream* output) {
-  std::string temp_dir = GetTemp() + "/heap_profile-XXXXXXX";
-  size_t itr = 0;
-  PERFETTO_CHECK(mkdtemp(&temp_dir[0]));
-  std::vector<ProfilePacket> rolling_profile_packets;
-  ForEachPacketInTrace(input, [&temp_dir, &itr, &rolling_profile_packets](
-                                  const protos::TracePacket& packet) {
-    if (!packet.has_profile_packet())
-      return;
-    rolling_profile_packets.emplace_back(packet.profile_packet());
-    if (!packet.profile_packet().continued()) {
-      for (size_t i = 1; i < rolling_profile_packets.size(); ++i) {
-        // Ensure we are not missing a chunk.
-        PERFETTO_CHECK(rolling_profile_packets[i - 1].index() + 1 ==
-                       rolling_profile_packets[i].index());
-      }
-      DumpProfilePacket(rolling_profile_packets,
-                        temp_dir + "/heap_dump." + std::to_string(++itr) + ".");
-      rolling_profile_packets.clear();
-    }
-  });
-
-  if (!rolling_profile_packets.empty()) {
-    *output << "WARNING: Truncated heap dump. Not generating profile."
-            << std::endl;
-  }
-
   *output << "Wrote profiles to " << temp_dir << std::endl;
-
   return 0;
 }