Merge "trace_processor: migrate cpu stack profile table to db tables"
diff --git a/Android.bp b/Android.bp
index 8a8bcec..40d42c4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -979,9 +979,9 @@
   name: "perfetto_include_perfetto_profiling_normalize",
 }
 
-// GN: //include/perfetto/profiling:symbolizer
+// GN: //include/perfetto/profiling:pprof_builder
 filegroup {
-  name: "perfetto_include_perfetto_profiling_symbolizer",
+  name: "perfetto_include_perfetto_profiling_pprof_builder",
 }
 
 // GN: //include/perfetto/protozero:protozero
@@ -5424,6 +5424,23 @@
   ],
 }
 
+// GN: //src/profiling/symbolizer:symbolize_database
+filegroup {
+  name: "perfetto_src_profiling_symbolizer_symbolize_database",
+  srcs: [
+    "src/profiling/symbolizer/symbolize_database.cc",
+  ],
+}
+
+// GN: //src/profiling/symbolizer:symbolizer
+filegroup {
+  name: "perfetto_src_profiling_symbolizer_symbolizer",
+  srcs: [
+    "src/profiling/symbolizer/local_symbolizer.cc",
+    "src/profiling/symbolizer/symbolizer.cc",
+  ],
+}
+
 // GN: //src/profiling:unittests
 filegroup {
   name: "perfetto_src_profiling_unittests",
@@ -6366,14 +6383,6 @@
   ],
 }
 
-// GN: //tools/trace_to_text:local_symbolizer
-filegroup {
-  name: "perfetto_tools_trace_to_text_local_symbolizer",
-  srcs: [
-    "tools/trace_to_text/local_symbolizer.cc",
-  ],
-}
-
 // GN: //tools/trace_to_text:pprofbuilder
 filegroup {
   name: "perfetto_tools_trace_to_text_pprofbuilder",
@@ -6382,14 +6391,6 @@
   ],
 }
 
-// GN: //tools/trace_to_text:symbolizer
-filegroup {
-  name: "perfetto_tools_trace_to_text_symbolizer",
-  srcs: [
-    "tools/trace_to_text/symbolizer.cc",
-  ],
-}
-
 // GN: //tools/trace_to_text:utils
 filegroup {
   name: "perfetto_tools_trace_to_text_utils",
@@ -6820,6 +6821,8 @@
     ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
     ":perfetto_protos_perfetto_trace_track_event_zero_gen",
     ":perfetto_src_base_base",
+    ":perfetto_src_profiling_symbolizer_symbolize_database",
+    ":perfetto_src_profiling_symbolizer_symbolizer",
     ":perfetto_src_protozero_protozero",
     ":perfetto_src_trace_processor_containers_containers",
     ":perfetto_src_trace_processor_db_lib",
@@ -6892,7 +6895,7 @@
     ":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_profiling_pprof_builder",
     ":perfetto_include_perfetto_protozero_protozero",
     ":perfetto_include_perfetto_trace_processor_basic_types",
     ":perfetto_include_perfetto_trace_processor_storage",
@@ -6927,6 +6930,8 @@
     ":perfetto_protos_third_party_pprof_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",
     ":perfetto_src_trace_processor_containers_containers",
     ":perfetto_src_trace_processor_db_lib",
@@ -6939,9 +6944,7 @@
     ":perfetto_src_trace_processor_tables_tables",
     ":perfetto_tools_trace_to_text_common",
     ":perfetto_tools_trace_to_text_full",
-    ":perfetto_tools_trace_to_text_local_symbolizer",
     ":perfetto_tools_trace_to_text_pprofbuilder",
-    ":perfetto_tools_trace_to_text_symbolizer",
     ":perfetto_tools_trace_to_text_utils",
   ],
   shared_libs: [
diff --git a/BUILD b/BUILD
index 1db0452..70f43b5 100644
--- a/BUILD
+++ b/BUILD
@@ -332,12 +332,11 @@
     ],
 )
 
-# GN target: //include/perfetto/profiling:symbolizer
+# GN target: //include/perfetto/profiling:pprof_builder
 filegroup(
-    name = "include_perfetto_profiling_symbolizer",
+    name = "include_perfetto_profiling_pprof_builder",
     srcs = [
         "include/perfetto/profiling/pprof_builder.h",
-        "include/perfetto/profiling/symbolizer.h",
     ],
 )
 
@@ -547,6 +546,26 @@
     ],
 )
 
+# GN target: //src/profiling/symbolizer:symbolize_database
+filegroup(
+    name = "src_profiling_symbolizer_symbolize_database",
+    srcs = [
+        "src/profiling/symbolizer/symbolize_database.cc",
+        "src/profiling/symbolizer/symbolize_database.h",
+    ],
+)
+
+# GN target: //src/profiling/symbolizer:symbolizer
+filegroup(
+    name = "src_profiling_symbolizer_symbolizer",
+    srcs = [
+        "src/profiling/symbolizer/local_symbolizer.cc",
+        "src/profiling/symbolizer/local_symbolizer.h",
+        "src/profiling/symbolizer/symbolizer.cc",
+        "src/profiling/symbolizer/symbolizer.h",
+    ],
+)
+
 # GN target: //src/profiling:deobfuscator
 filegroup(
     name = "src_profiling_deobfuscator",
@@ -1175,15 +1194,6 @@
     ],
 )
 
-# GN target: //tools/trace_to_text:local_symbolizer
-filegroup(
-    name = "tools_trace_to_text_local_symbolizer",
-    srcs = [
-        "tools/trace_to_text/local_symbolizer.cc",
-        "tools/trace_to_text/local_symbolizer.h",
-    ],
-)
-
 # GN target: //tools/trace_to_text:pprofbuilder
 filegroup(
     name = "tools_trace_to_text_pprofbuilder",
@@ -1192,14 +1202,6 @@
     ],
 )
 
