Merge "Use pid_t = unsigned int only for non gnuc compilers"
diff --git a/.gn b/.gn
index 692a951..d7ecabe 100644
--- a/.gn
+++ b/.gn
@@ -13,3 +13,4 @@
 # limitations under the License.
 
 buildconfig = "//gn/standalone/BUILDCONFIG.gn"
+script_executable = "python3"
diff --git a/BUILD b/BUILD
index 83099f4..37ac3de 100644
--- a/BUILD
+++ b/BUILD
@@ -1819,6 +1819,7 @@
 perfetto_cc_protozero_library(
     name = "protos_perfetto_config_android_zero",
     deps = [
+        ":protos_perfetto_common_zero",
         ":protos_perfetto_config_android_protos",
     ],
 )
@@ -1994,6 +1995,7 @@
 perfetto_cc_protozero_library(
     name = "protos_perfetto_config_interceptors_zero",
     deps = [
+        ":protos_perfetto_common_zero",
         ":protos_perfetto_config_interceptors_protos",
     ],
 )
@@ -2152,6 +2154,7 @@
 perfetto_cc_protozero_library(
     name = "protos_perfetto_config_profiling_zero",
     deps = [
+        ":protos_perfetto_common_zero",
         ":protos_perfetto_config_profiling_protos",
     ],
 )
@@ -2218,6 +2221,7 @@
 perfetto_cc_protozero_library(
     name = "protos_perfetto_config_sys_stats_zero",
     deps = [
+        ":protos_perfetto_common_zero",
         ":protos_perfetto_config_sys_stats_protos",
     ],
 )
@@ -2261,7 +2265,18 @@
 perfetto_cc_protozero_library(
     name = "protos_perfetto_config_zero",
     deps = [
+        ":protos_perfetto_common_zero",
+        ":protos_perfetto_config_android_zero",
+        ":protos_perfetto_config_ftrace_zero",
+        ":protos_perfetto_config_gpu_zero",
+        ":protos_perfetto_config_inode_file_zero",
+        ":protos_perfetto_config_interceptors_zero",
+        ":protos_perfetto_config_power_zero",
+        ":protos_perfetto_config_process_stats_zero",
+        ":protos_perfetto_config_profiling_zero",
         ":protos_perfetto_config_protos",
+        ":protos_perfetto_config_sys_stats_zero",
+        ":protos_perfetto_config_track_event_zero",
     ],
 )
 
@@ -2502,6 +2517,7 @@
 perfetto_cc_protozero_library(
     name = "protos_perfetto_trace_android_zero",
     deps = [
+        ":protos_perfetto_common_zero",
         ":protos_perfetto_trace_android_protos",
     ],
 )
@@ -2662,6 +2678,7 @@
 perfetto_cc_protozero_library(
     name = "protos_perfetto_trace_gpu_zero",
     deps = [
+        ":protos_perfetto_common_zero",
         ":protos_perfetto_trace_gpu_protos",
     ],
 )
@@ -2695,7 +2712,11 @@
 perfetto_cc_protozero_library(
     name = "protos_perfetto_trace_interned_data_zero",
     deps = [
+        ":protos_perfetto_common_zero",
+        ":protos_perfetto_trace_gpu_zero",
         ":protos_perfetto_trace_interned_data_protos",
+        ":protos_perfetto_trace_profiling_zero",
+        ":protos_perfetto_trace_track_event_zero",
     ],
 )
 
@@ -2755,6 +2776,18 @@
 perfetto_cc_protozero_library(
     name = "protos_perfetto_trace_minimal_zero",
     deps = [
+        ":protos_perfetto_common_zero",
+        ":protos_perfetto_config_android_zero",
+        ":protos_perfetto_config_ftrace_zero",
+        ":protos_perfetto_config_gpu_zero",
+        ":protos_perfetto_config_inode_file_zero",
+        ":protos_perfetto_config_interceptors_zero",
+        ":protos_perfetto_config_power_zero",
+        ":protos_perfetto_config_process_stats_zero",
+        ":protos_perfetto_config_profiling_zero",
+        ":protos_perfetto_config_sys_stats_zero",
+        ":protos_perfetto_config_track_event_zero",
+        ":protos_perfetto_config_zero",
         ":protos_perfetto_trace_minimal_protos",
     ],
 )
@@ -2815,7 +2848,33 @@
 perfetto_cc_protozero_library(
     name = "protos_perfetto_trace_non_minimal_zero",
     deps = [
+        ":protos_perfetto_common_zero",
+        ":protos_perfetto_config_android_zero",
+        ":protos_perfetto_config_ftrace_zero",
+        ":protos_perfetto_config_gpu_zero",
+        ":protos_perfetto_config_inode_file_zero",
+        ":protos_perfetto_config_interceptors_zero",
+        ":protos_perfetto_config_power_zero",
+        ":protos_perfetto_config_process_stats_zero",
+        ":protos_perfetto_config_profiling_zero",
+        ":protos_perfetto_config_sys_stats_zero",
+        ":protos_perfetto_config_track_event_zero",
+        ":protos_perfetto_config_zero",
+        ":protos_perfetto_trace_android_zero",
+        ":protos_perfetto_trace_chrome_zero",
+        ":protos_perfetto_trace_filesystem_zero",
+        ":protos_perfetto_trace_ftrace_zero",
+        ":protos_perfetto_trace_gpu_zero",
+        ":protos_perfetto_trace_interned_data_zero",
+        ":protos_perfetto_trace_minimal_zero",
         ":protos_perfetto_trace_non_minimal_protos",
+        ":protos_perfetto_trace_perfetto_zero",
+        ":protos_perfetto_trace_power_zero",
+        ":protos_perfetto_trace_profiling_zero",
+        ":protos_perfetto_trace_ps_zero",
+        ":protos_perfetto_trace_sys_stats_zero",
+        ":protos_perfetto_trace_system_info_zero",
+        ":protos_perfetto_trace_track_event_zero",
     ],
 )
 
@@ -2875,6 +2934,7 @@
 perfetto_cc_protozero_library(
     name = "protos_perfetto_trace_power_zero",
     deps = [
+        ":protos_perfetto_common_zero",
         ":protos_perfetto_trace_power_protos",
     ],
 )
@@ -2916,6 +2976,7 @@
 perfetto_cc_protozero_library(
     name = "protos_perfetto_trace_processor_zero",
     deps = [
+        ":protos_perfetto_common_zero",
         ":protos_perfetto_trace_processor_protos",
     ],
 )
@@ -2950,6 +3011,7 @@
 perfetto_cc_protozero_library(
     name = "protos_perfetto_trace_profiling_zero",
     deps = [
+        ":protos_perfetto_common_zero",
         ":protos_perfetto_trace_profiling_protos",
     ],
 )
@@ -3008,6 +3070,7 @@
 perfetto_cc_protozero_library(
     name = "protos_perfetto_trace_sys_stats_zero",
     deps = [
+        ":protos_perfetto_common_zero",
         ":protos_perfetto_trace_sys_stats_protos",
     ],
 )
@@ -3564,7 +3627,6 @@
         ":src_profiling_deobfuscator",
         ":src_profiling_symbolizer_symbolize_database",
         ":src_profiling_symbolizer_symbolizer",
-        ":src_trace_processor_containers_containers",
         ":tools_trace_to_text_pprofbuilder",
         ":tools_trace_to_text_utils",
     ],
diff --git a/bazel/rules.bzl b/bazel/rules.bzl
index 1767aa4..4503867 100644
--- a/bazel/rules.bzl
+++ b/bazel/rules.bzl
@@ -93,9 +93,20 @@
     ):
         return
 
+    # A perfetto_cc_protozero_library has two types of dependencies:
+    # 1. Exactly one dependency on a proto_library target. This defines the
+    #    .proto sources for the target
+    # 2. Zero or more deps on other perfetto_cc_protozero_library targets. This
+    #    to deal with the case of foo.proto including common.proto from another
+    #    target.
+    _proto_deps = [d for d in deps if d.endswith("_protos")]
+    _cc_deps = [d for d in deps if d not in _proto_deps]
+    if len(_proto_deps) != 1:
+        fail("Too many proto deps for target %s" % name)
+
     proto_gen(
         name = name + "_src",
-        deps = deps,
+        deps = _proto_deps,
         suffix = "pbzero",
         plugin = PERFETTO_CONFIG.root + ":protozero_plugin",
         wrapper_namespace = "pbzero",
@@ -113,7 +124,7 @@
         name = name,
         srcs = [":" + name + "_src"],
         hdrs = [":" + name + "_h"],
-        deps = [PERFETTO_CONFIG.root + ":protozero"],
+        deps = [PERFETTO_CONFIG.root + ":protozero"] + _cc_deps,
         **kwargs
     )
 
diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn
index a59c492..98022c9 100644
--- a/buildtools/BUILD.gn
+++ b/buildtools/BUILD.gn
@@ -1058,6 +1058,7 @@
   visibility = _buildtools_visibility
   cflags = [
     "-DJSON_USE_EXCEPTION=0",
+    "-Wno-deprecated-declarations",
 
     # Using -isystem instead of include_dirs (-I), so we don't need to suppress
     # warnings coming from third-party headers. Doing so would mask warnings in
diff --git a/debian/control b/debian/control
index be52af4..5467a93 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,15 @@
 Section: kernel
 Priority: optional
 Maintainer: Sami Kyostila <skyostil@google.com>
-Build-Depends: debhelper (>= 10)
+Build-Depends: debhelper (>= 10),
+  ca-certificates,
+  curl,
+  generate-ninja,
+  git,
+  libprotoc-dev,
+  ninja-build,
+  protobuf-compiler,
+  python3
 Standards-Version: 3.9.8
 Homepage: https://perfetto.dev
 Vcs-Git: https://android.googlesource.com/platform/external/perfetto/
diff --git a/debian/perfetto.install b/debian/perfetto.install
index 1ae73aa..ded0fdc 100644
--- a/debian/perfetto.install
+++ b/debian/perfetto.install
@@ -1,8 +1,6 @@
 out/release/libperfetto.so usr/lib
 out/release/traced usr/sbin
-out/release/traced_perf usr/sbin
 out/release/traced_probes usr/sbin
 out/release/perfetto usr/bin
 debian/traced.service lib/systemd/system
-debian/traced-perf.service lib/systemd/system
 debian/traced-probes.service lib/systemd/system
diff --git a/debian/rules b/debian/rules
index 594466f..e6f1de4 100755
--- a/debian/rules
+++ b/debian/rules
@@ -3,11 +3,14 @@
 	dh $@
 
 override_dh_auto_configure:
-	tools/install-build-deps
-	tools/gn gen out/release --args="is_debug=false"
+	tools/install-build-deps --no-toolchain
+	gn gen out/release --args="is_debug=false use_custom_libcxx=false\
+	  is_hermetic_clang=false is_system_compiler=true is_clang=false\
+	  skip_buildtools_check=true enable_perfetto_integration_tests=false\
+	  enable_perfetto_unittests=false perfetto_use_system_protobuf=true"
 
 override_dh_auto_build:
-	tools/ninja -C out/release
+	ninja -C out/release perfetto traced traced_probes
 
 override_dh_auto_clean:
-	tools/gn clean out/release
+	rm -rf out/release
diff --git a/debian/triggers b/debian/triggers
new file mode 100644
index 0000000..dd86603
--- /dev/null
+++ b/debian/triggers
@@ -0,0 +1 @@
+activate-noawait ldconfig
diff --git a/gn/write_buildflag_header.py b/gn/write_buildflag_header.py
index 8045cce..617ab7d 100644
--- a/gn/write_buildflag_header.py
+++ b/gn/write_buildflag_header.py
@@ -67,7 +67,7 @@
   guard = '%s_' % args.out.upper()
   guard = guard.replace('/', '_').replace('\\', '_').replace('.', '_')
   lines = []
-  lines.append('// Generated by %s' % __file__)
+  lines.append('// Generated by %s' % os.path.basename(__file__))
   lines.append('')
   lines.append('// fix_include_guards: off')
   lines.append('#ifndef %s' % guard)
diff --git a/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h b/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
index 7a34606..f0179c6 100644
--- a/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
+++ b/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// Generated by ../../gn/write_buildflag_header.py
+// Generated by write_buildflag_header.py
 
 // fix_include_guards: off
 #ifndef GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
diff --git a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
index ada8006..6745b3f 100644
--- a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
+++ b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// Generated by ../../gn/write_buildflag_header.py
+// Generated by write_buildflag_header.py
 
 // fix_include_guards: off
 #ifndef GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
diff --git a/include/perfetto/ext/base/string_utils.h b/include/perfetto/ext/base/string_utils.h
index 7310b01..e8fa418 100644
--- a/include/perfetto/ext/base/string_utils.h
+++ b/include/perfetto/ext/base/string_utils.h
@@ -98,6 +98,7 @@
 bool StartsWith(const std::string& str, const std::string& prefix);
 bool EndsWith(const std::string& str, const std::string& suffix);
 bool Contains(const std::string& haystack, const std::string& needle);
+bool Contains(const std::string& haystack, char needle);
 size_t Find(const StringView& needle, const StringView& haystack);
 bool CaseInsensitiveEqual(const std::string& first, const std::string& second);
 std::string Join(const std::vector<std::string>& parts,
diff --git a/include/perfetto/profiling/pprof_builder.h b/include/perfetto/profiling/pprof_builder.h
index df18c5a..20b9d2e 100644
--- a/include/perfetto/profiling/pprof_builder.h
+++ b/include/perfetto/profiling/pprof_builder.h
@@ -17,6 +17,7 @@
 #ifndef INCLUDE_PERFETTO_PROFILING_PPROF_BUILDER_H_
 #define INCLUDE_PERFETTO_PROFILING_PPROF_BUILDER_H_
 
+#include <iostream>
 #include <string>
 #include <vector>
 
@@ -47,19 +48,9 @@
 
 enum class ConversionMode { kHeapProfile, kPerfProfile };
 
-enum class ConversionFlags : uint64_t {
-  kNone = 0,
-  // Suffix frame names with additional information. Current annotations are
-  // specific to apps running within the Android runtime, and include
-  // information such as whether the given frame was interpreted / executed
-  // under JIT / etc.
-  kAnnotateFrames = 1
-};
-
 bool TraceToPprof(trace_processor::TraceProcessor* tp,
                   std::vector<SerializedProfile>* output,
                   ConversionMode mode = ConversionMode::kHeapProfile,
-                  uint64_t flags = 0,
                   uint64_t pid = 0,
                   const std::vector<uint64_t>& timestamps = {});
 
diff --git a/include/perfetto/protozero/proto_utils.h b/include/perfetto/protozero/proto_utils.h
index bb2e5ce..841042b 100644
--- a/include/perfetto/protozero/proto_utils.h
+++ b/include/perfetto/protozero/proto_utils.h
@@ -276,6 +276,21 @@
   using message_type = MessageType;
 };
 
+namespace internal {
+
+// Ideally we would create variables of FieldMetadata<...> type directly,
+// but before C++17's support for constexpr inline variables arrive, we have to
+// actually use pointers to inline functions instead to avoid having to define
+// symbols in *.pbzero.cc files.
+//
+// Note: protozero bindings will generate Message::kFieldName variable and which
+// can then be passed to TRACE_EVENT macro for inline writing of typed messages.
+// The fact that the former can be passed to the latter is a part of the stable
+// API, while the particular type is not and users should not rely on it.
+template <typename T>
+using FieldMetadataHelper = T (*)(void);
+
+}  // namespace internal
 }  // namespace proto_utils
 }  // namespace protozero
 
diff --git a/include/perfetto/tracing/internal/write_track_event_args.h b/include/perfetto/tracing/internal/write_track_event_args.h
index 8263110..9a68ab5 100644
--- a/include/perfetto/tracing/internal/write_track_event_args.h
+++ b/include/perfetto/tracing/internal/write_track_event_args.h
@@ -100,7 +100,8 @@
                               FieldMetadataType>::value>>
 PERFETTO_ALWAYS_INLINE void WriteTrackEventArgs(
     EventContext event_ctx,
-    const FieldMetadataType& field_name,
+    protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadataType>
+        field_name,
     ArgValue&& arg_value,
     Args&&... args) {
   static_assert(
diff --git a/include/perfetto/tracing/traced_proto.h b/include/perfetto/tracing/traced_proto.h
index d88dbc1..88ee99e 100644
--- a/include/perfetto/tracing/traced_proto.h
+++ b/include/perfetto/tracing/traced_proto.h
@@ -166,9 +166,10 @@
 }  // namespace internal
 
 template <typename MessageType, typename FieldMetadataType, typename ValueType>
-void WriteIntoTracedProto(TracedProto<MessageType> message,
-                          const FieldMetadataType&,
-                          ValueType&& value) {
+void WriteIntoTracedProto(
+    TracedProto<MessageType> message,
+    protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadataType>,
+    ValueType&& value) {
   static_assert(
       std::is_base_of<protozero::proto_utils::FieldMetadataBase,
                       FieldMetadataType>::value,
diff --git a/include/perfetto/tracing/traced_value.h b/include/perfetto/tracing/traced_value.h
index 838dd47..ee36fb5 100644
--- a/include/perfetto/tracing/traced_value.h
+++ b/include/perfetto/tracing/traced_value.h
@@ -49,9 +49,9 @@
 //
 // To define how a custom class should be written into the trace, users should
 // define one of the two following functions:
-// - Foo::WriteIntoTracedValue(TracedValue) const
+// - Foo::WriteIntoTrace(TracedValue) const
 //   (preferred for code which depends on perfetto directly)
-// - perfetto::TraceFormatTraits<T>::WriteIntoTracedValue(
+// - perfetto::TraceFormatTraits<T>::WriteIntoTrace(
 //       TracedValue, const T&);
 //   (should be used if T is defined in a library which doesn't know anything
 //   about tracing).
@@ -81,17 +81,17 @@
 // });
 //
 // template <typename T>
-// TraceFormatTraits<std::optional<T>>::WriteIntoTracedValue(
+// TraceFormatTraits<std::optional<T>>::WriteIntoTrace(
 //    TracedValue context, const std::optional<T>& value) {
 //  if (!value) {
 //    std::move(context).WritePointer(nullptr);
 //    return;
 //  }
-//  perfetto::WriteIntoTracedValue(std::move(context), *value);
+//  perfetto::WriteIntoTrace(std::move(context), *value);
 // }
 //
 // template <typename T>
-// TraceFormatTraits<std::vector<T>>::WriteIntoTracedValue(
+// TraceFormatTraits<std::vector<T>>::WriteIntoTrace(
 //    TracedValue context, const std::array<T>& value) {
 //  auto array = std::move(context).WriteArray();
 //  for (const auto& item: value) {
@@ -100,7 +100,7 @@
 // }
 //
 // class Foo {
-//   void WriteIntoTracedValue(TracedValue context) const {
+//   void WriteIntoTrace(TracedValue context) const {
 //     auto dict = std::move(context).WriteDictionary();
 //     dict->Set("key", 42);
 //     dict->Set("foo", "bar");
@@ -265,6 +265,13 @@
   value.WriteIntoTracedValue(std::move(context));
 }
 
+// If T has WriteIntoTrace member function, call it.
+template <typename T>
+decltype(std::declval<T>().WriteIntoTrace(std::declval<TracedValue>()), void())
+WriteImpl(base::priority_tag<4>, TracedValue context, T&& value) {
+  value.WriteIntoTrace(std::move(context));
+}
+
 // If perfetto::TraceFormatTraits<T>::WriteIntoTracedValue(TracedValue, const
 // T&) is available, use it.
 template <typename T>
@@ -277,6 +284,18 @@
       std::move(context), std::forward<T>(value));
 }
 
+// If perfetto::TraceFormatTraits<T>::WriteIntoTrace(TracedValue, const T&)
+// is available, use it.
+template <typename T>
+decltype(TraceFormatTraits<base::remove_cvref_t<T>>::WriteIntoTrace(
+             std::declval<TracedValue>(),
+             std::declval<T>()),
+         void())
+WriteImpl(base::priority_tag<3>, TracedValue context, T&& value) {
+  TraceFormatTraits<base::remove_cvref_t<T>>::WriteIntoTrace(
+      std::move(context), std::forward<T>(value));
+}
+
 // If T has operator(), which takes TracedValue, use it.
 // Very useful for lambda resolutions.
 template <typename T>
@@ -429,7 +448,7 @@
     typename std::enable_if<std::is_integral<T>::value &&
                             !std::is_same<T, bool>::value &&
                             std::is_signed<T>::value>::type> {
-  inline static void WriteIntoTracedValue(TracedValue context, T value) {
+  inline static void WriteIntoTrace(TracedValue context, T value) {
     std::move(context).WriteInt64(value);
   }
 };
@@ -442,7 +461,7 @@
     typename std::enable_if<std::is_integral<T>::value &&
                             !std::is_same<T, bool>::value &&
                             std::is_unsigned<T>::value>::type> {
-  inline static void WriteIntoTracedValue(TracedValue context, T value) {
+  inline static void WriteIntoTrace(TracedValue context, T value) {
     std::move(context).WriteUInt64(value);
   }
 };
@@ -450,7 +469,7 @@
 // Specialisation for bools.
 template <>
 struct TraceFormatTraits<bool> {
-  inline static void WriteIntoTracedValue(TracedValue context, bool value) {
+  inline static void WriteIntoTrace(TracedValue context, bool value) {
     std::move(context).WriteBoolean(value);
   }
 };
@@ -460,7 +479,7 @@
 struct TraceFormatTraits<
     T,
     typename std::enable_if<std::is_floating_point<T>::value>::type> {
-  inline static void WriteIntoTracedValue(TracedValue context, T value) {
+  inline static void WriteIntoTrace(TracedValue context, T value) {
     std::move(context).WriteDouble(static_cast<double>(value));
   }
 };
@@ -473,7 +492,7 @@
         std::is_enum<T>::value &&
         std::is_signed<
             typename internal::safe_underlying_type<T>::type>::value>::type> {
-  inline static void WriteIntoTracedValue(TracedValue context, T value) {
+  inline static void WriteIntoTrace(TracedValue context, T value) {
     std::move(context).WriteInt64(static_cast<int64_t>(value));
   }
 };
@@ -486,7 +505,7 @@
         std::is_enum<T>::value &&
         std::is_unsigned<
             typename internal::safe_underlying_type<T>::type>::value>::type> {
-  inline static void WriteIntoTracedValue(TracedValue context, T value) {
+  inline static void WriteIntoTrace(TracedValue context, T value) {
     std::move(context).WriteUInt64(static_cast<uint64_t>(value));
   }
 };
@@ -494,24 +513,21 @@
 // Specialisations for C-style strings.
 template <>
 struct TraceFormatTraits<const char*> {
-  inline static void WriteIntoTracedValue(TracedValue context,
-                                          const char* value) {
+  inline static void WriteIntoTrace(TracedValue context, const char* value) {
     std::move(context).WriteString(value);
   }
 };
 
 template <>
 struct TraceFormatTraits<char[]> {
-  inline static void WriteIntoTracedValue(TracedValue context,
-                                          const char value[]) {
+  inline static void WriteIntoTrace(TracedValue context, const char value[]) {
     std::move(context).WriteString(value);
   }
 };
 
 template <size_t N>
 struct TraceFormatTraits<char[N]> {
-  inline static void WriteIntoTracedValue(TracedValue context,
-                                          const char value[N]) {
+  inline static void WriteIntoTrace(TracedValue context, const char value[N]) {
     std::move(context).WriteString(value);
   }
 };
@@ -519,8 +535,8 @@
 // Specialisation for C++ strings.
 template <>
 struct TraceFormatTraits<std::string> {
-  inline static void WriteIntoTracedValue(TracedValue context,
-                                          const std::string& value) {
+  inline static void WriteIntoTrace(TracedValue context,
+                                    const std::string& value) {
     std::move(context).WriteString(value);
   }
 };
@@ -528,15 +544,14 @@
 // Specialisation for (const) void*, which writes the pointer value.
 template <>
 struct TraceFormatTraits<void*> {
-  inline static void WriteIntoTracedValue(TracedValue context, void* value) {
+  inline static void WriteIntoTrace(TracedValue context, void* value) {
     std::move(context).WritePointer(value);
   }
 };
 
 template <>
 struct TraceFormatTraits<const void*> {
-  inline static void WriteIntoTracedValue(TracedValue context,
-                                          const void* value) {
+  inline static void WriteIntoTrace(TracedValue context, const void* value) {
     std::move(context).WritePointer(value);
   }
 };
@@ -545,8 +560,8 @@
 // object it points to.
 template <typename T>
 struct TraceFormatTraits<std::unique_ptr<T>, check_traced_value_support_t<T>> {
-  inline static void WriteIntoTracedValue(TracedValue context,
-                                          const std::unique_ptr<T>& value) {
+  inline static void WriteIntoTrace(TracedValue context,
+                                    const std::unique_ptr<T>& value) {
     ::perfetto::WriteIntoTracedValue(std::move(context), value.get());
   }
 };
@@ -555,7 +570,7 @@
 // points to.
 template <typename T>
 struct TraceFormatTraits<T*, check_traced_value_support_t<T>> {
-  inline static void WriteIntoTracedValue(TracedValue context, T* value) {
+  inline static void WriteIntoTrace(TracedValue context, T* value) {
     if (!value) {
       std::move(context).WritePointer(nullptr);
       return;
@@ -567,7 +582,7 @@
 // Specialisation for nullptr.
 template <>
 struct TraceFormatTraits<std::nullptr_t> {
-  inline static void WriteIntoTracedValue(TracedValue context, std::nullptr_t) {
+  inline static void WriteIntoTrace(TracedValue context, std::nullptr_t) {
     std::move(context).WritePointer(nullptr);
   }
 };
diff --git a/infra/ci/config.py b/infra/ci/config.py
index f35178e..be325ab 100755
--- a/infra/ci/config.py
+++ b/infra/ci/config.py
@@ -145,6 +145,7 @@
 
   if len(sys.argv) > 1 and sys.argv[1] == 'js':
     jsn = json.dumps(vars, indent=2)
-    print('// Auto-generated by %s, do not edit.\n' % __file__)
+    print('// Auto-generated by %s, do not edit.\n' %
+          os.path.basename(__file__))
     print('\'use strict\';\n')
     print('const cfg = JSON.parse(`%s`);\n' % jsn.replace(r'\"', r'\\\"'))
diff --git a/infra/perfetto.dev/build.js b/infra/perfetto.dev/build.js
index 086234d..1fc657c 100644
--- a/infra/perfetto.dev/build.js
+++ b/infra/perfetto.dev/build.js
@@ -73,7 +73,11 @@
 
   // Check that deps are current before starting.
   const installBuildDeps = pjoin(ROOT_DIR, 'tools/install-build-deps');
-  const depsArgs = ['--check-only', '/dev/null', '--ui'];
+
+  // --filter=nodejs is to match what cloud_build_entrypoint.sh passes to
+  // install-build-deps. It doesn't bother installing the full toolchains
+  // because, unlike the Perfetto UI, it doesn't need Wasm.
+  const depsArgs = ['--check-only=/dev/null', '--ui', '--filter=nodejs'];
   exec(installBuildDeps, depsArgs);
 
   console.log('Entering', cfg.outDir);
diff --git a/protos/perfetto/trace/ftrace/cpuhp.proto b/protos/perfetto/trace/ftrace/cpuhp.proto
index 3214b51..dc45682 100644
--- a/protos/perfetto/trace/ftrace/cpuhp.proto
+++ b/protos/perfetto/trace/ftrace/cpuhp.proto
@@ -29,3 +29,9 @@
   optional uint32 state = 3;
   optional uint64 time = 4;
 }
+message CpuhpPauseFtraceEvent {
+  optional uint32 active_cpus = 1;
+  optional uint32 cpus = 2;
+  optional uint32 pause = 3;
+  optional uint32 time = 4;
+}
diff --git a/protos/perfetto/trace/ftrace/ftrace_event.proto b/protos/perfetto/trace/ftrace/ftrace_event.proto
index e8ac22a..24ebae0 100644
--- a/protos/perfetto/trace/ftrace/ftrace_event.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_event.proto
@@ -440,5 +440,6 @@
     G2dTracingMarkWriteFtraceEvent g2d_tracing_mark_write = 349;
     MaliTracingMarkWriteFtraceEvent mali_tracing_mark_write = 350;
     DmaHeapStatFtraceEvent dma_heap_stat = 351;
+    CpuhpPauseFtraceEvent cpuhp_pause = 352;
   }
 }
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index fd4bcb0..78c593a 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -3121,6 +3121,12 @@
   optional uint32 state = 3;
   optional uint64 time = 4;
 }
+message CpuhpPauseFtraceEvent {
+  optional uint32 active_cpus = 1;
+  optional uint32 cpus = 2;
+  optional uint32 pause = 3;
+  optional uint32 time = 4;
+}
 
 // End of protos/perfetto/trace/ftrace/cpuhp.proto
 
@@ -5394,6 +5400,7 @@
     G2dTracingMarkWriteFtraceEvent g2d_tracing_mark_write = 349;
     MaliTracingMarkWriteFtraceEvent mali_tracing_mark_write = 350;
     DmaHeapStatFtraceEvent dma_heap_stat = 351;
+    CpuhpPauseFtraceEvent cpuhp_pause = 352;
   }
 }
 
diff --git a/src/base/string_utils.cc b/src/base/string_utils.cc
index ac3b16b..c0b0780 100644
--- a/src/base/string_utils.cc
+++ b/src/base/string_utils.cc
@@ -98,6 +98,10 @@
   return haystack.find(needle) != std::string::npos;
 }
 
+bool Contains(const std::string& haystack, const char needle) {
+  return haystack.find(needle) != std::string::npos;
+}
+
 size_t Find(const StringView& needle, const StringView& haystack) {
   if (needle.empty())
     return 0;
diff --git a/src/base/subprocess_posix.cc b/src/base/subprocess_posix.cc
index c5321f6..85df339 100644
--- a/src/base/subprocess_posix.cc
+++ b/src/base/subprocess_posix.cc
@@ -381,7 +381,7 @@
     return;
 
   PERFETTO_DCHECK(args.input.empty() || s_->input_written < args.input.size());
-  if (args.input.size()) {
+  if (!args.input.empty()) {
     int64_t wsize =
         PERFETTO_EINTR(write(*s_->stdin_pipe.wr, &args.input[s_->input_written],
                              args.input.size() - s_->input_written));
diff --git a/src/perfetto_cmd/config.cc b/src/perfetto_cmd/config.cc
index 1dbba97..0e1dccf 100644
--- a/src/perfetto_cmd/config.cc
+++ b/src/perfetto_cmd/config.cc
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/tracing/core/data_source_config.h"
 #include "perfetto/tracing/core/trace_config.h"
 
@@ -116,10 +117,10 @@
   std::vector<std::string> atrace_apps = options.atrace_apps;
 
   for (const auto& category : options.categories) {
-    if (category.find('/') == std::string::npos) {
-      atrace_categories.push_back(category);
-    } else {
+    if (base::Contains(category, '/')) {
       ftrace_events.push_back(category);
+    } else {
+      atrace_categories.push_back(category);
     }
   }
 
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index 84d9af3..c76e064 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -926,6 +926,9 @@
   }
 
   if (save_to_incidentd_) {
+    if (!uuid_.empty()) {
+      PERFETTO_LOG("go/trace-uuid/%s", uuid_.c_str());
+    }
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
     SaveTraceIntoDropboxAndIncidentOrCrash();
 #endif
diff --git a/src/profiling/common/producer_support.cc b/src/profiling/common/producer_support.cc
index d6a58ec..fc92714 100644
--- a/src/profiling/common/producer_support.cc
+++ b/src/profiling/common/producer_support.cc
@@ -61,10 +61,6 @@
     return true;
   }
 
-  if (ds_config.enable_extra_guardrails()) {
-    return false;  // no extra guardrails on user builds.
-  }
-
   uint64_t uid_without_profile = uid % kAidUserOffset;
   if (uid_without_profile < kAidAppStart || kAidAppEnd < uid_without_profile) {
     // TODO(fmayer): relax this.
@@ -82,9 +78,14 @@
       PERFETTO_ELOG("Failed to parse packages.list.");
       return false;
     }
-    if (pkg.uid == uid_without_profile &&
-        (pkg.profileable_from_shell || pkg.debuggable)) {
-      return true;
+    if (pkg.uid == uid_without_profile) {
+      if (pkg.profileable) {
+        return true;
+      }
+      if (!ds_config.enable_extra_guardrails() &&
+          (pkg.profileable_from_shell || pkg.debuggable)) {
+        return true;
+      }
     }
   }
   return false;
diff --git a/src/profiling/common/producer_support_unittest.cc b/src/profiling/common/producer_support_unittest.cc
index df2129a..4b94e89 100644
--- a/src/profiling/common/producer_support_unittest.cc
+++ b/src/profiling/common/producer_support_unittest.cc
@@ -32,7 +32,7 @@
   EXPECT_TRUE(CanProfileAndroid(ds_config, 1, "userdebug", "/dev/null"));
 }
 
-TEST(CanProfileAndroidTest, NonUserNonProfileableApp) {
+TEST(CanProfileAndroidTest, NonUserNonProfileableByShellApp) {
   DataSourceConfig ds_config;
   ds_config.set_enable_extra_guardrails(false);
   auto tmp = base::TempFile::Create();
@@ -44,7 +44,7 @@
   EXPECT_TRUE(CanProfileAndroid(ds_config, 10001, "userdebug", tmp.path()));
 }
 
-TEST(CanProfileAndroidTest, NonUserNonProfileableAppExtraGuardrails) {
+TEST(CanProfileAndroidTest, NonUserNonProfileableByShellAppExtraGuardrails) {
   DataSourceConfig ds_config;
   ds_config.set_enable_extra_guardrails(true);
   auto tmp = base::TempFile::Create();
@@ -56,7 +56,7 @@
   EXPECT_TRUE(CanProfileAndroid(ds_config, 10001, "userdebug", tmp.path()));
 }
 
-TEST(CanProfileAndroidTest, UserProfileableApp) {
+TEST(CanProfileAndroidTest, UserProfileableByShellApp) {
   DataSourceConfig ds_config;
   ds_config.set_enable_extra_guardrails(false);
   auto tmp = base::TempFile::Create();
@@ -68,7 +68,7 @@
   EXPECT_TRUE(CanProfileAndroid(ds_config, 10001, "user", tmp.path()));
 }
 
-TEST(CanProfileAndroidTest, UserProfileableAppExtraGuardrails) {
+TEST(CanProfileAndroidTest, UserProfileableByShellAppExtraGuardrails) {
   DataSourceConfig ds_config;
   ds_config.set_enable_extra_guardrails(true);
   auto tmp = base::TempFile::Create();
@@ -80,7 +80,19 @@
   EXPECT_FALSE(CanProfileAndroid(ds_config, 10001, "user", tmp.path()));
 }
 
-TEST(CanProfileAndroidTest, UserProfileableAppMultiuser) {
+TEST(CanProfileAndroidTest, UserProfileableAppExtraGuardrails) {
+  DataSourceConfig ds_config;
+  ds_config.set_enable_extra_guardrails(true);
+  auto tmp = base::TempFile::Create();
+  constexpr char content[] =
+      "invalid.example.profileable 10001 0 "
+      "/data/user/0/invalid.example.profileable default:targetSdkVersion=10000 "
+      "none 2 1\n";
+  base::WriteAll(tmp.fd(), content, sizeof(content));
+  EXPECT_TRUE(CanProfileAndroid(ds_config, 10001, "user", tmp.path()));
+}
+
+TEST(CanProfileAndroidTest, UserProfileableByShellAppMultiuser) {
   DataSourceConfig ds_config;
   ds_config.set_enable_extra_guardrails(false);
   auto tmp = base::TempFile::Create();
@@ -92,7 +104,7 @@
   EXPECT_TRUE(CanProfileAndroid(ds_config, 210001, "user", tmp.path()));
 }
 
-TEST(CanProfileAndroidTest, UserNonProfileableApp) {
+TEST(CanProfileAndroidTest, UserNonProfileableByShellApp) {
   DataSourceConfig ds_config;
   ds_config.set_enable_extra_guardrails(false);
   auto tmp = base::TempFile::Create();
diff --git a/src/profiling/perf/unwinding.h b/src/profiling/perf/unwinding.h
index 2eb296f..398747a 100644
--- a/src/profiling/perf/unwinding.h
+++ b/src/profiling/perf/unwinding.h
@@ -17,6 +17,7 @@
 #ifndef SRC_PROFILING_PERF_UNWINDING_H_
 #define SRC_PROFILING_PERF_UNWINDING_H_
 
+#include <condition_variable>
 #include <map>
 #include <thread>
 
diff --git a/src/protozero/protoc_plugin/protozero_plugin.cc b/src/protozero/protoc_plugin/protozero_plugin.cc
index e2d3192..c3dc58b 100644
--- a/src/protozero/protoc_plugin/protozero_plugin.cc
+++ b/src/protozero/protoc_plugin/protozero_plugin.cc
@@ -831,16 +831,25 @@
 
   void GenerateFieldMetadata(const std::string& message_cpp_type,
                              const FieldDescriptor* field) {
-    const char* code_stub =
-        "using $field_metadata_type$ =\n"
-        "  ::protozero::proto_utils::FieldMetadata<\n"
-        "    $field_id$,\n"
-        "    ::protozero::proto_utils::RepetitionType::$repetition_type$,\n"
-        "    ::protozero::proto_utils::ProtoSchemaType::$proto_field_type$,\n"
-        "    $cpp_type$,\n"
-        "    $message_cpp_type$>;\n"
-        "static constexpr const $field_metadata_type$& $field_metadata_var$ = "
-        "{};\n";
+    const char* code_stub = R"(
+using $field_metadata_type$ =
+  ::protozero::proto_utils::FieldMetadata<
+    $field_id$,
+    ::protozero::proto_utils::RepetitionType::$repetition_type$,
+    ::protozero::proto_utils::ProtoSchemaType::$proto_field_type$,
+    $cpp_type$,
+    $message_cpp_type$>;
+
+// Ceci n'est pas une pipe.
+// This is actually a variable of FieldMetadataHelper<FieldMetadata<...>>
+// type (and users are expected to use it as such, hence kCamelCase name).
+// It is declared as a function to keep protozero bindings header-only as
+// inline constexpr variables are not available until C++17 (while inline
+// functions are).
+// TODO(altimin): Use inline variable instead after adopting C++17.  
+static constexpr $field_metadata_type$ $field_metadata_var$() { return {}; }
+)";
+
     stub_h_->Print(code_stub, "field_id", std::to_string(field->number()),
                    "repetition_type", FieldToRepetitionType(field),
                    "proto_field_type", FieldToProtoSchemaType(field),
diff --git a/src/trace_processor/forwarding_trace_parser.cc b/src/trace_processor/forwarding_trace_parser.cc
index 6583ead..4ba663e 100644
--- a/src/trace_processor/forwarding_trace_parser.cc
+++ b/src/trace_processor/forwarding_trace_parser.cc
@@ -161,7 +161,7 @@
     return kJsonTraceType;
 
   // Systrace with header but no leading HTML.
-  if (start.find("# tracer") != std::string::npos)
+  if (base::Contains(start, "# tracer"))
     return kSystraceTraceType;
 
   // Systrace with leading HTML.
@@ -170,7 +170,7 @@
     return kSystraceTraceType;
 
   // Ctrace is deflate'ed systrace.
-  if (start.find("TRACE:") != std::string::npos)
+  if (base::Contains(start, "TRACE:"))
     return kCtraceTraceType;
 
   // Ninja's buils log (.ninja_log).
diff --git a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
index d493173..1bf293a 100644
--- a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
@@ -24,7 +24,7 @@
 namespace trace_processor {
 namespace {
 
-std::array<MessageDescriptor, 351> descriptors{{
+std::array<MessageDescriptor, 353> descriptors{{
     {nullptr, 0, {}},
     {nullptr, 0, {}},
     {nullptr, 0, {}},
@@ -3741,6 +3741,27 @@
             {"value", ProtoSchemaType::kInt32},
         },
     },
+    {
+        "dma_heap_stat",
+        3,
+        {
+            {},
+            {"inode", ProtoSchemaType::kUint64},
+            {"len", ProtoSchemaType::kInt64},
+            {"total_allocated", ProtoSchemaType::kUint64},
+        },
+    },
+    {
+        "cpuhp_pause",
+        4,
+        {
+            {},
+            {"active_cpus", ProtoSchemaType::kUint32},
+            {"cpus", ProtoSchemaType::kUint32},
+            {"pause", ProtoSchemaType::kUint32},
+            {"time", ProtoSchemaType::kUint32},
+        },
+    },
 }};
 
 }  // namespace
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index 69929a8..c226575 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -30,6 +30,7 @@
 
 #include "protos/perfetto/common/gpu_counter_descriptor.pbzero.h"
 #include "protos/perfetto/trace/ftrace/binder.pbzero.h"
+#include "protos/perfetto/trace/ftrace/cpuhp.pbzero.h"
 #include "protos/perfetto/trace/ftrace/dmabuf_heap.pbzero.h"
 #include "protos/perfetto/trace/ftrace/dpu.pbzero.h"
 #include "protos/perfetto/trace/ftrace/fastrpc.pbzero.h"
@@ -553,6 +554,10 @@
         ParseMaliTracingMarkWrite(ts, pid, data);
         break;
       }
+      case FtraceEvent::kCpuhpPauseFieldNumber: {
+        ParseCpuhpPause(ts, pid, data);
+        break;
+      }
       default:
         break;
     }
@@ -1459,5 +1464,12 @@
       timestamp, static_cast<double>(evt.len()), delta_track);
 }
 
+void FtraceParser::ParseCpuhpPause(int64_t,
+                                   uint32_t,
+                                   protozero::ConstBytes blob) {
+  protos::pbzero::CpuhpPauseFtraceEvent::Decoder evt(blob.data, blob.size);
+  // TODO(b/183110813): Parse and visualize this event.
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index b1ba46e..68ee8e5 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -142,6 +142,7 @@
   void ParseFastRpcDmaStat(int64_t timestamp,
                            uint32_t pid,
                            protozero::ConstBytes);
+  void ParseCpuhpPause(int64_t, uint32_t, protozero::ConstBytes);
 
   TraceProcessorContext* context_;
   RssStatTracker rss_stat_tracker_;
diff --git a/src/trace_processor/importers/json/json_utils.cc b/src/trace_processor/importers/json/json_utils.cc
index 2265eab..5d0b2d1 100644
--- a/src/trace_processor/importers/json/json_utils.cc
+++ b/src/trace_processor/importers/json/json_utils.cc
@@ -22,7 +22,7 @@
 
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
 #include <json/reader.h>
-#include "include/perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_utils.h"
 #endif
 
 namespace perfetto {
diff --git a/src/trace_processor/importers/proto/profiler_util.cc b/src/trace_processor/importers/proto/profiler_util.cc
index f7fe9af..175942f 100644
--- a/src/trace_processor/importers/proto/profiler_util.cc
+++ b/src/trace_processor/importers/proto/profiler_util.cc
@@ -17,6 +17,7 @@
 #include "src/trace_processor/importers/proto/profiler_util.h"
 
 #include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
@@ -144,13 +145,13 @@
     protos::pbzero::ObfuscatedMember::Decoder& member) {
   std::string member_deobfuscated_name =
       member.deobfuscated_name().ToStdString();
-  if (member_deobfuscated_name.find('.') == std::string::npos) {
+  if (base::Contains(member_deobfuscated_name, '.')) {
+    // Fully qualified name.
+    return member_deobfuscated_name;
+  } else {
     // Name relative to class.
     return cls.deobfuscated_name().ToStdString() + "." +
            member_deobfuscated_name;
-  } else {
-    // Fully qualified name.
-    return member_deobfuscated_name;
   }
 }
 
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.cc b/src/trace_processor/importers/proto/proto_trace_reader.cc
index 16e7908..6c80e87 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.cc
+++ b/src/trace_processor/importers/proto/proto_trace_reader.cc
@@ -195,16 +195,10 @@
           context_->clock_tracker->ToTraceTime(converted_clock_id, timestamp);
       if (!trace_ts.has_value()) {
         // ToTraceTime() will increase the |clock_sync_failure| stat on failure.
-        static const char seq_extra_err[] =
-            " Because the clock id is sequence-scoped, the ClockSnapshot must "
-            "be emitted on the same TraceWriter sequence of the packet that "
-            "refers to that clock id.";
-        return util::ErrStatus(
-            "Failed to convert TracePacket's timestamp from clock_id=%" PRIu32
-            " seq_id=%" PRIu32
-            ". This is usually due to the lack of a prior ClockSnapshot "
-            "proto.%s",
-            timestamp_clock_id, seq_id, is_seq_scoped ? seq_extra_err : "");
+        // We don't return an error here as it will cause the trace to stop
+        // parsing. Instead, we rely on the stat increment in ToTraceTime() to
+        // inform the user about the error.
+        return util::OkStatus();
       }
       timestamp = trace_ts.value();
     }
diff --git a/src/trace_processor/metrics/android/android_sysui_cuj.sql b/src/trace_processor/metrics/android/android_sysui_cuj.sql
index 67f8ccf..2a0bba1 100644
--- a/src/trace_processor/metrics/android/android_sysui_cuj.sql
+++ b/src/trace_processor/metrics/android/android_sysui_cuj.sql
@@ -277,7 +277,7 @@
     WHERE remainder <> "")
   SELECT frame_number, jank_cause
   FROM split_jank_type
-  WHERE jank_cause NOT IN ('', 'App Deadline Missed', 'None')
+  WHERE jank_cause NOT IN ('', 'App Deadline Missed', 'None', 'Buffer Stuffing')
   ORDER BY frame_number ASC;
 
 DROP TABLE IF EXISTS android_sysui_cuj_missed_frames_hwui_times;
diff --git a/src/trace_processor/python/perfetto/trace_processor/metrics.descriptor b/src/trace_processor/python/perfetto/trace_processor/metrics.descriptor
index 1b2395a..f54fa96 100644
--- a/src/trace_processor/python/perfetto/trace_processor/metrics.descriptor
+++ b/src/trace_processor/python/perfetto/trace_processor/metrics.descriptor
Binary files differ
diff --git a/src/trace_processor/tables/profiler_tables.h b/src/trace_processor/tables/profiler_tables.h
index d793059..e93c0b4 100644
--- a/src/trace_processor/tables/profiler_tables.h
+++ b/src/trace_processor/tables/profiler_tables.h
@@ -203,7 +203,7 @@
 PERFETTO_TP_TABLE(PERFETTO_TP_PERF_SAMPLE_DEF);
 
 // Symbolization data for a frame. Rows with the same symbol_set_id describe
-// one callframe, with the most-inlined symbol having id == symbol_set_id.
+// one frame, with the bottom-most inlined frame having id == symbol_set_id.
 //
 // For instance, if the function foo has an inlined call to the function bar,
 // which has an inlined call to baz, the stack_profile_symbol table would look
@@ -212,9 +212,9 @@
 // ```
 // |id|symbol_set_id|name         |source_file|line_number|
 // |--|-------------|-------------|-----------|-----------|
-// |1 |      1      |baz          |foo.cc     | 36        |
+// |1 |      1      |foo          |foo.cc     | 60        |
 // |2 |      1      |bar          |foo.cc     | 30        |
-// |3 |      1      |foo          |foo.cc     | 60        |
+// |3 |      1      |baz          |foo.cc     | 36        |
 // ```
 // @param name name of the function.
 // @param source_file name of the source file containing the function.
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index f7c656c..073c5f5 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -265,7 +265,7 @@
       "SELECT name FROM sqlite_master WHERE type='table'");
   for (uint32_t rows = 0; tables_it.Next(); rows++) {
     std::string table_name = tables_it.Get(0).string_value;
-    PERFETTO_CHECK(table_name.find('\'') == std::string::npos);
+    PERFETTO_CHECK(base::Contains(table_name, '\''));
     std::string export_sql = "CREATE TABLE perfetto_export." + table_name +
                              " AS SELECT * FROM " + table_name;
 
diff --git a/src/traced/probes/ftrace/event_info.cc b/src/traced/probes/ftrace/event_info.cc
index 0bd85ba..e660f63 100644
--- a/src/traced/probes/ftrace/event_info.cc
+++ b/src/traced/probes/ftrace/event_info.cc
@@ -1085,6 +1085,25 @@
        kUnsetFtraceId,
        346,
        kUnsetSize},
+      {"cpuhp_pause",
+       "cpuhp",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "active_cpus", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "cpus", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "pause", 3, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "time", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       352,
+       kUnsetSize},
       {"dma_heap_stat",
        "dmabuf_heap",
        {
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.cc b/src/traced/probes/ftrace/ftrace_config_muxer.cc
index fa789ae..24b84b5 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.cc
@@ -269,6 +269,7 @@
         events.insert(GroupAndName("power", "suspend_resume"));
         events.insert(GroupAndName("cpuhp", "cpuhp_enter"));
         events.insert(GroupAndName("cpuhp", "cpuhp_exit"));
+        events.insert(GroupAndName("cpuhp", "cpuhp_pause"));
         AddEventGroup(table, "msm_bus", &events);
         events.insert(GroupAndName("msm_bus", "bus_update_request_end"));
         events.insert(GroupAndName("msm_bus", "bus_update_request"));
diff --git a/src/traced/probes/ftrace/test/data/synthetic/available_events b/src/traced/probes/ftrace/test/data/synthetic/available_events
index b20b698..2ee225a 100644
--- a/src/traced/probes/ftrace/test/data/synthetic/available_events
+++ b/src/traced/probes/ftrace/test/data/synthetic/available_events
@@ -11,3 +11,4 @@
 dpu:tracing_mark_write
 g2d:tracing_mark_write
 power:suspend_resume
+cpuhp:cpuhp_pause
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/cpuhp/cpuhp_pause/format b/src/traced/probes/ftrace/test/data/synthetic/events/cpuhp/cpuhp_pause/format
new file mode 100644
index 0000000..e0ff5e8
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/cpuhp/cpuhp_pause/format
@@ -0,0 +1,14 @@
+name: cpuhp_pause
+ID: 68
+format:
+	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
+	field:unsigned char common_flags;	offset:2;	size:1;	signed:0;
+	field:unsigned char common_preempt_count;	offset:3;	size:1;	signed:0;
+	field:int common_pid;	offset:4;	size:4;	signed:1;
+
+	field:unsigned int cpus;	offset:8;	size:4;	signed:0;
+	field:unsigned int active_cpus;	offset:12;	size:4;	signed:0;
+	field:unsigned int time;	offset:16;	size:4;	signed:0;
+	field:unsigned char pause;	offset:20;	size:1;	signed:0;
+
+print fmt: "req_cpus=0x%x act_cpus=0x%x time=%u us paused=%d", REC->cpus, REC->active_cpus, REC->time, REC->pause
diff --git a/src/traced/probes/packages_list/packages_list_parser.cc b/src/traced/probes/packages_list/packages_list_parser.cc
index bcbccaa..e00e296 100644
--- a/src/traced/probes/packages_list/packages_list_parser.cc
+++ b/src/traced/probes/packages_list/packages_list_parser.cc
@@ -50,12 +50,13 @@
       }
       case 6: {
         char* end;
-        long long profilable_from_shell = strtoll(ss.cur_token(), &end, 10);
+        long long profilable_level = strtoll(ss.cur_token(), &end, 10);
         if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
           PERFETTO_ELOG("Failed to parse packages.list profilable_from_shell.");
           return false;
         }
-        package->profileable_from_shell = profilable_from_shell != 0;
+        package->profileable_from_shell = profilable_level != 0;
+        package->profileable = profilable_level == 2;
         break;
       }
       case 7: {
diff --git a/src/traced/probes/packages_list/packages_list_parser.h b/src/traced/probes/packages_list/packages_list_parser.h
index 3deb3c5..ee35ddd 100644
--- a/src/traced/probes/packages_list/packages_list_parser.h
+++ b/src/traced/probes/packages_list/packages_list_parser.h
@@ -26,6 +26,7 @@
   std::string name;
   uint64_t uid = 0;
   bool debuggable = false;
+  bool profileable = false;
   bool profileable_from_shell = false;
   int64_t version_code = 0;
 };
diff --git a/src/tracing/traced_value_unittest.cc b/src/tracing/traced_value_unittest.cc
index 1aa050b..66c86db 100644
--- a/src/tracing/traced_value_unittest.cc
+++ b/src/tracing/traced_value_unittest.cc
@@ -91,7 +91,7 @@
 ASSERT_TYPE_SUPPORTED(const int*);
 ASSERT_TYPE_SUPPORTED(void*);
 ASSERT_TYPE_SUPPORTED(const void*);
-ASSERT_TYPE_SUPPORTED(nullptr_t);
+ASSERT_TYPE_SUPPORTED(std::nullptr_t);
 ASSERT_TYPE_NOT_SUPPORTED(NonSupportedType*);
 ASSERT_TYPE_NOT_SUPPORTED(const NonSupportedType*);
 
@@ -250,7 +250,7 @@
 
 namespace {
 
-class HasConvertorMember {
+class HasWriteIntoTracedValueConvertorMember {
  public:
   void WriteIntoTracedValue(TracedValue context) const {
     auto dict = std::move(context).WriteDictionary();
@@ -259,7 +259,17 @@
   }
 };
 
-class HasExternalConvertor {};
+class HasWriteIntoTraceConvertorMember {
+ public:
+  void WriteIntoTrace(TracedValue context) const {
+    auto dict = std::move(context).WriteDictionary();
+    dict.Add("int", 42);
+    dict.Add("bool", false);
+  }
+};
+
+class HasExternalWriteIntoTraceConvertor {};
+class HasExternalWriteIntoTracedValueConvertor {};
 
 class HasAllConversionMethods {
  public:
@@ -302,9 +312,18 @@
 }  // namespace
 
 template <>
-struct TraceFormatTraits<HasExternalConvertor> {
-  static void WriteIntoTracedValue(TracedValue context,
-                                   const HasExternalConvertor&) {
+struct TraceFormatTraits<HasExternalWriteIntoTraceConvertor> {
+  static void WriteIntoTrace(TracedValue context,
+                             const HasExternalWriteIntoTraceConvertor&) {
+    std::move(context).WriteString("TraceFormatTraits::WriteIntoTrace");
+  }
+};
+
+template <>
+struct TraceFormatTraits<HasExternalWriteIntoTracedValueConvertor> {
+  static void WriteIntoTracedValue(
+      TracedValue context,
+      const HasExternalWriteIntoTracedValueConvertor&) {
     std::move(context).WriteString("TraceFormatTraits::WriteIntoTracedValue");
   }
 };
@@ -326,8 +345,10 @@
   return internal::DebugAnnotationToString(message.SerializeAsString());
 }
 
-ASSERT_TYPE_SUPPORTED(HasConvertorMember);
-ASSERT_TYPE_SUPPORTED(HasExternalConvertor);
+ASSERT_TYPE_SUPPORTED(HasWriteIntoTraceConvertorMember);
+ASSERT_TYPE_SUPPORTED(HasWriteIntoTracedValueConvertorMember);
+ASSERT_TYPE_SUPPORTED(HasExternalWriteIntoTraceConvertor);
+ASSERT_TYPE_SUPPORTED(HasExternalWriteIntoTracedValueConvertor);
 ASSERT_TYPE_SUPPORTED(HasAllConversionMethods);
 
 ASSERT_TYPE_SUPPORTED(HasConstWriteMember);
@@ -364,19 +385,27 @@
 ASSERT_TYPE_SUPPORTED(std::unique_ptr<const HasConstAndNonConstWriteMember*>);
 
 TEST(TracedValueTest, UserDefinedConvertors) {
-  HasConvertorMember value1;
+  HasWriteIntoTraceConvertorMember value1;
   EXPECT_EQ(TracedValueToString(value1), "{int:42,bool:false}");
   EXPECT_EQ(TracedValueToString(&value1), "{int:42,bool:false}");
 
-  HasExternalConvertor value2;
-  EXPECT_EQ(TracedValueToString(value2),
+  HasWriteIntoTracedValueConvertorMember value2;
+  EXPECT_EQ(TracedValueToString(value2), "{int:42,bool:false}");
+  EXPECT_EQ(TracedValueToString(&value2), "{int:42,bool:false}");
+
+  HasExternalWriteIntoTracedValueConvertor value3;
+  EXPECT_EQ(TracedValueToString(value3),
             "TraceFormatTraits::WriteIntoTracedValue");
-  EXPECT_EQ(TracedValueToString(&value2),
+  EXPECT_EQ(TracedValueToString(&value3),
             "TraceFormatTraits::WriteIntoTracedValue");
 
-  HasAllConversionMethods value3;
-  EXPECT_EQ(TracedValueToString(value3), "T::WriteIntoTracedValue");
-  EXPECT_EQ(TracedValueToString(&value3), "T::WriteIntoTracedValue");
+  HasExternalWriteIntoTraceConvertor value4;
+  EXPECT_EQ(TracedValueToString(value4), "TraceFormatTraits::WriteIntoTrace");
+  EXPECT_EQ(TracedValueToString(&value4), "TraceFormatTraits::WriteIntoTrace");
+
+  HasAllConversionMethods value5;
+  EXPECT_EQ(TracedValueToString(value5), "T::WriteIntoTracedValue");
+  EXPECT_EQ(TracedValueToString(&value5), "T::WriteIntoTracedValue");
 }
 
 TEST(TracedValueTest, WriteAsLambda) {
diff --git a/test/trace_processor/graphics/android_sysui_cuj.out b/test/trace_processor/graphics/android_sysui_cuj.out
index cf6c74e..1cdea90 100644
--- a/test/trace_processor/graphics/android_sysui_cuj.out
+++ b/test/trace_processor/graphics/android_sysui_cuj.out
@@ -63,7 +63,6 @@
     number: 8
     ts: 400000000
     dur: 10000000
-    jank_cause: "Buffer Stuffing"
     vsync: 120
   }
 }
diff --git a/test/trace_processor/graphics/android_sysui_cuj_event.out b/test/trace_processor/graphics/android_sysui_cuj_event.out
index e36ea72..4519815 100644
--- a/test/trace_processor/graphics/android_sysui_cuj_event.out
+++ b/test/trace_processor/graphics/android_sysui_cuj_event.out
@@ -6,4 +6,3 @@
 "slice","SHADE_ROW_EXPAND - jank cause",70000000,10000000,"RenderThread - scheduler"
 "slice","SHADE_ROW_EXPAND - jank cause",100000000,15000000,"GPU completion - long completion time"
 "slice","SHADE_ROW_EXPAND - jank cause",200000000,15000000,"SurfaceFlinger GPU Deadline Missed,SurfaceFlinger Scheduling"
-"slice","SHADE_ROW_EXPAND - jank cause",400000000,15000000,"Buffer Stuffing"
diff --git a/tools/ftrace_proto_gen/event_list b/tools/ftrace_proto_gen/event_list
index 45727f0..1351543 100644
--- a/tools/ftrace_proto_gen/event_list
+++ b/tools/ftrace_proto_gen/event_list
@@ -346,3 +346,4 @@
 g2d/tracing_mark_write
 mali/tracing_mark_write
 dmabuf_heap/dma_heap_stat
+cpuhp/cpuhp_pause
diff --git a/tools/gen_bazel b/tools/gen_bazel
index 11719bb..ca38248 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -318,13 +318,13 @@
   plugin_label.comment = target.name
   plugin_label.deps += [':' + sources_label_name]
 
-  # When using the cppgen plugin we need to pass down also the transitive deps.
+  # When using the plugins we need to pass down also the transitive deps.
   # For instance consider foo.proto including common.proto. The generated
   # foo.cc will #include "common.gen.h". Hence the generated cc_protocpp_library
   # rule need to pass down the dependency on the target that generates
-  # common.gen.{cc,h}. This is not needed for protozero because protozero
-  # headers are fully hermetic deps-wise and use only on forward declarations.
-  if target.proto_deps and target.proto_plugin in ('cppgen', 'ipc'):
+  # common.gen.{cc,h}.
+  if target.proto_deps and target.proto_plugin in (
+      'cppgen', 'ipc', 'protozero'):
     plugin_label.deps += [
         ':' + get_bazel_label_name(x) for x in target.proto_deps
     ]
diff --git a/tools/heap_profile b/tools/heap_profile
index 41b386d..6e42672 100755
--- a/tools/heap_profile
+++ b/tools/heap_profile
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # Copyright (C) 2017 The Android Open Source Project
 #
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 560c1ba..40a1501 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -55,7 +55,7 @@
 ]
 
 # Dependencies required to build code on the host or when targeting desktop OS.
-BUILD_DEPS_HOST = [
+BUILD_DEPS_TOOLCHAIN_HOST = [
     # GN. From https://chrome-infra-packages.appspot.com/dl/gn/gn/.
     # git_revision:83dad00afb232d7235dd70dff1ee90292d72a01e .
     Dependency(
@@ -117,6 +117,21 @@
         '6f8af488be74ed8787d04e107080d05330587a4198ba047bd5b7f5b0c3150d61',
         'windows', 'x64'),
 
+    # Keep the revision in sync with Chrome's PACKAGE_VERSION in
+    # tools/clang/scripts/update.py.
+    Dependency(
+        'buildtools/linux64/clang.tgz',
+        'https://commondatastorage.googleapis.com/chromium-browser-clang/Linux_x64/clang-llvmorg-12-init-5035-gd0abc757-3.tgz',
+        'b0c3015209b6d624844ad230064eb5c9b4429a2eafd4854981e73217c563d93d',
+        'linux', 'x64'),
+    Dependency(
+        'buildtools/win/clang.tgz',
+        'https://commondatastorage.googleapis.com/chromium-browser-clang/Win/clang-llvmorg-12-init-5035-gd0abc757-3.tgz',
+        'b2854d871a466e3a060469b5edb24ca355ef64576d38778f64acbd3c6d7cf530',
+        'windows', 'x64'),
+]
+
+BUILD_DEPS_HOST = [
     # Keep in sync with Android's //external/googletest/README.version.
     Dependency(
         'buildtools/googletest',
@@ -145,19 +160,6 @@
         'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libunwind.git',
         'd999d54f4bca789543a2eb6c995af2d9b5a1f3ed', 'all', 'all'),
 
-    # Keep the revision in sync with Chrome's PACKAGE_VERSION in
-    # tools/clang/scripts/update.py.
-    Dependency(
-        'buildtools/linux64/clang.tgz',
-        'https://commondatastorage.googleapis.com/chromium-browser-clang/Linux_x64/clang-llvmorg-12-init-5035-gd0abc757-3.tgz',
-        'b0c3015209b6d624844ad230064eb5c9b4429a2eafd4854981e73217c563d93d',
-        'linux', 'x64'),
-    Dependency(
-        'buildtools/win/clang.tgz',
-        'https://commondatastorage.googleapis.com/chromium-browser-clang/Win/clang-llvmorg-12-init-5035-gd0abc757-3.tgz',
-        'b2854d871a466e3a060469b5edb24ca355ef64576d38778f64acbd3c6d7cf530',
-        'windows', 'x64'),
-
     # Keep in sync with chromium DEPS.
     Dependency(
         'buildtools/libfuzzer',
@@ -208,7 +210,7 @@
     Dependency(
         'buildtools/android-unwinding',
         'https://android.googlesource.com/platform/system/unwinding.git',
-        '5f15cdbdb71db0eb6512e9432a63b8c4075a6c00', 'all', 'all'),
+        'b296bb6fcaa7f59e0ed57bb7a4238ee0f9bfb5bf', 'all', 'all'),
     Dependency('buildtools/android-logging',
                'https://android.googlesource.com/platform/system/logging.git',
                '7b36b566c9113fc703d68f76e8f40c0c2432481c', 'all', 'all'),
@@ -476,11 +478,15 @@
   parser.add_argument('--check-only')
   parser.add_argument('--filter', default='')
   parser.add_argument('--verify', help='Check all URLs', action='store_true')
+  parser.add_argument('--no-toolchain', help='Do not download toolchain',
+                      action='store_true')
   args = parser.parse_args()
   if args.verify:
     CheckHashes()
     return 0
   deps = BUILD_DEPS_HOST
+  if not args.no_toolchain:
+    deps += BUILD_DEPS_TOOLCHAIN_HOST
   if args.android:
     deps += BUILD_DEPS_ANDROID + TEST_DEPS_ANDROID
   if args.ui:
diff --git a/tools/java_heap_dump b/tools/java_heap_dump
index ced597e..03abb01 100755
--- a/tools/java_heap_dump
+++ b/tools/java_heap_dump
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # Copyright (C) 2020 The Android Open Source Project
 #
@@ -168,10 +168,11 @@
     print(cfg)
     return 0
 
-  user = subprocess.check_output(['adb', 'shell', 'whoami']).strip()
+  user = subprocess.check_output(
+    ['adb', 'shell', 'whoami']).strip().decode('utf8')
   perfetto_pid = subprocess.check_output(
       ['adb', 'exec-out',
-       PERFETTO_CMD.format(cfg=cfg, user=user)]).strip()
+       PERFETTO_CMD.format(cfg=cfg, user=user)]).strip().decode('utf8')
   try:
     int(perfetto_pid.strip())
   except ValueError:
diff --git a/tools/trace_processor b/tools/trace_processor
index 5b84f0c..d15dd62 100755
--- a/tools/trace_processor
+++ b/tools/trace_processor
@@ -21,7 +21,7 @@
 # cat ./trace_processor | python -
 
 BASH_FALLBACK = """ "
-exec python - "$@" <<'#'EOF
+exec python3 - "$@" <<'#'EOF
 #"""
 
 import hashlib
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index 1ef6484..c1674d8 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -71,7 +71,6 @@
     "../../protos/third_party/pprof:zero",
     "../../src/profiling/symbolizer",
     "../../src/profiling/symbolizer:symbolize_database",
-    "../../src/trace_processor/containers:containers",
   ]
   sources = [ "pprof_builder.cc" ]
 }
diff --git a/tools/trace_to_text/main.cc b/tools/trace_to_text/main.cc
index e4a9f1d..8384b7a 100644
--- a/tools/trace_to_text/main.cc
+++ b/tools/trace_to_text/main.cc
@@ -32,6 +32,7 @@
 #include "tools/trace_to_text/trace_to_systrace.h"
 #include "tools/trace_to_text/trace_to_text.h"
 
+
 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 #include <unistd.h>
 #endif
@@ -42,19 +43,17 @@
 
 int Usage(const char* argv0) {
   fprintf(stderr,
-          "Usage: %s MODE [OPTIONS] [input file] [output file]\n"
-          "modes:\n"
-          "  systrace|json|ctrace|text|profile|hprof|symbolize|deobfuscate\n"
-          "options:\n"
-          "  [--truncate start|end]\n"
-          "  [--full-sort]\n"
-          "\"profile\" mode options:\n"
-          "  [--perf] generate a perf profile instead of a heap profile\n"
-          "  [--no-annotations] do not suffix frame names with derived "
-          "annotations\n"
-          "  [--timestamps TIMESTAMP1,TIMESTAMP2,...] generate profiles "
-          "only for these *specific* timestamps\n"
-          "  [--pid PID] generate profiles only for this process id\n",
+          "Usage: %s systrace|json|ctrace|text|profile [--pid PID] "
+          "[--timestamps TIMESTAMP1,TIMESTAMP2,...] "
+          "[--truncate start|end] "
+          "[--full-sort] "
+          "[trace.pb] "
+          "[trace.txt]\n"
+          "\nProfile mode only:\n"
+          "\t--perf generate a perf profile\n"
+          "\t--timestamps TIMESTAMP1,TIMESTAMP2,... generate profiles "
+          "only for these timestamps\n"
+          "\t--pid PID generate profiles only for this process id\n",
           argv0);
   return 1;
 }
@@ -76,7 +75,6 @@
   std::vector<uint64_t> timestamps;
   bool full_sort = false;
   bool perf_profile = false;
-  bool profile_no_annotations = false;
   for (int i = 1; i < argc; i++) {
     if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
       printf("%s\n", base::GetVersionString());
@@ -105,8 +103,6 @@
       }
     } else if (strcmp(argv[i], "--perf") == 0) {
       perf_profile = true;
-    } else if (strcmp(argv[i], "--no-annotations") == 0) {
-      profile_no_annotations = true;
     } else if (strcmp(argv[i], "--full-sort") == 0) {
       full_sort = true;
     } else {
@@ -191,11 +187,10 @@
     return TraceToText(input_stream, output_stream);
 
   if (format == "profile") {
-    return perf_profile
-               ? TraceToPerfProfile(input_stream, output_stream, pid,
-                                    timestamps, !profile_no_annotations)
-               : TraceToHeapProfile(input_stream, output_stream, pid,
-                                    timestamps, !profile_no_annotations);
+    return perf_profile ? TraceToPerfProfile(input_stream, output_stream, pid,
+                                             timestamps)
+                        : TraceToHeapProfile(input_stream, output_stream, pid,
+                                             timestamps);
   }
 
   if (format == "hprof")
diff --git a/tools/trace_to_text/pprof_builder.cc b/tools/trace_to_text/pprof_builder.cc
index 588c438..fdb8717 100644
--- a/tools/trace_to_text/pprof_builder.cc
+++ b/tools/trace_to_text/pprof_builder.cc
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#include "perfetto/base/build_config.h"
-
 #include "perfetto/profiling/pprof_builder.h"
 
+#include "perfetto/base/build_config.h"
+
 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 #include <cxxabi.h>
 #endif
@@ -27,120 +27,29 @@
 #include <algorithm>
 #include <map>
 #include <set>
-#include <unordered_map>
 #include <utility>
 #include <vector>
 
 #include "tools/trace_to_text/utils.h"
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/hash.h"
+#include "perfetto/base/time.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/utils.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 "src/trace_processor/containers/string_pool.h"
 
 #include "protos/perfetto/trace/trace.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 #include "protos/third_party/pprof/profile.pbzero.h"
 
-// Quick hint on navigating the file:
-// Conversions for both perf and heap profiles start with |TraceToPprof|.
-// Non-shared logic is in the |heap_profile| and |perf_profile| namespaces.
-//
-// To build one or more profiles, first the callstack information is queried
-// from the SQL tables, and converted into an in-memory representation by
-// |PreprocessLocations|. Then an instance of |GProfileBuilder| is used to
-// accumulate samples for that profile, and emit all additional information as a
-// serialized proto. Only the entities referenced by that particular
-// |GProfileBuilder| instance are emitted.
-//
-// See protos/third_party/pprof/profile.proto for the meaning of terms like
-// function/location/line.
-
-namespace {
-using StringId = ::perfetto::trace_processor::StringPool::Id;
-
-// In-memory representation of a Profile.Function.
-struct Function {
-  StringId name_id = StringId::Null();
-  StringId system_name_id = StringId::Null();
-  StringId filename_id = StringId::Null();
-
-  Function(StringId n, StringId s, StringId f)
-      : name_id(n), system_name_id(s), filename_id(f) {}
-
-  bool operator==(const Function& other) const {
-    return std::tie(name_id, system_name_id, filename_id) ==
-           std::tie(other.name_id, other.system_name_id, other.filename_id);
-  }
-};
-
-// In-memory representation of a Profile.Line.
-struct Line {
-  int64_t function_id = 0;  // LocationTracker's interned Function id
-  int64_t line_no = 0;
-
-  Line(int64_t func, int64_t line) : function_id(func), line_no(line) {}
-
-  bool operator==(const Line& other) const {
-    return function_id == other.function_id && line_no == other.line_no;
-  }
-};
-
-// In-memory representation of a Profile.Location.
-struct Location {
-  int64_t mapping_id = 0;  // sqlite row id
-  // Common case: location references a single function.
-  int64_t single_function_id = 0;  // interned Function id
-  // Alternatively: multiple inlined functions, recovered via offline
-  // symbolisation. Leaf-first ordering.
-  std::vector<Line> inlined_functions;
-
-  Location(int64_t map, int64_t func, std::vector<Line> inlines)
-      : mapping_id(map),
-        single_function_id(func),
-        inlined_functions(std::move(inlines)) {}
-
-  bool operator==(const Location& other) const {
-    return std::tie(mapping_id, single_function_id, inlined_functions) ==
-           std::tie(other.mapping_id, other.single_function_id,
-                    other.inlined_functions);
-  }
-};
-}  // namespace
-
-template <>
-struct std::hash<Function> {
-  size_t operator()(const Function& loc) const {
-    perfetto::base::Hash hasher;
-    hasher.Update(loc.name_id.raw_id());
-    hasher.Update(loc.system_name_id.raw_id());
-    hasher.Update(loc.filename_id.raw_id());
-    return static_cast<size_t>(hasher.digest());
-  }
-};
-
-template <>
-struct std::hash<Location> {
-  size_t operator()(const Location& loc) const {
-    perfetto::base::Hash hasher;
-    hasher.Update(loc.mapping_id);
-    hasher.Update(loc.single_function_id);
-    for (auto line : loc.inlined_functions) {
-      hasher.Update(line.function_id);
-      hasher.Update(line.line_no);
-    }
-    return static_cast<size_t>(hasher.digest());
-  }
-};
-
 namespace perfetto {
 namespace trace_to_text {
+
 namespace {
 
 using ::perfetto::trace_processor::Iterator;
@@ -174,6 +83,84 @@
   return ret;
 }
 
+// Return map from callsite_id to list of frame_ids that make up the callstack.
+std::vector<std::vector<int64_t>> GetCallsiteToFrames(
+    trace_processor::TraceProcessor* tp) {
+  Iterator count_it =
+      tp->ExecuteQuery("select count(*) from stack_profile_callsite;");
+  if (!count_it.Next()) {
+    PERFETTO_DFATAL_OR_ELOG("Failed to get number of callsites: %s",
+                            count_it.Status().message().c_str());
+    return {};
+  }
+  int64_t count = count_it.Get(0).AsLong();
+
+  Iterator it = tp->ExecuteQuery(
+      "select id, parent_id, frame_id from stack_profile_callsite order by "
+      "depth;");
+  std::vector<std::vector<int64_t>> result(static_cast<size_t>(count));
+  while (it.Next()) {
+    int64_t id = it.Get(0).AsLong();
+    int64_t frame_id = it.Get(2).AsLong();
+    std::vector<int64_t>& path = result[static_cast<size_t>(id)];
+    path.push_back(frame_id);
+
+    auto parent_id_value = it.Get(1);
+    if (!parent_id_value.is_null()) {
+      const std::vector<int64_t>& parent_path =
+          result[static_cast<size_t>(parent_id_value.AsLong())];
+      path.insert(path.end(), parent_path.begin(), parent_path.end());
+    }
+  }
+
+  if (!it.Status().ok()) {
+    PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
+                            it.Status().message().c_str());
+    return {};
+  }
+  return result;
+}
+
+base::Optional<int64_t> GetMaxSymbolId(trace_processor::TraceProcessor* tp) {
+  auto max_symbol_id_it =
+      tp->ExecuteQuery("select max(id) from stack_profile_symbol");
+  if (!max_symbol_id_it.Next()) {
+    PERFETTO_DFATAL_OR_ELOG("Failed to get max symbol set id: %s",
+                            max_symbol_id_it.Status().message().c_str());
+    return base::nullopt;
+  }
+  auto value = max_symbol_id_it.Get(0);
+  if (value.is_null())
+    return base::nullopt;
+  return base::make_optional(value.AsLong());
+}
+
+struct Line {
+  int64_t symbol_id;
+  uint32_t line_number;
+};
+
+std::map<int64_t, std::vector<Line>> GetSymbolSetIdToLines(
+    trace_processor::TraceProcessor* tp) {
+  std::map<int64_t, std::vector<Line>> result;
+  Iterator it = tp->ExecuteQuery(
+      "SELECT symbol_set_id, id, line_number FROM stack_profile_symbol;");
+  while (it.Next()) {
+    int64_t symbol_set_id = it.Get(0).AsLong();
+    int64_t id = it.Get(1).AsLong();
+    int64_t line_number = it.Get(2).AsLong();
+    result[symbol_set_id].emplace_back(
+        Line{id, static_cast<uint32_t>(line_number)});
+  }
+
+  if (!it.Status().ok()) {
+    PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
+                            it.Status().message().c_str());
+    return {};
+  }
+  return result;
+}
+
 base::Optional<int64_t> GetStatsEntry(
     trace_processor::TraceProcessor* tp,
     const std::string& name,
@@ -195,373 +182,73 @@
   return base::make_optional(it.Get(0).AsLong());
 }
 
-// Interns Locations, Lines, and Functions. Interning is done by the entity's
-// contents, and has no relation to the row ids in the SQL tables.
-// Contains all data for the trace, so can be reused when emitting multiple
-// profiles.
-//
-// TODO(rsavitski): consider moving mappings into here as well. For now, they're
-// still emitted in a single scan during profile building. Mappings should be
-// unique-enough already in the SQL tables, with only incremental state clearing
-// duplicating entries.
-class LocationTracker {
- public:
-  int64_t InternLocation(Location loc) {
-    auto it = locations_.find(loc);
-    if (it == locations_.end()) {
-      bool inserted = false;
-      std::tie(it, inserted) = locations_.emplace(
-          std::move(loc), static_cast<int64_t>(locations_.size()));
-      PERFETTO_DCHECK(inserted);
-    }
-    return it->second;
-  }
-
-  int64_t InternFunction(Function func) {
-    auto it = functions_.find(func);
-    if (it == functions_.end()) {
-      bool inserted = false;
-      std::tie(it, inserted) =
-          functions_.emplace(func, static_cast<int64_t>(functions_.size()));
-      PERFETTO_DCHECK(inserted);
-    }
-    return it->second;
-  }
-
-  bool IsCallsiteProcessed(int64_t callstack_id) const {
-    return callsite_to_locations_.find(callstack_id) !=
-           callsite_to_locations_.end();
-  }
-
-  void MaybeSetCallsiteLocations(int64_t callstack_id,
-                                 const std::vector<int64_t>& locs) {
-    // nop if already set
-    callsite_to_locations_.emplace(callstack_id, locs);
-  }
-
-  const std::vector<int64_t>& LocationsForCallstack(
-      int64_t callstack_id) const {
-    auto it = callsite_to_locations_.find(callstack_id);
-    PERFETTO_CHECK(callstack_id >= 0 && it != callsite_to_locations_.end());
-    return it->second;
-  }
-
-  const std::unordered_map<Location, int64_t>& AllLocations() const {
-    return locations_;
-  }
-  const std::unordered_map<Function, int64_t>& AllFunctions() const {
-    return functions_;
-  }
-
- private:
-  // Root-first location ids for a given callsite id.
-  std::unordered_map<int64_t, std::vector<int64_t>> callsite_to_locations_;
-  std::unordered_map<Location, int64_t> locations_;
-  std::unordered_map<Function, int64_t> functions_;
-};
-
-struct PreprocessedInline {
-  StringId system_name_id = StringId::Null();
-  StringId filename_id = StringId::Null();
-  int64_t line_no = 0;
-
-  PreprocessedInline(StringId s, StringId f, int64_t line)
-      : system_name_id(s), filename_id(f), line_no(line) {}
-};
-
-std::unordered_map<int64_t, std::vector<PreprocessedInline>>
-PreprocessInliningInfo(trace_processor::TraceProcessor* tp,
-                       trace_processor::StringPool* interner) {
-  std::unordered_map<int64_t, std::vector<PreprocessedInline>> inlines;
-
-  // Most-inlined function (leaf) has the lowest id within a symbol set. Query
-  // such that the per-set line vectors are built up leaf-first.
-  Iterator it = tp->ExecuteQuery(
-      "select symbol_set_id, name, source_file, line_number from "
-      "stack_profile_symbol order by symbol_set_id asc, id asc;");
-  while (it.Next()) {
-    int64_t symbol_set_id = it.Get(0).AsLong();
-    auto func_sysname = it.Get(1).is_null() ? "" : it.Get(1).AsString();
-    auto filename = it.Get(2).is_null() ? "" : it.Get(2).AsString();
-    int64_t line_no = it.Get(3).AsLong();
-
-    inlines[symbol_set_id].emplace_back(interner->InternString(func_sysname),
-                                        interner->InternString(filename),
-                                        line_no);
-  }
-
-  if (!it.Status().ok()) {
-    PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
-                            it.Status().message().c_str());
-    return {};
-  }
-  return inlines;
-}
-
-// Extracts and interns the unique frames and locations (as defined by the proto
-// format) from the callstack SQL tables.
-//
-// Approach:
-//   * for each callstack (callsite ids of the leaves):
-//     * use experimental_annotated_callstack to build the full list of
-//       constituent frames
-//     * for each frame (root to leaf):
-//         * intern the location and function(s)
-//         * remember the mapping from callsite_id to the callstack so far (from
-//            the root and including the frame being considered)
-//
-// Optionally mixes in the annotations as a frame name suffix (since there's no
-// good way to attach extra info to locations in the proto format). This relies
-// on the annotations (produced by experimental_annotated_callstack) to be
-// stable for a given callsite (equivalently: dependent only on their parents).
-LocationTracker PreprocessLocations(trace_processor::TraceProcessor* tp,
-                                    trace_processor::StringPool* interner,
-                                    bool annotate_frames) {
-  LocationTracker tracker;
-
-  // Keyed by symbol_set_id, discarded once this function converts the inlines
-  // into Line and Function entries.
-  std::unordered_map<int64_t, std::vector<PreprocessedInline>> inlining_info =
-      PreprocessInliningInfo(tp, interner);
-
-  // Higher callsite ids most likely correspond to the deepest stacks, so we'll
-  // fill more of the overall callsite->location map by visiting the callsited
-  // in decreasing id order. Since processing a callstack also fills in the data
-  // for all parent callsites.
-  Iterator cid_it = tp->ExecuteQuery(
-      "select id from stack_profile_callsite order by id desc;");
-  while (cid_it.Next()) {
-    int64_t query_cid = cid_it.Get(0).AsLong();
-
-    // If the leaf has been processed, the rest of the stack is already known.
-    if (tracker.IsCallsiteProcessed(query_cid))
-      continue;
-
-    std::string annotated_query =
-        "select sp.id, sp.annotation, spf.mapping, "
-        "ifnull(spf.deobfuscated_name, spf.name), spf.symbol_set_id from "
-        "experimental_annotated_callstack(" +
-        std::to_string(query_cid) +
-        ") sp join stack_profile_frame spf on (sp.frame_id == spf.id) "
-        "order by depth asc";
-    Iterator c_it = tp->ExecuteQuery(annotated_query);
-
-    std::vector<int64_t> callstack_loc_ids;
-    while (c_it.Next()) {
-      int64_t cid = c_it.Get(0).AsLong();
-      int64_t mapping_id = c_it.Get(2).AsLong();
-      auto annotation = c_it.Get(1).is_null() ? "" : c_it.Get(1).AsString();
-      auto func_sysname = c_it.Get(3).is_null() ? "" : c_it.Get(3).AsString();
-      base::Optional<int64_t> symbol_set_id =
-          c_it.Get(4).is_null() ? base::nullopt
-                                : base::make_optional(c_it.Get(4).AsLong());
-
-      Location loc(mapping_id, /*single_function_id=*/-1, {});
-
-      auto intern_function = [interner, &tracker, annotate_frames](
-                                 StringId func_sysname_id, StringId filename_id,
-                                 const std::string& anno) {
-        std::string func_name = interner->Get(func_sysname_id).ToStdString();
-        MaybeDemangle(&func_name);
-        if (annotate_frames && !anno.empty() && !func_name.empty())
-          func_name = func_name + " [" + anno + "]";
-        StringId func_name_id =
-            interner->InternString(base::StringView(func_name));
-        Function func(func_name_id, func_sysname_id, filename_id);
-        return tracker.InternFunction(func);
-      };
-
-      // Inlining information available
-      if (symbol_set_id.has_value()) {
-        auto it = inlining_info.find(*symbol_set_id);
-        if (it == inlining_info.end()) {
-          PERFETTO_DFATAL_OR_ELOG(
-              "Failed to find stack_profile_symbol entry for symbol_set_id "
-              "%" PRIi64 "",
-              *symbol_set_id);
-          return {};
-        }
-
-        // N inlined functions
-        for (const auto& line : it->second) {
-          int64_t func_id = intern_function(line.system_name_id,
-                                            line.filename_id, annotation);
-
-          loc.inlined_functions.emplace_back(func_id, line.line_no);
-        }
-      } else {
-        // Otherwise - single function
-        int64_t func_id =
-            intern_function(interner->InternString(func_sysname),
-                            /*filename_id=*/StringId::Null(), annotation);
-        loc.single_function_id = func_id;
-      }
-
-      int64_t loc_id = tracker.InternLocation(std::move(loc));
-
-      // Update the tracker with the locations so far (for example, at depth 2,
-      // we'll have 3 root-most locations in |callstack_loc_ids|).
-      callstack_loc_ids.push_back(loc_id);
-      tracker.MaybeSetCallsiteLocations(cid, callstack_loc_ids);
-    }
-
-    if (!c_it.Status().ok()) {
-      PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
-                              c_it.Status().message().c_str());
-      return {};
-    }
-  }
-
-  if (!cid_it.Status().ok()) {
-    PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
-                            cid_it.Status().message().c_str());
-    return {};
-  }
-
-  return tracker;
-}
-
-// Builds the |perftools.profiles.Profile| proto.
+// Helper for constructing |perftools.profiles.Profile| protos.
 class GProfileBuilder {
  public:
-  GProfileBuilder(const LocationTracker& locations,
-                  trace_processor::StringPool* interner)
-      : locations_(locations), interner_(interner) {
-    // The pprof format requires the first entry in the string table to be the
+  GProfileBuilder(
+      const std::vector<std::vector<int64_t>>& callsite_to_frames,
+      const std::map<int64_t, std::vector<Line>>& symbol_set_id_to_lines,
+      int64_t max_symbol_id)
+      : callsite_to_frames_(callsite_to_frames),
+        symbol_set_id_to_lines_(symbol_set_id_to_lines),
+        max_symbol_id_(max_symbol_id) {
+    // The pprof format expects the first entry in the string table to be the
     // empty string.
-    int64_t empty_id = ToStringTableId(StringId::Null());
+    int64_t empty_id = Intern("");
     PERFETTO_CHECK(empty_id == 0);
   }
 
   void WriteSampleTypes(
       const std::vector<std::pair<std::string, std::string>>& sample_types) {
+    // The interner might eagerly append to the profile proto, prevent it from
+    // breaking up other messages by making a separate pass.
+    for (const auto& st : sample_types) {
+      Intern(st.first);
+      Intern(st.second);
+    }
     for (const auto& st : sample_types) {
       auto* sample_type = result_->add_sample_type();
-      sample_type->set_type(
-          ToStringTableId(interner_->InternString(base::StringView(st.first))));
-      sample_type->set_unit(ToStringTableId(
-          interner_->InternString(base::StringView(st.second))));
+      sample_type->set_type(Intern(st.first));
+      sample_type->set_unit(Intern(st.second));
     }
   }
 
   bool AddSample(const protozero::PackedVarInt& values, int64_t callstack_id) {
-    const auto& location_ids = locations_.LocationsForCallstack(callstack_id);
-    if (location_ids.empty()) {
+    const auto& frames = FramesForCallstack(callstack_id);
+    if (frames.empty()) {
       PERFETTO_DFATAL_OR_ELOG(
           "Failed to find frames for callstack id %" PRIi64 "", callstack_id);
       return false;
     }
-
-    // LocationTracker stores location lists root-first, but the pprof format
-    // requires leaf-first.
-    protozero::PackedVarInt packed_locs;
-    for (auto it = location_ids.rbegin(); it != location_ids.rend(); ++it)
-      packed_locs.Append(ToPprofId(*it));
+    protozero::PackedVarInt location_ids;
+    for (int64_t frame : frames)
+      location_ids.Append(ToPprofId(frame));
 
     auto* gsample = result_->add_sample();
     gsample->set_value(values);
-    gsample->set_location_id(packed_locs);
+    gsample->set_location_id(location_ids);
 
-    // Remember the locations s.t. we only serialize the referenced ones.
-    seen_locations_.insert(location_ids.cbegin(), location_ids.cend());
+    // remember frames to be emitted
+    seen_frames_.insert(frames.cbegin(), frames.cend());
+
     return true;
   }
 
   std::string CompleteProfile(trace_processor::TraceProcessor* tp) {
     std::set<int64_t> seen_mappings;
-    std::set<int64_t> seen_functions;
+    std::set<int64_t> seen_symbol_ids;
 
-    if (!WriteLocations(&seen_mappings, &seen_functions))
-      return {};
-    if (!WriteFunctions(seen_functions))
+    // Write the location info for frames referenced by the added samples.
+    if (!WriteFrames(tp, &seen_mappings, &seen_symbol_ids))
       return {};
     if (!WriteMappings(tp, seen_mappings))
       return {};
-
-    WriteStringTable();
+    if (!WriteSymbols(tp, seen_symbol_ids))
+      return {};
     return result_.SerializeAsString();
   }
 
  private:
-  // Serializes the Profile.Location entries referenced by this profile.
-  bool WriteLocations(std::set<int64_t>* seen_mappings,
-                      std::set<int64_t>* seen_functions) {
-    const std::unordered_map<Location, int64_t>& locations =
-        locations_.AllLocations();
-
-    size_t written_locations = 0;
-    for (const auto& loc_and_id : locations) {
-      const auto& loc = loc_and_id.first;
-      int64_t id = loc_and_id.second;
-
-      if (seen_locations_.find(id) == seen_locations_.end())
-        continue;
-
-      written_locations += 1;
-      seen_mappings->emplace(loc.mapping_id);
-
-      auto* glocation = result_->add_location();
-      glocation->set_id(ToPprofId(id));
-      glocation->set_mapping_id(ToPprofId(loc.mapping_id));
-
-      if (!loc.inlined_functions.empty()) {
-        for (const auto& line : loc.inlined_functions) {
-          seen_functions->insert(line.function_id);
-
-          auto* gline = glocation->add_line();
-          gline->set_function_id(ToPprofId(line.function_id));
-          gline->set_line(line.line_no);
-        }
-      } else {
-        seen_functions->insert(loc.single_function_id);
-
-        glocation->add_line()->set_function_id(
-            ToPprofId(loc.single_function_id));
-      }
-    }
-
-    if (written_locations != seen_locations_.size()) {
-      PERFETTO_DFATAL_OR_ELOG(
-          "Found only %zu/%zu locations during serialization.",
-          written_locations, seen_locations_.size());
-      return false;
-    }
-    return true;
-  }
-
-  // Serializes the Profile.Function entries referenced by this profile.
-  bool WriteFunctions(const std::set<int64_t>& seen_functions) {
-    const std::unordered_map<Function, int64_t>& functions =
-        locations_.AllFunctions();
-
-    size_t written_functions = 0;
-    for (const auto& func_and_id : functions) {
-      const auto& func = func_and_id.first;
-      int64_t id = func_and_id.second;
-
-      if (seen_functions.find(id) == seen_functions.end())
-        continue;
-
-      written_functions += 1;
-
-      auto* gfunction = result_->add_function();
-      gfunction->set_id(ToPprofId(id));
-      gfunction->set_name(ToStringTableId(func.name_id));
-      gfunction->set_system_name(ToStringTableId(func.system_name_id));
-      if (!func.filename_id.is_null())
-        gfunction->set_filename(ToStringTableId(func.filename_id));
-    }
-
-    if (written_functions != seen_functions.size()) {
-      PERFETTO_DFATAL_OR_ELOG(
-          "Found only %zu/%zu functions during serialization.",
-          written_functions, seen_functions.size());
-      return false;
-    }
-    return true;
-  }
-
-  // Serializes the Profile.Mapping entries referenced by this profile.
   bool WriteMappings(trace_processor::TraceProcessor* tp,
                      const std::set<int64_t>& seen_mappings) {
     Iterator mapping_it = tp->ExecuteQuery(
@@ -573,8 +260,7 @@
       if (seen_mappings.find(id) == seen_mappings.end())
         continue;
       ++mappings_no;
-      auto interned_filename = ToStringTableId(
-          interner_->InternString(mapping_it.Get(4).AsString()));
+      auto interned_filename = Intern(mapping_it.Get(4).AsString());
       auto* gmapping = result_->add_mapping();
       gmapping->set_id(ToPprofId(id));
       // Do not set the build_id here to avoid downstream services
@@ -599,48 +285,145 @@
     return true;
   }
 
-  void WriteStringTable() {
-    for (StringId id : string_table_) {
-      trace_processor::NullTermStringView s = interner_->Get(id);
-      result_->add_string_table(s.data(), s.size());
+  bool WriteSymbols(trace_processor::TraceProcessor* tp,
+                    const std::set<int64_t>& seen_symbol_ids) {
+    Iterator symbol_it = tp->ExecuteQuery(
+        "SELECT id, name, source_file FROM stack_profile_symbol");
+    size_t symbols_no = 0;
+    while (symbol_it.Next()) {
+      int64_t id = symbol_it.Get(0).AsLong();
+      if (seen_symbol_ids.find(id) == seen_symbol_ids.end())
+        continue;
+      ++symbols_no;
+      const std::string& name = symbol_it.Get(1).AsString();
+      std::string demangled_name = name;
+      MaybeDemangle(&demangled_name);
+
+      auto interned_demangled_name = Intern(demangled_name);
+      auto interned_system_name = Intern(name);
+      auto interned_filename = Intern(symbol_it.Get(2).AsString());
+      auto* gfunction = result_->add_function();
+      gfunction->set_id(ToPprofId(id));
+      gfunction->set_name(interned_demangled_name);
+      gfunction->set_system_name(interned_system_name);
+      gfunction->set_filename(interned_filename);
     }
+
+    if (!symbol_it.Status().ok()) {
+      PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
+                              symbol_it.Status().message().c_str());
+      return false;
+    }
+
+    if (symbols_no != seen_symbol_ids.size()) {
+      PERFETTO_DFATAL_OR_ELOG("Missing symbols.");
+      return false;
+    }
+    return true;
   }
 
-  int64_t ToStringTableId(StringId interned_id) {
-    auto it = interning_remapper_.find(interned_id);
-    if (it == interning_remapper_.end()) {
-      int64_t table_id = static_cast<int64_t>(string_table_.size());
-      string_table_.push_back(interned_id);
-      bool inserted = false;
-      std::tie(it, inserted) =
-          interning_remapper_.emplace(interned_id, table_id);
-      PERFETTO_DCHECK(inserted);
+  bool WriteFrames(trace_processor::TraceProcessor* tp,
+                   std::set<int64_t>* seen_mappings,
+                   std::set<int64_t>* seen_symbol_ids) {
+    Iterator frame_it = tp->ExecuteQuery(
+        "SELECT spf.id, IFNULL(spf.deobfuscated_name, spf.name), spf.mapping, "
+        "spf.rel_pc, spf.symbol_set_id "
+        "FROM stack_profile_frame spf;");
+    size_t frames_no = 0;
+    while (frame_it.Next()) {
+      int64_t frame_id = frame_it.Get(0).AsLong();
+      if (seen_frames_.find(frame_id) == seen_frames_.end())
+        continue;
+      frames_no++;
+      std::string frame_name = frame_it.Get(1).AsString();
+      int64_t mapping_id = frame_it.Get(2).AsLong();
+      int64_t rel_pc = frame_it.Get(3).AsLong();
+      base::Optional<int64_t> symbol_set_id;
+      if (!frame_it.Get(4).is_null())
+        symbol_set_id = frame_it.Get(4).AsLong();
+
+      seen_mappings->emplace(mapping_id);
+      auto* glocation = result_->add_location();
+      glocation->set_id(ToPprofId(frame_id));
+      glocation->set_mapping_id(ToPprofId(mapping_id));
+      // TODO(fmayer): Convert to abspc.
+      // relpc + (mapping.start - (mapping.exact_offset -
+      //                           mapping.start_offset)).
+      glocation->set_address(static_cast<uint64_t>(rel_pc));
+      if (symbol_set_id) {
+        for (const Line& line : LineForSymbolSetId(*symbol_set_id)) {
+          seen_symbol_ids->emplace(line.symbol_id);
+          auto* gline = glocation->add_line();
+          gline->set_line(line.line_number);
+          gline->set_function_id(ToPprofId(line.symbol_id));
+        }
+      } else {
+        int64_t synthesized_symbol_id = ++max_symbol_id_;
+        std::string demangled_name = frame_name;
+        MaybeDemangle(&demangled_name);
+
+        auto* gline = glocation->add_line();
+        gline->set_line(0);
+        gline->set_function_id(ToPprofId(synthesized_symbol_id));
+
+        auto interned_demangled_name = Intern(demangled_name);
+        auto interned_system_name = Intern(frame_name);
+        auto* gfunction = result_->add_function();
+        gfunction->set_id(ToPprofId(synthesized_symbol_id));
+        gfunction->set_name(interned_demangled_name);
+        gfunction->set_system_name(interned_system_name);
+      }
+    }
+
+    if (!frame_it.Status().ok()) {
+      PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
+                              frame_it.Status().message().c_str());
+      return false;
+    }
+    if (frames_no != seen_frames_.size()) {
+      PERFETTO_DFATAL_OR_ELOG("Missing frames.");
+      return false;
+    }
+    return true;
+  }
+
+  const std::vector<int64_t>& FramesForCallstack(int64_t callstack_id) {
+    size_t callsite_idx = static_cast<size_t>(callstack_id);
+    PERFETTO_CHECK(callstack_id >= 0 &&
+                   callsite_idx < callsite_to_frames_.size());
+    return callsite_to_frames_[callsite_idx];
+  }
+
+  const std::vector<Line>& LineForSymbolSetId(int64_t symbol_set_id) {
+    auto it = symbol_set_id_to_lines_.find(symbol_set_id);
+    if (it == symbol_set_id_to_lines_.end())
+      return empty_line_vector_;
+    return it->second;
+  }
+
+  int64_t Intern(const std::string& s) {
+    auto it = string_table_.find(s);
+    if (it == string_table_.end()) {
+      std::tie(it, std::ignore) =
+          string_table_.emplace(s, string_table_.size());
+      result_->add_string_table(s);
     }
     return it->second;
   }
 
-  // Contains all locations, lines, functions (in memory):
-  const LocationTracker& locations_;
-
-  // String interner, strings referenced by LocationTracker are already
-  // interned. The new internings will come from mappings, and sample types.
-  trace_processor::StringPool* interner_;
-
-  // The profile format uses the repeated string_table field's index as an
-  // implicit id, so these structures remap the interned strings into sequential
-  // ids. Only the strings referenced by this GProfileBuilder instance will be
-  // added to the table.
-  std::unordered_map<StringId, int64_t> interning_remapper_;
-  std::vector<StringId> string_table_;
-
-  // Profile proto being serialized.
   protozero::HeapBuffered<third_party::perftools::profiles::pbzero::Profile>
       result_;
+  std::map<std::string, int64_t> string_table_;
+  const std::vector<std::vector<int64_t>>& callsite_to_frames_;
+  const std::map<int64_t, std::vector<Line>>& symbol_set_id_to_lines_;
+  const std::vector<Line> empty_line_vector_;
+  int64_t max_symbol_id_;
 
-  // Set of locations referenced by the added samples.
-  std::set<int64_t> seen_locations_;
+  std::set<int64_t> seen_frames_;
 };
 
+}  // namespace
+
 namespace heap_profile {
 struct View {
   const char* type;
@@ -760,12 +543,13 @@
 
 static bool TraceToHeapPprof(trace_processor::TraceProcessor* tp,
                              std::vector<SerializedProfile>* output,
-                             bool annotate_frames,
                              uint64_t target_pid,
                              const std::vector<uint64_t>& target_timestamps) {
-  trace_processor::StringPool interner;
-  LocationTracker locations =
-      PreprocessLocations(tp, &interner, annotate_frames);
+  const auto callsite_to_frames = GetCallsiteToFrames(tp);
+  const auto symbol_set_id_to_lines = GetSymbolSetIdToLines(tp);
+  base::Optional<int64_t> max_symbol_id = GetMaxSymbolId(tp);
+  if (!max_symbol_id.has_value())
+    return false;
 
   bool any_fail = false;
   Iterator it = tp->ExecuteQuery(
@@ -773,7 +557,8 @@
       "from heap_profile_allocation hpa, "
       "process p where p.upid = hpa.upid;");
   while (it.Next()) {
-    GProfileBuilder builder(locations, &interner);
+    GProfileBuilder builder(callsite_to_frames, symbol_set_id_to_lines,
+                            max_symbol_id.value());
     uint64_t upid = static_cast<uint64_t>(it.Get(0).AsLong());
     uint64_t ts = static_cast<uint64_t>(it.Get(1).AsLong());
     uint64_t profile_pid = static_cast<uint64_t>(it.Get(2).AsLong());
@@ -901,11 +686,12 @@
 // perf and heap profiles.
 static bool TraceToPerfPprof(trace_processor::TraceProcessor* tp,
                              std::vector<SerializedProfile>* output,
-                             bool annotate_frames,
                              uint64_t target_pid) {
-  trace_processor::StringPool interner;
-  LocationTracker locations =
-      PreprocessLocations(tp, &interner, annotate_frames);
+  const auto callsite_to_frames = GetCallsiteToFrames(tp);
+  const auto symbol_set_id_to_lines = GetSymbolSetIdToLines(tp);
+  base::Optional<int64_t> max_symbol_id = GetMaxSymbolId(tp);
+  if (!max_symbol_id.has_value())
+    return false;
 
   LogTracePerfEventIssues(tp);
 
@@ -917,7 +703,9 @@
     if (target_pid != 0 && process.pid != target_pid)
       continue;
 
-    GProfileBuilder builder(locations, &interner);
+    GProfileBuilder builder(callsite_to_frames, symbol_set_id_to_lines,
+                            max_symbol_id.value());
+
     builder.WriteSampleTypes({{"samples", "count"}});
 
     std::string query = "select callsite_id from perf_sample where utid in (" +
@@ -945,22 +733,17 @@
   return true;
 }
 }  // namespace perf_profile
-}  // namespace
 
 bool TraceToPprof(trace_processor::TraceProcessor* tp,
                   std::vector<SerializedProfile>* output,
                   ConversionMode mode,
-                  uint64_t flags,
                   uint64_t pid,
                   const std::vector<uint64_t>& timestamps) {
-  bool annotate_frames =
-      flags & static_cast<uint64_t>(ConversionFlags::kAnnotateFrames);
   switch (mode) {
     case (ConversionMode::kHeapProfile):
-      return heap_profile::TraceToHeapPprof(tp, output, annotate_frames, pid,
-                                            timestamps);
+      return heap_profile::TraceToHeapPprof(tp, output, pid, timestamps);
     case (ConversionMode::kPerfProfile):
-      return perf_profile::TraceToPerfPprof(tp, output, annotate_frames, pid);
+      return perf_profile::TraceToPerfPprof(tp, output, pid);
   }
   PERFETTO_FATAL("unknown conversion option");  // for gcc
 }
diff --git a/tools/trace_to_text/trace_to_profile.cc b/tools/trace_to_text/trace_to_profile.cc
index c71a827..a394e2b 100644
--- a/tools/trace_to_text/trace_to_profile.cc
+++ b/tools/trace_to_text/trace_to_profile.cc
@@ -50,12 +50,6 @@
 namespace trace_to_text {
 namespace {
 
-uint64_t ToConversionFlags(bool annotate_frames) {
-  return static_cast<uint64_t>(annotate_frames
-                                   ? ConversionFlags::kAnnotateFrames
-                                   : ConversionFlags::kNone);
-}
-
 std::string GetRandomString(size_t n) {
   std::random_device r;
   auto rng = std::default_random_engine(r());
@@ -98,7 +92,6 @@
     uint64_t pid,
     std::vector<uint64_t> timestamps,
     ConversionMode conversion_mode,
-    uint64_t conversion_flags,
     std::string dirname_prefix,
     std::function<std::string(const SerializedProfile&)> filename_fn) {
   std::vector<SerializedProfile> profiles;
@@ -113,8 +106,7 @@
   MaybeSymbolize(tp.get());
   MaybeDeobfuscate(tp.get());
 
-  TraceToPprof(tp.get(), &profiles, conversion_mode, conversion_flags, pid,
-               timestamps);
+  TraceToPprof(tp.get(), &profiles, conversion_mode, pid, timestamps);
   if (profiles.empty()) {
     return 0;
   }
@@ -140,33 +132,31 @@
 int TraceToHeapProfile(std::istream* input,
                        std::ostream* output,
                        uint64_t pid,
-                       std::vector<uint64_t> timestamps,
-                       bool annotate_frames) {
+                       std::vector<uint64_t> timestamps) {
   int file_idx = 0;
   auto filename_fn = [&file_idx](const SerializedProfile& profile) {
     return "heap_dump." + std::to_string(++file_idx) + "." +
            std::to_string(profile.pid) + "." + profile.heap_name + ".pb";
   };
 
-  return TraceToProfile(
-      input, output, pid, timestamps, ConversionMode::kHeapProfile,
-      ToConversionFlags(annotate_frames), "heap_profile-", filename_fn);
+  return TraceToProfile(input, output, pid, timestamps,
+                        ConversionMode::kHeapProfile, "heap_profile-",
+                        filename_fn);
 }
 
 int TraceToPerfProfile(std::istream* input,
                        std::ostream* output,
                        uint64_t pid,
-                       std::vector<uint64_t> timestamps,
-                       bool annotate_frames) {
+                       std::vector<uint64_t> timestamps) {
   int file_idx = 0;
   auto filename_fn = [&file_idx](const SerializedProfile& profile) {
     return "profile." + std::to_string(++file_idx) + ".pid." +
            std::to_string(profile.pid) + ".pb";
   };
 
-  return TraceToProfile(
-      input, output, pid, timestamps, ConversionMode::kPerfProfile,
-      ToConversionFlags(annotate_frames), "perf_profile-", filename_fn);
+  return TraceToProfile(input, output, pid, timestamps,
+                        ConversionMode::kPerfProfile, "perf_profile-",
+                        filename_fn);
 }
 
 }  // namespace trace_to_text
diff --git a/tools/trace_to_text/trace_to_profile.h b/tools/trace_to_text/trace_to_profile.h
index 313b32b..1c41aad 100644
--- a/tools/trace_to_text/trace_to_profile.h
+++ b/tools/trace_to_text/trace_to_profile.h
@@ -27,15 +27,13 @@
 int TraceToHeapProfile(std::istream* input,
                        std::ostream* output,
                        uint64_t pid,
-                       std::vector<uint64_t> timestamps,
-                       bool annotate_frames);
+                       std::vector<uint64_t> timestamps);
 
 // 0: success
 int TraceToPerfProfile(std::istream* input,
                        std::ostream* output,
                        uint64_t pid,
-                       std::vector<uint64_t> timestamps,
-                       bool annotate_frames);
+                       std::vector<uint64_t> timestamps);
 
 }  // namespace trace_to_text
 }  // namespace perfetto
diff --git a/tools/traceconv b/tools/traceconv
index c0cff60..bb90486 100755
--- a/tools/traceconv
+++ b/tools/traceconv
@@ -21,7 +21,7 @@
 # cat ./traceconv | python -
 
 BASH_FALLBACK = """ "
-exec python - "$@" <<'#'EOF
+exec python3 - "$@" <<'#'EOF
 #"""
 
 import hashlib
diff --git a/tools/write_version_header.py b/tools/write_version_header.py
index 0325f6f..dd02a69 100755
--- a/tools/write_version_header.py
+++ b/tools/write_version_header.py
@@ -123,7 +123,7 @@
     guard = '%s_' % args.cpp_out.upper()
     guard = re.sub(r'[^\w]', '_', guard)
     lines = []
-    lines.append('// Generated by %s' % __file__)
+    lines.append('// Generated by %s' % os.path.basename(__file__))
     lines.append('')
     lines.append('#ifndef %s' % guard)
     lines.append('#define %s' % guard)
diff --git a/ui/release/channels.json b/ui/release/channels.json
index 6d53d32..160b544 100644
--- a/ui/release/channels.json
+++ b/ui/release/channels.json
@@ -2,11 +2,11 @@
   "channels": [
     {
       "name": "stable",
-      "rev": "b7ab2469bd84ac0a4883c7f6a8a67e73903459b0"
+      "rev": "06350fe212f5051d9e890411ca24ab4a84b33ae1"
     },
     {
       "name": "canary",
-      "rev": "06350fe212f5051d9e890411ca24ab4a84b33ae1"
+      "rev": "aaab326dc5b38858d516171ba68f4542e9e92d65"
     },
     {
       "name": "autopush",
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index 65c78d0..8d47424 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -519,11 +519,13 @@
     'accessibility',
     'AccountFetcherService',
     'android_webview',
+    'aogh',
     'audio',
     'base',
     'benchmark',
     'blink',
     'blink.animations',
+    'blink.bindings',
     'blink.console',
     'blink_gc',
     'blink.net',
@@ -534,8 +536,13 @@
     'browser',
     'browsing_data',
     'CacheStorage',
+    'Calculators',
+    'CameraStream',
     'camera',
+    'cast_app',
     'cast_perf_test',
+    'cast.mdns',
+    'cast.mdns.socket',
     'cast.stream',
     'cc',
     'cc.debug',
@@ -545,14 +552,17 @@
     'compositor',
     'content',
     'content_capture',
+    'device',
     'devtools',
+    'devtools.contrast',
     'devtools.timeline',
+    'disk_cache',
     'download',
     'download_service',
     'drm',
     'drmcursor',
     'dwrite',
-    'DXVA Decoding',
+    'DXVA_Decoding',
     'EarlyJava',
     'evdev',
     'event',
@@ -563,10 +573,12 @@
     'fonts',
     'GAMEPAD',
     'gpu',
+    'gpu.angle',
     'gpu.capture',
     'headless',
     'hwoverlays',
     'identity',
+    'ime',
     'IndexedDB',
     'input',
     'io',
@@ -594,12 +606,17 @@
     'omnibox',
     'oobe',
     'ozone',
+    'partition_alloc',
     'passwords',
     'p2p',
     'page-serialization',
+    'paint_preview',
     'pepper',
+    'PlatformMalloc',
+    'power',
     'ppapi',
-    'ppapi proxy',
+    'ppapi_proxy',
+    'print',
     'rail',
     'renderer',
     'renderer_host',
@@ -610,21 +627,26 @@
     'sequence_manager',
     'service_manager',
     'ServiceWorker',
+    'sharing',
     'shell',
     'shortcut_viewer',
     'shutdown',
     'SiteEngagement',
     'skia',
+    'sql',
+    'stadia_media',
+    'stadia_rtc',
     'startup',
     'sync',
     'sync_lock_contention',
-    'thread_pool',
     'test_gpu',
-    'test_tracing',
+    'thread_pool',
     'toplevel',
+    'toplevel.flow',
     'ui',
     'v8',
     'v8.execute',
+    'v8.wasm',
     'ValueStoreFrontend::Backend',
     'views',
     'views.frame',
@@ -637,6 +659,7 @@
     'webrtc',
     'xr',
     'disabled-by-default-animation-worklet',
+    'disabled-by-default-audio',
     'disabled-by-default-audio-worklet',
     'disabled-by-default-blink.debug',
     'disabled-by-default-blink.debug.display_lock',
@@ -654,6 +677,7 @@
     'disabled-by-default-cc.debug.scheduler',
     'disabled-by-default-cc.debug.scheduler.frames',
     'disabled-by-default-cc.debug.scheduler.now',
+    'disabled-by-default-content.verbose',
     'disabled-by-default-cpu_profiler',
     'disabled-by-default-cpu_profiler.debug',
     'disabled-by-default-devtools.screenshot',
@@ -668,34 +692,41 @@
     'disabled-by-default-gpu_cmd_queue',
     'disabled-by-default-gpu.dawn',
     'disabled-by-default-gpu.debug',
-    'disabled-by-default-gpu_decoder',
+    'disabled-by-default-gpu.decoder',
     'disabled-by-default-gpu.device',
     'disabled-by-default-gpu.service',
+    'disabled-by-default-gpu.vulkan.vma',
     'disabled-by-default-histogram_samples',
     'disabled-by-default-ipc.flow',
     'disabled-by-default-java-heap-profiler',
     'disabled-by-default-layer-element',
+    'disabled-by-default-layout_shift.debug',
     'disabled-by-default-lifecycles',
     'disabled-by-default-loading',
+    'disabled-by-default-mediastream',
     'disabled-by-default-memory-infra',
     'disabled-by-default-memory-infra.v8.code_stats',
+    'disabled-by-default-mojom',
     'disabled-by-default-net',
     'disabled-by-default-network',
     'disabled-by-default-paint-worklet',
     'disabled-by-default-power',
     'disabled-by-default-renderer.scheduler',
     'disabled-by-default-renderer.scheduler.debug',
+    'disabled-by-default-sandbox',
     'disabled-by-default-sequence_manager',
     'disabled-by-default-sequence_manager.debug',
     'disabled-by-default-sequence_manager.verbose_snapshots',
     'disabled-by-default-skia',
     'disabled-by-default-skia.gpu',
     'disabled-by-default-skia.gpu.cache',
+    'disabled-by-default-skia.shaders',
     'disabled-by-default-SyncFileSystem',
     'disabled-by-default-system_stats',
     'disabled-by-default-thread_pool_diagnostics',
     'disabled-by-default-toplevel.flow',
     'disabled-by-default-toplevel.ipc',
+    'disabled-by-default-user_action_samples',
     'disabled-by-default-v8.compile',
     'disabled-by-default-v8.cpu_profiler',
     'disabled-by-default-v8.cpu_profiler.hires',
@@ -705,17 +736,24 @@
     'disabled-by-default-v8.runtime',
     'disabled-by-default-v8.runtime_stats',
     'disabled-by-default-v8.runtime_stats_sampling',
+    'disabled-by-default-v8.stack_trace',
     'disabled-by-default-v8.turbofan',
     'disabled-by-default-v8.wasm',
+    'disabled-by-default-v8.wasm.detailed',
+    'disabled-by-default-v8.wasm.turbofan',
     'disabled-by-default-video_and_image_capture',
     'disabled-by-default-viz.debug.overlay_planes',
+    'disabled-by-default-viz.gpu_composite_time',
     'disabled-by-default-viz.hit_testing_flow',
     'disabled-by-default-viz.overdraw',
     'disabled-by-default-viz.quads',
     'disabled-by-default-viz.surface_id_flow',
     'disabled-by-default-viz.surface_lifetime',
     'disabled-by-default-viz.triangles',
+    'disabled-by-default-webaudio.audionode',
+    'disabled-by-default-webrtc',
     'disabled-by-default-worker.scheduler',
+    'disabled-by-default-xr.debug',
   ];
 }