-# GN target: //tools/trace_to_text:symbolizer
-filegroup(
-    name = "tools_trace_to_text_symbolizer",
-    srcs = [
-        "tools/trace_to_text/symbolizer.cc",
-    ],
-)
-
 # GN target: //tools/trace_to_text:utils
 filegroup(
     name = "tools_trace_to_text_utils",
@@ -2593,6 +2595,8 @@
         ":include_perfetto_trace_processor_trace_processor",
         ":src_base_base",
         ":src_base_unix_socket",
+        ":src_profiling_symbolizer_symbolize_database",
+        ":src_profiling_symbolizer_symbolizer",
         ":src_protozero_protozero",
         ":src_trace_processor_containers_containers",
         ":src_trace_processor_db_lib",
@@ -2684,15 +2688,16 @@
     name = "libpprofbuilder",
     srcs = [
         ":src_profiling_deobfuscator",
+        ":src_profiling_symbolizer_symbolize_database",
+        ":src_profiling_symbolizer_symbolizer",
         ":tools_trace_to_text_pprofbuilder",
-        ":tools_trace_to_text_symbolizer",
         ":tools_trace_to_text_utils",
     ],
     hdrs = [
         ":include_perfetto_base_base",
         ":include_perfetto_ext_base_base",
         ":include_perfetto_profiling_deobfuscator",
-        ":include_perfetto_profiling_symbolizer",
+        ":include_perfetto_profiling_pprof_builder",
         ":include_perfetto_protozero_protozero",
         ":include_perfetto_trace_processor_basic_types",
         ":include_perfetto_trace_processor_storage",
@@ -2739,13 +2744,15 @@
         ":include_perfetto_ext_trace_processor_export_json",
         ":include_perfetto_ext_traced_sys_stats_counters",
         ":include_perfetto_profiling_deobfuscator",
-        ":include_perfetto_profiling_symbolizer",
+        ":include_perfetto_profiling_pprof_builder",
         ":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_profiling_symbolizer_symbolize_database",
+        ":src_profiling_symbolizer_symbolizer",
         ":src_protozero_protozero",
         ":src_trace_processor_containers_containers",
         ":src_trace_processor_db_lib",
@@ -2759,9 +2766,7 @@
         ":src_trace_processor_tables_tables",
         ":tools_trace_to_text_common",
         ":tools_trace_to_text_full",
-        ":tools_trace_to_text_local_symbolizer",
         ":tools_trace_to_text_pprofbuilder",
-        ":tools_trace_to_text_symbolizer",
         ":tools_trace_to_text_utils",
     ],
     visibility = [
diff --git a/docs/heapprofd.md b/docs/heapprofd.md
index 50ae55f..5abb440 100644
--- a/docs/heapprofd.md
+++ b/docs/heapprofd.md
@@ -166,15 +166,24 @@
 enumerated in the output directory.
 
 ## Symbolization
-If the profiled binary or libraries do not have debug symbols, you can use
-pprof to symbolize offline.
+If the profiled binary or libraries do not have debug symbols, you can
+symbolize profiles offline. All tools (traceconv, trace_processor_shell,
+the heap_profile script) support specifying `PERFETTO_BINARY_PATH` as an
+environment variable.
 
-To do so, copy symbolized versions of your binary and/or libraries into a
-directory. Then run
-`PPROF_BINARY_PATH=thatdirectory pprof heap_profile.${n}.${pid}.gz`, and pprof
-will read symbol information from these files.
+For instance, run
 
-You can save the symbolized version by issuing the `proto` command in pprof.
+```
+PERFETTO_BINARY_PATH=somedir tools/heap_profile --name ${NAME}
+```
+
+and the output files (`*.pb.gz`) will be symbolized.
+
+You can persist symbols for a trace by running
+`PERFETTO_BINARY_PATH=somedir tools/traceconv symbolize raw-trace > symbols`.
+You can then concatenate the symbols to the trace (
+`cat raw-trace symbols > symbolized-trace`) and the symbols will part of
+`symbolized-trace`.
 
 ## Troubleshooting
 
diff --git a/include/perfetto/profiling/BUILD.gn b/include/perfetto/profiling/BUILD.gn
index 03f8350..ce7c188 100644
--- a/include/perfetto/profiling/BUILD.gn
+++ b/include/perfetto/profiling/BUILD.gn
@@ -12,13 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# TODO(135923303): as part of fixing b/135923303, symbolizer.h will be removed
-# and namespacing of pprof_builder.h will be changed so do not depend on this
-# target.
-source_set("symbolizer") {
+source_set("pprof_builder") {
   sources = [
     "pprof_builder.h",
-    "symbolizer.h",
   ]
 }
 
diff --git a/include/perfetto/profiling/pprof_builder.h b/include/perfetto/profiling/pprof_builder.h
index 4227a8c..7e26f51 100644
--- a/include/perfetto/profiling/pprof_builder.h
+++ b/include/perfetto/profiling/pprof_builder.h
@@ -27,9 +27,11 @@
 class TraceProcessor;
 }
 
-namespace trace_to_text {
-
+namespace profiling {
 class Symbolizer;
+}
+
+namespace trace_to_text {
 
 struct SerializedProfile {
   uint64_t pid;
@@ -38,13 +40,13 @@
 
 bool TraceToPprof(trace_processor::TraceProcessor*,
                   std::vector<SerializedProfile>* output,
-                  Symbolizer* symbolizer,
+                  profiling::Symbolizer* symbolizer,
                   uint64_t pid = 0,
                   const std::vector<uint64_t>& timestamps = {});
 
 bool TraceToPprof(std::istream* input,
                   std::vector<SerializedProfile>* output,
-                  Symbolizer* symbolizer,
+                  profiling::Symbolizer* symbolizer,
                   uint64_t pid = 0,
                   const std::vector<uint64_t>& timestamps = {});
 
diff --git a/src/profiling/symbolizer/BUILD.gn b/src/profiling/symbolizer/BUILD.gn
new file mode 100644
index 0000000..3bbc385
--- /dev/null
+++ b/src/profiling/symbolizer/BUILD.gn
@@ -0,0 +1,48 @@
+# 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.
+
+import("../../../gn/perfetto.gni")
+
+source_set("symbolizer") {
+  public_deps = [
+    "../../../include/perfetto/ext/base",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+  ]
+  sources = [
+    "local_symbolizer.cc",
+    "local_symbolizer.h",
+    "symbolizer.cc",
+    "symbolizer.h",
+  ]
+}
+
+source_set("symbolize_database") {
+  public_deps = [
+    ":symbolizer",
+    "../../../include/perfetto/ext/base",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../include/perfetto/protozero",
+    "../../../include/perfetto/trace_processor:trace_processor",
+    "../../../protos/perfetto/trace:zero",
+    "../../../protos/perfetto/trace/profiling:zero",
+  ]
+  sources = [
+    "symbolize_database.cc",
+    "symbolize_database.h",
+  ]
+}
diff --git a/tools/trace_to_text/local_symbolizer.cc b/src/profiling/symbolizer/local_symbolizer.cc
similarity index 98%
rename from tools/trace_to_text/local_symbolizer.cc
rename to src/profiling/symbolizer/local_symbolizer.cc
index 3daa0be..1874df1 100644
--- a/tools/trace_to_text/local_symbolizer.cc
+++ b/src/profiling/symbolizer/local_symbolizer.cc
@@ -20,7 +20,7 @@
 // This translation unit is built only on Linux. See //gn/BUILD.gn.
 #if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
 
-#include "tools/trace_to_text/local_symbolizer.h"
+#include "src/profiling/symbolizer/local_symbolizer.h"
 
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
@@ -35,7 +35,7 @@
 #include <unistd.h>
 
 namespace perfetto {
-namespace trace_to_text {
+namespace profiling {
 
 namespace {
 
@@ -60,7 +60,6 @@
   return lines;
 }
 
-
 struct Elf32 {
   using Ehdr = Elf32_Ehdr;
   using Shdr = Elf32_Shdr;
@@ -402,7 +401,7 @@
 
 LocalSymbolizer::~LocalSymbolizer() = default;
 
-}  // namespace trace_to_text
+}  // namespace profiling
 }  // namespace perfetto
 
 #endif  // PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
diff --git a/tools/trace_to_text/local_symbolizer.h b/src/profiling/symbolizer/local_symbolizer.h
similarity index 90%
rename from tools/trace_to_text/local_symbolizer.h
rename to src/profiling/symbolizer/local_symbolizer.h
index e1eef9e..eb3a717 100644
--- a/tools/trace_to_text/local_symbolizer.h
+++ b/src/profiling/symbolizer/local_symbolizer.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef TOOLS_TRACE_TO_TEXT_LOCAL_SYMBOLIZER_H_
-#define TOOLS_TRACE_TO_TEXT_LOCAL_SYMBOLIZER_H_
+#ifndef SRC_PROFILING_SYMBOLIZER_LOCAL_SYMBOLIZER_H_
+#define SRC_PROFILING_SYMBOLIZER_LOCAL_SYMBOLIZER_H_
 
 #include <map>
 #include <string>
@@ -23,10 +23,10 @@
 
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/pipe.h"
-#include "perfetto/profiling/symbolizer.h"
+#include "src/profiling/symbolizer/symbolizer.h"
 
 namespace perfetto {
-namespace trace_to_text {
+namespace profiling {
 
 class LocalBinaryFinder {
  public:
@@ -93,7 +93,7 @@
   LocalBinaryFinder finder_;
 };
 
-}  // namespace trace_to_text
+}  // namespace profiling
 }  // namespace perfetto
 
-#endif  // TOOLS_TRACE_TO_TEXT_LOCAL_SYMBOLIZER_H_
+#endif  // SRC_PROFILING_SYMBOLIZER_LOCAL_SYMBOLIZER_H_
diff --git a/src/profiling/symbolizer/symbolize_database.cc b/src/profiling/symbolizer/symbolize_database.cc
new file mode 100644
index 0000000..23ab640
--- /dev/null
+++ b/src/profiling/symbolizer/symbolize_database.cc
@@ -0,0 +1,137 @@
+/*
+ * 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 "src/profiling/symbolizer/symbolize_database.h"
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_splitter.h"
+
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "perfetto/trace_processor/trace_processor.h"
+
+#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
+#include "protos/perfetto/trace/trace.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+namespace profiling {
+
+namespace {
+using Iterator = trace_processor::TraceProcessor::Iterator;
+
+constexpr const char* kQueryUnsymbolized =
+    "select spm.name, spm.build_id, spf.rel_pc "
+    "from stack_profile_frame spf "
+    "join stack_profile_mapping spm "
+    "on spf.mapping = spm.id "
+    "where spm.build_id != '' and spf.symbol_set_id == 0";
+
+using NameAndBuildIdPair = std::pair<std::string, std::string>;
+
+std::string FromHex(const char* str, size_t size) {
+  if (size % 2) {
+    PERFETTO_DFATAL_OR_ELOG("Failed to parse hex %s", str);
+    return "";
+  }
+  std::string result(size / 2, '\0');
+  for (size_t i = 0; i < size; i += 2) {
+    char hex_byte[3];
+    hex_byte[0] = str[i];
+    hex_byte[1] = str[i + 1];
+    hex_byte[2] = '\0';
+    char* end;
+    long int byte = strtol(hex_byte, &end, 16);
+    if (*end != '\0') {
+      PERFETTO_DFATAL_OR_ELOG("Failed to parse hex %s", str);
+      return "";
+    }
+    result[i / 2] = static_cast<char>(byte);
+  }
+  return result;
+}
+
+std::string FromHex(const std::string& str) {
+  return FromHex(str.c_str(), str.size());
+}
+
+std::map<NameAndBuildIdPair, std::vector<uint64_t>> GetUnsymbolizedFrames(
+    trace_processor::TraceProcessor* tp) {
+  std::map<std::pair<std::string, std::string>, std::vector<uint64_t>> res;
+  Iterator it = tp->ExecuteQuery(kQueryUnsymbolized);
+  while (it.Next()) {
+    auto name_and_buildid =
+        std::make_pair(it.Get(0).string_value, FromHex(it.Get(1).string_value));
+    int64_t rel_pc = it.Get(2).long_value;
+    res[name_and_buildid].emplace_back(rel_pc);
+  }
+  if (!it.Status().ok()) {
+    PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
+                            it.Status().message().c_str());
+    return {};
+  }
+  return res;
+}
+}  // namespace
+
+void SymbolizeDatabase(trace_processor::TraceProcessor* tp,
+                       Symbolizer* symbolizer,
+                       std::function<void(const std::string&)> callback) {
+  PERFETTO_CHECK(symbolizer);
+  auto unsymbolized = GetUnsymbolizedFrames(tp);
+  for (auto it = unsymbolized.cbegin(); it != unsymbolized.cend(); ++it) {
+    const auto& name_and_buildid = it->first;
+    const std::vector<uint64_t>& rel_pcs = it->second;
+    auto res = symbolizer->Symbolize(name_and_buildid.first,
+                                     name_and_buildid.second, rel_pcs);
+    if (res.empty())
+      continue;
+
+    protozero::HeapBuffered<perfetto::protos::pbzero::Trace> trace;
+    auto* packet = trace->add_packet();
+    auto* module_symbols = packet->set_module_symbols();
+    module_symbols->set_path(name_and_buildid.first);
+    module_symbols->set_build_id(name_and_buildid.second);
+    PERFETTO_DCHECK(res.size() == rel_pcs.size());
+    for (size_t i = 0; i < res.size(); ++i) {
+      auto* address_symbols = module_symbols->add_address_symbols();
+      address_symbols->set_address(rel_pcs[i]);
+      for (const SymbolizedFrame& frame : res[i]) {
+        auto* line = address_symbols->add_lines();
+        line->set_function_name(frame.function_name);
+        line->set_source_file_name(frame.file_name);
+        line->set_line_number(frame.line);
+      }
+    }
+    callback(trace.SerializeAsString());
+  }
+}
+
+std::vector<std::string> GetPerfettoBinaryPath() {
+  std::vector<std::string> roots;
+  const char* root = getenv("PERFETTO_BINARY_PATH");
+  if (root != nullptr) {
+    for (base::StringSplitter sp(std::string(root), ':'); sp.Next();)
+      roots.emplace_back(sp.cur_token(), sp.cur_token_size());
+  }
+  return roots;
+}
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/symbolizer/symbolize_database.h b/src/profiling/symbolizer/symbolize_database.h
new file mode 100644
index 0000000..ee9cfc1
--- /dev/null
+++ b/src/profiling/symbolizer/symbolize_database.h
@@ -0,0 +1,40 @@
+/*
+ * 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 SRC_PROFILING_SYMBOLIZER_SYMBOLIZE_DATABASE_H_
+#define SRC_PROFILING_SYMBOLIZER_SYMBOLIZE_DATABASE_H_
+
+#include "src/profiling/symbolizer/symbolizer.h"
+
+#include <functional>
+#include <string>
+#include <vector>
+
+namespace perfetto {
+namespace trace_processor {
+class TraceProcessor;
+}
+namespace profiling {
+std::vector<std::string> GetPerfettoBinaryPath();
+// Generate ModuleSymbol protos for all unsymbolized frames in the database.
+// Wrap them in proto-encoded TracePackets messages and call callback.
+void SymbolizeDatabase(trace_processor::TraceProcessor* tp,
+                       Symbolizer* symbolizer,
+                       std::function<void(const std::string&)> callback);
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_SYMBOLIZER_SYMBOLIZE_DATABASE_H_
diff --git a/tools/trace_to_text/symbolizer.cc b/src/profiling/symbolizer/symbolizer.cc
similarity index 87%
rename from tools/trace_to_text/symbolizer.cc
rename to src/profiling/symbolizer/symbolizer.cc
index 1cd83cb..0aa5d8b 100644
--- a/tools/trace_to_text/symbolizer.cc
+++ b/src/profiling/symbolizer/symbolizer.cc
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#include "perfetto/profiling/symbolizer.h"
+#include "src/profiling/symbolizer/symbolizer.h"
 
 namespace perfetto {
-namespace trace_to_text {
+namespace profiling {
 
 Symbolizer::~Symbolizer() = default;
 
-}  // namespace trace_to_text
+}  // namespace profiling
 }  // namespace perfetto
diff --git a/include/perfetto/profiling/symbolizer.h b/src/profiling/symbolizer/symbolizer.h
similarity index 80%
rename from include/perfetto/profiling/symbolizer.h
rename to src/profiling/symbolizer/symbolizer.h
index 2a5a79e..ff4ee50 100644
--- a/include/perfetto/profiling/symbolizer.h
+++ b/src/profiling/symbolizer/symbolizer.h
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef INCLUDE_PERFETTO_PROFILING_SYMBOLIZER_H_
-#define INCLUDE_PERFETTO_PROFILING_SYMBOLIZER_H_
+#ifndef SRC_PROFILING_SYMBOLIZER_SYMBOLIZER_H_
+#define SRC_PROFILING_SYMBOLIZER_SYMBOLIZER_H_
 
 #include <map>
 #include <string>
 #include <vector>
 
-// TODO(135923303): do not depend on anything in this file as it will be
-// removed as part of fixing b/135923303.
 namespace perfetto {
-namespace trace_to_text {
+namespace profiling {
 
 struct SymbolizedFrame {
   std::string function_name;
@@ -46,7 +44,7 @@
   virtual ~Symbolizer();
 };
 
-}  // namespace trace_to_text
+}  // namespace profiling
 }  // namespace perfetto
 
-#endif  // INCLUDE_PERFETTO_PROFILING_SYMBOLIZER_H_
+#endif  // SRC_PROFILING_SYMBOLIZER_SYMBOLIZER_H_
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index ee7a309..cd48854 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -377,6 +377,8 @@
       ":lib",
       "../../gn:default_deps",
       "../../gn:protoc_lib",
+      "../../src/profiling/symbolizer",
+      "../../src/profiling/symbolizer:symbolize_database",
       "../base",
       "metrics:lib",
     ]
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc
index 0788267..0a61e0c 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser.cc
@@ -389,8 +389,12 @@
       src_allocation.pid = entry.pid();
       src_allocation.timestamp = timestamp;
       src_allocation.callstack_id = sample.callstack_id();
-      src_allocation.self_allocated = sample.self_allocated();
-      src_allocation.self_freed = sample.self_freed();
+      if (sample.self_max()) {
+        src_allocation.self_allocated = sample.self_max();
+      } else {
+        src_allocation.self_allocated = sample.self_allocated();
+        src_allocation.self_freed = sample.self_freed();
+      }
       src_allocation.alloc_count = sample.alloc_count();
       src_allocation.free_count = sample.free_count();
 
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index 3ba6b59..a044ea7 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -28,6 +28,7 @@
 
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 #include "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.h"
+#include "protos/perfetto/trace/track_event/chrome_histogram_sample.pbzero.h"
 #include "protos/perfetto/trace/track_event/chrome_keyed_service.pbzero.h"
 #include "protos/perfetto/trace/track_event/chrome_legacy_ipc.pbzero.h"
 #include "protos/perfetto/trace/track_event/chrome_user_event.pbzero.h"
@@ -145,6 +146,12 @@
           context->storage->InternString("legacy_ipc.line")),
       chrome_keyed_service_name_args_key_id_(
           context->storage->InternString("keyed_service.name")),
+      chrome_histogram_sample_name_hash_args_key_id_(
+          context->storage->InternString("histogram_sample.name_hash")),
+      chrome_histogram_sample_name_args_key_id_(
+          context->storage->InternString("histogram_sample.name")),
+      chrome_histogram_sample_sample_args_key_id_(
+          context->storage->InternString("histogram_sample.sample")),
       chrome_legacy_ipc_class_ids_{
           {context->storage->InternString("UNSPECIFIED"),
            context->storage->InternString("AUTOMATION"),
@@ -490,6 +497,10 @@
       ParseChromeKeyedService(event.chrome_keyed_service(), args_tracker,
                               row_id);
     }
+    if (event.has_chrome_histogram_sample()) {
+      ParseChromeHistogramSample(event.chrome_histogram_sample(), args_tracker,
+                                 row_id);
+    }
 
     if (legacy_tid) {
       args_tracker->AddArg(row_id, legacy_event_original_tid_id_,
@@ -1120,5 +1131,31 @@
   }
 }
 
+void TrackEventParser::ParseChromeHistogramSample(
+    protozero::ConstBytes chrome_histogram_sample,
+    ArgsTracker* args_tracker,
+    RowId row) {
+  protos::pbzero::ChromeHistogramSample::Decoder event(
+      chrome_histogram_sample.data, chrome_histogram_sample.size);
+  if (event.has_name_hash()) {
+    uint64_t name_hash = static_cast<uint64_t>(event.name_hash());
+    args_tracker->AddArg(row, chrome_histogram_sample_name_hash_args_key_id_,
+                         chrome_histogram_sample_name_hash_args_key_id_,
+                         Variadic::UnsignedInteger(name_hash));
+  }
+  if (event.has_name()) {
+    StringId name = context_->storage->InternString(event.name());
+    args_tracker->AddArg(row, chrome_keyed_service_name_args_key_id_,
+                         chrome_keyed_service_name_args_key_id_,
+                         Variadic::String(name));
+  }
+  if (event.has_sample()) {
+    int64_t sample = static_cast<int64_t>(event.sample());
+    args_tracker->AddArg(row, chrome_histogram_sample_sample_args_key_id_,
+                         chrome_histogram_sample_sample_args_key_id_,
+                         Variadic::Integer(sample));
+  }
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/track_event_parser.h b/src/trace_processor/importers/proto/track_event_parser.h
index e44b76d..6b23187 100644
--- a/src/trace_processor/importers/proto/track_event_parser.h
+++ b/src/trace_processor/importers/proto/track_event_parser.h
@@ -89,6 +89,9 @@
   void ParseChromeKeyedService(protozero::ConstBytes chrome_keyed_service,
                                ArgsTracker*,
                                RowId);
+  void ParseChromeHistogramSample(protozero::ConstBytes chrome_keyed_service,
+                                  ArgsTracker*,
+                                  RowId);
 
  private:
   TraceProcessorContext* context_;
@@ -122,6 +125,9 @@
   const StringId chrome_legacy_ipc_class_args_key_id_;
   const StringId chrome_legacy_ipc_line_args_key_id_;
   const StringId chrome_keyed_service_name_args_key_id_;
+  const StringId chrome_histogram_sample_name_hash_args_key_id_;
+  const StringId chrome_histogram_sample_name_args_key_id_;
+  const StringId chrome_histogram_sample_sample_args_key_id_;
 
   std::array<StringId, 38> chrome_legacy_ipc_class_ids_;
 };
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 54855c5..5cc06cb 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -43,6 +43,13 @@
 #include "src/trace_processor/rpc/httpd.h"
 #endif
 
+#include "src/profiling/symbolizer/symbolize_database.h"
+#include "src/profiling/symbolizer/symbolizer.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
+#include "src/profiling/symbolizer/local_symbolizer.h"
+#endif
+
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
     PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
@@ -913,6 +920,31 @@
                     options.trace_file_path.c_str(), read_status.c_message());
       return 1;
     }
+
+    std::unique_ptr<profiling::Symbolizer> symbolizer;
+    auto binary_path = profiling::GetPerfettoBinaryPath();
+    if (!binary_path.empty()) {
+#if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
+      symbolizer.reset(new profiling::LocalSymbolizer(std::move(binary_path)));
+#else
+      PERFETTO_FATAL("This build does not support local symbolization.");
+#endif
+    }
+    if (symbolizer) {
+      profiling::SymbolizeDatabase(
+          tp.get(), 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;
+            }
+          });
+      tp->NotifyEndOfFile();
+    }
+
     t_load = base::GetWallTimeNs() - t_load_start;
     double t_load_s = t_load.count() / 1E9;
     PERFETTO_ILOG("Trace loaded: %.2f MB (%.1f MB/s)", size_mb,
diff --git a/tools/heap_profile b/tools/heap_profile
index 735fc58..eb5035f 100755
--- a/tools/heap_profile
+++ b/tools/heap_profile
@@ -31,8 +31,8 @@
 import urllib
 
 TRACE_TO_TEXT_SHAS = {
-    'linux': '74cb40d9413d7ad756e327e09e46a8adde90db92',
-    'mac': '47045bc6abd4f9113969211f37fdd2259b0c1cc3',
+    'linux': 'cd6d89e0ada48c0d7850206d80c324b912a3db42',
+    'mac': 'aadf14ce5988e5d5e283fb816f72bb2bba8cc9e4',
 }
 TRACE_TO_TEXT_PATH = tempfile.gettempdir()
 TRACE_TO_TEXT_BASE_URL = ('https://storage.googleapis.com/perfetto/')
@@ -376,8 +376,10 @@
       '/tmp/profile'
   ],
                         stdout=NULL)
+
   trace_to_text_output = subprocess.check_output(
-      [trace_to_text_binary, 'profile', '/tmp/profile'])
+      [trace_to_text_binary, 'profile', '/tmp/profile'],
+      env=os.environ)
   profile_path = None
   for word in trace_to_text_output.split():
     if 'heap_profile-' in word:
@@ -406,6 +408,17 @@
   os.symlink(profile_path, symlink_path)
   shutil.copyfile('/tmp/profile', os.path.join(profile_path, 'raw-trace'))
 
+  binary_path = os.getenv('PERFETTO_BINARY_PATH')
+  if binary_path is not None:
+      with open(os.path.join(profile_path, 'symbols'), 'w') as fd:
+          ret = subprocess.call([
+              trace_to_text_binary, 'symbolize',
+              os.path.join(profile_path, 'raw-trace')],
+              env=os.environ,
+              stdout=fd)
+          if ret != 0:
+              print("Failed to symbolize.", file=sys.stderr)
+
   print("Wrote profiles to {} (symlink {})".format(profile_path, symlink_path))
   print("These can be viewed using pprof. Googlers: head to pprof/ and "
         "upload them.")
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index 4b2044c..41c844d 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -38,7 +38,6 @@
 
 source_set("utils") {
   deps = [
-    ":symbolizer",
     "../../gn:default_deps",
     "../../include/perfetto/profiling:deobfuscator",
     "../../include/perfetto/protozero",
@@ -47,12 +46,13 @@
     "../../protos/perfetto/trace/interned_data:zero",
     "../../protos/perfetto/trace/profiling:zero",
     "../../src/profiling:deobfuscator",
+    "../../src/profiling/symbolizer",
+    "../../src/profiling/symbolizer:symbolize_database",
   ]
   public_deps = [
     "../../gn:zlib",
     "../../include/perfetto/ext/base",
     "../../include/perfetto/profiling:deobfuscator",
-    "../../include/perfetto/profiling:symbolizer",
   ]
   sources = [
     "utils.cc",
@@ -60,45 +60,21 @@
   ]
 }
 
-source_set("local_symbolizer") {
-  public_deps = [
-    "../../include/perfetto/ext/base",
-    "../../include/perfetto/profiling:symbolizer",
-  ]
-  deps = [
-    ":symbolizer",
-    ":utils",
-    "../../gn:default_deps",
-    "../../include/perfetto/protozero",
-  ]
-  sources = [
-    "local_symbolizer.cc",
-    "local_symbolizer.h",
-  ]
-}
-
-source_set("symbolizer") {
-  deps = [
-    "../../gn:default_deps",
-    "../../include/perfetto/profiling:symbolizer",
-  ]
-  sources = [
-    "symbolizer.cc",
-  ]
-}
-
 source_set("pprofbuilder") {
+  public_deps = [
+    "../../include/perfetto/profiling:pprof_builder",
+  ]
   deps = [
-    ":symbolizer",
     ":utils",
     "../../gn:default_deps",
     "../../include/perfetto/base",
-    "../../include/perfetto/profiling:symbolizer",
     "../../include/perfetto/protozero",
     "../../include/perfetto/trace_processor",
     "../../protos/perfetto/trace:zero",
     "../../protos/perfetto/trace/profiling:zero",
     "../../protos/third_party/pprof:zero",
+    "../../src/profiling/symbolizer",
+    "../../src/profiling/symbolizer:symbolize_database",
   ]
   sources = [
     "pprof_builder.cc",
@@ -117,7 +93,6 @@
 # executable) and by the "lite" version (the WASM module for the UI).
 source_set("common") {
   deps = [
-    ":local_symbolizer",
     ":pprofbuilder",
     ":utils",
     "../../gn:default_deps",
@@ -126,6 +101,8 @@
     "../../include/perfetto/profiling:deobfuscator",
     "../../include/perfetto/protozero",
     "../../protos/perfetto/trace:zero",
+    "../../src/profiling/symbolizer",
+    "../../src/profiling/symbolizer:symbolize_database",
     "../../src/trace_processor:lib",
   ]
   sources = [
diff --git a/tools/trace_to_text/pprof_builder.cc b/tools/trace_to_text/pprof_builder.cc
index 77dc8615..4da0d9d 100644
--- a/tools/trace_to_text/pprof_builder.cc
+++ b/tools/trace_to_text/pprof_builder.cc
@@ -31,11 +31,13 @@
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/utils.h"
-#include "perfetto/profiling/symbolizer.h"
 #include "perfetto/protozero/packed_repeated_fields.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "perfetto/trace_processor/trace_processor.h"
 
+#include "src/profiling/symbolizer/symbolize_database.h"
+#include "src/profiling/symbolizer/symbolizer.h"
+
 #include "protos/perfetto/trace/trace.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 #include "protos/third_party/pprof/profile.pbzero.h"
@@ -497,7 +499,7 @@
 
 bool TraceToPprof(std::istream* input,
                   std::vector<SerializedProfile>* output,
-                  Symbolizer* symbolizer,
+                  profiling::Symbolizer* symbolizer,
                   uint64_t pid,
                   const std::vector<uint64_t>& timestamps) {
   trace_processor::Config config;
@@ -513,26 +515,21 @@
 
 bool TraceToPprof(trace_processor::TraceProcessor* tp,
                   std::vector<SerializedProfile>* output,
-                  Symbolizer* symbolizer,
+                  profiling::Symbolizer* symbolizer,
                   uint64_t pid,
                   const std::vector<uint64_t>& timestamps) {
   if (symbolizer) {
-    SymbolizeDatabase(tp, symbolizer, [&tp](const std::string& packet_proto) {
-      std::unique_ptr<uint8_t[]> buf(new uint8_t[11 + packet_proto.size()]);
-      uint8_t* wptr = &buf[0];
-      *(wptr++) =
-          MakeTagLengthDelimited(protos::pbzero::Trace::kPacketFieldNumber);
-      wptr = WriteVarInt(packet_proto.size(), wptr);
-      memcpy(wptr, packet_proto.data(), packet_proto.size());
-      wptr += packet_proto.size();
-      size_t buf_size = static_cast<size_t>(wptr - &buf[0]);
-      auto status = tp->Parse(std::move(buf), buf_size);
-      if (!status.ok()) {
-        PERFETTO_DFATAL_OR_ELOG("Failed to parse: %s",
-                                status.message().c_str());
-        return;
-      }
-    });
+    profiling::SymbolizeDatabase(
+        tp, symbolizer, [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;
+          }
+        });
   }
 
   tp->NotifyEndOfFile();
diff --git a/tools/trace_to_text/symbolize_profile.cc b/tools/trace_to_text/symbolize_profile.cc
index b354572..acf25f0 100644
--- a/tools/trace_to_text/symbolize_profile.cc
+++ b/tools/trace_to_text/symbolize_profile.cc
@@ -19,11 +19,13 @@
 #include <vector>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/profiling/symbolizer.h"
 #include "perfetto/trace_processor/trace_processor.h"
 
+#include "src/profiling/symbolizer/symbolize_database.h"
+#include "src/profiling/symbolizer/symbolizer.h"
+
 #if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
-#include "tools/trace_to_text/local_symbolizer.h"
+#include "src/profiling/symbolizer/local_symbolizer.h"
 #endif
 
 #include "protos/perfetto/trace/trace.pbzero.h"
@@ -36,11 +38,11 @@
 // Ingest profile, and emit a symbolization table for each sequence. This can
 // be prepended to the profile to attach the symbol information.
 int SymbolizeProfile(std::istream* input, std::ostream* output) {
-  std::unique_ptr<Symbolizer> symbolizer;
-  auto binary_path = GetPerfettoBinaryPath();
+  std::unique_ptr<profiling::Symbolizer> symbolizer;
+  auto binary_path = profiling::GetPerfettoBinaryPath();
   if (!binary_path.empty()) {
 #if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
-    symbolizer.reset(new LocalSymbolizer(GetPerfettoBinaryPath()));
+    symbolizer.reset(new profiling::LocalSymbolizer(std::move(binary_path)));
 #else
     PERFETTO_FATAL("This build does not support local symbolization.");
 #endif
@@ -57,10 +59,9 @@
 
   tp->NotifyEndOfFile();
 
-  SymbolizeDatabase(tp.get(), symbolizer.get(),
-                    [output](const std::string& packet_proto) {
-                      WriteTracePacket(packet_proto, output);
-                    });
+  SymbolizeDatabase(
+      tp.get(), symbolizer.get(),
+      [output](const std::string& trace_proto) { *output << trace_proto; });
   return 0;
 }
 
diff --git a/tools/trace_to_text/trace_to_profile.cc b/tools/trace_to_text/trace_to_profile.cc
index 4f5f1ec..1d3846c 100644
--- a/tools/trace_to_text/trace_to_profile.cc
+++ b/tools/trace_to_text/trace_to_profile.cc
@@ -21,8 +21,9 @@
 
 #include "perfetto/base/build_config.h"
 
+#include "src/profiling/symbolizer/symbolize_database.h"
 #if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
-#include "tools/trace_to_text/local_symbolizer.h"
+#include "src/profiling/symbolizer/local_symbolizer.h"
 #endif
 #include "tools/trace_to_text/utils.h"
 
@@ -31,7 +32,7 @@
 #include "perfetto/ext/base/temp_file.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/profiling/pprof_builder.h"
-#include "perfetto/profiling/symbolizer.h"
+#include "src/profiling/symbolizer/symbolizer.h"
 
 namespace {
 
@@ -53,11 +54,11 @@
                    std::ostream* output,
                    uint64_t pid,
                    std::vector<uint64_t> timestamps) {
-  std::unique_ptr<Symbolizer> symbolizer;
-  auto binary_path = GetPerfettoBinaryPath();
+  std::unique_ptr<profiling::Symbolizer> symbolizer;
+  auto binary_path = profiling::GetPerfettoBinaryPath();
   if (!binary_path.empty()) {
 #if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
-    symbolizer.reset(new LocalSymbolizer(GetPerfettoBinaryPath()));
+    symbolizer.reset(new profiling::LocalSymbolizer(std::move(binary_path)));
 #else
     PERFETTO_ELOG(
         "This build does not support local symbolization. "
diff --git a/tools/trace_to_text/utils.cc b/tools/trace_to_text/utils.cc
index 7b9b358..73645f6 100644
--- a/tools/trace_to_text/utils.cc
+++ b/tools/trace_to_text/utils.cc
@@ -25,7 +25,6 @@
 #include <utility>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "perfetto/trace_processor/trace_processor.h"
 
@@ -40,61 +39,9 @@
 
 using Iterator = trace_processor::TraceProcessor::Iterator;
 
-constexpr const char* kQueryUnsymbolized =
-    "select spm.name, spm.build_id, spf.rel_pc "
-    "from stack_profile_frame spf "
-    "join stack_profile_mapping spm "
-    "on spf.mapping = spm.id "
-    "where spm.build_id != '' and spf.symbol_set_id == 0";
 
 constexpr size_t kCompressionBufferSize = 500 * 1024;
 
-std::string FromHex(const char* str, size_t size) {
-  if (size % 2) {
-    PERFETTO_DFATAL_OR_ELOG("Failed to parse hex %s", str);
-    return "";
-  }
-  std::string result(size / 2, '\0');
-  for (size_t i = 0; i < size; i += 2) {
-    char hex_byte[3];
-    hex_byte[0] = str[i];
-    hex_byte[1] = str[i + 1];
-    hex_byte[2] = '\0';
-    char* end;
-    long int byte = strtol(hex_byte, &end, 16);
-    if (*end != '\0') {
-      PERFETTO_DFATAL_OR_ELOG("Failed to parse hex %s", str);
-      return "";
-    }
-    result[i / 2] = static_cast<char>(byte);
-  }
-  return result;
-}
-
-std::string FromHex(const std::string& str) {
-  return FromHex(str.c_str(), str.size());
-}
-
-using NameAndBuildIdPair = std::pair<std::string, std::string>;
-
-std::map<NameAndBuildIdPair, std::vector<uint64_t>> GetUnsymbolizedFrames(
-    trace_processor::TraceProcessor* tp) {
-  std::map<std::pair<std::string, std::string>, std::vector<uint64_t>> res;
-  Iterator it = tp->ExecuteQuery(kQueryUnsymbolized);
-  while (it.Next()) {
-    auto name_and_buildid =
-        std::make_pair(it.Get(0).string_value, FromHex(it.Get(1).string_value));
-    int64_t rel_pc = it.Get(2).long_value;
-    res[name_and_buildid].emplace_back(rel_pc);
-  }
-  if (!it.Status().ok()) {
-    PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
-                            it.Status().message().c_str());
-    return {};
-  }
-  return res;
-}
-
 std::map<std::string, std::set<std::string>> GetHeapGraphClasses(
     trace_processor::TraceProcessor* tp) {
   std::map<std::string, std::set<std::string>> res;
@@ -185,16 +132,6 @@
   }
 }
 
-std::vector<std::string> GetPerfettoBinaryPath() {
-  std::vector<std::string> roots;
-  const char* root = getenv("PERFETTO_BINARY_PATH");
-  if (root != nullptr) {
-    for (base::StringSplitter sp(std::string(root), ':'); sp.Next();)
-      roots.emplace_back(sp.cur_token(), sp.cur_token_size());
-  }
-  return roots;
-}
-
 base::Optional<std::string> GetPerfettoProguardMapPath() {
   base::Optional<std::string> proguard_map;
   const char* env = getenv("PERFETTO_PROGUARD_MAP");
@@ -242,37 +179,6 @@
   return true;
 }
 
-void SymbolizeDatabase(trace_processor::TraceProcessor* tp,
-                       Symbolizer* symbolizer,
-                       std::function<void(const std::string&)> callback) {
-  PERFETTO_CHECK(symbolizer);
-  auto unsymbolized = GetUnsymbolizedFrames(tp);
-  for (auto it = unsymbolized.cbegin(); it != unsymbolized.cend(); ++it) {
-    const auto& name_and_buildid = it->first;
-    const std::vector<uint64_t>& rel_pcs = it->second;
-    auto res = symbolizer->Symbolize(name_and_buildid.first,
-                                     name_and_buildid.second, rel_pcs);
-    if (res.empty())
-      continue;
-
-    protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket> packet;
-    auto* module_symbols = packet->set_module_symbols();
-    module_symbols->set_path(name_and_buildid.first);
-    module_symbols->set_build_id(name_and_buildid.second);
-    PERFETTO_DCHECK(res.size() == rel_pcs.size());
-    for (size_t i = 0; i < res.size(); ++i) {
-      auto* address_symbols = module_symbols->add_address_symbols();
-      address_symbols->set_address(rel_pcs[i]);
-      for (const SymbolizedFrame& frame : res[i]) {
-        auto* line = address_symbols->add_lines();
-        line->set_function_name(frame.function_name);
-        line->set_source_file_name(frame.file_name);
-        line->set_line_number(frame.line);
-      }
-    }
-    callback(packet.SerializeAsString());
-  }
-}
 
 void DeobfuscateDatabase(
     trace_processor::TraceProcessor* tp,
diff --git a/tools/trace_to_text/utils.h b/tools/trace_to_text/utils.h
index b2e2c0d..19bd3ca 100644
--- a/tools/trace_to_text/utils.h
+++ b/tools/trace_to_text/utils.h
@@ -32,7 +32,6 @@
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/paged_memory.h"
 #include "perfetto/profiling/deobfuscator.h"
-#include "perfetto/profiling/symbolizer.h"
 
 namespace perfetto {
 
@@ -58,19 +57,12 @@
     std::istream* input,
     const std::function<void(std::unique_ptr<char[]>, size_t)>&);
 
-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 proto-encoded TracePackets messages and call callback.
-void SymbolizeDatabase(trace_processor::TraceProcessor* tp,
-                       Symbolizer* symbolizer,
-                       std::function<void(const std::string&)> callback);
-
 // Generate ObfuscationMapping protos for all obfuscated java names in the
 // database.
 // Wrap them in proto-encoded TracePackets messages and call callback.
diff --git a/ui/index.html b/ui/index.html
index c375f93..0e1833a 100644
--- a/ui/index.html
+++ b/ui/index.html
@@ -3,6 +3,9 @@
 <head>
   <title>Perfetto UI</title>
   <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
+  <!-- WebComponents V0 origin trial token for https://ui.perfetto.dev Expires 17 Dec 2020.
+  See https://crbug.com/1021137. -->
+  <meta http-equiv="origin-trial" content="AtzsILqIzNPGftktQTEYxI9GpnqFBuse5uB5n4JQO3Wa1ky4TCKmnXZli0A9g9p7Es7Il9pqarELntnfm0HriwkAAABreyJvcmlnaW4iOiJodHRwczovL3VpLnBlcmZldHRvLmRldjo0NDMiLCJmZWF0dXJlIjoiV2ViQ29tcG9uZW50c1YwIiwiZXhwaXJ5IjoxNjA4MjI2NDQzLCJpc1N1YmRvbWFpbiI6dHJ1ZX0=">
   <link href="perfetto.css" rel="stylesheet">
   <link rel="icon" type="image/png" href="assets/logo.png">
   <link rel="preload" href="controller_bundle.js" as="script">
diff --git a/ui/src/frontend/legacy_trace_viewer.ts b/ui/src/frontend/legacy_trace_viewer.ts
index 4dafe4c..c55451d 100644
--- a/ui/src/frontend/legacy_trace_viewer.ts
+++ b/ui/src/frontend/legacy_trace_viewer.ts
@@ -22,7 +22,8 @@
   fileName = fileName.toLowerCase();
   return (
       fileName.endsWith('.json') || fileName.endsWith('.json.gz') ||
-      fileName.endsWith('.zip') || fileName.endsWith('.ctrace'));
+      fileName.endsWith('.zip') || fileName.endsWith('.ctrace') ||
+      fileName.endsWith('.html'));
 }
 
 export function openFileWithLegacyTraceViewer(file: File) {