Snap for 9680074 from 1cc5a2a8c7f6c85049ba28cce1b79bdca2f6d32c to busytown-mac-infra-release

Change-Id: I0e9f65da2ae4108d7dcdaf103e4f4a01afdaff30
diff --git a/Android.bp b/Android.bp
index 420723f..a8d21de 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2019,6 +2019,7 @@
         ":perfetto_src_trace_processor_storage_storage",
         ":perfetto_src_trace_processor_tables_tables",
         ":perfetto_src_trace_processor_types_types",
+        ":perfetto_src_trace_processor_util_bump_allocator",
         ":perfetto_src_trace_processor_util_descriptors",
         ":perfetto_src_trace_processor_util_glob",
         ":perfetto_src_trace_processor_util_gzip",
@@ -4838,6 +4839,7 @@
         "protos/perfetto/trace/perfetto/perfetto_metatrace.proto",
         "protos/perfetto/trace/perfetto/tracing_service_event.proto",
         "protos/perfetto/trace/power/android_energy_estimation_breakdown.proto",
+        "protos/perfetto/trace/power/android_entity_state_residency.proto",
         "protos/perfetto/trace/power/battery_counters.proto",
         "protos/perfetto/trace/power/power_rails.proto",
         "protos/perfetto/trace/profiling/deobfuscation.proto",
@@ -6579,6 +6581,7 @@
     name: "perfetto_protos_perfetto_trace_power_cpp_gen",
     srcs: [
         "protos/perfetto/trace/power/android_energy_estimation_breakdown.proto",
+        "protos/perfetto/trace/power/android_entity_state_residency.proto",
         "protos/perfetto/trace/power/battery_counters.proto",
         "protos/perfetto/trace/power/power_rails.proto",
     ],
@@ -6589,6 +6592,7 @@
     cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(in)",
     out: [
         "external/perfetto/protos/perfetto/trace/power/android_energy_estimation_breakdown.gen.cc",
+        "external/perfetto/protos/perfetto/trace/power/android_entity_state_residency.gen.cc",
         "external/perfetto/protos/perfetto/trace/power/battery_counters.gen.cc",
         "external/perfetto/protos/perfetto/trace/power/power_rails.gen.cc",
     ],
@@ -6599,6 +6603,7 @@
     name: "perfetto_protos_perfetto_trace_power_cpp_gen_headers",
     srcs: [
         "protos/perfetto/trace/power/android_energy_estimation_breakdown.proto",
+        "protos/perfetto/trace/power/android_entity_state_residency.proto",
         "protos/perfetto/trace/power/battery_counters.proto",
         "protos/perfetto/trace/power/power_rails.proto",
     ],
@@ -6609,6 +6614,7 @@
     cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(in)",
     out: [
         "external/perfetto/protos/perfetto/trace/power/android_energy_estimation_breakdown.gen.h",
+        "external/perfetto/protos/perfetto/trace/power/android_entity_state_residency.gen.h",
         "external/perfetto/protos/perfetto/trace/power/battery_counters.gen.h",
         "external/perfetto/protos/perfetto/trace/power/power_rails.gen.h",
     ],
@@ -6623,6 +6629,7 @@
     name: "perfetto_protos_perfetto_trace_power_lite_gen",
     srcs: [
         "protos/perfetto/trace/power/android_energy_estimation_breakdown.proto",
+        "protos/perfetto/trace/power/android_entity_state_residency.proto",
         "protos/perfetto/trace/power/battery_counters.proto",
         "protos/perfetto/trace/power/power_rails.proto",
     ],
@@ -6632,6 +6639,7 @@
     cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
     out: [
         "external/perfetto/protos/perfetto/trace/power/android_energy_estimation_breakdown.pb.cc",
+        "external/perfetto/protos/perfetto/trace/power/android_entity_state_residency.pb.cc",
         "external/perfetto/protos/perfetto/trace/power/battery_counters.pb.cc",
         "external/perfetto/protos/perfetto/trace/power/power_rails.pb.cc",
     ],
@@ -6642,6 +6650,7 @@
     name: "perfetto_protos_perfetto_trace_power_lite_gen_headers",
     srcs: [
         "protos/perfetto/trace/power/android_energy_estimation_breakdown.proto",
+        "protos/perfetto/trace/power/android_entity_state_residency.proto",
         "protos/perfetto/trace/power/battery_counters.proto",
         "protos/perfetto/trace/power/power_rails.proto",
     ],
@@ -6651,6 +6660,7 @@
     cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
     out: [
         "external/perfetto/protos/perfetto/trace/power/android_energy_estimation_breakdown.pb.h",
+        "external/perfetto/protos/perfetto/trace/power/android_entity_state_residency.pb.h",
         "external/perfetto/protos/perfetto/trace/power/battery_counters.pb.h",
         "external/perfetto/protos/perfetto/trace/power/power_rails.pb.h",
     ],
@@ -6665,6 +6675,7 @@
     name: "perfetto_protos_perfetto_trace_power_zero_gen",
     srcs: [
         "protos/perfetto/trace/power/android_energy_estimation_breakdown.proto",
+        "protos/perfetto/trace/power/android_entity_state_residency.proto",
         "protos/perfetto/trace/power/battery_counters.proto",
         "protos/perfetto/trace/power/power_rails.proto",
     ],
@@ -6675,6 +6686,7 @@
     cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(in)",
     out: [
         "external/perfetto/protos/perfetto/trace/power/android_energy_estimation_breakdown.pbzero.cc",
+        "external/perfetto/protos/perfetto/trace/power/android_entity_state_residency.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/power/battery_counters.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/power/power_rails.pbzero.cc",
     ],
@@ -6685,6 +6697,7 @@
     name: "perfetto_protos_perfetto_trace_power_zero_gen_headers",
     srcs: [
         "protos/perfetto/trace/power/android_energy_estimation_breakdown.proto",
+        "protos/perfetto/trace/power/android_entity_state_residency.proto",
         "protos/perfetto/trace/power/battery_counters.proto",
         "protos/perfetto/trace/power/power_rails.proto",
     ],
@@ -6695,6 +6708,7 @@
     cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(in)",
     out: [
         "external/perfetto/protos/perfetto/trace/power/android_energy_estimation_breakdown.pbzero.h",
+        "external/perfetto/protos/perfetto/trace/power/android_entity_state_residency.pbzero.h",
         "external/perfetto/protos/perfetto/trace/power/battery_counters.pbzero.h",
         "external/perfetto/protos/perfetto/trace/power/power_rails.pbzero.h",
     ],
@@ -6744,6 +6758,7 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_processor_zero_gen",
     srcs: [
+        "protos/perfetto/trace_processor/cloud_trace_processor.proto",
         "protos/perfetto/trace_processor/metatrace_categories.proto",
         "protos/perfetto/trace_processor/trace_processor.proto",
     ],
@@ -6753,6 +6768,7 @@
     ],
     cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(in)",
     out: [
+        "external/perfetto/protos/perfetto/trace_processor/cloud_trace_processor.pbzero.cc",
         "external/perfetto/protos/perfetto/trace_processor/metatrace_categories.pbzero.cc",
         "external/perfetto/protos/perfetto/trace_processor/trace_processor.pbzero.cc",
     ],
@@ -6762,6 +6778,7 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_processor_zero_gen_headers",
     srcs: [
+        "protos/perfetto/trace_processor/cloud_trace_processor.proto",
         "protos/perfetto/trace_processor/metatrace_categories.proto",
         "protos/perfetto/trace_processor/trace_processor.proto",
     ],
@@ -6771,6 +6788,7 @@
     ],
     cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(in)",
     out: [
+        "external/perfetto/protos/perfetto/trace_processor/cloud_trace_processor.pbzero.h",
         "external/perfetto/protos/perfetto/trace_processor/metatrace_categories.pbzero.h",
         "external/perfetto/protos/perfetto/trace_processor/trace_processor.pbzero.h",
     ],
@@ -8139,6 +8157,7 @@
 filegroup {
     name: "perfetto_src_base_threading_unittests",
     srcs: [
+        "src/base/threading/channel_unittest.cc",
         "src/base/threading/thread_pool_unittest.cc",
     ],
 }
@@ -8736,6 +8755,8 @@
     srcs: [
         "src/profiling/symbolizer/breakpad_parser.cc",
         "src/profiling/symbolizer/breakpad_symbolizer.cc",
+        "src/profiling/symbolizer/filesystem_posix.cc",
+        "src/profiling/symbolizer/filesystem_windows.cc",
         "src/profiling/symbolizer/local_symbolizer.cc",
         "src/profiling/symbolizer/scoped_read_mmap_posix.cc",
         "src/profiling/symbolizer/scoped_read_mmap_windows.cc",
@@ -8849,6 +8870,7 @@
     name: "perfetto_src_protozero_protozero",
     srcs: [
         "src/protozero/field.cc",
+        "src/protozero/gen_field_helpers.cc",
         "src/protozero/message.cc",
         "src/protozero/message_arena.cc",
         "src/protozero/message_handle.cc",
@@ -9678,13 +9700,13 @@
         "src/trace_processor/metrics/sql/android/android_trusty_workqueues.sql",
         "src/trace_processor/metrics/sql/android/composer_execution.sql",
         "src/trace_processor/metrics/sql/android/composition_layers.sql",
+        "src/trace_processor/metrics/sql/android/counter_span_view_merged.sql",
         "src/trace_processor/metrics/sql/android/cpu_info.sql",
         "src/trace_processor/metrics/sql/android/display_metrics.sql",
         "src/trace_processor/metrics/sql/android/frame_missed.sql",
         "src/trace_processor/metrics/sql/android/g2d.sql",
         "src/trace_processor/metrics/sql/android/g2d_duration.sql",
         "src/trace_processor/metrics/sql/android/global_counter_span_view.sql",
-        "src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql",
         "src/trace_processor/metrics/sql/android/gpu_counter_span_view.sql",
         "src/trace_processor/metrics/sql/android/jank/cujs.sql",
         "src/trace_processor/metrics/sql/android/jank/cujs_boundaries.sql",
@@ -9887,6 +9909,7 @@
     name: "perfetto_src_trace_processor_sorter_sorter",
     srcs: [
         "src/trace_processor/sorter/trace_sorter.cc",
+        "src/trace_processor/sorter/trace_token_buffer.cc",
     ],
 }
 
@@ -9894,8 +9917,8 @@
 filegroup {
     name: "perfetto_src_trace_processor_sorter_unittests",
     srcs: [
-        "src/trace_processor/sorter/trace_sorter_queue_unittest.cc",
         "src/trace_processor/sorter/trace_sorter_unittest.cc",
+        "src/trace_processor/sorter/trace_token_buffer_unittest.cc",
     ],
 }
 
@@ -11130,6 +11153,7 @@
         "protos/perfetto/trace/perfetto/perfetto_metatrace.proto",
         "protos/perfetto/trace/perfetto/tracing_service_event.proto",
         "protos/perfetto/trace/power/android_energy_estimation_breakdown.proto",
+        "protos/perfetto/trace/power/android_entity_state_residency.proto",
         "protos/perfetto/trace/power/battery_counters.proto",
         "protos/perfetto/trace/power/power_rails.proto",
         "protos/perfetto/trace/profiling/deobfuscation.proto",
@@ -11800,6 +11824,13 @@
         "src/traced/probes/filesystem/testdata/**/*",
         "src/traced/probes/ftrace/test/data/**/*",
     ],
+    target: {
+        musl: {
+            static_libs: [
+                "libfts",
+            ],
+        },
+    },
 }
 
 // GN: //test/vts:perfetto_vts_deps
@@ -12214,6 +12245,7 @@
         ":perfetto_src_trace_processor_storage_storage",
         ":perfetto_src_trace_processor_tables_tables",
         ":perfetto_src_trace_processor_types_types",
+        ":perfetto_src_trace_processor_util_bump_allocator",
         ":perfetto_src_trace_processor_util_descriptors",
         ":perfetto_src_trace_processor_util_glob",
         ":perfetto_src_trace_processor_util_gzip",
@@ -12321,6 +12353,11 @@
                 ],
             },
         },
+        musl: {
+            static_libs: [
+                "libfts",
+            ],
+        },
     },
 }
 
@@ -12425,6 +12462,7 @@
         ":perfetto_src_trace_processor_storage_storage",
         ":perfetto_src_trace_processor_tables_tables",
         ":perfetto_src_trace_processor_types_types",
+        ":perfetto_src_trace_processor_util_bump_allocator",
         ":perfetto_src_trace_processor_util_descriptors",
         ":perfetto_src_trace_processor_util_glob",
         ":perfetto_src_trace_processor_util_gzip",
@@ -12504,6 +12542,13 @@
     cflags: [
         "-DHAVE_HIDDEN",
     ],
+    target: {
+        musl: {
+            static_libs: [
+                "libfts",
+            ],
+        },
+    },
 }
 
 // GN: //src/traced/service:traced
@@ -12888,6 +12933,8 @@
     srcs: [
         "protos/perfetto/trace/perfetto_trace.proto",
     ],
+    // b/267831518: Pin tradefed and dependencies to Java 11.
+    java_version: "11",
 }
 
 java_library_host {
@@ -12898,6 +12945,8 @@
     srcs: [
         "protos/perfetto/metrics/perfetto_merged_metrics.proto",
     ],
+    // b/267831518: Pin tradefed and dependencies to Java 11.
+    java_version: "11",
 }
 
 // This sample target shows how to use the perfetto client API from within the
diff --git a/Android.bp.extras b/Android.bp.extras
index f1251b4..ded8bdb 100644
--- a/Android.bp.extras
+++ b/Android.bp.extras
@@ -18,6 +18,8 @@
     srcs: [
         "protos/perfetto/trace/perfetto_trace.proto",
     ],
+    // b/267831518: Pin tradefed and dependencies to Java 11.
+    java_version: "11",
 }
 
 java_library_host {
@@ -28,6 +30,8 @@
     srcs: [
         "protos/perfetto/metrics/perfetto_merged_metrics.proto",
     ],
+    // b/267831518: Pin tradefed and dependencies to Java 11.
+    java_version: "11",
 }
 
 // This sample target shows how to use the perfetto client API from within the
diff --git a/BUILD b/BUILD
index c4c9781..170f40c 100644
--- a/BUILD
+++ b/BUILD
@@ -126,6 +126,7 @@
     name = "protozero",
     srcs = [
         "src/protozero/field.cc",
+        "src/protozero/gen_field_helpers.cc",
         "src/protozero/message.cc",
         "src/protozero/message_arena.cc",
         "src/protozero/message_handle.cc",
@@ -554,6 +555,7 @@
         "include/perfetto/protozero/cpp_message_obj.h",
         "include/perfetto/protozero/field.h",
         "include/perfetto/protozero/field_writer.h",
+        "include/perfetto/protozero/gen_field_helpers.h",
         "include/perfetto/protozero/message.h",
         "include/perfetto/protozero/message_arena.h",
         "include/perfetto/protozero/message_handle.h",
@@ -572,6 +574,7 @@
 perfetto_filegroup(
     name = "include_perfetto_public_abi_base",
     srcs = [
+        "include/perfetto/public/abi/atomic.h",
         "include/perfetto/public/abi/export.h",
     ],
 )
@@ -651,6 +654,7 @@
         "include/perfetto/tracing/internal/checked_scope.h",
         "include/perfetto/tracing/internal/compile_time_hash.h",
         "include/perfetto/tracing/internal/data_source_internal.h",
+        "include/perfetto/tracing/internal/data_source_type.h",
         "include/perfetto/tracing/internal/in_process_tracing_backend.h",
         "include/perfetto/tracing/internal/interceptor_trace_writer.h",
         "include/perfetto/tracing/internal/system_tracing_backend.h",
@@ -943,6 +947,9 @@
         "src/profiling/symbolizer/breakpad_symbolizer.cc",
         "src/profiling/symbolizer/breakpad_symbolizer.h",
         "src/profiling/symbolizer/elf.h",
+        "src/profiling/symbolizer/filesystem.h",
+        "src/profiling/symbolizer/filesystem_posix.cc",
+        "src/profiling/symbolizer/filesystem_windows.cc",
         "src/profiling/symbolizer/local_symbolizer.cc",
         "src/profiling/symbolizer/local_symbolizer.h",
         "src/profiling/symbolizer/scoped_read_mmap.h",
@@ -1551,13 +1558,13 @@
         "src/trace_processor/metrics/sql/android/android_trusty_workqueues.sql",
         "src/trace_processor/metrics/sql/android/composer_execution.sql",
         "src/trace_processor/metrics/sql/android/composition_layers.sql",
+        "src/trace_processor/metrics/sql/android/counter_span_view_merged.sql",
         "src/trace_processor/metrics/sql/android/cpu_info.sql",
         "src/trace_processor/metrics/sql/android/display_metrics.sql",
         "src/trace_processor/metrics/sql/android/frame_missed.sql",
         "src/trace_processor/metrics/sql/android/g2d.sql",
         "src/trace_processor/metrics/sql/android/g2d_duration.sql",
         "src/trace_processor/metrics/sql/android/global_counter_span_view.sql",
-        "src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql",
         "src/trace_processor/metrics/sql/android/gpu_counter_span_view.sql",
         "src/trace_processor/metrics/sql/android/jank/cujs.sql",
         "src/trace_processor/metrics/sql/android/jank/cujs_boundaries.sql",
@@ -1819,8 +1826,8 @@
     srcs = [
         "src/trace_processor/sorter/trace_sorter.cc",
         "src/trace_processor/sorter/trace_sorter.h",
-        "src/trace_processor/sorter/trace_sorter_internal.h",
-        "src/trace_processor/sorter/trace_sorter_queue.h",
+        "src/trace_processor/sorter/trace_token_buffer.cc",
+        "src/trace_processor/sorter/trace_token_buffer.h",
     ],
 )
 
@@ -1982,6 +1989,15 @@
     ],
 )
 
+# GN target: //src/trace_processor/util:bump_allocator
+perfetto_filegroup(
+    name = "src_trace_processor_util_bump_allocator",
+    srcs = [
+        "src/trace_processor/util/bump_allocator.cc",
+        "src/trace_processor/util/bump_allocator.h",
+    ],
+)
+
 # GN target: //src/trace_processor/util:descriptors
 perfetto_filegroup(
     name = "src_trace_processor_util_descriptors",
@@ -3990,6 +4006,7 @@
     name = "protos_perfetto_trace_power_protos",
     srcs = [
         "protos/perfetto/trace/power/android_energy_estimation_breakdown.proto",
+        "protos/perfetto/trace/power/android_entity_state_residency.proto",
         "protos/perfetto/trace/power/battery_counters.proto",
         "protos/perfetto/trace/power/power_rails.proto",
     ],
@@ -4033,6 +4050,7 @@
 perfetto_proto_library(
     name = "protos_perfetto_trace_processor_protos",
     srcs = [
+        "protos/perfetto/trace_processor/cloud_trace_processor.proto",
         "protos/perfetto/trace_processor/metatrace_categories.proto",
         "protos/perfetto/trace_processor/trace_processor.proto",
     ],
@@ -4575,6 +4593,7 @@
         ":src_trace_processor_tables_tables",
         ":src_trace_processor_tables_tables_python",
         ":src_trace_processor_types_types",
+        ":src_trace_processor_util_bump_allocator",
         ":src_trace_processor_util_descriptors",
         ":src_trace_processor_util_glob",
         ":src_trace_processor_util_gzip",
@@ -4725,6 +4744,7 @@
         ":src_trace_processor_tables_tables",
         ":src_trace_processor_tables_tables_python",
         ":src_trace_processor_types_types",
+        ":src_trace_processor_util_bump_allocator",
         ":src_trace_processor_util_descriptors",
         ":src_trace_processor_util_glob",
         ":src_trace_processor_util_gzip",
@@ -4930,6 +4950,7 @@
         ":src_trace_processor_tables_tables",
         ":src_trace_processor_tables_tables_python",
         ":src_trace_processor_types_types",
+        ":src_trace_processor_util_bump_allocator",
         ":src_trace_processor_util_descriptors",
         ":src_trace_processor_util_glob",
         ":src_trace_processor_util_gzip",
diff --git a/BUILD.gn b/BUILD.gn
index bac6cfc..ac13eb3 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -113,6 +113,9 @@
     "test:client_api_example",
     "test/stress_test",
   ]
+  if (!is_win) {
+    all_targets += [ "examples/shared_lib:example_shlib_data_source" ]
+  }
 }
 
 if (enable_perfetto_trace_processor_json) {
diff --git a/CHANGELOG b/CHANGELOG
index ac77b20..5c92fd9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -8,6 +8,20 @@
   SDK:
     *
 
+v33.0 - 2023-03-02:
+  All:
+    * Switched to a C++17-only project by removing C++11 opt-out. This completes
+      the migration started in v31.0.
+  SDK:
+    * Tracing::Initialize() can be called more than once to initialize different
+      backends separately.
+    * Reduce binary size impact of autogenerated code.
+
+v32.2 - 2023-02-16:
+  SDK:
+    * Fix MSVC warnings.
+
+
 v32.1 - 2023-02-01:
   Trace Processor:
     * Fix build on windows.
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 6682fec..7b11412 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -41,6 +41,7 @@
         files_to_check='.*',
         files_to_skip=[
             'Android[.]bp',
+            "buildtools/grpc/BUILD.gn",
             '.*[.]json$',
             '.*[.]sql$',
             '.*[.]out$',
diff --git a/buildtools/.gitignore b/buildtools/.gitignore
index 869315f..ad3768e 100644
--- a/buildtools/.gitignore
+++ b/buildtools/.gitignore
@@ -17,6 +17,7 @@
 debian_sid_armel-sysroot/
 emulator/
 googletest/
+grpc/src
 jsoncpp/
 libbacktrace/
 libbase/
diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn
index 5eced89..c6bcd5c 100644
--- a/buildtools/BUILD.gn
+++ b/buildtools/BUILD.gn
@@ -129,21 +129,8 @@
 
   defines = [ "HAVE_PTHREAD=1" ]
   cflags = []
-  if (is_clang) {
-    # We do NOT build libprotobuf with -Wextra or -Weverything. But still it
-    # hits some warnings that we need to suppress.
-    cflags += [
-      "-Wno-unknown-warning-option",
-      "-Wno-enum-compare-switch",
-      "-Wno-user-defined-warnings",
-      "-Wno-tautological-constant-compare",
-      "-Wno-inconsistent-missing-override",
-    ]
-  } else if (!is_win) {  # implies gcc
-    cflags += [
-      "-Wno-return-type",
-      "-Wno-stringop-overread",
-    ]
+  if (!is_clang && !is_win) {  # implies gcc
+    cflags += [ "-Wno-stringop-overread" ]
   }
   if (is_win) {
     cflags += [ "/W0" ]
@@ -156,63 +143,110 @@
   cflags = []
   if (is_clang) {
     cflags += [ "-Wno-switch-enum" ]
+    if (is_win) {
+      cflags += [ "-Wno-undef" ]
+    }
   }
 }
 
 source_set("protobuf_lite") {
   visibility = _buildtools_visibility
   sources = [
+    "protobuf/src/google/protobuf/any.h",
+    "protobuf/src/google/protobuf/any.pb.h",
     "protobuf/src/google/protobuf/any_lite.cc",
+    "protobuf/src/google/protobuf/api.pb.h",
     "protobuf/src/google/protobuf/arena.cc",
     "protobuf/src/google/protobuf/arena.h",
     "protobuf/src/google/protobuf/arena_impl.h",
+    "protobuf/src/google/protobuf/arenastring.cc",
     "protobuf/src/google/protobuf/arenastring.h",
+    "protobuf/src/google/protobuf/arenaz_sampler.cc",
+    "protobuf/src/google/protobuf/arenaz_sampler.h",
+    "protobuf/src/google/protobuf/compiler/importer.h",
+    "protobuf/src/google/protobuf/compiler/parser.h",
+    "protobuf/src/google/protobuf/descriptor.h",
+    "protobuf/src/google/protobuf/descriptor.pb.h",
+    "protobuf/src/google/protobuf/descriptor_database.h",
+    "protobuf/src/google/protobuf/duration.pb.h",
+    "protobuf/src/google/protobuf/dynamic_message.h",
+    "protobuf/src/google/protobuf/empty.pb.h",
+    "protobuf/src/google/protobuf/explicitly_constructed.h",
     "protobuf/src/google/protobuf/extension_set.cc",
     "protobuf/src/google/protobuf/extension_set.h",
+    "protobuf/src/google/protobuf/extension_set_inl.h",
+    "protobuf/src/google/protobuf/field_access_listener.h",
+    "protobuf/src/google/protobuf/field_mask.pb.h",
+    "protobuf/src/google/protobuf/generated_enum_reflection.h",
     "protobuf/src/google/protobuf/generated_enum_util.cc",
     "protobuf/src/google/protobuf/generated_enum_util.h",
-    "protobuf/src/google/protobuf/generated_message_table_driven_lite.cc",
-    "protobuf/src/google/protobuf/generated_message_table_driven_lite.h",
+    "protobuf/src/google/protobuf/generated_message_bases.h",
+    "protobuf/src/google/protobuf/generated_message_reflection.h",
+    "protobuf/src/google/protobuf/generated_message_tctable_decl.h",
+    "protobuf/src/google/protobuf/generated_message_tctable_impl.h",
+    "protobuf/src/google/protobuf/generated_message_tctable_lite.cc",
     "protobuf/src/google/protobuf/generated_message_util.cc",
     "protobuf/src/google/protobuf/generated_message_util.h",
     "protobuf/src/google/protobuf/has_bits.h",
     "protobuf/src/google/protobuf/implicit_weak_message.cc",
     "protobuf/src/google/protobuf/implicit_weak_message.h",
+    "protobuf/src/google/protobuf/inlined_string_field.cc",
     "protobuf/src/google/protobuf/inlined_string_field.h",
     "protobuf/src/google/protobuf/io/coded_stream.cc",
     "protobuf/src/google/protobuf/io/coded_stream.h",
-    "protobuf/src/google/protobuf/io/coded_stream_inl.h",
     "protobuf/src/google/protobuf/io/io_win32.cc",
     "protobuf/src/google/protobuf/io/io_win32.h",
+    "protobuf/src/google/protobuf/io/printer.h",
     "protobuf/src/google/protobuf/io/strtod.cc",
     "protobuf/src/google/protobuf/io/strtod.h",
+    "protobuf/src/google/protobuf/io/tokenizer.h",
     "protobuf/src/google/protobuf/io/zero_copy_stream.cc",
     "protobuf/src/google/protobuf/io/zero_copy_stream.h",
     "protobuf/src/google/protobuf/io/zero_copy_stream_impl.cc",
     "protobuf/src/google/protobuf/io/zero_copy_stream_impl.h",
     "protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.cc",
     "protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h",
+    "protobuf/src/google/protobuf/map.cc",
     "protobuf/src/google/protobuf/map.h",
+    "protobuf/src/google/protobuf/map_entry.h",
     "protobuf/src/google/protobuf/map_entry_lite.h",
+    "protobuf/src/google/protobuf/map_field.h",
+    "protobuf/src/google/protobuf/map_field_inl.h",
     "protobuf/src/google/protobuf/map_field_lite.h",
     "protobuf/src/google/protobuf/map_type_handler.h",
+    "protobuf/src/google/protobuf/message.h",
     "protobuf/src/google/protobuf/message_lite.cc",
     "protobuf/src/google/protobuf/message_lite.h",
+    "protobuf/src/google/protobuf/metadata.h",
+    "protobuf/src/google/protobuf/metadata_lite.h",
+    "protobuf/src/google/protobuf/parse_context.cc",
+    "protobuf/src/google/protobuf/parse_context.h",
+    "protobuf/src/google/protobuf/port.h",
+    "protobuf/src/google/protobuf/port_def.inc",
+    "protobuf/src/google/protobuf/port_undef.inc",
+    "protobuf/src/google/protobuf/reflection.h",
+    "protobuf/src/google/protobuf/reflection_ops.h",
     "protobuf/src/google/protobuf/repeated_field.cc",
     "protobuf/src/google/protobuf/repeated_field.h",
+    "protobuf/src/google/protobuf/repeated_ptr_field.cc",
+    "protobuf/src/google/protobuf/repeated_ptr_field.h",
+    "protobuf/src/google/protobuf/service.h",
+    "protobuf/src/google/protobuf/source_context.pb.h",
+    "protobuf/src/google/protobuf/string_member_robber.h",
+    "protobuf/src/google/protobuf/struct.pb.h",
     "protobuf/src/google/protobuf/stubs/bytestream.cc",
     "protobuf/src/google/protobuf/stubs/bytestream.h",
     "protobuf/src/google/protobuf/stubs/callback.h",
     "protobuf/src/google/protobuf/stubs/casts.h",
     "protobuf/src/google/protobuf/stubs/common.cc",
     "protobuf/src/google/protobuf/stubs/common.h",
-    "protobuf/src/google/protobuf/stubs/fastmem.h",
     "protobuf/src/google/protobuf/stubs/hash.h",
     "protobuf/src/google/protobuf/stubs/int128.cc",
     "protobuf/src/google/protobuf/stubs/int128.h",
     "protobuf/src/google/protobuf/stubs/logging.h",
     "protobuf/src/google/protobuf/stubs/macros.h",
     "protobuf/src/google/protobuf/stubs/map_util.h",
+    "protobuf/src/google/protobuf/stubs/mathutil.h",
     "protobuf/src/google/protobuf/stubs/mutex.h",
     "protobuf/src/google/protobuf/stubs/once.h",
     "protobuf/src/google/protobuf/stubs/platform_macros.h",
@@ -233,8 +267,22 @@
     "protobuf/src/google/protobuf/stubs/template_util.h",
     "protobuf/src/google/protobuf/stubs/time.cc",
     "protobuf/src/google/protobuf/stubs/time.h",
+    "protobuf/src/google/protobuf/text_format.h",
+    "protobuf/src/google/protobuf/timestamp.pb.h",
+    "protobuf/src/google/protobuf/type.pb.h",
+    "protobuf/src/google/protobuf/unknown_field_set.h",
+    "protobuf/src/google/protobuf/util/delimited_message_util.h",
+    "protobuf/src/google/protobuf/util/field_comparator.h",
+    "protobuf/src/google/protobuf/util/field_mask_util.h",
+    "protobuf/src/google/protobuf/util/json_util.h",
+    "protobuf/src/google/protobuf/util/message_differencer.h",
+    "protobuf/src/google/protobuf/util/time_util.h",
+    "protobuf/src/google/protobuf/util/type_resolver.h",
+    "protobuf/src/google/protobuf/util/type_resolver_util.h",
+    "protobuf/src/google/protobuf/wire_format.h",
     "protobuf/src/google/protobuf/wire_format_lite.cc",
     "protobuf/src/google/protobuf/wire_format_lite.h",
+    "protobuf/src/google/protobuf/wrappers.pb.h",
   ]
   configs -= [ "//gn/standalone:extra_warnings" ]
   if (is_win) {
@@ -259,6 +307,10 @@
     "protobuf/src/google/protobuf/any.pb.h",
     "protobuf/src/google/protobuf/api.pb.cc",
     "protobuf/src/google/protobuf/api.pb.h",
+    "protobuf/src/google/protobuf/arena.h",
+    "protobuf/src/google/protobuf/arena_impl.h",
+    "protobuf/src/google/protobuf/arenastring.h",
+    "protobuf/src/google/protobuf/arenaz_sampler.h",
     "protobuf/src/google/protobuf/compiler/importer.cc",
     "protobuf/src/google/protobuf/compiler/importer.h",
     "protobuf/src/google/protobuf/compiler/parser.cc",
@@ -275,40 +327,85 @@
     "protobuf/src/google/protobuf/dynamic_message.h",
     "protobuf/src/google/protobuf/empty.pb.cc",
     "protobuf/src/google/protobuf/empty.pb.h",
+    "protobuf/src/google/protobuf/explicitly_constructed.h",
+    "protobuf/src/google/protobuf/extension_set.h",
     "protobuf/src/google/protobuf/extension_set_heavy.cc",
+    "protobuf/src/google/protobuf/extension_set_inl.h",
+    "protobuf/src/google/protobuf/field_access_listener.h",
     "protobuf/src/google/protobuf/field_mask.pb.cc",
     "protobuf/src/google/protobuf/field_mask.pb.h",
     "protobuf/src/google/protobuf/generated_enum_reflection.h",
+    "protobuf/src/google/protobuf/generated_enum_util.h",
+    "protobuf/src/google/protobuf/generated_message_bases.cc",
+    "protobuf/src/google/protobuf/generated_message_bases.h",
     "protobuf/src/google/protobuf/generated_message_reflection.cc",
     "protobuf/src/google/protobuf/generated_message_reflection.h",
+    "protobuf/src/google/protobuf/generated_message_tctable_decl.h",
+    "protobuf/src/google/protobuf/generated_message_tctable_full.cc",
+    "protobuf/src/google/protobuf/generated_message_tctable_impl.h",
+    "protobuf/src/google/protobuf/generated_message_util.h",
+    "protobuf/src/google/protobuf/has_bits.h",
+    "protobuf/src/google/protobuf/implicit_weak_message.h",
+    "protobuf/src/google/protobuf/inlined_string_field.h",
+    "protobuf/src/google/protobuf/io/coded_stream.h",
     "protobuf/src/google/protobuf/io/gzip_stream.cc",
-    "protobuf/src/google/protobuf/io/gzip_stream.h",
+    "protobuf/src/google/protobuf/io/io_win32.h",
     "protobuf/src/google/protobuf/io/printer.cc",
     "protobuf/src/google/protobuf/io/printer.h",
+    "protobuf/src/google/protobuf/io/strtod.h",
     "protobuf/src/google/protobuf/io/tokenizer.cc",
     "protobuf/src/google/protobuf/io/tokenizer.h",
+    "protobuf/src/google/protobuf/io/zero_copy_stream.h",
+    "protobuf/src/google/protobuf/io/zero_copy_stream_impl.h",
+    "protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h",
+    "protobuf/src/google/protobuf/map.h",
     "protobuf/src/google/protobuf/map_entry.h",
+    "protobuf/src/google/protobuf/map_entry_lite.h",
     "protobuf/src/google/protobuf/map_field.cc",
     "protobuf/src/google/protobuf/map_field.h",
     "protobuf/src/google/protobuf/map_field_inl.h",
+    "protobuf/src/google/protobuf/map_field_lite.h",
+    "protobuf/src/google/protobuf/map_type_handler.h",
     "protobuf/src/google/protobuf/message.cc",
     "protobuf/src/google/protobuf/message.h",
+    "protobuf/src/google/protobuf/message_lite.h",
     "protobuf/src/google/protobuf/metadata.h",
+    "protobuf/src/google/protobuf/metadata_lite.h",
+    "protobuf/src/google/protobuf/parse_context.h",
+    "protobuf/src/google/protobuf/port.h",
+    "protobuf/src/google/protobuf/port_def.inc",
+    "protobuf/src/google/protobuf/port_undef.inc",
     "protobuf/src/google/protobuf/reflection.h",
     "protobuf/src/google/protobuf/reflection_internal.h",
     "protobuf/src/google/protobuf/reflection_ops.cc",
     "protobuf/src/google/protobuf/reflection_ops.h",
+    "protobuf/src/google/protobuf/repeated_field.h",
+    "protobuf/src/google/protobuf/repeated_ptr_field.h",
     "protobuf/src/google/protobuf/service.cc",
     "protobuf/src/google/protobuf/service.h",
     "protobuf/src/google/protobuf/source_context.pb.cc",
     "protobuf/src/google/protobuf/source_context.pb.h",
     "protobuf/src/google/protobuf/struct.pb.cc",
     "protobuf/src/google/protobuf/struct.pb.h",
-    "protobuf/src/google/protobuf/stubs/mathlimits.cc",
-    "protobuf/src/google/protobuf/stubs/mathlimits.h",
-    "protobuf/src/google/protobuf/stubs/mathutil.h",
+    "protobuf/src/google/protobuf/stubs/bytestream.h",
+    "protobuf/src/google/protobuf/stubs/callback.h",
+    "protobuf/src/google/protobuf/stubs/casts.h",
+    "protobuf/src/google/protobuf/stubs/common.h",
+    "protobuf/src/google/protobuf/stubs/hash.h",
+    "protobuf/src/google/protobuf/stubs/logging.h",
+    "protobuf/src/google/protobuf/stubs/macros.h",
+    "protobuf/src/google/protobuf/stubs/map_util.h",
+    "protobuf/src/google/protobuf/stubs/mutex.h",
+    "protobuf/src/google/protobuf/stubs/once.h",
+    "protobuf/src/google/protobuf/stubs/platform_macros.h",
+    "protobuf/src/google/protobuf/stubs/port.h",
+    "protobuf/src/google/protobuf/stubs/status.h",
+    "protobuf/src/google/protobuf/stubs/stl_util.h",
+    "protobuf/src/google/protobuf/stubs/stringpiece.h",
+    "protobuf/src/google/protobuf/stubs/strutil.h",
     "protobuf/src/google/protobuf/stubs/substitute.cc",
     "protobuf/src/google/protobuf/stubs/substitute.h",
+    "protobuf/src/google/protobuf/stubs/template_util.h",
     "protobuf/src/google/protobuf/text_format.cc",
     "protobuf/src/google/protobuf/text_format.h",
     "protobuf/src/google/protobuf/timestamp.pb.cc",
@@ -317,6 +414,8 @@
     "protobuf/src/google/protobuf/type.pb.h",
     "protobuf/src/google/protobuf/unknown_field_set.cc",
     "protobuf/src/google/protobuf/unknown_field_set.h",
+    "protobuf/src/google/protobuf/util/delimited_message_util.cc",
+    "protobuf/src/google/protobuf/util/delimited_message_util.h",
     "protobuf/src/google/protobuf/util/field_comparator.cc",
     "protobuf/src/google/protobuf/util/field_comparator.h",
     "protobuf/src/google/protobuf/util/field_mask_util.cc",
@@ -328,6 +427,7 @@
     "protobuf/src/google/protobuf/util/internal/default_value_objectwriter.h",
     "protobuf/src/google/protobuf/util/internal/error_listener.cc",
     "protobuf/src/google/protobuf/util/internal/error_listener.h",
+    "protobuf/src/google/protobuf/util/internal/expecting_objectwriter.h",
     "protobuf/src/google/protobuf/util/internal/field_mask_utility.cc",
     "protobuf/src/google/protobuf/util/internal/field_mask_utility.h",
     "protobuf/src/google/protobuf/util/internal/json_escaping.cc",
@@ -337,6 +437,7 @@
     "protobuf/src/google/protobuf/util/internal/json_stream_parser.cc",
     "protobuf/src/google/protobuf/util/internal/json_stream_parser.h",
     "protobuf/src/google/protobuf/util/internal/location_tracker.h",
+    "protobuf/src/google/protobuf/util/internal/mock_error_listener.h",
     "protobuf/src/google/protobuf/util/internal/object_location_tracker.h",
     "protobuf/src/google/protobuf/util/internal/object_source.h",
     "protobuf/src/google/protobuf/util/internal/object_writer.cc",
@@ -350,7 +451,6 @@
     "protobuf/src/google/protobuf/util/internal/structured_objectwriter.h",
     "protobuf/src/google/protobuf/util/internal/type_info.cc",
     "protobuf/src/google/protobuf/util/internal/type_info.h",
-    "protobuf/src/google/protobuf/util/internal/type_info_test_helper.cc",
     "protobuf/src/google/protobuf/util/internal/type_info_test_helper.h",
     "protobuf/src/google/protobuf/util/internal/utility.cc",
     "protobuf/src/google/protobuf/util/internal/utility.h",
@@ -365,6 +465,7 @@
     "protobuf/src/google/protobuf/util/type_resolver_util.h",
     "protobuf/src/google/protobuf/wire_format.cc",
     "protobuf/src/google/protobuf/wire_format.h",
+    "protobuf/src/google/protobuf/wire_format_lite.h",
     "protobuf/src/google/protobuf/wrappers.pb.cc",
     "protobuf/src/google/protobuf/wrappers.pb.h",
   ]
@@ -412,9 +513,12 @@
     "protobuf/src/google/protobuf/compiler/cpp/cpp_message_field.cc",
     "protobuf/src/google/protobuf/compiler/cpp/cpp_message_field.h",
     "protobuf/src/google/protobuf/compiler/cpp/cpp_message_layout_helper.h",
+    "protobuf/src/google/protobuf/compiler/cpp/cpp_names.h",
     "protobuf/src/google/protobuf/compiler/cpp/cpp_options.h",
     "protobuf/src/google/protobuf/compiler/cpp/cpp_padding_optimizer.cc",
     "protobuf/src/google/protobuf/compiler/cpp/cpp_padding_optimizer.h",
+    "protobuf/src/google/protobuf/compiler/cpp/cpp_parse_function_generator.cc",
+    "protobuf/src/google/protobuf/compiler/cpp/cpp_parse_function_generator.h",
     "protobuf/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc",
     "protobuf/src/google/protobuf/compiler/cpp/cpp_primitive_field.h",
     "protobuf/src/google/protobuf/compiler/cpp/cpp_service.cc",
@@ -439,6 +543,7 @@
     "protobuf/src/google/protobuf/compiler/csharp/csharp_message.h",
     "protobuf/src/google/protobuf/compiler/csharp/csharp_message_field.cc",
     "protobuf/src/google/protobuf/compiler/csharp/csharp_message_field.h",
+    "protobuf/src/google/protobuf/compiler/csharp/csharp_names.h",
     "protobuf/src/google/protobuf/compiler/csharp/csharp_options.h",
     "protobuf/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc",
     "protobuf/src/google/protobuf/compiler/csharp/csharp_primitive_field.h",
@@ -480,6 +585,8 @@
     "protobuf/src/google/protobuf/compiler/java/java_generator_factory.h",
     "protobuf/src/google/protobuf/compiler/java/java_helpers.cc",
     "protobuf/src/google/protobuf/compiler/java/java_helpers.h",
+    "protobuf/src/google/protobuf/compiler/java/java_kotlin_generator.cc",
+    "protobuf/src/google/protobuf/compiler/java/java_kotlin_generator.h",
     "protobuf/src/google/protobuf/compiler/java/java_map_field.cc",
     "protobuf/src/google/protobuf/compiler/java/java_map_field.h",
     "protobuf/src/google/protobuf/compiler/java/java_map_field_lite.cc",
@@ -498,6 +605,7 @@
     "protobuf/src/google/protobuf/compiler/java/java_message_lite.h",
     "protobuf/src/google/protobuf/compiler/java/java_name_resolver.cc",
     "protobuf/src/google/protobuf/compiler/java/java_name_resolver.h",
+    "protobuf/src/google/protobuf/compiler/java/java_names.h",
     "protobuf/src/google/protobuf/compiler/java/java_options.h",
     "protobuf/src/google/protobuf/compiler/java/java_primitive_field.cc",
     "protobuf/src/google/protobuf/compiler/java/java_primitive_field.h",
@@ -535,6 +643,7 @@
     "protobuf/src/google/protobuf/compiler/objectivec/objectivec_message.h",
     "protobuf/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc",
     "protobuf/src/google/protobuf/compiler/objectivec/objectivec_message_field.h",
+    "protobuf/src/google/protobuf/compiler/objectivec/objectivec_nsobject_methods.h",
     "protobuf/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc",
     "protobuf/src/google/protobuf/compiler/objectivec/objectivec_oneof.h",
     "protobuf/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc",
@@ -547,8 +656,13 @@
     "protobuf/src/google/protobuf/compiler/plugin.pb.h",
     "protobuf/src/google/protobuf/compiler/python/python_generator.cc",
     "protobuf/src/google/protobuf/compiler/python/python_generator.h",
+    "protobuf/src/google/protobuf/compiler/python/python_helpers.cc",
+    "protobuf/src/google/protobuf/compiler/python/python_helpers.h",
+    "protobuf/src/google/protobuf/compiler/python/python_pyi_generator.cc",
+    "protobuf/src/google/protobuf/compiler/python/python_pyi_generator.h",
     "protobuf/src/google/protobuf/compiler/ruby/ruby_generator.cc",
     "protobuf/src/google/protobuf/compiler/ruby/ruby_generator.h",
+    "protobuf/src/google/protobuf/compiler/scc.h",
     "protobuf/src/google/protobuf/compiler/subprocess.cc",
     "protobuf/src/google/protobuf/compiler/subprocess.h",
     "protobuf/src/google/protobuf/compiler/zip_writer.cc",
@@ -708,12 +822,8 @@
       "//gn/standalone:extra_warnings",
       "//gn/standalone:no_exceptions",
       "//gn/standalone:no_rtti",
+      "//gn/standalone:c++17",
     ]
-    if (perfetto_cpp11_until_q1_2023) {
-      configs -= [ "//gn/standalone:c++11" ]
-    } else {
-      configs -= [ "//gn/standalone:c++17" ]
-    }
     configs += [
       ":libc++config",
       "//gn/standalone/sanitizers:sanitizer_options_link_helper",
@@ -811,12 +921,8 @@
       "//gn/standalone:extra_warnings",
       "//gn/standalone:no_exceptions",
       "//gn/standalone:no_rtti",
+      "//gn/standalone:c++17",
     ]
-    if (perfetto_cpp11_until_q1_2023) {
-      configs -= [ "//gn/standalone:c++11" ]
-    } else {
-      configs -= [ "//gn/standalone:c++17" ]
-    }
     if ((is_android || is_mac) && !custom_libcxx_is_static) {
       # Use libc++_perfetto to avoid conflicting with system libc++
       output_name = "libc++_perfettto"
@@ -1199,11 +1305,6 @@
     cflags += [ "-Wno-deprecated-declarations" ]
   }
   public_configs = [ ":libunwindstack_config" ]
-
-  if (perfetto_cpp11_until_q1_2023) {
-    configs -= [ "//gn/standalone:c++11" ]
-    public_configs += [ "//gn/standalone:c++17" ]
-  }
 }
 
 config("bionic_kernel_uapi_headers") {
@@ -1312,16 +1413,6 @@
 source_set("llvm_demangle") {
   visibility = _buildtools_visibility
   configs -= [ "//gn/standalone:extra_warnings" ]
-
-  # NB: this is built under c++14 and linked into code that is c++11 by default.
-  # We rely on the ABIs being compatible for this to be sane. At the time of
-  # writing, the only c++14 specific code is behind an #ifndef NDEBUG, so we
-  # could keep building as c++11 in non-debug builds, but we always use c++14 for
-  # consistency.
-  if (perfetto_cpp11_until_q1_2023) {
-    configs -= [ "//gn/standalone:c++11" ]
-    configs += [ "//gn/standalone:c++14" ]
-  }
   public_configs = [ ":llvm_demangle_config" ]
   sources = [
     "llvm-project/llvm/include/llvm/Demangle/Demangle.h",
@@ -1340,3 +1431,65 @@
   ]
   deps = [ "//gn:default_deps" ]
 }
+
+if (enable_perfetto_grpc) {
+  config("grpc_absl_config") {
+    visibility = _buildtools_visibility
+    include_dirs = [ "grpc/src/third_party/abseil-cpp" ]
+    cflags = [ "-Wno-deprecated-builtins" ]
+  }
+
+  config("grpc_boringssl_config") {
+    visibility = _buildtools_visibility
+    include_dirs = [ "grpc/src/third_party/boringssl-with-bazel/src/include" ]
+  }
+
+  config("grpc_upb_config") {
+    visibility = _buildtools_visibility
+    include_dirs = [
+      "grpc/src/third_party/upb",
+      "grpc/src/src/core/ext/upb-generated",
+      "grpc/src/src/core/ext/upbdefs-generated",
+    ]
+  }
+
+  config("grpc_re2_config") {
+    visibility = _buildtools_visibility
+    include_dirs = [ "grpc/src/third_party/re2" ]
+  }
+
+  config("grpc_internal_config") {
+    visibility = _buildtools_visibility
+    include_dirs = [
+      "grpc/src/include",
+      "grpc/src",
+      "grpc/src/third_party/address_sorting/include",
+      "grpc/src/third_party/re2",
+      "grpc/src/src/core/ext/upb-generated",
+      "grpc/src/src/core/ext/upbdefs-generated",
+      "grpc/src/third_party/xxhash",
+    ]
+    cflags = [ "-DGRPC_ARES=0" ]
+  }
+
+  config("grpc_gen_config") {
+    cflags = [
+      # Using -isystem instead of include_dirs (-I), so we don't need to
+      # suppress warnings coming from libprotobuf headers. Doing so would mask
+      # warnings in our own code.
+      perfetto_isystem_cflag,
+      rebase_path("grpc/src/include", root_build_dir),
+      perfetto_isystem_cflag,
+      rebase_path("grpc/src/third_party/abseil-cpp", root_build_dir),
+    ]
+
+    # This issues appear in .grpc.pb.h files so we unfortunately need to leak
+    # the cflags outside of just compiling this code.
+    if (is_clang && !is_win) {
+      cflags += [
+        "-Wno-weak-vtables",
+        "-Wno-suggest-destructor-override",
+      ]
+    }
+  }
+}
diff --git a/buildtools/grpc/BUILD.gn b/buildtools/grpc/BUILD.gn
new file mode 100644
index 0000000..a13fb50
--- /dev/null
+++ b/buildtools/grpc/BUILD.gn
@@ -0,0 +1,6451 @@
+#
+# DO NOT EDIT. AUTOGENERATED file
+#
+# This file is generated with the command:
+# tools/gen_grpc_build_gn.py > buildtools/grpc/BUILD.gn
+#
+
+import("../../gn/perfetto.gni")
+
+# Prevent the gRPC from being depended upon without explicitly being opted in.
+assert(enable_perfetto_grpc)
+
+# BoringSSL has assembly code which is tied to platform-specific. For now, we
+# only care about Linux x64 so assert this as the case.
+assert(is_linux && current_cpu == "x64")
+
+source_set("absl_algorithm_algorithm") {
+  sources = [ "src/third_party/abseil-cpp/absl/algorithm/algorithm.h" ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_algorithm_container") {
+  sources = [ "src/third_party/abseil-cpp/absl/algorithm/container.h" ]
+  public_deps = [
+    ":absl_algorithm_algorithm",
+    ":absl_base_core_headers",
+    ":absl_meta_type_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_atomic_hook") {
+  sources = [ "src/third_party/abseil-cpp/absl/base/internal/atomic_hook.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_base") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/base/call_once.h",
+    "src/third_party/abseil-cpp/absl/base/casts.h",
+    "src/third_party/abseil-cpp/absl/base/internal/cycleclock.cc",
+    "src/third_party/abseil-cpp/absl/base/internal/cycleclock.h",
+    "src/third_party/abseil-cpp/absl/base/internal/low_level_scheduling.h",
+    "src/third_party/abseil-cpp/absl/base/internal/per_thread_tls.h",
+    "src/third_party/abseil-cpp/absl/base/internal/spinlock.cc",
+    "src/third_party/abseil-cpp/absl/base/internal/spinlock.h",
+    "src/third_party/abseil-cpp/absl/base/internal/sysinfo.cc",
+    "src/third_party/abseil-cpp/absl/base/internal/sysinfo.h",
+    "src/third_party/abseil-cpp/absl/base/internal/thread_identity.cc",
+    "src/third_party/abseil-cpp/absl/base/internal/thread_identity.h",
+    "src/third_party/abseil-cpp/absl/base/internal/tsan_mutex_interface.h",
+    "src/third_party/abseil-cpp/absl/base/internal/unscaledcycleclock.cc",
+    "src/third_party/abseil-cpp/absl/base/internal/unscaledcycleclock.h",
+  ]
+  public_deps = [
+    ":absl_base_atomic_hook",
+    ":absl_base_base_internal",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_cycleclock_internal",
+    ":absl_base_dynamic_annotations",
+    ":absl_base_log_severity",
+    ":absl_base_raw_logging_internal",
+    ":absl_base_spinlock_wait",
+    ":absl_meta_type_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_base_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/base/internal/hide_ptr.h",
+    "src/third_party/abseil-cpp/absl/base/internal/identity.h",
+    "src/third_party/abseil-cpp/absl/base/internal/inline_variable.h",
+    "src/third_party/abseil-cpp/absl/base/internal/invoke.h",
+    "src/third_party/abseil-cpp/absl/base/internal/scheduling_mode.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_meta_type_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_config") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/base/config.h",
+    "src/third_party/abseil-cpp/absl/base/options.h",
+    "src/third_party/abseil-cpp/absl/base/policy_checks.h",
+  ]
+  public_deps = []
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_core_headers") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/base/attributes.h",
+    "src/third_party/abseil-cpp/absl/base/const_init.h",
+    "src/third_party/abseil-cpp/absl/base/internal/thread_annotations.h",
+    "src/third_party/abseil-cpp/absl/base/macros.h",
+    "src/third_party/abseil-cpp/absl/base/optimization.h",
+    "src/third_party/abseil-cpp/absl/base/port.h",
+    "src/third_party/abseil-cpp/absl/base/thread_annotations.h",
+  ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_cycleclock_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/base/internal/cycleclock_config.h",
+    "src/third_party/abseil-cpp/absl/base/internal/unscaledcycleclock_config.h",
+  ]
+  public_deps = [
+    ":absl_base_base_internal",
+    ":absl_base_config",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_dynamic_annotations") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/base/dynamic_annotations.h",
+    "src/third_party/abseil-cpp/absl/base/internal/dynamic_annotations.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_endian") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/base/internal/endian.h",
+    "src/third_party/abseil-cpp/absl/base/internal/unaligned_access.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_errno_saver") {
+  sources = [ "src/third_party/abseil-cpp/absl/base/internal/errno_saver.h" ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_fast_type_id") {
+  sources = [ "src/third_party/abseil-cpp/absl/base/internal/fast_type_id.h" ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_log_severity") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/base/log_severity.cc",
+    "src/third_party/abseil-cpp/absl/base/log_severity.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_malloc_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/base/internal/direct_mmap.h",
+    "src/third_party/abseil-cpp/absl/base/internal/low_level_alloc.cc",
+    "src/third_party/abseil-cpp/absl/base/internal/low_level_alloc.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_base_internal",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_dynamic_annotations",
+    ":absl_base_raw_logging_internal",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_prefetch") {
+  sources = [ "src/third_party/abseil-cpp/absl/base/internal/prefetch.h" ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_pretty_function") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/base/internal/pretty_function.h" ]
+  public_deps = []
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_raw_logging_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/base/internal/raw_logging.cc",
+    "src/third_party/abseil-cpp/absl/base/internal/raw_logging.h",
+  ]
+  public_deps = [
+    ":absl_base_atomic_hook",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_errno_saver",
+    ":absl_base_log_severity",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_spinlock_wait") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/base/internal/spinlock_akaros.inc",
+    "src/third_party/abseil-cpp/absl/base/internal/spinlock_linux.inc",
+    "src/third_party/abseil-cpp/absl/base/internal/spinlock_posix.inc",
+    "src/third_party/abseil-cpp/absl/base/internal/spinlock_wait.cc",
+    "src/third_party/abseil-cpp/absl/base/internal/spinlock_wait.h",
+    "src/third_party/abseil-cpp/absl/base/internal/spinlock_win32.inc",
+  ]
+  public_deps = [
+    ":absl_base_base_internal",
+    ":absl_base_core_headers",
+    ":absl_base_errno_saver",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_strerror") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/base/internal/strerror.cc",
+    "src/third_party/abseil-cpp/absl/base/internal/strerror.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_errno_saver",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_base_throw_delegate") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/base/internal/throw_delegate.cc",
+    "src/third_party/abseil-cpp/absl/base/internal/throw_delegate.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_raw_logging_internal",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_cleanup_cleanup") {
+  sources = [ "src/third_party/abseil-cpp/absl/cleanup/cleanup.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_cleanup_cleanup_internal",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_cleanup_cleanup_internal") {
+  sources = [ "src/third_party/abseil-cpp/absl/cleanup/internal/cleanup.h" ]
+  public_deps = [
+    ":absl_base_base_internal",
+    ":absl_base_core_headers",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_btree") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/container/btree_map.h",
+    "src/third_party/abseil-cpp/absl/container/btree_set.h",
+    "src/third_party/abseil-cpp/absl/container/internal/btree.h",
+    "src/third_party/abseil-cpp/absl/container/internal/btree_container.h",
+  ]
+  public_deps = [
+    ":absl_base_core_headers",
+    ":absl_base_raw_logging_internal",
+    ":absl_base_throw_delegate",
+    ":absl_container_common",
+    ":absl_container_common_policy_traits",
+    ":absl_container_compressed_tuple",
+    ":absl_container_container_memory",
+    ":absl_container_layout",
+    ":absl_memory_memory",
+    ":absl_meta_type_traits",
+    ":absl_strings_cord",
+    ":absl_strings_strings",
+    ":absl_types_compare",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_common") {
+  sources = [ "src/third_party/abseil-cpp/absl/container/internal/common.h" ]
+  public_deps = [
+    ":absl_meta_type_traits",
+    ":absl_types_optional",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_common_policy_traits") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/container/internal/common_policy_traits.h",
+  ]
+  public_deps = [ ":absl_meta_type_traits" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_compressed_tuple") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/container/internal/compressed_tuple.h",
+  ]
+  public_deps = [ ":absl_utility_utility" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_container_memory") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/container/internal/container_memory.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_memory_memory",
+    ":absl_meta_type_traits",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_fixed_array") {
+  sources = [ "src/third_party/abseil-cpp/absl/container/fixed_array.h" ]
+  public_deps = [
+    ":absl_algorithm_algorithm",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_dynamic_annotations",
+    ":absl_base_throw_delegate",
+    ":absl_container_compressed_tuple",
+    ":absl_memory_memory",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_flat_hash_map") {
+  sources = [ "src/third_party/abseil-cpp/absl/container/flat_hash_map.h" ]
+  public_deps = [
+    ":absl_algorithm_container",
+    ":absl_base_core_headers",
+    ":absl_container_container_memory",
+    ":absl_container_hash_function_defaults",
+    ":absl_container_raw_hash_map",
+    ":absl_memory_memory",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_flat_hash_set") {
+  sources = [ "src/third_party/abseil-cpp/absl/container/flat_hash_set.h" ]
+  public_deps = [
+    ":absl_algorithm_container",
+    ":absl_base_core_headers",
+    ":absl_container_container_memory",
+    ":absl_container_hash_function_defaults",
+    ":absl_container_raw_hash_set",
+    ":absl_memory_memory",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_hash_function_defaults") {
+  sources = [ "src/third_party/abseil-cpp/absl/container/internal/hash_function_defaults.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_hash_hash",
+    ":absl_strings_cord",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_hash_policy_traits") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/container/internal/hash_policy_traits.h",
+  ]
+  public_deps = [
+    ":absl_container_common_policy_traits",
+    ":absl_meta_type_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_hashtable_debug") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/container/internal/hashtable_debug.h" ]
+  public_deps = [ ":absl_container_hashtable_debug_hooks" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_hashtable_debug_hooks") {
+  sources = [ "src/third_party/abseil-cpp/absl/container/internal/hashtable_debug_hooks.h" ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_hashtablez_sampler") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.cc",
+    "src/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.h",
+    "src/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_debugging_stacktrace",
+    ":absl_memory_memory",
+    ":absl_profiling_exponential_biased",
+    ":absl_profiling_sample_recorder",
+    ":absl_synchronization_synchronization",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_inlined_vector") {
+  sources = [ "src/third_party/abseil-cpp/absl/container/inlined_vector.h" ]
+  public_deps = [
+    ":absl_algorithm_algorithm",
+    ":absl_base_core_headers",
+    ":absl_base_throw_delegate",
+    ":absl_container_inlined_vector_internal",
+    ":absl_memory_memory",
+    ":absl_meta_type_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_inlined_vector_internal") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/container/internal/inlined_vector.h" ]
+  public_deps = [
+    ":absl_base_core_headers",
+    ":absl_container_compressed_tuple",
+    ":absl_memory_memory",
+    ":absl_meta_type_traits",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_layout") {
+  sources = [ "src/third_party/abseil-cpp/absl/container/internal/layout.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_meta_type_traits",
+    ":absl_strings_strings",
+    ":absl_types_span",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_node_hash_map") {
+  sources = [ "src/third_party/abseil-cpp/absl/container/node_hash_map.h" ]
+  public_deps = [
+    ":absl_algorithm_container",
+    ":absl_base_core_headers",
+    ":absl_container_container_memory",
+    ":absl_container_hash_function_defaults",
+    ":absl_container_node_slot_policy",
+    ":absl_container_raw_hash_map",
+    ":absl_memory_memory",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_node_hash_set") {
+  sources = [ "src/third_party/abseil-cpp/absl/container/node_hash_set.h" ]
+  public_deps = [
+    ":absl_algorithm_container",
+    ":absl_base_core_headers",
+    ":absl_container_hash_function_defaults",
+    ":absl_container_node_slot_policy",
+    ":absl_container_raw_hash_set",
+    ":absl_memory_memory",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_node_slot_policy") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/container/internal/node_slot_policy.h",
+  ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_raw_hash_map") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/container/internal/raw_hash_map.h" ]
+  public_deps = [
+    ":absl_base_throw_delegate",
+    ":absl_container_container_memory",
+    ":absl_container_raw_hash_set",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_container_raw_hash_set") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc",
+    "src/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_endian",
+    ":absl_base_prefetch",
+    ":absl_base_raw_logging_internal",
+    ":absl_container_common",
+    ":absl_container_compressed_tuple",
+    ":absl_container_container_memory",
+    ":absl_container_hash_policy_traits",
+    ":absl_container_hashtable_debug_hooks",
+    ":absl_container_hashtablez_sampler",
+    ":absl_memory_memory",
+    ":absl_meta_type_traits",
+    ":absl_numeric_bits",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_crc_cpu_detect") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/crc/internal/cpu_detect.cc",
+    "src/third_party/abseil-cpp/absl/crc/internal/cpu_detect.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_crc_crc32c") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/crc/crc32c.cc",
+    "src/third_party/abseil-cpp/absl/crc/crc32c.h",
+    "src/third_party/abseil-cpp/absl/crc/internal/crc32c.h",
+    "src/third_party/abseil-cpp/absl/crc/internal/crc32c_inline.h",
+    "src/third_party/abseil-cpp/absl/crc/internal/crc_memcpy.h",
+    "src/third_party/abseil-cpp/absl/crc/internal/crc_memcpy_fallback.cc",
+    "src/third_party/abseil-cpp/absl/crc/internal/crc_memcpy_x86_64.cc",
+    "src/third_party/abseil-cpp/absl/crc/internal/crc_non_temporal_memcpy.cc",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_dynamic_annotations",
+    ":absl_base_endian",
+    ":absl_base_prefetch",
+    ":absl_crc_cpu_detect",
+    ":absl_crc_crc_internal",
+    ":absl_crc_non_temporal_memcpy",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_crc_crc_cord_state") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/crc/internal/crc_cord_state.cc",
+    "src/third_party/abseil-cpp/absl/crc/internal/crc_cord_state.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_crc_crc32c",
+    ":absl_numeric_bits",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_crc_crc_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/crc/internal/crc.cc",
+    "src/third_party/abseil-cpp/absl/crc/internal/crc.h",
+    "src/third_party/abseil-cpp/absl/crc/internal/crc32_x86_arm_combined_simd.h",
+    "src/third_party/abseil-cpp/absl/crc/internal/crc_internal.h",
+    "src/third_party/abseil-cpp/absl/crc/internal/crc_x86_arm_combined.cc",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_dynamic_annotations",
+    ":absl_base_endian",
+    ":absl_base_prefetch",
+    ":absl_base_raw_logging_internal",
+    ":absl_crc_cpu_detect",
+    ":absl_memory_memory",
+    ":absl_numeric_bits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_crc_non_temporal_arm_intrinsics") {
+  sources = [ "src/third_party/abseil-cpp/absl/crc/internal/non_temporal_arm_intrinsics.h" ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_crc_non_temporal_memcpy") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/crc/internal/non_temporal_memcpy.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_crc_non_temporal_arm_intrinsics",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_debugging_debugging_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/debugging/internal/address_is_readable.cc",
+    "src/third_party/abseil-cpp/absl/debugging/internal/address_is_readable.h",
+    "src/third_party/abseil-cpp/absl/debugging/internal/elf_mem_image.cc",
+    "src/third_party/abseil-cpp/absl/debugging/internal/elf_mem_image.h",
+    "src/third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc",
+    "src/third_party/abseil-cpp/absl/debugging/internal/vdso_support.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_dynamic_annotations",
+    ":absl_base_errno_saver",
+    ":absl_base_raw_logging_internal",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_debugging_demangle_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/debugging/internal/demangle.cc",
+    "src/third_party/abseil-cpp/absl/debugging/internal/demangle.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_debugging_examine_stack") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/debugging/internal/examine_stack.cc",
+    "src/third_party/abseil-cpp/absl/debugging/internal/examine_stack.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_raw_logging_internal",
+    ":absl_debugging_stacktrace",
+    ":absl_debugging_symbolize",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_debugging_failure_signal_handler") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/debugging/failure_signal_handler.cc",
+    "src/third_party/abseil-cpp/absl/debugging/failure_signal_handler.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_raw_logging_internal",
+    ":absl_debugging_examine_stack",
+    ":absl_debugging_stacktrace",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_debugging_leak_check") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/debugging/leak_check.cc",
+    "src/third_party/abseil-cpp/absl/debugging/leak_check.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_debugging_stacktrace") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc",
+    "src/third_party/abseil-cpp/absl/debugging/internal/stacktrace_arm-inl.inc",
+    "src/third_party/abseil-cpp/absl/debugging/internal/stacktrace_config.h",
+    "src/third_party/abseil-cpp/absl/debugging/internal/stacktrace_emscripten-inl.inc",
+    "src/third_party/abseil-cpp/absl/debugging/internal/stacktrace_generic-inl.inc",
+    "src/third_party/abseil-cpp/absl/debugging/internal/stacktrace_powerpc-inl.inc",
+    "src/third_party/abseil-cpp/absl/debugging/internal/stacktrace_riscv-inl.inc",
+    "src/third_party/abseil-cpp/absl/debugging/internal/stacktrace_unimplemented-inl.inc",
+    "src/third_party/abseil-cpp/absl/debugging/internal/stacktrace_win32-inl.inc",
+    "src/third_party/abseil-cpp/absl/debugging/internal/stacktrace_x86-inl.inc",
+    "src/third_party/abseil-cpp/absl/debugging/stacktrace.cc",
+    "src/third_party/abseil-cpp/absl/debugging/stacktrace.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_raw_logging_internal",
+    ":absl_debugging_debugging_internal",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_debugging_symbolize") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/debugging/internal/symbolize.h",
+    "src/third_party/abseil-cpp/absl/debugging/symbolize.cc",
+    "src/third_party/abseil-cpp/absl/debugging/symbolize.h",
+    "src/third_party/abseil-cpp/absl/debugging/symbolize_darwin.inc",
+    "src/third_party/abseil-cpp/absl/debugging/symbolize_elf.inc",
+    "src/third_party/abseil-cpp/absl/debugging/symbolize_emscripten.inc",
+    "src/third_party/abseil-cpp/absl/debugging/symbolize_unimplemented.inc",
+    "src/third_party/abseil-cpp/absl/debugging/symbolize_win32.inc",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_dynamic_annotations",
+    ":absl_base_malloc_internal",
+    ":absl_base_raw_logging_internal",
+    ":absl_debugging_debugging_internal",
+    ":absl_debugging_demangle_internal",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_flags_commandlineflag") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/flags/commandlineflag.cc",
+    "src/third_party/abseil-cpp/absl/flags/commandlineflag.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_fast_type_id",
+    ":absl_flags_commandlineflag_internal",
+    ":absl_strings_strings",
+    ":absl_types_optional",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_flags_commandlineflag_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/flags/internal/commandlineflag.cc",
+    "src/third_party/abseil-cpp/absl/flags/internal/commandlineflag.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_fast_type_id",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_flags_config") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/flags/config.h",
+    "src/third_party/abseil-cpp/absl/flags/usage_config.cc",
+    "src/third_party/abseil-cpp/absl/flags/usage_config.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_flags_path_util",
+    ":absl_flags_program_name",
+    ":absl_strings_strings",
+    ":absl_synchronization_synchronization",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_flags_flag") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/flags/declare.h",
+    "src/third_party/abseil-cpp/absl/flags/flag.cc",
+    "src/third_party/abseil-cpp/absl/flags/flag.h",
+    "src/third_party/abseil-cpp/absl/flags/internal/flag_msvc.inc",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_flags_config",
+    ":absl_flags_flag_internal",
+    ":absl_flags_reflection",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_flags_flag_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/flags/internal/flag.cc",
+    "src/third_party/abseil-cpp/absl/flags/internal/flag.h",
+    "src/third_party/abseil-cpp/absl/flags/internal/sequence_lock.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_dynamic_annotations",
+    ":absl_flags_commandlineflag",
+    ":absl_flags_commandlineflag_internal",
+    ":absl_flags_config",
+    ":absl_flags_marshalling",
+    ":absl_flags_reflection",
+    ":absl_memory_memory",
+    ":absl_meta_type_traits",
+    ":absl_strings_strings",
+    ":absl_synchronization_synchronization",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_flags_marshalling") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/flags/marshalling.cc",
+    "src/third_party/abseil-cpp/absl/flags/marshalling.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_log_severity",
+    ":absl_strings_str_format",
+    ":absl_strings_strings",
+    ":absl_types_optional",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_flags_parse") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/flags/internal/parse.h",
+    "src/third_party/abseil-cpp/absl/flags/parse.cc",
+    "src/third_party/abseil-cpp/absl/flags/parse.h",
+  ]
+  public_deps = [
+    ":absl_algorithm_container",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_flags_commandlineflag",
+    ":absl_flags_commandlineflag_internal",
+    ":absl_flags_config",
+    ":absl_flags_flag",
+    ":absl_flags_flag_internal",
+    ":absl_flags_private_handle_accessor",
+    ":absl_flags_program_name",
+    ":absl_flags_reflection",
+    ":absl_flags_usage",
+    ":absl_flags_usage_internal",
+    ":absl_strings_strings",
+    ":absl_synchronization_synchronization",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_flags_path_util") {
+  sources = [ "src/third_party/abseil-cpp/absl/flags/internal/path_util.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_flags_private_handle_accessor") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/flags/internal/private_handle_accessor.cc",
+    "src/third_party/abseil-cpp/absl/flags/internal/private_handle_accessor.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_flags_commandlineflag",
+    ":absl_flags_commandlineflag_internal",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_flags_program_name") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/flags/internal/program_name.cc",
+    "src/third_party/abseil-cpp/absl/flags/internal/program_name.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_flags_path_util",
+    ":absl_strings_strings",
+    ":absl_synchronization_synchronization",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_flags_reflection") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/flags/internal/registry.h",
+    "src/third_party/abseil-cpp/absl/flags/reflection.cc",
+    "src/third_party/abseil-cpp/absl/flags/reflection.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_container_flat_hash_map",
+    ":absl_flags_commandlineflag",
+    ":absl_flags_commandlineflag_internal",
+    ":absl_flags_config",
+    ":absl_flags_private_handle_accessor",
+    ":absl_strings_strings",
+    ":absl_synchronization_synchronization",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_flags_usage") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/flags/usage.cc",
+    "src/third_party/abseil-cpp/absl/flags/usage.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_flags_usage_internal",
+    ":absl_strings_strings",
+    ":absl_synchronization_synchronization",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_flags_usage_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/flags/internal/usage.cc",
+    "src/third_party/abseil-cpp/absl/flags/internal/usage.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_container_flat_hash_map",
+    ":absl_flags_commandlineflag",
+    ":absl_flags_config",
+    ":absl_flags_flag",
+    ":absl_flags_flag_internal",
+    ":absl_flags_path_util",
+    ":absl_flags_private_handle_accessor",
+    ":absl_flags_program_name",
+    ":absl_flags_reflection",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_functional_any_invocable") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/functional/any_invocable.h",
+    "src/third_party/abseil-cpp/absl/functional/internal/any_invocable.h",
+  ]
+  public_deps = [
+    ":absl_base_base_internal",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_meta_type_traits",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_functional_bind_front") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/functional/bind_front.h",
+    "src/third_party/abseil-cpp/absl/functional/internal/front_binder.h",
+  ]
+  public_deps = [
+    ":absl_base_base_internal",
+    ":absl_container_compressed_tuple",
+    ":absl_meta_type_traits",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_functional_function_ref") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/functional/function_ref.h",
+    "src/third_party/abseil-cpp/absl/functional/internal/function_ref.h",
+  ]
+  public_deps = [
+    ":absl_base_base_internal",
+    ":absl_base_core_headers",
+    ":absl_meta_type_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_hash_city") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/hash/internal/city.cc",
+    "src/third_party/abseil-cpp/absl/hash/internal/city.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_endian",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_hash_hash") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/hash/hash.h",
+    "src/third_party/abseil-cpp/absl/hash/internal/hash.cc",
+    "src/third_party/abseil-cpp/absl/hash/internal/hash.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_endian",
+    ":absl_container_fixed_array",
+    ":absl_functional_function_ref",
+    ":absl_hash_city",
+    ":absl_hash_low_level_hash",
+    ":absl_meta_type_traits",
+    ":absl_numeric_bits",
+    ":absl_numeric_int128",
+    ":absl_strings_strings",
+    ":absl_types_optional",
+    ":absl_types_variant",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_hash_low_level_hash") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/hash/internal/low_level_hash.cc",
+    "src/third_party/abseil-cpp/absl/hash/internal/low_level_hash.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_endian",
+    ":absl_numeric_int128",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_append_truncated") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/log/internal/append_truncated.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_strings_strings",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_check_impl") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/internal/check_impl.h" ]
+  public_deps = [
+    ":absl_base_core_headers",
+    ":absl_log_internal_check_op",
+    ":absl_log_internal_conditions",
+    ":absl_log_internal_log_message",
+    ":absl_log_internal_strip",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_check_op") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/log/internal/check_op.cc",
+    "src/third_party/abseil-cpp/absl/log/internal/check_op.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_log_internal_nullguard",
+    ":absl_log_internal_nullstream",
+    ":absl_log_internal_strip",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_conditions") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/log/internal/conditions.cc",
+    "src/third_party/abseil-cpp/absl/log/internal/conditions.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_log_internal_voidify",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_config") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/internal/config.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_flags") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/internal/flags.h" ]
+  public_deps = [ ":absl_flags_flag" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_format") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/log/internal/log_format.cc",
+    "src/third_party/abseil-cpp/absl/log/internal/log_format.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_log_severity",
+    ":absl_log_internal_append_truncated",
+    ":absl_log_internal_config",
+    ":absl_log_internal_globals",
+    ":absl_strings_str_format",
+    ":absl_strings_strings",
+    ":absl_time_time",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_globals") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/log/internal/globals.cc",
+    "src/third_party/abseil-cpp/absl/log/internal/globals.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_log_severity",
+    ":absl_base_raw_logging_internal",
+    ":absl_strings_strings",
+    ":absl_time_time",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_log_impl") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/internal/log_impl.h" ]
+  public_deps = [
+    ":absl_log_internal_conditions",
+    ":absl_log_internal_log_message",
+    ":absl_log_internal_strip",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_log_message") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/log/internal/log_message.cc",
+    "src/third_party/abseil-cpp/absl/log/internal/log_message.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_errno_saver",
+    ":absl_base_log_severity",
+    ":absl_base_raw_logging_internal",
+    ":absl_base_strerror",
+    ":absl_container_inlined_vector",
+    ":absl_debugging_examine_stack",
+    ":absl_log_globals",
+    ":absl_log_internal_append_truncated",
+    ":absl_log_internal_format",
+    ":absl_log_internal_globals",
+    ":absl_log_internal_log_sink_set",
+    ":absl_log_internal_nullguard",
+    ":absl_log_internal_proto",
+    ":absl_log_log_entry",
+    ":absl_log_log_sink",
+    ":absl_log_log_sink_registry",
+    ":absl_memory_memory",
+    ":absl_strings_strings",
+    ":absl_time_time",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_log_sink_set") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/log/internal/log_sink_set.cc",
+    "src/third_party/abseil-cpp/absl/log/internal/log_sink_set.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_log_severity",
+    ":absl_base_raw_logging_internal",
+    ":absl_cleanup_cleanup",
+    ":absl_log_globals",
+    ":absl_log_internal_config",
+    ":absl_log_internal_globals",
+    ":absl_log_log_entry",
+    ":absl_log_log_sink",
+    ":absl_strings_strings",
+    ":absl_synchronization_synchronization",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_nullguard") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/log/internal/nullguard.cc",
+    "src/third_party/abseil-cpp/absl/log/internal/nullguard.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_nullstream") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/internal/nullstream.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_log_severity",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_proto") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/log/internal/proto.cc",
+    "src/third_party/abseil-cpp/absl/log/internal/proto.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_strings_strings",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_strip") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/internal/strip.h" ]
+  public_deps = [
+    ":absl_base_log_severity",
+    ":absl_log_internal_log_message",
+    ":absl_log_internal_nullstream",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_structured") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/internal/structured.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_log_internal_log_message",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_internal_voidify") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/internal/voidify.h" ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_absl_check") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/absl_check.h" ]
+  public_deps = [ ":absl_log_internal_check_impl" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_absl_log") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/absl_log.h" ]
+  public_deps = [ ":absl_log_internal_log_impl" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_check") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/check.h" ]
+  public_deps = [
+    ":absl_log_internal_check_impl",
+    ":absl_log_internal_check_op",
+    ":absl_log_internal_conditions",
+    ":absl_log_internal_log_message",
+    ":absl_log_internal_strip",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_die_if_null") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/log/die_if_null.cc",
+    "src/third_party/abseil-cpp/absl/log/die_if_null.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_log_log",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_flags") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/log/flags.cc",
+    "src/third_party/abseil-cpp/absl/log/flags.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_log_severity",
+    ":absl_flags_flag",
+    ":absl_flags_marshalling",
+    ":absl_log_globals",
+    ":absl_log_internal_config",
+    ":absl_log_internal_flags",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_globals") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/log/globals.cc",
+    "src/third_party/abseil-cpp/absl/log/globals.h",
+  ]
+  public_deps = [
+    ":absl_base_atomic_hook",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_log_severity",
+    ":absl_hash_hash",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_initialize") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/log/initialize.cc",
+    "src/third_party/abseil-cpp/absl/log/initialize.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_log_globals",
+    ":absl_log_internal_globals",
+    ":absl_time_time",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_log") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/log.h" ]
+  public_deps = [ ":absl_log_internal_log_impl" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_log_entry") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/log/log_entry.cc",
+    "src/third_party/abseil-cpp/absl/log/log_entry.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_log_severity",
+    ":absl_log_internal_config",
+    ":absl_strings_strings",
+    ":absl_time_time",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_log_sink") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/log/log_sink.cc",
+    "src/third_party/abseil-cpp/absl/log/log_sink.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_log_log_entry",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_log_sink_registry") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/log_sink_registry.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_log_internal_log_sink_set",
+    ":absl_log_log_sink",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_log_streamer") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/log_streamer.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_log_severity",
+    ":absl_log_absl_log",
+    ":absl_strings_internal",
+    ":absl_strings_strings",
+    ":absl_types_optional",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_log_structured") {
+  sources = [ "src/third_party/abseil-cpp/absl/log/structured.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_log_internal_structured",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_memory_memory") {
+  sources = [ "src/third_party/abseil-cpp/absl/memory/memory.h" ]
+  public_deps = [
+    ":absl_base_core_headers",
+    ":absl_meta_type_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_meta_type_traits") {
+  sources = [ "src/third_party/abseil-cpp/absl/meta/type_traits.h" ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_numeric_bits") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/numeric/bits.h",
+    "src/third_party/abseil-cpp/absl/numeric/internal/bits.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_numeric_int128") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/numeric/int128.cc",
+    "src/third_party/abseil-cpp/absl/numeric/int128.h",
+    "src/third_party/abseil-cpp/absl/numeric/int128_have_intrinsic.inc",
+    "src/third_party/abseil-cpp/absl/numeric/int128_no_intrinsic.inc",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_numeric_bits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_numeric_representation") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/numeric/internal/representation.h" ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_profiling_exponential_biased") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/profiling/internal/exponential_biased.cc",
+    "src/third_party/abseil-cpp/absl/profiling/internal/exponential_biased.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_profiling_periodic_sampler") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/profiling/internal/periodic_sampler.cc",
+    "src/third_party/abseil-cpp/absl/profiling/internal/periodic_sampler.h",
+  ]
+  public_deps = [
+    ":absl_base_core_headers",
+    ":absl_profiling_exponential_biased",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_profiling_sample_recorder") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/profiling/internal/sample_recorder.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_synchronization_synchronization",
+    ":absl_time_time",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_distribution_caller") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/random/internal/distribution_caller.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_fast_type_id",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_fast_uniform_bits") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/random/internal/fast_uniform_bits.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_meta_type_traits",
+    ":absl_random_internal_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_fastmath") {
+  sources = [ "src/third_party/abseil-cpp/absl/random/internal/fastmath.h" ]
+  public_deps = [ ":absl_numeric_bits" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_generate_real") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/random/internal/generate_real.h" ]
+  public_deps = [
+    ":absl_meta_type_traits",
+    ":absl_numeric_bits",
+    ":absl_random_internal_fastmath",
+    ":absl_random_internal_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_iostream_state_saver") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/random/internal/iostream_state_saver.h",
+  ]
+  public_deps = [
+    ":absl_meta_type_traits",
+    ":absl_numeric_int128",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_mock_helpers") {
+  sources = [ "src/third_party/abseil-cpp/absl/random/internal/mock_helpers.h" ]
+  public_deps = [
+    ":absl_base_fast_type_id",
+    ":absl_types_optional",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_nanobenchmark") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/random/internal/nanobenchmark.cc",
+    "src/third_party/abseil-cpp/absl/random/internal/nanobenchmark.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_raw_logging_internal",
+    ":absl_random_internal_platform",
+    ":absl_random_internal_randen_engine",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_nonsecure_base") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/random/internal/nonsecure_base.h" ]
+  public_deps = [
+    ":absl_base_core_headers",
+    ":absl_container_inlined_vector",
+    ":absl_meta_type_traits",
+    ":absl_random_internal_pool_urbg",
+    ":absl_random_internal_salted_seed_seq",
+    ":absl_random_internal_seed_material",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_pcg_engine") {
+  sources = [ "src/third_party/abseil-cpp/absl/random/internal/pcg_engine.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_meta_type_traits",
+    ":absl_numeric_bits",
+    ":absl_numeric_int128",
+    ":absl_random_internal_fastmath",
+    ":absl_random_internal_iostream_state_saver",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_platform") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/random/internal/platform.h",
+    "src/third_party/abseil-cpp/absl/random/internal/randen_round_keys.cc",
+    "src/third_party/abseil-cpp/absl/random/internal/randen_traits.h",
+  ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_pool_urbg") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/random/internal/pool_urbg.cc",
+    "src/third_party/abseil-cpp/absl/random/internal/pool_urbg.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_endian",
+    ":absl_base_raw_logging_internal",
+    ":absl_random_internal_randen",
+    ":absl_random_internal_seed_material",
+    ":absl_random_internal_traits",
+    ":absl_random_seed_gen_exception",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_randen") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/random/internal/randen.cc",
+    "src/third_party/abseil-cpp/absl/random/internal/randen.h",
+  ]
+  public_deps = [
+    ":absl_base_raw_logging_internal",
+    ":absl_random_internal_platform",
+    ":absl_random_internal_randen_hwaes",
+    ":absl_random_internal_randen_slow",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_randen_engine") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/random/internal/randen_engine.h" ]
+  public_deps = [
+    ":absl_base_endian",
+    ":absl_meta_type_traits",
+    ":absl_random_internal_iostream_state_saver",
+    ":absl_random_internal_randen",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_randen_hwaes") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/random/internal/randen_detect.cc",
+    "src/third_party/abseil-cpp/absl/random/internal/randen_detect.h",
+    "src/third_party/abseil-cpp/absl/random/internal/randen_hwaes.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_random_internal_platform",
+    ":absl_random_internal_randen_hwaes_impl",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_randen_hwaes_impl") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/random/internal/randen_hwaes.cc",
+    "src/third_party/abseil-cpp/absl/random/internal/randen_hwaes.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_numeric_int128",
+    ":absl_random_internal_platform",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_randen_slow") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/random/internal/randen_slow.cc",
+    "src/third_party/abseil-cpp/absl/random/internal/randen_slow.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_endian",
+    ":absl_numeric_int128",
+    ":absl_random_internal_platform",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_salted_seed_seq") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/random/internal/salted_seed_seq.h" ]
+  public_deps = [
+    ":absl_container_inlined_vector",
+    ":absl_meta_type_traits",
+    ":absl_random_internal_seed_material",
+    ":absl_types_optional",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_seed_material") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/random/internal/seed_material.cc",
+    "src/third_party/abseil-cpp/absl/random/internal/seed_material.h",
+  ]
+  public_deps = [
+    ":absl_base_core_headers",
+    ":absl_base_dynamic_annotations",
+    ":absl_base_raw_logging_internal",
+    ":absl_random_internal_fast_uniform_bits",
+    ":absl_strings_strings",
+    ":absl_types_optional",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_traits") {
+  sources = [ "src/third_party/abseil-cpp/absl/random/internal/traits.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_numeric_bits",
+    ":absl_numeric_int128",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_uniform_helper") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/random/internal/uniform_helper.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_meta_type_traits",
+    ":absl_numeric_int128",
+    ":absl_random_internal_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_internal_wide_multiply") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/random/internal/wide_multiply.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_numeric_bits",
+    ":absl_numeric_int128",
+    ":absl_random_internal_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_bit_gen_ref") {
+  sources = [ "src/third_party/abseil-cpp/absl/random/bit_gen_ref.h" ]
+  public_deps = [
+    ":absl_base_core_headers",
+    ":absl_base_fast_type_id",
+    ":absl_meta_type_traits",
+    ":absl_random_internal_distribution_caller",
+    ":absl_random_internal_fast_uniform_bits",
+    ":absl_random_random",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_distributions") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/random/bernoulli_distribution.h",
+    "src/third_party/abseil-cpp/absl/random/beta_distribution.h",
+    "src/third_party/abseil-cpp/absl/random/discrete_distribution.cc",
+    "src/third_party/abseil-cpp/absl/random/discrete_distribution.h",
+    "src/third_party/abseil-cpp/absl/random/distributions.h",
+    "src/third_party/abseil-cpp/absl/random/exponential_distribution.h",
+    "src/third_party/abseil-cpp/absl/random/gaussian_distribution.cc",
+    "src/third_party/abseil-cpp/absl/random/gaussian_distribution.h",
+    "src/third_party/abseil-cpp/absl/random/log_uniform_int_distribution.h",
+    "src/third_party/abseil-cpp/absl/random/poisson_distribution.h",
+    "src/third_party/abseil-cpp/absl/random/uniform_int_distribution.h",
+    "src/third_party/abseil-cpp/absl/random/uniform_real_distribution.h",
+    "src/third_party/abseil-cpp/absl/random/zipf_distribution.h",
+  ]
+  public_deps = [
+    ":absl_base_base_internal",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_meta_type_traits",
+    ":absl_numeric_bits",
+    ":absl_random_internal_distribution_caller",
+    ":absl_random_internal_fast_uniform_bits",
+    ":absl_random_internal_fastmath",
+    ":absl_random_internal_generate_real",
+    ":absl_random_internal_iostream_state_saver",
+    ":absl_random_internal_traits",
+    ":absl_random_internal_uniform_helper",
+    ":absl_random_internal_wide_multiply",
+    ":absl_strings_strings",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_random") {
+  sources = [ "src/third_party/abseil-cpp/absl/random/random.h" ]
+  public_deps = [
+    ":absl_random_distributions",
+    ":absl_random_internal_nonsecure_base",
+    ":absl_random_internal_pcg_engine",
+    ":absl_random_internal_pool_urbg",
+    ":absl_random_internal_randen_engine",
+    ":absl_random_seed_sequences",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_seed_gen_exception") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/random/seed_gen_exception.cc",
+    "src/third_party/abseil-cpp/absl/random/seed_gen_exception.h",
+  ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_random_seed_sequences") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/random/seed_sequences.cc",
+    "src/third_party/abseil-cpp/absl/random/seed_sequences.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_random_internal_pool_urbg",
+    ":absl_random_internal_salted_seed_seq",
+    ":absl_random_internal_seed_material",
+    ":absl_random_seed_gen_exception",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_status_status") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/status/internal/status_internal.h",
+    "src/third_party/abseil-cpp/absl/status/status.cc",
+    "src/third_party/abseil-cpp/absl/status/status.h",
+    "src/third_party/abseil-cpp/absl/status/status_payload_printer.cc",
+    "src/third_party/abseil-cpp/absl/status/status_payload_printer.h",
+  ]
+  public_deps = [
+    ":absl_base_atomic_hook",
+    ":absl_base_core_headers",
+    ":absl_base_raw_logging_internal",
+    ":absl_base_strerror",
+    ":absl_container_inlined_vector",
+    ":absl_debugging_stacktrace",
+    ":absl_debugging_symbolize",
+    ":absl_functional_function_ref",
+    ":absl_strings_cord",
+    ":absl_strings_str_format",
+    ":absl_strings_strings",
+    ":absl_types_optional",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_status_statusor") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/status/internal/statusor_internal.h",
+    "src/third_party/abseil-cpp/absl/status/statusor.cc",
+    "src/third_party/abseil-cpp/absl/status/statusor.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_core_headers",
+    ":absl_base_raw_logging_internal",
+    ":absl_meta_type_traits",
+    ":absl_status_status",
+    ":absl_strings_strings",
+    ":absl_types_variant",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_strings_cord") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/strings/cord.cc",
+    "src/third_party/abseil-cpp/absl/strings/cord.h",
+    "src/third_party/abseil-cpp/absl/strings/cord_analysis.cc",
+    "src/third_party/abseil-cpp/absl/strings/cord_analysis.h",
+    "src/third_party/abseil-cpp/absl/strings/cord_buffer.cc",
+    "src/third_party/abseil-cpp/absl/strings/cord_buffer.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_endian",
+    ":absl_base_raw_logging_internal",
+    ":absl_container_fixed_array",
+    ":absl_container_inlined_vector",
+    ":absl_crc_crc_cord_state",
+    ":absl_functional_function_ref",
+    ":absl_meta_type_traits",
+    ":absl_numeric_bits",
+    ":absl_strings_cord_internal",
+    ":absl_strings_cordz_functions",
+    ":absl_strings_cordz_info",
+    ":absl_strings_cordz_statistics",
+    ":absl_strings_cordz_update_scope",
+    ":absl_strings_cordz_update_tracker",
+    ":absl_strings_internal",
+    ":absl_strings_str_format",
+    ":absl_strings_strings",
+    ":absl_types_optional",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_strings_cord_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_data_edge.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_internal.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_internal.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_reader.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_reader.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_rep_consume.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_rep_consume.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_rep_crc.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_rep_crc.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_rep_flat.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_rep_ring.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_rep_ring.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/cord_rep_ring_reader.h",
+  ]
+  public_deps = [
+    ":absl_base_base_internal",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_endian",
+    ":absl_base_raw_logging_internal",
+    ":absl_base_throw_delegate",
+    ":absl_container_compressed_tuple",
+    ":absl_container_container_memory",
+    ":absl_container_inlined_vector",
+    ":absl_container_layout",
+    ":absl_crc_crc_cord_state",
+    ":absl_functional_function_ref",
+    ":absl_meta_type_traits",
+    ":absl_strings_strings",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_strings_cordz_functions") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/strings/internal/cordz_functions.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/cordz_functions.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_raw_logging_internal",
+    ":absl_profiling_exponential_biased",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_strings_cordz_handle") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/strings/internal/cordz_handle.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/cordz_handle.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_raw_logging_internal",
+    ":absl_synchronization_synchronization",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_strings_cordz_info") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/strings/internal/cordz_info.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/cordz_info.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_raw_logging_internal",
+    ":absl_container_inlined_vector",
+    ":absl_debugging_stacktrace",
+    ":absl_strings_cord_internal",
+    ":absl_strings_cordz_functions",
+    ":absl_strings_cordz_handle",
+    ":absl_strings_cordz_statistics",
+    ":absl_strings_cordz_update_tracker",
+    ":absl_synchronization_synchronization",
+    ":absl_types_span",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_strings_cordz_sample_token") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_strings_cordz_handle",
+    ":absl_strings_cordz_info",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_strings_cordz_statistics") {
+  sources =
+      [ "src/third_party/abseil-cpp/absl/strings/internal/cordz_statistics.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_strings_cordz_update_tracker",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_strings_cordz_update_scope") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/strings/internal/cordz_update_scope.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_strings_cord_internal",
+    ":absl_strings_cordz_info",
+    ":absl_strings_cordz_update_tracker",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_strings_cordz_update_tracker") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker.h",
+  ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_strings_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/strings/internal/char_map.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/escaping.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/escaping.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/ostringstream.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/ostringstream.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/resize_uninitialized.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/utf8.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/utf8.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_endian",
+    ":absl_base_raw_logging_internal",
+    ":absl_meta_type_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_strings_str_format") {
+  sources = [ "src/third_party/abseil-cpp/absl/strings/str_format.h" ]
+  public_deps = [ ":absl_strings_str_format_internal" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_strings_str_format_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_format/arg.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_format/bind.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_format/bind.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_format/checker.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_format/constexpr_parser.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_format/extension.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_format/extension.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_format/float_conversion.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_format/float_conversion.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_format/output.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_format/output.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_format/parser.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_format/parser.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_functional_function_ref",
+    ":absl_meta_type_traits",
+    ":absl_numeric_bits",
+    ":absl_numeric_int128",
+    ":absl_numeric_representation",
+    ":absl_strings_strings",
+    ":absl_types_optional",
+    ":absl_types_span",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_strings_strings") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/strings/ascii.cc",
+    "src/third_party/abseil-cpp/absl/strings/ascii.h",
+    "src/third_party/abseil-cpp/absl/strings/charconv.cc",
+    "src/third_party/abseil-cpp/absl/strings/charconv.h",
+    "src/third_party/abseil-cpp/absl/strings/escaping.cc",
+    "src/third_party/abseil-cpp/absl/strings/escaping.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/charconv_bigint.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/charconv_bigint.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/charconv_parse.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/charconv_parse.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/damerau_levenshtein_distance.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/damerau_levenshtein_distance.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/has_absl_stringify.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/memutil.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/memutil.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/stl_type_traits.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_join_internal.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/str_split_internal.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/string_constant.h",
+    "src/third_party/abseil-cpp/absl/strings/internal/stringify_sink.cc",
+    "src/third_party/abseil-cpp/absl/strings/internal/stringify_sink.h",
+    "src/third_party/abseil-cpp/absl/strings/match.cc",
+    "src/third_party/abseil-cpp/absl/strings/match.h",
+    "src/third_party/abseil-cpp/absl/strings/numbers.cc",
+    "src/third_party/abseil-cpp/absl/strings/numbers.h",
+    "src/third_party/abseil-cpp/absl/strings/str_cat.cc",
+    "src/third_party/abseil-cpp/absl/strings/str_cat.h",
+    "src/third_party/abseil-cpp/absl/strings/str_join.h",
+    "src/third_party/abseil-cpp/absl/strings/str_replace.cc",
+    "src/third_party/abseil-cpp/absl/strings/str_replace.h",
+    "src/third_party/abseil-cpp/absl/strings/str_split.cc",
+    "src/third_party/abseil-cpp/absl/strings/str_split.h",
+    "src/third_party/abseil-cpp/absl/strings/string_view.cc",
+    "src/third_party/abseil-cpp/absl/strings/string_view.h",
+    "src/third_party/abseil-cpp/absl/strings/strip.h",
+    "src/third_party/abseil-cpp/absl/strings/substitute.cc",
+    "src/third_party/abseil-cpp/absl/strings/substitute.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_endian",
+    ":absl_base_raw_logging_internal",
+    ":absl_base_throw_delegate",
+    ":absl_memory_memory",
+    ":absl_meta_type_traits",
+    ":absl_numeric_bits",
+    ":absl_numeric_int128",
+    ":absl_strings_internal",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_synchronization_graphcycles_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/synchronization/internal/graphcycles.cc",
+    "src/third_party/abseil-cpp/absl/synchronization/internal/graphcycles.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_base_internal",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_malloc_internal",
+    ":absl_base_raw_logging_internal",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_synchronization_kernel_timeout_internal") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/synchronization/internal/kernel_timeout.h",
+  ]
+  public_deps = [
+    ":absl_base_core_headers",
+    ":absl_base_raw_logging_internal",
+    ":absl_time_time",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_synchronization_synchronization") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/synchronization/barrier.cc",
+    "src/third_party/abseil-cpp/absl/synchronization/barrier.h",
+    "src/third_party/abseil-cpp/absl/synchronization/blocking_counter.cc",
+    "src/third_party/abseil-cpp/absl/synchronization/blocking_counter.h",
+    "src/third_party/abseil-cpp/absl/synchronization/internal/create_thread_identity.cc",
+    "src/third_party/abseil-cpp/absl/synchronization/internal/create_thread_identity.h",
+    "src/third_party/abseil-cpp/absl/synchronization/internal/futex.h",
+    "src/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem.cc",
+    "src/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem.h",
+    "src/third_party/abseil-cpp/absl/synchronization/internal/waiter.cc",
+    "src/third_party/abseil-cpp/absl/synchronization/internal/waiter.h",
+    "src/third_party/abseil-cpp/absl/synchronization/mutex.cc",
+    "src/third_party/abseil-cpp/absl/synchronization/mutex.h",
+    "src/third_party/abseil-cpp/absl/synchronization/notification.cc",
+    "src/third_party/abseil-cpp/absl/synchronization/notification.h",
+  ]
+  public_deps = [
+    ":absl_base_atomic_hook",
+    ":absl_base_base",
+    ":absl_base_base_internal",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_dynamic_annotations",
+    ":absl_base_malloc_internal",
+    ":absl_base_raw_logging_internal",
+    ":absl_debugging_stacktrace",
+    ":absl_debugging_symbolize",
+    ":absl_synchronization_graphcycles_internal",
+    ":absl_synchronization_kernel_timeout_internal",
+    ":absl_time_time",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_time_internal_cctz_civil_time") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/civil_time.h",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/civil_time_detail.h",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/civil_time_detail.cc",
+  ]
+  public_deps = [ ":absl_base_config" ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_time_internal_cctz_time_zone") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/time_zone.h",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/zone_info_source.h",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.h",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.cc",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.h",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.h",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.h",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.h",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_posix.cc",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_posix.h",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/tzfile.h",
+    "src/third_party/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_time_internal_cctz_civil_time",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_time_time") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/time/civil_time.cc",
+    "src/third_party/abseil-cpp/absl/time/civil_time.h",
+    "src/third_party/abseil-cpp/absl/time/clock.cc",
+    "src/third_party/abseil-cpp/absl/time/clock.h",
+    "src/third_party/abseil-cpp/absl/time/duration.cc",
+    "src/third_party/abseil-cpp/absl/time/format.cc",
+    "src/third_party/abseil-cpp/absl/time/internal/get_current_time_chrono.inc",
+    "src/third_party/abseil-cpp/absl/time/internal/get_current_time_posix.inc",
+    "src/third_party/abseil-cpp/absl/time/time.cc",
+    "src/third_party/abseil-cpp/absl/time/time.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_raw_logging_internal",
+    ":absl_numeric_int128",
+    ":absl_strings_strings",
+    ":absl_time_internal_cctz_civil_time",
+    ":absl_time_internal_cctz_time_zone",
+    ":absl_types_optional",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_types_any") {
+  sources = [ "src/third_party/abseil-cpp/absl/types/any.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_base_fast_type_id",
+    ":absl_meta_type_traits",
+    ":absl_types_bad_any_cast",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_types_bad_any_cast") {
+  sources = [ "src/third_party/abseil-cpp/absl/types/bad_any_cast.h" ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_types_bad_any_cast_impl",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_types_bad_any_cast_impl") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/types/bad_any_cast.cc",
+    "src/third_party/abseil-cpp/absl/types/bad_any_cast.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_raw_logging_internal",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_types_bad_optional_access") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/types/bad_optional_access.cc",
+    "src/third_party/abseil-cpp/absl/types/bad_optional_access.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_raw_logging_internal",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_types_bad_variant_access") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/types/bad_variant_access.cc",
+    "src/third_party/abseil-cpp/absl/types/bad_variant_access.h",
+  ]
+  public_deps = [
+    ":absl_base_config",
+    ":absl_base_raw_logging_internal",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_types_compare") {
+  sources = [ "src/third_party/abseil-cpp/absl/types/compare.h" ]
+  public_deps = [
+    ":absl_base_core_headers",
+    ":absl_meta_type_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_types_optional") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/types/internal/optional.h",
+    "src/third_party/abseil-cpp/absl/types/optional.h",
+  ]
+  public_deps = [
+    ":absl_base_base_internal",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_memory_memory",
+    ":absl_meta_type_traits",
+    ":absl_types_bad_optional_access",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_types_span") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/types/internal/span.h",
+    "src/third_party/abseil-cpp/absl/types/span.h",
+  ]
+  public_deps = [
+    ":absl_algorithm_algorithm",
+    ":absl_base_core_headers",
+    ":absl_base_throw_delegate",
+    ":absl_meta_type_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_types_variant") {
+  sources = [
+    "src/third_party/abseil-cpp/absl/types/internal/variant.h",
+    "src/third_party/abseil-cpp/absl/types/variant.h",
+  ]
+  public_deps = [
+    ":absl_base_base_internal",
+    ":absl_base_config",
+    ":absl_base_core_headers",
+    ":absl_meta_type_traits",
+    ":absl_types_bad_variant_access",
+    ":absl_utility_utility",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("absl_utility_utility") {
+  sources = [ "src/third_party/abseil-cpp/absl/utility/utility.h" ]
+  public_deps = [
+    ":absl_base_base_internal",
+    ":absl_base_config",
+    ":absl_meta_type_traits",
+  ]
+  public_configs = [ "..:grpc_absl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+static_library("upb") {
+  sources = [
+    "src/src/core/ext/upb-generated/google/protobuf/descriptor.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/descriptor.upb.h",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/descriptor.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/descriptor.upbdefs.h",
+    "src/third_party/upb/third_party/utf8_range/naive.c",
+    "src/third_party/upb/third_party/utf8_range/range2-neon.c",
+    "src/third_party/upb/third_party/utf8_range/range2-sse.c",
+    "src/third_party/upb/third_party/utf8_range/utf8_range.h",
+    "src/third_party/upb/upb/arena.c",
+    "src/third_party/upb/upb/arena.h",
+    "src/third_party/upb/upb/array.c",
+    "src/third_party/upb/upb/array.h",
+    "src/third_party/upb/upb/collections.h",
+    "src/third_party/upb/upb/decode.c",
+    "src/third_party/upb/upb/decode.h",
+    "src/third_party/upb/upb/decode_fast.c",
+    "src/third_party/upb/upb/decode_fast.h",
+    "src/third_party/upb/upb/def.c",
+    "src/third_party/upb/upb/def.h",
+    "src/third_party/upb/upb/def.hpp",
+    "src/third_party/upb/upb/encode.c",
+    "src/third_party/upb/upb/encode.h",
+    "src/third_party/upb/upb/extension_registry.c",
+    "src/third_party/upb/upb/extension_registry.h",
+    "src/third_party/upb/upb/internal/decode.h",
+    "src/third_party/upb/upb/internal/table.h",
+    "src/third_party/upb/upb/internal/upb.h",
+    "src/third_party/upb/upb/internal/vsnprintf_compat.h",
+    "src/third_party/upb/upb/json_decode.c",
+    "src/third_party/upb/upb/json_decode.h",
+    "src/third_party/upb/upb/json_encode.c",
+    "src/third_party/upb/upb/json_encode.h",
+    "src/third_party/upb/upb/map.c",
+    "src/third_party/upb/upb/map.h",
+    "src/third_party/upb/upb/message_value.h",
+    "src/third_party/upb/upb/mini_table.c",
+    "src/third_party/upb/upb/mini_table.h",
+    "src/third_party/upb/upb/mini_table.hpp",
+    "src/third_party/upb/upb/msg.c",
+    "src/third_party/upb/upb/msg.h",
+    "src/third_party/upb/upb/msg_internal.h",
+    "src/third_party/upb/upb/port_def.inc",
+    "src/third_party/upb/upb/port_undef.inc",
+    "src/third_party/upb/upb/reflection.c",
+    "src/third_party/upb/upb/reflection.h",
+    "src/third_party/upb/upb/reflection.hpp",
+    "src/third_party/upb/upb/status.c",
+    "src/third_party/upb/upb/status.h",
+    "src/third_party/upb/upb/table.c",
+    "src/third_party/upb/upb/table_internal.h",
+    "src/third_party/upb/upb/text_encode.c",
+    "src/third_party/upb/upb/text_encode.h",
+    "src/third_party/upb/upb/upb.c",
+    "src/third_party/upb/upb/upb.h",
+    "src/third_party/upb/upb/upb.hpp",
+  ]
+  public_deps = []
+  public_configs = [ "..:grpc_upb_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+static_library("re2") {
+  sources = [
+    "src/third_party/re2/re2/bitmap256.h",
+    "src/third_party/re2/re2/bitstate.cc",
+    "src/third_party/re2/re2/compile.cc",
+    "src/third_party/re2/re2/dfa.cc",
+    "src/third_party/re2/re2/filtered_re2.cc",
+    "src/third_party/re2/re2/filtered_re2.h",
+    "src/third_party/re2/re2/mimics_pcre.cc",
+    "src/third_party/re2/re2/nfa.cc",
+    "src/third_party/re2/re2/onepass.cc",
+    "src/third_party/re2/re2/parse.cc",
+    "src/third_party/re2/re2/perl_groups.cc",
+    "src/third_party/re2/re2/pod_array.h",
+    "src/third_party/re2/re2/prefilter.cc",
+    "src/third_party/re2/re2/prefilter.h",
+    "src/third_party/re2/re2/prefilter_tree.cc",
+    "src/third_party/re2/re2/prefilter_tree.h",
+    "src/third_party/re2/re2/prog.cc",
+    "src/third_party/re2/re2/prog.h",
+    "src/third_party/re2/re2/re2.cc",
+    "src/third_party/re2/re2/re2.h",
+    "src/third_party/re2/re2/regexp.cc",
+    "src/third_party/re2/re2/regexp.h",
+    "src/third_party/re2/re2/set.cc",
+    "src/third_party/re2/re2/set.h",
+    "src/third_party/re2/re2/simplify.cc",
+    "src/third_party/re2/re2/sparse_array.h",
+    "src/third_party/re2/re2/sparse_set.h",
+    "src/third_party/re2/re2/stringpiece.cc",
+    "src/third_party/re2/re2/stringpiece.h",
+    "src/third_party/re2/re2/tostring.cc",
+    "src/third_party/re2/re2/unicode_casefold.cc",
+    "src/third_party/re2/re2/unicode_casefold.h",
+    "src/third_party/re2/re2/unicode_groups.cc",
+    "src/third_party/re2/re2/unicode_groups.h",
+    "src/third_party/re2/re2/walker-inl.h",
+    "src/third_party/re2/util/benchmark.h",
+    "src/third_party/re2/util/flags.h",
+    "src/third_party/re2/util/logging.h",
+    "src/third_party/re2/util/malloc_counter.h",
+    "src/third_party/re2/util/mix.h",
+    "src/third_party/re2/util/mutex.h",
+    "src/third_party/re2/util/pcre.cc",
+    "src/third_party/re2/util/pcre.h",
+    "src/third_party/re2/util/rune.cc",
+    "src/third_party/re2/util/strutil.cc",
+    "src/third_party/re2/util/strutil.h",
+    "src/third_party/re2/util/test.h",
+    "src/third_party/re2/util/utf.h",
+    "src/third_party/re2/util/util.h",
+  ]
+  public_deps = []
+  public_configs = [ "..:grpc_re2_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+static_library("boringssl") {
+  sources = [
+    "src/third_party/boringssl-with-bazel/err_data.c",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/chacha/chacha-x86_64.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/aesni-x86_64.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/ghash-x86_64.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/md5-x86_64.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/p256-x86_64-asm.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/rdrand-x86_64.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/rsaz-avx2.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/sha1-x86_64.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/sha256-x86_64.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/sha512-x86_64.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/vpaes-x86_64.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/x86_64-mont.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/fipsmodule/x86_64-mont5.S",
+    "src/third_party/boringssl-with-bazel/linux-x86_64/crypto/test/trampoline-x86_64.S",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_bitstr.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_bool.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_d2i_fp.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_dup.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_enum.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_gentm.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_i2d_fp.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_int.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_mbstr.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_object.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_octet.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_print.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_strex.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_strnid.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_time.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_type.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_utctm.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/a_utf8.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/asn1_lib.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/asn1_par.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/asn_pack.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/charmap.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/f_int.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/f_string.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/tasn_dec.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/tasn_enc.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/tasn_fre.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/tasn_new.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/tasn_typ.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/tasn_utl.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/asn1/time_support.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/base64/base64.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bio/bio.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bio/bio_mem.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bio/connect.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bio/fd.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bio/file.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bio/hexdump.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bio/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/bio/pair.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bio/printf.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bio/socket.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bio/socket_helper.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/blake2/blake2.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bn_extra/bn_asn1.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bn_extra/convert.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/buf/buf.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bytestring/asn1_compat.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bytestring/ber.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bytestring/cbb.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bytestring/cbs.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/bytestring/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/bytestring/unicode.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/chacha/chacha.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/chacha/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/cipher_extra/cipher_extra.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cipher_extra/derive_key.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cipher_extra/e_aesccm.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cipher_extra/e_aesctrhmac.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cipher_extra/e_aesgcmsiv.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cipher_extra/e_chacha20poly1305.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cipher_extra/e_null.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cipher_extra/e_rc2.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cipher_extra/e_rc4.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cipher_extra/e_tls.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cipher_extra/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/cipher_extra/tls_cbc.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cmac/cmac.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/conf/conf.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/conf/conf_def.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/conf/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/cpu-aarch64-fuchsia.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cpu-aarch64-linux.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cpu-aarch64-win.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cpu-arm-linux.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cpu-arm-linux.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/cpu-arm.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cpu-intel.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/cpu-ppc64le.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/crypto.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/curve25519/curve25519.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/curve25519/curve25519_tables.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/curve25519/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/curve25519/spake25519.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/dh_extra/dh_asn1.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/dh_extra/params.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/digest_extra/digest_extra.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/dsa/dsa.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/dsa/dsa_asn1.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/dsa/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/ec_extra/ec_asn1.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/ec_extra/ec_derive.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/ec_extra/hash_to_curve.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/ec_extra/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/ecdh_extra/ecdh_extra.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/ecdsa_extra/ecdsa_asn1.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/engine/engine.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/err/err.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/err/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/digestsign.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/evp.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/evp_asn1.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/evp_ctx.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/p_dsa_asn1.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/p_ec.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/p_ec_asn1.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/p_ed25519.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/p_ed25519_asn1.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/p_rsa.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/p_rsa_asn1.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/p_x25519.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/p_x25519_asn1.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/pbkdf.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/print.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/scrypt.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/evp/sign.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/ex_data.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/aes/aes.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/aes/aes_nohw.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/aes/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/aes/key_wrap.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/aes/mode_wrappers.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bcm.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/add.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/asm/x86_64-gcc.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/bn.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/bytes.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/cmp.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/ctx.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/div.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/div_extra.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/exponentiation.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/gcd.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/gcd_extra.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/generic.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/jacobi.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/montgomery.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/montgomery_inv.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/mul.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/prime.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/random.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/rsaz_exp.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/rsaz_exp.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/shift.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/bn/sqrt.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/cipher/aead.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/cipher/cipher.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/cipher/e_aes.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/cipher/e_des.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/cipher/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/delocate.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/des/des.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/des/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/dh/check.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/dh/dh.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/digest/digest.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/digest/digests.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/digest/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/digest/md32_common.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/ec.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/ec_key.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/ec_montgomery.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/felem.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/oct.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/p224-64.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/p256-x86_64-table.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/p256-x86_64.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/p256-x86_64.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/p256.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/p256_table.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/scalar.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/simple.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/simple_mul.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/util.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/wnaf.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ecdh/ecdh.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ecdsa/ecdsa.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/ecdsa/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/fips_shared_support.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/hmac/hmac.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/md4/md4.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/md5/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/md5/md5.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/modes/cbc.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/modes/cfb.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/modes/ctr.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/modes/gcm.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/modes/gcm_nohw.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/modes/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/modes/ofb.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/modes/polyval.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/rand/ctrdrbg.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/rand/fork_detect.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/rand/fork_detect.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/rand/getrandom_fillin.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/rand/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/rand/rand.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/rand/urandom.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/rsa/blinding.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/rsa/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/rsa/padding.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/rsa/rsa.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/rsa/rsa_impl.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/self_check/fips.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/self_check/self_check.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/sha/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/sha/sha1-altivec.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/sha/sha1.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/sha/sha256.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/sha/sha512.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/tls/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/fipsmodule/tls/kdf.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/hkdf/hkdf.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/hpke/hpke.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/hrss/asm/poly_rq_mul.S",
+    "src/third_party/boringssl-with-bazel/src/crypto/hrss/hrss.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/hrss/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/lhash/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/lhash/lhash.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/mem.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/obj/obj.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/obj/obj_dat.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/obj/obj_xref.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/pem/pem_all.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/pem/pem_info.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/pem/pem_lib.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/pem/pem_oth.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/pem/pem_pk8.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/pem/pem_pkey.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/pem/pem_x509.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/pem/pem_xaux.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/pkcs7/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/pkcs7/pkcs7.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/pkcs7/pkcs7_x509.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/pkcs8/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/pkcs8/p5_pbev2.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/pkcs8/pkcs8.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/pkcs8/pkcs8_x509.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/poly1305/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/poly1305/poly1305.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/poly1305/poly1305_arm.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/poly1305/poly1305_vec.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/pool/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/pool/pool.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/rand_extra/deterministic.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/rand_extra/forkunsafe.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/rand_extra/fuchsia.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/rand_extra/passive.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/rand_extra/rand_extra.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/rand_extra/windows.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/rc4/rc4.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/refcount_c11.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/refcount_lock.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/rsa_extra/rsa_asn1.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/rsa_extra/rsa_print.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/siphash/siphash.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/stack/stack.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/thread.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/thread_none.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/thread_pthread.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/thread_win.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/trust_token/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/trust_token/pmbtoken.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/trust_token/trust_token.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/trust_token/voprf.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/a_digest.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/a_sign.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/a_verify.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/algorithm.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/asn1_gen.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/by_dir.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/by_file.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/i2d_pr.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/name_print.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/rsa_pss.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/t_crl.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/t_req.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/t_x509.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/t_x509a.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509_att.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509_cmp.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509_d2.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509_def.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509_ext.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509_lu.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509_obj.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509_req.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509_set.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509_trs.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509_txt.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509_v3.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509_vfy.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509_vpm.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509cset.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509name.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509rset.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x509spki.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_algor.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_all.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_attrib.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_crl.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_exten.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_info.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_name.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_pkey.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_pubkey.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_req.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_sig.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_spki.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_val.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_x509.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509/x_x509a.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/ext_dat.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/internal.h",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/pcy_cache.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/pcy_data.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/pcy_lib.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/pcy_map.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/pcy_node.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/pcy_tree.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_akey.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_akeya.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_alt.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_bcons.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_bitst.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_conf.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_cpols.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_crld.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_enum.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_extku.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_genn.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_ia5.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_info.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_int.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_lib.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_ncons.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_ocsp.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_pci.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_pcia.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_pcons.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_pmaps.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_prn.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_purp.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_skey.c",
+    "src/third_party/boringssl-with-bazel/src/crypto/x509v3/v3_utl.c",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/aead.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/aes.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/arm_arch.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/asn1.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/asn1_mac.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/asn1t.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/base.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/base64.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/bio.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/blake2.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/blowfish.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/bn.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/buf.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/buffer.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/bytestring.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/cast.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/chacha.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/cipher.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/cmac.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/conf.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/cpu.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/crypto.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/curve25519.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/des.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/dh.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/digest.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/dsa.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/dtls1.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/e_os2.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/ec.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/ec_key.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/ecdh.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/ecdsa.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/engine.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/err.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/evp.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/evp_errors.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/ex_data.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/hkdf.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/hmac.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/hpke.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/hrss.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/is_boringssl.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/lhash.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/md4.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/md5.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/mem.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/nid.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/obj.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/obj_mac.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/objects.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/opensslconf.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/opensslv.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/ossl_typ.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/pem.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/pkcs12.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/pkcs7.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/pkcs8.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/poly1305.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/pool.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/rand.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/rc4.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/ripemd.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/rsa.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/safestack.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/sha.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/siphash.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/span.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/srtp.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/ssl.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/ssl3.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/stack.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/thread.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/tls1.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/trust_token.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/type_check.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/x509.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/x509_vfy.h",
+    "src/third_party/boringssl-with-bazel/src/include/openssl/x509v3.h",
+    "src/third_party/boringssl-with-bazel/src/ssl/bio_ssl.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/d1_both.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/d1_lib.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/d1_pkt.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/d1_srtp.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/dtls_method.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/dtls_record.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/encrypted_client_hello.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/extensions.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/handoff.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/handshake.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/handshake_client.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/handshake_server.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/internal.h",
+    "src/third_party/boringssl-with-bazel/src/ssl/s3_both.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/s3_lib.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/s3_pkt.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/ssl_aead_ctx.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/ssl_asn1.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/ssl_buffer.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/ssl_cert.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/ssl_cipher.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/ssl_file.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/ssl_key_share.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/ssl_lib.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/ssl_privkey.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/ssl_session.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/ssl_stat.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/ssl_transcript.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/ssl_versions.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/ssl_x509.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/t1_enc.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/tls13_both.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/tls13_client.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/tls13_enc.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/tls13_server.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/tls_method.cc",
+    "src/third_party/boringssl-with-bazel/src/ssl/tls_record.cc",
+    "src/third_party/boringssl-with-bazel/src/third_party/fiat/curve25519_32.h",
+    "src/third_party/boringssl-with-bazel/src/third_party/fiat/curve25519_64.h",
+    "src/third_party/boringssl-with-bazel/src/third_party/fiat/p256_32.h",
+    "src/third_party/boringssl-with-bazel/src/third_party/fiat/p256_64.h",
+  ]
+  public_deps = []
+  public_configs = [ "..:grpc_boringssl_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("address_sorting") {
+  sources = [
+    "src/third_party/address_sorting/address_sorting.c",
+    "src/third_party/address_sorting/address_sorting_internal.h",
+    "src/third_party/address_sorting/address_sorting_posix.c",
+    "src/third_party/address_sorting/address_sorting_windows.c",
+    "src/third_party/address_sorting/include/address_sorting/address_sorting.h",
+  ]
+  public_deps = []
+  public_configs = [ "..:grpc_internal_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("gpr") {
+  sources = [
+    "src/src/core/lib/event_engine/thread_local.cc",
+    "src/src/core/lib/event_engine/thread_local.h",
+    "src/src/core/lib/gpr/alloc.cc",
+    "src/src/core/lib/gpr/alloc.h",
+    "src/src/core/lib/gpr/atm.cc",
+    "src/src/core/lib/gpr/cpu_iphone.cc",
+    "src/src/core/lib/gpr/cpu_linux.cc",
+    "src/src/core/lib/gpr/cpu_posix.cc",
+    "src/src/core/lib/gpr/cpu_windows.cc",
+    "src/src/core/lib/gpr/log.cc",
+    "src/src/core/lib/gpr/log_android.cc",
+    "src/src/core/lib/gpr/log_linux.cc",
+    "src/src/core/lib/gpr/log_posix.cc",
+    "src/src/core/lib/gpr/log_windows.cc",
+    "src/src/core/lib/gpr/string.cc",
+    "src/src/core/lib/gpr/string.h",
+    "src/src/core/lib/gpr/string_posix.cc",
+    "src/src/core/lib/gpr/string_util_windows.cc",
+    "src/src/core/lib/gpr/string_windows.cc",
+    "src/src/core/lib/gpr/sync.cc",
+    "src/src/core/lib/gpr/sync_abseil.cc",
+    "src/src/core/lib/gpr/sync_posix.cc",
+    "src/src/core/lib/gpr/sync_windows.cc",
+    "src/src/core/lib/gpr/time.cc",
+    "src/src/core/lib/gpr/time_posix.cc",
+    "src/src/core/lib/gpr/time_precise.cc",
+    "src/src/core/lib/gpr/time_precise.h",
+    "src/src/core/lib/gpr/time_windows.cc",
+    "src/src/core/lib/gpr/tmpfile.h",
+    "src/src/core/lib/gpr/tmpfile_msys.cc",
+    "src/src/core/lib/gpr/tmpfile_posix.cc",
+    "src/src/core/lib/gpr/tmpfile_windows.cc",
+    "src/src/core/lib/gpr/useful.h",
+    "src/src/core/lib/gpr/wrap_memcpy.cc",
+    "src/src/core/lib/gprpp/construct_destruct.h",
+    "src/src/core/lib/gprpp/crash.cc",
+    "src/src/core/lib/gprpp/crash.h",
+    "src/src/core/lib/gprpp/debug_location.h",
+    "src/src/core/lib/gprpp/env.h",
+    "src/src/core/lib/gprpp/env_linux.cc",
+    "src/src/core/lib/gprpp/env_posix.cc",
+    "src/src/core/lib/gprpp/env_windows.cc",
+    "src/src/core/lib/gprpp/examine_stack.cc",
+    "src/src/core/lib/gprpp/examine_stack.h",
+    "src/src/core/lib/gprpp/fork.cc",
+    "src/src/core/lib/gprpp/fork.h",
+    "src/src/core/lib/gprpp/global_config.h",
+    "src/src/core/lib/gprpp/global_config_custom.h",
+    "src/src/core/lib/gprpp/global_config_env.cc",
+    "src/src/core/lib/gprpp/global_config_env.h",
+    "src/src/core/lib/gprpp/global_config_generic.h",
+    "src/src/core/lib/gprpp/host_port.cc",
+    "src/src/core/lib/gprpp/host_port.h",
+    "src/src/core/lib/gprpp/memory.h",
+    "src/src/core/lib/gprpp/mpscq.cc",
+    "src/src/core/lib/gprpp/mpscq.h",
+    "src/src/core/lib/gprpp/no_destruct.h",
+    "src/src/core/lib/gprpp/stat.h",
+    "src/src/core/lib/gprpp/stat_posix.cc",
+    "src/src/core/lib/gprpp/stat_windows.cc",
+    "src/src/core/lib/gprpp/strerror.cc",
+    "src/src/core/lib/gprpp/strerror.h",
+    "src/src/core/lib/gprpp/sync.h",
+    "src/src/core/lib/gprpp/tchar.cc",
+    "src/src/core/lib/gprpp/tchar.h",
+    "src/src/core/lib/gprpp/thd.h",
+    "src/src/core/lib/gprpp/thd_posix.cc",
+    "src/src/core/lib/gprpp/thd_windows.cc",
+    "src/src/core/lib/gprpp/time_util.cc",
+    "src/src/core/lib/gprpp/time_util.h",
+  ]
+  public_deps = [
+    ":absl_base_base",
+    ":absl_base_core_headers",
+    ":absl_memory_memory",
+    ":absl_random_random",
+    ":absl_status_status",
+    ":absl_strings_cord",
+    ":absl_strings_str_format",
+    ":absl_strings_strings",
+    ":absl_synchronization_synchronization",
+    ":absl_time_time",
+    ":absl_types_optional",
+    ":absl_types_variant",
+  ]
+  public_configs = [ "..:grpc_internal_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("grpc") {
+  sources = [
+    "src/src/core/ext/filters/census/grpc_context.cc",
+    "src/src/core/ext/filters/channel_idle/channel_idle_filter.cc",
+    "src/src/core/ext/filters/channel_idle/channel_idle_filter.h",
+    "src/src/core/ext/filters/channel_idle/idle_filter_state.cc",
+    "src/src/core/ext/filters/channel_idle/idle_filter_state.h",
+    "src/src/core/ext/filters/client_channel/backend_metric.cc",
+    "src/src/core/ext/filters/client_channel/backend_metric.h",
+    "src/src/core/ext/filters/client_channel/backup_poller.cc",
+    "src/src/core/ext/filters/client_channel/backup_poller.h",
+    "src/src/core/ext/filters/client_channel/channel_connectivity.cc",
+    "src/src/core/ext/filters/client_channel/client_channel.cc",
+    "src/src/core/ext/filters/client_channel/client_channel.h",
+    "src/src/core/ext/filters/client_channel/client_channel_channelz.cc",
+    "src/src/core/ext/filters/client_channel/client_channel_channelz.h",
+    "src/src/core/ext/filters/client_channel/client_channel_factory.cc",
+    "src/src/core/ext/filters/client_channel/client_channel_factory.h",
+    "src/src/core/ext/filters/client_channel/client_channel_plugin.cc",
+    "src/src/core/ext/filters/client_channel/client_channel_service_config.cc",
+    "src/src/core/ext/filters/client_channel/client_channel_service_config.h",
+    "src/src/core/ext/filters/client_channel/config_selector.cc",
+    "src/src/core/ext/filters/client_channel/config_selector.h",
+    "src/src/core/ext/filters/client_channel/connector.h",
+    "src/src/core/ext/filters/client_channel/dynamic_filters.cc",
+    "src/src/core/ext/filters/client_channel/dynamic_filters.h",
+    "src/src/core/ext/filters/client_channel/global_subchannel_pool.cc",
+    "src/src/core/ext/filters/client_channel/global_subchannel_pool.h",
+    "src/src/core/ext/filters/client_channel/health/health_check_client.cc",
+    "src/src/core/ext/filters/client_channel/health/health_check_client.h",
+    "src/src/core/ext/filters/client_channel/http_proxy.cc",
+    "src/src/core/ext/filters/client_channel/http_proxy.h",
+    "src/src/core/ext/filters/client_channel/lb_call_state_internal.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/address_filtering.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/address_filtering.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/backend_metric_data.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/child_policy_handler.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/child_policy_handler.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_balancer_addresses.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_balancer_addresses.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/oob_backend_metric.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/oob_backend_metric.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/oob_backend_metric_internal.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/outlier_detection/outlier_detection.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/outlier_detection/outlier_detection.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/priority/priority.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/rls/rls.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/weighted_round_robin/static_stride_scheduler.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/weighted_round_robin/static_stride_scheduler.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/weighted_round_robin/weighted_round_robin.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/weighted_target/weighted_target.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/xds/cds.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/xds/xds_attributes.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/xds/xds_attributes.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_args.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_impl.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_manager.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_resolver.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/xds/xds_override_host.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/xds/xds_override_host.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/xds/xds_wrr_locality.cc",
+    "src/src/core/ext/filters/client_channel/local_subchannel_pool.cc",
+    "src/src/core/ext/filters/client_channel/local_subchannel_pool.h",
+    "src/src/core/ext/filters/client_channel/resolver/binder/binder_resolver.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h",
+    "src/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc",
+    "src/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc",
+    "src/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h",
+    "src/src/core/ext/filters/client_channel/resolver/google_c2p/google_c2p_resolver.cc",
+    "src/src/core/ext/filters/client_channel/resolver/polling_resolver.cc",
+    "src/src/core/ext/filters/client_channel/resolver/polling_resolver.h",
+    "src/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc",
+    "src/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc",
+    "src/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.h",
+    "src/src/core/ext/filters/client_channel/retry_filter.cc",
+    "src/src/core/ext/filters/client_channel/retry_filter.h",
+    "src/src/core/ext/filters/client_channel/retry_service_config.cc",
+    "src/src/core/ext/filters/client_channel/retry_service_config.h",
+    "src/src/core/ext/filters/client_channel/retry_throttle.cc",
+    "src/src/core/ext/filters/client_channel/retry_throttle.h",
+    "src/src/core/ext/filters/client_channel/service_config_channel_arg_filter.cc",
+    "src/src/core/ext/filters/client_channel/subchannel.cc",
+    "src/src/core/ext/filters/client_channel/subchannel.h",
+    "src/src/core/ext/filters/client_channel/subchannel_interface_internal.h",
+    "src/src/core/ext/filters/client_channel/subchannel_pool_interface.cc",
+    "src/src/core/ext/filters/client_channel/subchannel_pool_interface.h",
+    "src/src/core/ext/filters/client_channel/subchannel_stream_client.cc",
+    "src/src/core/ext/filters/client_channel/subchannel_stream_client.h",
+    "src/src/core/ext/filters/deadline/deadline_filter.cc",
+    "src/src/core/ext/filters/deadline/deadline_filter.h",
+    "src/src/core/ext/filters/fault_injection/fault_injection_filter.cc",
+    "src/src/core/ext/filters/fault_injection/fault_injection_filter.h",
+    "src/src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc",
+    "src/src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h",
+    "src/src/core/ext/filters/http/client/http_client_filter.cc",
+    "src/src/core/ext/filters/http/client/http_client_filter.h",
+    "src/src/core/ext/filters/http/client_authority_filter.cc",
+    "src/src/core/ext/filters/http/client_authority_filter.h",
+    "src/src/core/ext/filters/http/http_filters_plugin.cc",
+    "src/src/core/ext/filters/http/message_compress/compression_filter.cc",
+    "src/src/core/ext/filters/http/message_compress/compression_filter.h",
+    "src/src/core/ext/filters/http/server/http_server_filter.cc",
+    "src/src/core/ext/filters/http/server/http_server_filter.h",
+    "src/src/core/ext/filters/message_size/message_size_filter.cc",
+    "src/src/core/ext/filters/message_size/message_size_filter.h",
+    "src/src/core/ext/filters/rbac/rbac_filter.cc",
+    "src/src/core/ext/filters/rbac/rbac_filter.h",
+    "src/src/core/ext/filters/rbac/rbac_service_config_parser.cc",
+    "src/src/core/ext/filters/rbac/rbac_service_config_parser.h",
+    "src/src/core/ext/filters/server_config_selector/server_config_selector.h",
+    "src/src/core/ext/filters/server_config_selector/server_config_selector_filter.cc",
+    "src/src/core/ext/filters/server_config_selector/server_config_selector_filter.h",
+    "src/src/core/ext/filters/stateful_session/stateful_session_filter.cc",
+    "src/src/core/ext/filters/stateful_session/stateful_session_filter.h",
+    "src/src/core/ext/filters/stateful_session/stateful_session_service_config_parser.cc",
+    "src/src/core/ext/filters/stateful_session/stateful_session_service_config_parser.h",
+    "src/src/core/ext/transport/chttp2/alpn/alpn.cc",
+    "src/src/core/ext/transport/chttp2/alpn/alpn.h",
+    "src/src/core/ext/transport/chttp2/client/chttp2_connector.cc",
+    "src/src/core/ext/transport/chttp2/client/chttp2_connector.h",
+    "src/src/core/ext/transport/chttp2/server/chttp2_server.cc",
+    "src/src/core/ext/transport/chttp2/server/chttp2_server.h",
+    "src/src/core/ext/transport/chttp2/transport/bin_decoder.cc",
+    "src/src/core/ext/transport/chttp2/transport/bin_decoder.h",
+    "src/src/core/ext/transport/chttp2/transport/bin_encoder.cc",
+    "src/src/core/ext/transport/chttp2/transport/bin_encoder.h",
+    "src/src/core/ext/transport/chttp2/transport/chttp2_transport.cc",
+    "src/src/core/ext/transport/chttp2/transport/chttp2_transport.h",
+    "src/src/core/ext/transport/chttp2/transport/context_list.cc",
+    "src/src/core/ext/transport/chttp2/transport/context_list.h",
+    "src/src/core/ext/transport/chttp2/transport/decode_huff.cc",
+    "src/src/core/ext/transport/chttp2/transport/decode_huff.h",
+    "src/src/core/ext/transport/chttp2/transport/flow_control.cc",
+    "src/src/core/ext/transport/chttp2/transport/flow_control.h",
+    "src/src/core/ext/transport/chttp2/transport/frame.h",
+    "src/src/core/ext/transport/chttp2/transport/frame_data.cc",
+    "src/src/core/ext/transport/chttp2/transport/frame_data.h",
+    "src/src/core/ext/transport/chttp2/transport/frame_goaway.cc",
+    "src/src/core/ext/transport/chttp2/transport/frame_goaway.h",
+    "src/src/core/ext/transport/chttp2/transport/frame_ping.cc",
+    "src/src/core/ext/transport/chttp2/transport/frame_ping.h",
+    "src/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc",
+    "src/src/core/ext/transport/chttp2/transport/frame_rst_stream.h",
+    "src/src/core/ext/transport/chttp2/transport/frame_settings.cc",
+    "src/src/core/ext/transport/chttp2/transport/frame_settings.h",
+    "src/src/core/ext/transport/chttp2/transport/frame_window_update.cc",
+    "src/src/core/ext/transport/chttp2/transport/frame_window_update.h",
+    "src/src/core/ext/transport/chttp2/transport/hpack_constants.h",
+    "src/src/core/ext/transport/chttp2/transport/hpack_encoder.cc",
+    "src/src/core/ext/transport/chttp2/transport/hpack_encoder.h",
+    "src/src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc",
+    "src/src/core/ext/transport/chttp2/transport/hpack_encoder_table.h",
+    "src/src/core/ext/transport/chttp2/transport/hpack_parser.cc",
+    "src/src/core/ext/transport/chttp2/transport/hpack_parser.h",
+    "src/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc",
+    "src/src/core/ext/transport/chttp2/transport/hpack_parser_table.h",
+    "src/src/core/ext/transport/chttp2/transport/http2_settings.cc",
+    "src/src/core/ext/transport/chttp2/transport/http2_settings.h",
+    "src/src/core/ext/transport/chttp2/transport/http_trace.cc",
+    "src/src/core/ext/transport/chttp2/transport/http_trace.h",
+    "src/src/core/ext/transport/chttp2/transport/huffsyms.cc",
+    "src/src/core/ext/transport/chttp2/transport/huffsyms.h",
+    "src/src/core/ext/transport/chttp2/transport/internal.h",
+    "src/src/core/ext/transport/chttp2/transport/parsing.cc",
+    "src/src/core/ext/transport/chttp2/transport/stream_lists.cc",
+    "src/src/core/ext/transport/chttp2/transport/stream_map.cc",
+    "src/src/core/ext/transport/chttp2/transport/stream_map.h",
+    "src/src/core/ext/transport/chttp2/transport/varint.cc",
+    "src/src/core/ext/transport/chttp2/transport/varint.h",
+    "src/src/core/ext/transport/chttp2/transport/writing.cc",
+    "src/src/core/ext/transport/inproc/inproc_plugin.cc",
+    "src/src/core/ext/transport/inproc/inproc_transport.cc",
+    "src/src/core/ext/transport/inproc/inproc_transport.h",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/certs.upb.c",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/certs.upb.h",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/clusters.upb.c",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/clusters.upb.h",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/config_dump.upb.c",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/config_dump.upb.h",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/config_dump_shared.upb.c",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/config_dump_shared.upb.h",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/init_dump.upb.c",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/init_dump.upb.h",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/listeners.upb.c",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/listeners.upb.h",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/memory.upb.c",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/memory.upb.h",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/metrics.upb.c",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/metrics.upb.h",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/mutex_stats.upb.c",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/mutex_stats.upb.h",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/server_info.upb.c",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/server_info.upb.h",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/tap.upb.c",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/tap.upb.h",
+    "src/src/core/ext/upb-generated/envoy/annotations/deprecation.upb.c",
+    "src/src/core/ext/upb-generated/envoy/annotations/deprecation.upb.h",
+    "src/src/core/ext/upb-generated/envoy/annotations/resource.upb.c",
+    "src/src/core/ext/upb-generated/envoy/annotations/resource.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/accesslog/v3/accesslog.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/accesslog/v3/accesslog.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/bootstrap/v3/bootstrap.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/bootstrap/v3/bootstrap.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/cluster/v3/circuit_breaker.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/cluster/v3/circuit_breaker.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/cluster/v3/cluster.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/cluster/v3/cluster.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/cluster/v3/filter.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/cluster/v3/filter.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/cluster/v3/outlier_detection.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/cluster/v3/outlier_detection.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/common/matcher/v3/matcher.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/common/matcher/v3/matcher.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/address.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/address.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/backoff.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/backoff.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/base.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/base.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/config_source.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/config_source.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/event_service_config.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/event_service_config.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/extension.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/extension.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/grpc_method_list.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/grpc_method_list.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/grpc_service.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/grpc_service.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/health_check.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/health_check.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/http_uri.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/http_uri.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/protocol.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/protocol.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/proxy_protocol.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/proxy_protocol.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/resolver.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/resolver.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/socket_option.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/socket_option.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/substitution_format_string.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/substitution_format_string.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/udp_socket_config.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/core/v3/udp_socket_config.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/endpoint/v3/endpoint.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/endpoint/v3/endpoint.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/endpoint/v3/endpoint_components.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/endpoint/v3/endpoint_components.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/endpoint/v3/load_report.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/endpoint/v3/load_report.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/listener/v3/api_listener.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/listener/v3/api_listener.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/listener/v3/listener.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/listener/v3/listener.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/listener/v3/listener_components.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/listener/v3/listener_components.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/listener/v3/quic_config.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/listener/v3/quic_config.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/listener/v3/udp_listener_config.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/listener/v3/udp_listener_config.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/metrics/v3/metrics_service.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/metrics/v3/metrics_service.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/metrics/v3/stats.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/metrics/v3/stats.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/overload/v3/overload.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/overload/v3/overload.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/rbac/v3/rbac.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/rbac/v3/rbac.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/route/v3/route.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/route/v3/route.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/route/v3/route_components.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/route/v3/route_components.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/route/v3/scoped_route.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/route/v3/scoped_route.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/tap/v3/common.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/tap/v3/common.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/datadog.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/datadog.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/dynamic_ot.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/dynamic_ot.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/http_tracer.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/http_tracer.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/lightstep.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/lightstep.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/opencensus.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/opencensus.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/opentelemetry.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/opentelemetry.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/service.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/service.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/skywalking.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/skywalking.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/trace.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/trace.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/xray.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/xray.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/zipkin.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/trace/v3/zipkin.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/clusters/aggregate/v3/cluster.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/clusters/aggregate/v3/cluster.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/common/fault/v3/fault.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/common/fault/v3/fault.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/fault/v3/fault.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/fault/v3/fault.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/rbac/v3/rbac.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/rbac/v3/rbac.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/router/v3/router.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/router/v3/router.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/stateful_session/v3/stateful_session.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/stateful_session/v3/stateful_session.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/http/stateful_session/cookie/v3/cookie.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/http/stateful_session/cookie/v3/cookie.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/load_balancing_policies/client_side_weighted_round_robin/v3/client_side_weighted_round_robin.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/load_balancing_policies/client_side_weighted_round_robin/v3/client_side_weighted_round_robin.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/load_balancing_policies/common/v3/common.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/load_balancing_policies/common/v3/common.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/load_balancing_policies/ring_hash/v3/ring_hash.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/load_balancing_policies/ring_hash/v3/ring_hash.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/load_balancing_policies/wrr_locality/v3/wrr_locality.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/load_balancing_policies/wrr_locality/v3/wrr_locality.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/cert.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/cert.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/common.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/common.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/secret.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/secret.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/tls.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/tls.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.upb.h",
+    "src/src/core/ext/upb-generated/envoy/service/discovery/v3/ads.upb.c",
+    "src/src/core/ext/upb-generated/envoy/service/discovery/v3/ads.upb.h",
+    "src/src/core/ext/upb-generated/envoy/service/discovery/v3/discovery.upb.c",
+    "src/src/core/ext/upb-generated/envoy/service/discovery/v3/discovery.upb.h",
+    "src/src/core/ext/upb-generated/envoy/service/load_stats/v3/lrs.upb.c",
+    "src/src/core/ext/upb-generated/envoy/service/load_stats/v3/lrs.upb.h",
+    "src/src/core/ext/upb-generated/envoy/service/status/v3/csds.upb.c",
+    "src/src/core/ext/upb-generated/envoy/service/status/v3/csds.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/http/v3/cookie.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/http/v3/cookie.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/http/v3/path_transformation.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/http/v3/path_transformation.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/filter_state.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/filter_state.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/http_inputs.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/http_inputs.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/metadata.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/metadata.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/node.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/node.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/number.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/number.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/path.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/path.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/regex.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/regex.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/status_code_input.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/status_code_input.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/string.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/string.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/struct.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/struct.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/value.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/value.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/metadata/v3/metadata.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/metadata/v3/metadata.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/tracing/v3/custom_tag.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/tracing/v3/custom_tag.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/v3/hash_policy.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/v3/hash_policy.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/v3/http.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/v3/http.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/v3/http_status.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/v3/http_status.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/v3/percent.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/v3/percent.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/v3/range.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/v3/range.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/v3/ratelimit_strategy.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/v3/ratelimit_strategy.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/v3/ratelimit_unit.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/v3/ratelimit_unit.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/v3/semantic_version.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/v3/semantic_version.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/v3/token_bucket.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/v3/token_bucket.upb.h",
+    "src/src/core/ext/upb-generated/google/api/annotations.upb.c",
+    "src/src/core/ext/upb-generated/google/api/annotations.upb.h",
+    "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/checked.upb.c",
+    "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/checked.upb.h",
+    "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/syntax.upb.c",
+    "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/syntax.upb.h",
+    "src/src/core/ext/upb-generated/google/api/http.upb.c",
+    "src/src/core/ext/upb-generated/google/api/http.upb.h",
+    "src/src/core/ext/upb-generated/google/api/httpbody.upb.c",
+    "src/src/core/ext/upb-generated/google/api/httpbody.upb.h",
+    "src/src/core/ext/upb-generated/google/protobuf/any.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/any.upb.h",
+    "src/src/core/ext/upb-generated/google/protobuf/descriptor.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/descriptor.upb.h",
+    "src/src/core/ext/upb-generated/google/protobuf/duration.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/duration.upb.h",
+    "src/src/core/ext/upb-generated/google/protobuf/empty.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/empty.upb.h",
+    "src/src/core/ext/upb-generated/google/protobuf/struct.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/struct.upb.h",
+    "src/src/core/ext/upb-generated/google/protobuf/timestamp.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/timestamp.upb.h",
+    "src/src/core/ext/upb-generated/google/protobuf/wrappers.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/wrappers.upb.h",
+    "src/src/core/ext/upb-generated/google/rpc/status.upb.c",
+    "src/src/core/ext/upb-generated/google/rpc/status.upb.h",
+    "src/src/core/ext/upb-generated/opencensus/proto/trace/v1/trace_config.upb.c",
+    "src/src/core/ext/upb-generated/opencensus/proto/trace/v1/trace_config.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/altscontext.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/altscontext.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/handshaker.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/handshaker.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/transport_security_common.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/transport_security_common.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/health/v1/health.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/health/v1/health.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/lb/v1/load_balancer.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/lb/v1/load_balancer.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/lookup/v1/rls.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/lookup/v1/rls.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/lookup/v1/rls_config.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/lookup/v1/rls_config.upb.h",
+    "src/src/core/ext/upb-generated/udpa/annotations/migrate.upb.c",
+    "src/src/core/ext/upb-generated/udpa/annotations/migrate.upb.h",
+    "src/src/core/ext/upb-generated/udpa/annotations/security.upb.c",
+    "src/src/core/ext/upb-generated/udpa/annotations/security.upb.h",
+    "src/src/core/ext/upb-generated/udpa/annotations/sensitive.upb.c",
+    "src/src/core/ext/upb-generated/udpa/annotations/sensitive.upb.h",
+    "src/src/core/ext/upb-generated/udpa/annotations/status.upb.c",
+    "src/src/core/ext/upb-generated/udpa/annotations/status.upb.h",
+    "src/src/core/ext/upb-generated/udpa/annotations/versioning.upb.c",
+    "src/src/core/ext/upb-generated/udpa/annotations/versioning.upb.h",
+    "src/src/core/ext/upb-generated/validate/validate.upb.c",
+    "src/src/core/ext/upb-generated/validate/validate.upb.h",
+    "src/src/core/ext/upb-generated/xds/annotations/v3/migrate.upb.c",
+    "src/src/core/ext/upb-generated/xds/annotations/v3/migrate.upb.h",
+    "src/src/core/ext/upb-generated/xds/annotations/v3/security.upb.c",
+    "src/src/core/ext/upb-generated/xds/annotations/v3/security.upb.h",
+    "src/src/core/ext/upb-generated/xds/annotations/v3/sensitive.upb.c",
+    "src/src/core/ext/upb-generated/xds/annotations/v3/sensitive.upb.h",
+    "src/src/core/ext/upb-generated/xds/annotations/v3/status.upb.c",
+    "src/src/core/ext/upb-generated/xds/annotations/v3/status.upb.h",
+    "src/src/core/ext/upb-generated/xds/annotations/v3/versioning.upb.c",
+    "src/src/core/ext/upb-generated/xds/annotations/v3/versioning.upb.h",
+    "src/src/core/ext/upb-generated/xds/core/v3/authority.upb.c",
+    "src/src/core/ext/upb-generated/xds/core/v3/authority.upb.h",
+    "src/src/core/ext/upb-generated/xds/core/v3/cidr.upb.c",
+    "src/src/core/ext/upb-generated/xds/core/v3/cidr.upb.h",
+    "src/src/core/ext/upb-generated/xds/core/v3/collection_entry.upb.c",
+    "src/src/core/ext/upb-generated/xds/core/v3/collection_entry.upb.h",
+    "src/src/core/ext/upb-generated/xds/core/v3/context_params.upb.c",
+    "src/src/core/ext/upb-generated/xds/core/v3/context_params.upb.h",
+    "src/src/core/ext/upb-generated/xds/core/v3/extension.upb.c",
+    "src/src/core/ext/upb-generated/xds/core/v3/extension.upb.h",
+    "src/src/core/ext/upb-generated/xds/core/v3/resource.upb.c",
+    "src/src/core/ext/upb-generated/xds/core/v3/resource.upb.h",
+    "src/src/core/ext/upb-generated/xds/core/v3/resource_locator.upb.c",
+    "src/src/core/ext/upb-generated/xds/core/v3/resource_locator.upb.h",
+    "src/src/core/ext/upb-generated/xds/core/v3/resource_name.upb.c",
+    "src/src/core/ext/upb-generated/xds/core/v3/resource_name.upb.h",
+    "src/src/core/ext/upb-generated/xds/data/orca/v3/orca_load_report.upb.c",
+    "src/src/core/ext/upb-generated/xds/data/orca/v3/orca_load_report.upb.h",
+    "src/src/core/ext/upb-generated/xds/service/orca/v3/orca.upb.c",
+    "src/src/core/ext/upb-generated/xds/service/orca/v3/orca.upb.h",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/cel.upb.c",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/cel.upb.h",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/domain.upb.c",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/domain.upb.h",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/http_inputs.upb.c",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/http_inputs.upb.h",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/ip.upb.c",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/ip.upb.h",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/matcher.upb.c",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/matcher.upb.h",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/range.upb.c",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/range.upb.h",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/regex.upb.c",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/regex.upb.h",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/string.upb.c",
+    "src/src/core/ext/upb-generated/xds/type/matcher/v3/string.upb.h",
+    "src/src/core/ext/upb-generated/xds/type/v3/cel.upb.c",
+    "src/src/core/ext/upb-generated/xds/type/v3/cel.upb.h",
+    "src/src/core/ext/upb-generated/xds/type/v3/range.upb.c",
+    "src/src/core/ext/upb-generated/xds/type/v3/range.upb.h",
+    "src/src/core/ext/upb-generated/xds/type/v3/typed_struct.upb.c",
+    "src/src/core/ext/upb-generated/xds/type/v3/typed_struct.upb.h",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/certs.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/certs.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/clusters.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/clusters.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/config_dump.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/config_dump.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/config_dump_shared.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/config_dump_shared.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/init_dump.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/init_dump.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/listeners.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/listeners.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/memory.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/memory.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/metrics.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/metrics.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/mutex_stats.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/mutex_stats.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/server_info.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/server_info.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/tap.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/tap.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/annotations/deprecation.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/annotations/deprecation.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/annotations/resource.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/annotations/resource.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/accesslog/v3/accesslog.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/accesslog/v3/accesslog.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/bootstrap/v3/bootstrap.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/bootstrap/v3/bootstrap.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/circuit_breaker.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/circuit_breaker.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/cluster.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/cluster.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/filter.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/filter.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/outlier_detection.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/outlier_detection.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/common/matcher/v3/matcher.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/common/matcher/v3/matcher.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/address.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/address.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/backoff.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/backoff.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/base.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/base.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/config_source.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/config_source.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/event_service_config.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/event_service_config.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/extension.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/extension.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/grpc_method_list.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/grpc_method_list.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/grpc_service.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/grpc_service.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/health_check.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/health_check.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/http_uri.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/http_uri.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/protocol.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/protocol.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/proxy_protocol.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/proxy_protocol.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/resolver.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/resolver.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/socket_option.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/socket_option.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/substitution_format_string.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/substitution_format_string.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/udp_socket_config.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/core/v3/udp_socket_config.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/endpoint/v3/endpoint.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/endpoint/v3/endpoint.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/endpoint/v3/endpoint_components.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/endpoint/v3/endpoint_components.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/endpoint/v3/load_report.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/endpoint/v3/load_report.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/api_listener.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/api_listener.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/listener.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/listener.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/listener_components.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/listener_components.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/quic_config.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/quic_config.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/udp_listener_config.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/udp_listener_config.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/metrics/v3/metrics_service.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/metrics/v3/metrics_service.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/metrics/v3/stats.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/metrics/v3/stats.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/overload/v3/overload.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/overload/v3/overload.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/rbac/v3/rbac.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/rbac/v3/rbac.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/route.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/route.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/route_components.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/route_components.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/scoped_route.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/scoped_route.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/tap/v3/common.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/tap/v3/common.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/datadog.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/datadog.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/dynamic_ot.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/dynamic_ot.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/http_tracer.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/http_tracer.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/lightstep.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/lightstep.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/opencensus.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/opencensus.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/opentelemetry.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/opentelemetry.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/service.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/service.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/skywalking.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/skywalking.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/trace.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/trace.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/xray.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/xray.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/zipkin.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/zipkin.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/clusters/aggregate/v3/cluster.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/clusters/aggregate/v3/cluster.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/common/fault/v3/fault.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/common/fault/v3/fault.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/fault/v3/fault.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/fault/v3/fault.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/rbac/v3/rbac.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/rbac/v3/rbac.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/router/v3/router.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/router/v3/router.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/stateful_session/v3/stateful_session.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/stateful_session/v3/stateful_session.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/http/stateful_session/cookie/v3/cookie.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/http/stateful_session/cookie/v3/cookie.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/cert.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/cert.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/common.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/common.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/secret.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/secret.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/tls.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/tls.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/service/discovery/v3/ads.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/service/discovery/v3/ads.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/service/discovery/v3/discovery.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/service/discovery/v3/discovery.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/service/load_stats/v3/lrs.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/service/load_stats/v3/lrs.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/service/status/v3/csds.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/service/status/v3/csds.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/http/v3/cookie.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/http/v3/cookie.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/http/v3/path_transformation.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/http/v3/path_transformation.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/filter_state.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/filter_state.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/http_inputs.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/http_inputs.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/metadata.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/metadata.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/node.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/node.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/number.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/number.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/path.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/path.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/regex.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/regex.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/status_code_input.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/status_code_input.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/string.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/string.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/struct.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/struct.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/value.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/value.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/metadata/v3/metadata.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/metadata/v3/metadata.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/tracing/v3/custom_tag.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/tracing/v3/custom_tag.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/hash_policy.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/hash_policy.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/http.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/http.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/http_status.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/http_status.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/percent.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/percent.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/range.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/range.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/ratelimit_strategy.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/ratelimit_strategy.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/ratelimit_unit.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/ratelimit_unit.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/semantic_version.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/semantic_version.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/token_bucket.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/v3/token_bucket.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/api/annotations.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/api/annotations.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/api/expr/v1alpha1/checked.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/api/expr/v1alpha1/checked.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/api/expr/v1alpha1/syntax.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/api/expr/v1alpha1/syntax.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/api/http.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/api/http.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/api/httpbody.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/api/httpbody.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/any.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/any.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/descriptor.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/descriptor.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/duration.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/duration.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/empty.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/empty.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/struct.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/struct.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/timestamp.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/timestamp.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/wrappers.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/wrappers.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/rpc/status.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/rpc/status.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/opencensus/proto/trace/v1/trace_config.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/opencensus/proto/trace/v1/trace_config.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/src/proto/grpc/lookup/v1/rls_config.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/src/proto/grpc/lookup/v1/rls_config.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/udpa/annotations/migrate.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/udpa/annotations/migrate.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/udpa/annotations/security.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/udpa/annotations/security.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/udpa/annotations/sensitive.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/udpa/annotations/sensitive.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/udpa/annotations/status.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/udpa/annotations/status.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/udpa/annotations/versioning.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/udpa/annotations/versioning.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/validate/validate.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/validate/validate.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/annotations/v3/migrate.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/annotations/v3/migrate.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/annotations/v3/security.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/annotations/v3/security.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/annotations/v3/sensitive.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/annotations/v3/sensitive.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/annotations/v3/status.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/annotations/v3/status.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/annotations/v3/versioning.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/annotations/v3/versioning.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/authority.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/authority.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/cidr.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/cidr.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/collection_entry.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/collection_entry.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/context_params.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/context_params.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/extension.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/extension.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/resource.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/resource.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/resource_locator.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/resource_locator.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/resource_name.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/core/v3/resource_name.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/cel.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/cel.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/domain.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/domain.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/http_inputs.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/http_inputs.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/ip.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/ip.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/matcher.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/matcher.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/range.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/range.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/regex.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/regex.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/string.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/type/matcher/v3/string.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/type/v3/cel.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/type/v3/cel.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/type/v3/range.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/type/v3/range.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/xds/type/v3/typed_struct.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/xds/type/v3/typed_struct.upbdefs.h",
+    "src/src/core/ext/xds/certificate_provider_store.cc",
+    "src/src/core/ext/xds/certificate_provider_store.h",
+    "src/src/core/ext/xds/file_watcher_certificate_provider_factory.cc",
+    "src/src/core/ext/xds/file_watcher_certificate_provider_factory.h",
+    "src/src/core/ext/xds/upb_utils.h",
+    "src/src/core/ext/xds/xds_api.cc",
+    "src/src/core/ext/xds/xds_api.h",
+    "src/src/core/ext/xds/xds_bootstrap.cc",
+    "src/src/core/ext/xds/xds_bootstrap.h",
+    "src/src/core/ext/xds/xds_bootstrap_grpc.cc",
+    "src/src/core/ext/xds/xds_bootstrap_grpc.h",
+    "src/src/core/ext/xds/xds_certificate_provider.cc",
+    "src/src/core/ext/xds/xds_certificate_provider.h",
+    "src/src/core/ext/xds/xds_channel_args.h",
+    "src/src/core/ext/xds/xds_channel_stack_modifier.cc",
+    "src/src/core/ext/xds/xds_channel_stack_modifier.h",
+    "src/src/core/ext/xds/xds_client.cc",
+    "src/src/core/ext/xds/xds_client.h",
+    "src/src/core/ext/xds/xds_client_grpc.cc",
+    "src/src/core/ext/xds/xds_client_grpc.h",
+    "src/src/core/ext/xds/xds_client_stats.cc",
+    "src/src/core/ext/xds/xds_client_stats.h",
+    "src/src/core/ext/xds/xds_cluster.cc",
+    "src/src/core/ext/xds/xds_cluster.h",
+    "src/src/core/ext/xds/xds_cluster_specifier_plugin.cc",
+    "src/src/core/ext/xds/xds_cluster_specifier_plugin.h",
+    "src/src/core/ext/xds/xds_common_types.cc",
+    "src/src/core/ext/xds/xds_common_types.h",
+    "src/src/core/ext/xds/xds_endpoint.cc",
+    "src/src/core/ext/xds/xds_endpoint.h",
+    "src/src/core/ext/xds/xds_health_status.cc",
+    "src/src/core/ext/xds/xds_health_status.h",
+    "src/src/core/ext/xds/xds_http_fault_filter.cc",
+    "src/src/core/ext/xds/xds_http_fault_filter.h",
+    "src/src/core/ext/xds/xds_http_filters.cc",
+    "src/src/core/ext/xds/xds_http_filters.h",
+    "src/src/core/ext/xds/xds_http_rbac_filter.cc",
+    "src/src/core/ext/xds/xds_http_rbac_filter.h",
+    "src/src/core/ext/xds/xds_http_stateful_session_filter.cc",
+    "src/src/core/ext/xds/xds_http_stateful_session_filter.h",
+    "src/src/core/ext/xds/xds_lb_policy_registry.cc",
+    "src/src/core/ext/xds/xds_lb_policy_registry.h",
+    "src/src/core/ext/xds/xds_listener.cc",
+    "src/src/core/ext/xds/xds_listener.h",
+    "src/src/core/ext/xds/xds_resource_type.h",
+    "src/src/core/ext/xds/xds_resource_type_impl.h",
+    "src/src/core/ext/xds/xds_route_config.cc",
+    "src/src/core/ext/xds/xds_route_config.h",
+    "src/src/core/ext/xds/xds_routing.cc",
+    "src/src/core/ext/xds/xds_routing.h",
+    "src/src/core/ext/xds/xds_server_config_fetcher.cc",
+    "src/src/core/ext/xds/xds_transport.h",
+    "src/src/core/ext/xds/xds_transport_grpc.cc",
+    "src/src/core/ext/xds/xds_transport_grpc.h",
+    "src/src/core/lib/address_utils/parse_address.cc",
+    "src/src/core/lib/address_utils/parse_address.h",
+    "src/src/core/lib/address_utils/sockaddr_utils.cc",
+    "src/src/core/lib/address_utils/sockaddr_utils.h",
+    "src/src/core/lib/avl/avl.h",
+    "src/src/core/lib/backoff/backoff.cc",
+    "src/src/core/lib/backoff/backoff.h",
+    "src/src/core/lib/channel/call_finalization.h",
+    "src/src/core/lib/channel/call_tracer.h",
+    "src/src/core/lib/channel/channel_args.cc",
+    "src/src/core/lib/channel/channel_args.h",
+    "src/src/core/lib/channel/channel_args_preconditioning.cc",
+    "src/src/core/lib/channel/channel_args_preconditioning.h",
+    "src/src/core/lib/channel/channel_fwd.h",
+    "src/src/core/lib/channel/channel_stack.cc",
+    "src/src/core/lib/channel/channel_stack.h",
+    "src/src/core/lib/channel/channel_stack_builder.cc",
+    "src/src/core/lib/channel/channel_stack_builder.h",
+    "src/src/core/lib/channel/channel_stack_builder_impl.cc",
+    "src/src/core/lib/channel/channel_stack_builder_impl.h",
+    "src/src/core/lib/channel/channel_trace.cc",
+    "src/src/core/lib/channel/channel_trace.h",
+    "src/src/core/lib/channel/channelz.cc",
+    "src/src/core/lib/channel/channelz.h",
+    "src/src/core/lib/channel/channelz_registry.cc",
+    "src/src/core/lib/channel/channelz_registry.h",
+    "src/src/core/lib/channel/connected_channel.cc",
+    "src/src/core/lib/channel/connected_channel.h",
+    "src/src/core/lib/channel/context.h",
+    "src/src/core/lib/channel/promise_based_filter.cc",
+    "src/src/core/lib/channel/promise_based_filter.h",
+    "src/src/core/lib/channel/status_util.cc",
+    "src/src/core/lib/channel/status_util.h",
+    "src/src/core/lib/compression/compression.cc",
+    "src/src/core/lib/compression/compression_internal.cc",
+    "src/src/core/lib/compression/compression_internal.h",
+    "src/src/core/lib/compression/message_compress.cc",
+    "src/src/core/lib/compression/message_compress.h",
+    "src/src/core/lib/config/core_configuration.cc",
+    "src/src/core/lib/config/core_configuration.h",
+    "src/src/core/lib/debug/event_log.cc",
+    "src/src/core/lib/debug/event_log.h",
+    "src/src/core/lib/debug/histogram_view.cc",
+    "src/src/core/lib/debug/histogram_view.h",
+    "src/src/core/lib/debug/stats.cc",
+    "src/src/core/lib/debug/stats.h",
+    "src/src/core/lib/debug/stats_data.cc",
+    "src/src/core/lib/debug/stats_data.h",
+    "src/src/core/lib/debug/trace.cc",
+    "src/src/core/lib/debug/trace.h",
+    "src/src/core/lib/event_engine/channel_args_endpoint_config.cc",
+    "src/src/core/lib/event_engine/channel_args_endpoint_config.h",
+    "src/src/core/lib/event_engine/common_closures.h",
+    "src/src/core/lib/event_engine/default_event_engine.cc",
+    "src/src/core/lib/event_engine/default_event_engine.h",
+    "src/src/core/lib/event_engine/default_event_engine_factory.cc",
+    "src/src/core/lib/event_engine/default_event_engine_factory.h",
+    "src/src/core/lib/event_engine/event_engine.cc",
+    "src/src/core/lib/event_engine/executor/executor.h",
+    "src/src/core/lib/event_engine/forkable.cc",
+    "src/src/core/lib/event_engine/forkable.h",
+    "src/src/core/lib/event_engine/handle_containers.h",
+    "src/src/core/lib/event_engine/memory_allocator.cc",
+    "src/src/core/lib/event_engine/poller.h",
+    "src/src/core/lib/event_engine/posix.h",
+    "src/src/core/lib/event_engine/posix_engine/ev_epoll1_linux.cc",
+    "src/src/core/lib/event_engine/posix_engine/ev_epoll1_linux.h",
+    "src/src/core/lib/event_engine/posix_engine/ev_poll_posix.cc",
+    "src/src/core/lib/event_engine/posix_engine/ev_poll_posix.h",
+    "src/src/core/lib/event_engine/posix_engine/event_poller.h",
+    "src/src/core/lib/event_engine/posix_engine/event_poller_posix_default.cc",
+    "src/src/core/lib/event_engine/posix_engine/event_poller_posix_default.h",
+    "src/src/core/lib/event_engine/posix_engine/internal_errqueue.cc",
+    "src/src/core/lib/event_engine/posix_engine/internal_errqueue.h",
+    "src/src/core/lib/event_engine/posix_engine/lockfree_event.cc",
+    "src/src/core/lib/event_engine/posix_engine/lockfree_event.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_endpoint.cc",
+    "src/src/core/lib/event_engine/posix_engine/posix_endpoint.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine.cc",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_closure.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_listener.cc",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_listener.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_listener_utils.cc",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_listener_utils.h",
+    "src/src/core/lib/event_engine/posix_engine/tcp_socket_utils.cc",
+    "src/src/core/lib/event_engine/posix_engine/tcp_socket_utils.h",
+    "src/src/core/lib/event_engine/posix_engine/timer.cc",
+    "src/src/core/lib/event_engine/posix_engine/timer.h",
+    "src/src/core/lib/event_engine/posix_engine/timer_heap.cc",
+    "src/src/core/lib/event_engine/posix_engine/timer_heap.h",
+    "src/src/core/lib/event_engine/posix_engine/timer_manager.cc",
+    "src/src/core/lib/event_engine/posix_engine/timer_manager.h",
+    "src/src/core/lib/event_engine/posix_engine/traced_buffer_list.cc",
+    "src/src/core/lib/event_engine/posix_engine/traced_buffer_list.h",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_eventfd.cc",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_eventfd.h",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_pipe.cc",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_pipe.h",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_posix.h",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_posix_default.cc",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_posix_default.h",
+    "src/src/core/lib/event_engine/resolved_address.cc",
+    "src/src/core/lib/event_engine/resolved_address_internal.h",
+    "src/src/core/lib/event_engine/shim.cc",
+    "src/src/core/lib/event_engine/shim.h",
+    "src/src/core/lib/event_engine/slice.cc",
+    "src/src/core/lib/event_engine/slice_buffer.cc",
+    "src/src/core/lib/event_engine/tcp_socket_utils.cc",
+    "src/src/core/lib/event_engine/tcp_socket_utils.h",
+    "src/src/core/lib/event_engine/thread_pool.cc",
+    "src/src/core/lib/event_engine/thread_pool.h",
+    "src/src/core/lib/event_engine/time_util.cc",
+    "src/src/core/lib/event_engine/time_util.h",
+    "src/src/core/lib/event_engine/trace.cc",
+    "src/src/core/lib/event_engine/trace.h",
+    "src/src/core/lib/event_engine/utils.cc",
+    "src/src/core/lib/event_engine/utils.h",
+    "src/src/core/lib/event_engine/windows/iocp.cc",
+    "src/src/core/lib/event_engine/windows/iocp.h",
+    "src/src/core/lib/event_engine/windows/win_socket.cc",
+    "src/src/core/lib/event_engine/windows/win_socket.h",
+    "src/src/core/lib/event_engine/windows/windows_endpoint.cc",
+    "src/src/core/lib/event_engine/windows/windows_endpoint.h",
+    "src/src/core/lib/event_engine/windows/windows_engine.cc",
+    "src/src/core/lib/event_engine/windows/windows_engine.h",
+    "src/src/core/lib/experiments/config.cc",
+    "src/src/core/lib/experiments/config.h",
+    "src/src/core/lib/experiments/experiments.cc",
+    "src/src/core/lib/experiments/experiments.h",
+    "src/src/core/lib/gpr/spinlock.h",
+    "src/src/core/lib/gprpp/atomic_utils.h",
+    "src/src/core/lib/gprpp/bitset.h",
+    "src/src/core/lib/gprpp/chunked_vector.h",
+    "src/src/core/lib/gprpp/cpp_impl_of.h",
+    "src/src/core/lib/gprpp/dual_ref_counted.h",
+    "src/src/core/lib/gprpp/load_file.cc",
+    "src/src/core/lib/gprpp/load_file.h",
+    "src/src/core/lib/gprpp/manual_constructor.h",
+    "src/src/core/lib/gprpp/match.h",
+    "src/src/core/lib/gprpp/notification.h",
+    "src/src/core/lib/gprpp/orphanable.h",
+    "src/src/core/lib/gprpp/overload.h",
+    "src/src/core/lib/gprpp/packed_table.h",
+    "src/src/core/lib/gprpp/per_cpu.h",
+    "src/src/core/lib/gprpp/ref_counted.h",
+    "src/src/core/lib/gprpp/ref_counted_ptr.h",
+    "src/src/core/lib/gprpp/single_set_ptr.h",
+    "src/src/core/lib/gprpp/sorted_pack.h",
+    "src/src/core/lib/gprpp/status_helper.cc",
+    "src/src/core/lib/gprpp/status_helper.h",
+    "src/src/core/lib/gprpp/table.h",
+    "src/src/core/lib/gprpp/time.cc",
+    "src/src/core/lib/gprpp/time.h",
+    "src/src/core/lib/gprpp/time_averaged_stats.cc",
+    "src/src/core/lib/gprpp/time_averaged_stats.h",
+    "src/src/core/lib/gprpp/unique_type_name.h",
+    "src/src/core/lib/gprpp/validation_errors.cc",
+    "src/src/core/lib/gprpp/validation_errors.h",
+    "src/src/core/lib/gprpp/work_serializer.cc",
+    "src/src/core/lib/gprpp/work_serializer.h",
+    "src/src/core/lib/handshaker/proxy_mapper.h",
+    "src/src/core/lib/handshaker/proxy_mapper_registry.cc",
+    "src/src/core/lib/handshaker/proxy_mapper_registry.h",
+    "src/src/core/lib/http/format_request.cc",
+    "src/src/core/lib/http/format_request.h",
+    "src/src/core/lib/http/httpcli.cc",
+    "src/src/core/lib/http/httpcli.h",
+    "src/src/core/lib/http/httpcli_security_connector.cc",
+    "src/src/core/lib/http/httpcli_ssl_credentials.h",
+    "src/src/core/lib/http/parser.cc",
+    "src/src/core/lib/http/parser.h",
+    "src/src/core/lib/iomgr/block_annotate.h",
+    "src/src/core/lib/iomgr/buffer_list.cc",
+    "src/src/core/lib/iomgr/buffer_list.h",
+    "src/src/core/lib/iomgr/call_combiner.cc",
+    "src/src/core/lib/iomgr/call_combiner.h",
+    "src/src/core/lib/iomgr/cfstream_handle.cc",
+    "src/src/core/lib/iomgr/cfstream_handle.h",
+    "src/src/core/lib/iomgr/closure.cc",
+    "src/src/core/lib/iomgr/closure.h",
+    "src/src/core/lib/iomgr/combiner.cc",
+    "src/src/core/lib/iomgr/combiner.h",
+    "src/src/core/lib/iomgr/dualstack_socket_posix.cc",
+    "src/src/core/lib/iomgr/dynamic_annotations.h",
+    "src/src/core/lib/iomgr/endpoint.cc",
+    "src/src/core/lib/iomgr/endpoint.h",
+    "src/src/core/lib/iomgr/endpoint_cfstream.cc",
+    "src/src/core/lib/iomgr/endpoint_cfstream.h",
+    "src/src/core/lib/iomgr/endpoint_pair.h",
+    "src/src/core/lib/iomgr/endpoint_pair_posix.cc",
+    "src/src/core/lib/iomgr/endpoint_pair_windows.cc",
+    "src/src/core/lib/iomgr/error.cc",
+    "src/src/core/lib/iomgr/error.h",
+    "src/src/core/lib/iomgr/error_cfstream.cc",
+    "src/src/core/lib/iomgr/error_cfstream.h",
+    "src/src/core/lib/iomgr/ev_apple.cc",
+    "src/src/core/lib/iomgr/ev_apple.h",
+    "src/src/core/lib/iomgr/ev_epoll1_linux.cc",
+    "src/src/core/lib/iomgr/ev_epoll1_linux.h",
+    "src/src/core/lib/iomgr/ev_poll_posix.cc",
+    "src/src/core/lib/iomgr/ev_poll_posix.h",
+    "src/src/core/lib/iomgr/ev_posix.cc",
+    "src/src/core/lib/iomgr/ev_posix.h",
+    "src/src/core/lib/iomgr/ev_windows.cc",
+    "src/src/core/lib/iomgr/event_engine_shims/closure.cc",
+    "src/src/core/lib/iomgr/event_engine_shims/closure.h",
+    "src/src/core/lib/iomgr/event_engine_shims/endpoint.cc",
+    "src/src/core/lib/iomgr/event_engine_shims/endpoint.h",
+    "src/src/core/lib/iomgr/event_engine_shims/tcp_client.cc",
+    "src/src/core/lib/iomgr/event_engine_shims/tcp_client.h",
+    "src/src/core/lib/iomgr/exec_ctx.cc",
+    "src/src/core/lib/iomgr/exec_ctx.h",
+    "src/src/core/lib/iomgr/executor.cc",
+    "src/src/core/lib/iomgr/executor.h",
+    "src/src/core/lib/iomgr/fork_posix.cc",
+    "src/src/core/lib/iomgr/fork_windows.cc",
+    "src/src/core/lib/iomgr/gethostname.h",
+    "src/src/core/lib/iomgr/gethostname_fallback.cc",
+    "src/src/core/lib/iomgr/gethostname_host_name_max.cc",
+    "src/src/core/lib/iomgr/gethostname_sysconf.cc",
+    "src/src/core/lib/iomgr/grpc_if_nametoindex.h",
+    "src/src/core/lib/iomgr/grpc_if_nametoindex_posix.cc",
+    "src/src/core/lib/iomgr/grpc_if_nametoindex_unsupported.cc",
+    "src/src/core/lib/iomgr/internal_errqueue.cc",
+    "src/src/core/lib/iomgr/internal_errqueue.h",
+    "src/src/core/lib/iomgr/iocp_windows.cc",
+    "src/src/core/lib/iomgr/iocp_windows.h",
+    "src/src/core/lib/iomgr/iomgr.cc",
+    "src/src/core/lib/iomgr/iomgr.h",
+    "src/src/core/lib/iomgr/iomgr_fwd.h",
+    "src/src/core/lib/iomgr/iomgr_internal.cc",
+    "src/src/core/lib/iomgr/iomgr_internal.h",
+    "src/src/core/lib/iomgr/iomgr_posix.cc",
+    "src/src/core/lib/iomgr/iomgr_posix_cfstream.cc",
+    "src/src/core/lib/iomgr/iomgr_windows.cc",
+    "src/src/core/lib/iomgr/load_file.cc",
+    "src/src/core/lib/iomgr/load_file.h",
+    "src/src/core/lib/iomgr/lockfree_event.cc",
+    "src/src/core/lib/iomgr/lockfree_event.h",
+    "src/src/core/lib/iomgr/nameser.h",
+    "src/src/core/lib/iomgr/polling_entity.cc",
+    "src/src/core/lib/iomgr/polling_entity.h",
+    "src/src/core/lib/iomgr/pollset.cc",
+    "src/src/core/lib/iomgr/pollset.h",
+    "src/src/core/lib/iomgr/pollset_set.cc",
+    "src/src/core/lib/iomgr/pollset_set.h",
+    "src/src/core/lib/iomgr/pollset_set_windows.cc",
+    "src/src/core/lib/iomgr/pollset_set_windows.h",
+    "src/src/core/lib/iomgr/pollset_windows.cc",
+    "src/src/core/lib/iomgr/pollset_windows.h",
+    "src/src/core/lib/iomgr/port.h",
+    "src/src/core/lib/iomgr/python_util.h",
+    "src/src/core/lib/iomgr/resolve_address.cc",
+    "src/src/core/lib/iomgr/resolve_address.h",
+    "src/src/core/lib/iomgr/resolve_address_impl.h",
+    "src/src/core/lib/iomgr/resolve_address_posix.cc",
+    "src/src/core/lib/iomgr/resolve_address_posix.h",
+    "src/src/core/lib/iomgr/resolve_address_windows.cc",
+    "src/src/core/lib/iomgr/resolve_address_windows.h",
+    "src/src/core/lib/iomgr/resolved_address.h",
+    "src/src/core/lib/iomgr/sockaddr.h",
+    "src/src/core/lib/iomgr/sockaddr_posix.h",
+    "src/src/core/lib/iomgr/sockaddr_utils_posix.cc",
+    "src/src/core/lib/iomgr/sockaddr_windows.h",
+    "src/src/core/lib/iomgr/socket_factory_posix.cc",
+    "src/src/core/lib/iomgr/socket_factory_posix.h",
+    "src/src/core/lib/iomgr/socket_mutator.cc",
+    "src/src/core/lib/iomgr/socket_mutator.h",
+    "src/src/core/lib/iomgr/socket_utils.h",
+    "src/src/core/lib/iomgr/socket_utils_common_posix.cc",
+    "src/src/core/lib/iomgr/socket_utils_linux.cc",
+    "src/src/core/lib/iomgr/socket_utils_posix.cc",
+    "src/src/core/lib/iomgr/socket_utils_posix.h",
+    "src/src/core/lib/iomgr/socket_utils_windows.cc",
+    "src/src/core/lib/iomgr/socket_windows.cc",
+    "src/src/core/lib/iomgr/socket_windows.h",
+    "src/src/core/lib/iomgr/systemd_utils.cc",
+    "src/src/core/lib/iomgr/systemd_utils.h",
+    "src/src/core/lib/iomgr/tcp_client.cc",
+    "src/src/core/lib/iomgr/tcp_client.h",
+    "src/src/core/lib/iomgr/tcp_client_cfstream.cc",
+    "src/src/core/lib/iomgr/tcp_client_posix.cc",
+    "src/src/core/lib/iomgr/tcp_client_posix.h",
+    "src/src/core/lib/iomgr/tcp_client_windows.cc",
+    "src/src/core/lib/iomgr/tcp_posix.cc",
+    "src/src/core/lib/iomgr/tcp_posix.h",
+    "src/src/core/lib/iomgr/tcp_server.cc",
+    "src/src/core/lib/iomgr/tcp_server.h",
+    "src/src/core/lib/iomgr/tcp_server_posix.cc",
+    "src/src/core/lib/iomgr/tcp_server_utils_posix.h",
+    "src/src/core/lib/iomgr/tcp_server_utils_posix_common.cc",
+    "src/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc",
+    "src/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc",
+    "src/src/core/lib/iomgr/tcp_server_windows.cc",
+    "src/src/core/lib/iomgr/tcp_windows.cc",
+    "src/src/core/lib/iomgr/tcp_windows.h",
+    "src/src/core/lib/iomgr/timer.cc",
+    "src/src/core/lib/iomgr/timer.h",
+    "src/src/core/lib/iomgr/timer_generic.cc",
+    "src/src/core/lib/iomgr/timer_generic.h",
+    "src/src/core/lib/iomgr/timer_heap.cc",
+    "src/src/core/lib/iomgr/timer_heap.h",
+    "src/src/core/lib/iomgr/timer_manager.cc",
+    "src/src/core/lib/iomgr/timer_manager.h",
+    "src/src/core/lib/iomgr/unix_sockets_posix.cc",
+    "src/src/core/lib/iomgr/unix_sockets_posix.h",
+    "src/src/core/lib/iomgr/unix_sockets_posix_noop.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_eventfd.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_nospecial.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_pipe.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_pipe.h",
+    "src/src/core/lib/iomgr/wakeup_fd_posix.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_posix.h",
+    "src/src/core/lib/json/json.h",
+    "src/src/core/lib/json/json_args.h",
+    "src/src/core/lib/json/json_channel_args.h",
+    "src/src/core/lib/json/json_object_loader.cc",
+    "src/src/core/lib/json/json_object_loader.h",
+    "src/src/core/lib/json/json_reader.cc",
+    "src/src/core/lib/json/json_util.cc",
+    "src/src/core/lib/json/json_util.h",
+    "src/src/core/lib/json/json_writer.cc",
+    "src/src/core/lib/load_balancing/lb_policy.cc",
+    "src/src/core/lib/load_balancing/lb_policy.h",
+    "src/src/core/lib/load_balancing/lb_policy_factory.h",
+    "src/src/core/lib/load_balancing/lb_policy_registry.cc",
+    "src/src/core/lib/load_balancing/lb_policy_registry.h",
+    "src/src/core/lib/load_balancing/subchannel_interface.h",
+    "src/src/core/lib/matchers/matchers.cc",
+    "src/src/core/lib/matchers/matchers.h",
+    "src/src/core/lib/promise/activity.cc",
+    "src/src/core/lib/promise/activity.h",
+    "src/src/core/lib/promise/arena_promise.h",
+    "src/src/core/lib/promise/context.h",
+    "src/src/core/lib/promise/detail/basic_join.h",
+    "src/src/core/lib/promise/detail/basic_seq.h",
+    "src/src/core/lib/promise/detail/promise_factory.h",
+    "src/src/core/lib/promise/detail/promise_like.h",
+    "src/src/core/lib/promise/detail/status.h",
+    "src/src/core/lib/promise/detail/switch.h",
+    "src/src/core/lib/promise/exec_ctx_wakeup_scheduler.h",
+    "src/src/core/lib/promise/if.h",
+    "src/src/core/lib/promise/interceptor_list.h",
+    "src/src/core/lib/promise/intra_activity_waiter.h",
+    "src/src/core/lib/promise/latch.h",
+    "src/src/core/lib/promise/loop.h",
+    "src/src/core/lib/promise/map.h",
+    "src/src/core/lib/promise/pipe.h",
+    "src/src/core/lib/promise/poll.h",
+    "src/src/core/lib/promise/promise.h",
+    "src/src/core/lib/promise/race.h",
+    "src/src/core/lib/promise/seq.h",
+    "src/src/core/lib/promise/sleep.cc",
+    "src/src/core/lib/promise/sleep.h",
+    "src/src/core/lib/promise/trace.cc",
+    "src/src/core/lib/promise/trace.h",
+    "src/src/core/lib/promise/try_join.h",
+    "src/src/core/lib/promise/try_seq.h",
+    "src/src/core/lib/resolver/resolver.cc",
+    "src/src/core/lib/resolver/resolver.h",
+    "src/src/core/lib/resolver/resolver_factory.h",
+    "src/src/core/lib/resolver/resolver_registry.cc",
+    "src/src/core/lib/resolver/resolver_registry.h",
+    "src/src/core/lib/resolver/server_address.cc",
+    "src/src/core/lib/resolver/server_address.h",
+    "src/src/core/lib/resource_quota/api.cc",
+    "src/src/core/lib/resource_quota/api.h",
+    "src/src/core/lib/resource_quota/arena.cc",
+    "src/src/core/lib/resource_quota/arena.h",
+    "src/src/core/lib/resource_quota/memory_quota.cc",
+    "src/src/core/lib/resource_quota/memory_quota.h",
+    "src/src/core/lib/resource_quota/periodic_update.cc",
+    "src/src/core/lib/resource_quota/periodic_update.h",
+    "src/src/core/lib/resource_quota/resource_quota.cc",
+    "src/src/core/lib/resource_quota/resource_quota.h",
+    "src/src/core/lib/resource_quota/thread_quota.cc",
+    "src/src/core/lib/resource_quota/thread_quota.h",
+    "src/src/core/lib/resource_quota/trace.cc",
+    "src/src/core/lib/resource_quota/trace.h",
+    "src/src/core/lib/security/authorization/authorization_engine.h",
+    "src/src/core/lib/security/authorization/authorization_policy_provider.h",
+    "src/src/core/lib/security/authorization/authorization_policy_provider_vtable.cc",
+    "src/src/core/lib/security/authorization/evaluate_args.cc",
+    "src/src/core/lib/security/authorization/evaluate_args.h",
+    "src/src/core/lib/security/authorization/grpc_authorization_engine.cc",
+    "src/src/core/lib/security/authorization/grpc_authorization_engine.h",
+    "src/src/core/lib/security/authorization/grpc_server_authz_filter.cc",
+    "src/src/core/lib/security/authorization/grpc_server_authz_filter.h",
+    "src/src/core/lib/security/authorization/matchers.cc",
+    "src/src/core/lib/security/authorization/matchers.h",
+    "src/src/core/lib/security/authorization/rbac_policy.cc",
+    "src/src/core/lib/security/authorization/rbac_policy.h",
+    "src/src/core/lib/security/certificate_provider/certificate_provider_factory.h",
+    "src/src/core/lib/security/certificate_provider/certificate_provider_registry.cc",
+    "src/src/core/lib/security/certificate_provider/certificate_provider_registry.h",
+    "src/src/core/lib/security/context/security_context.cc",
+    "src/src/core/lib/security/context/security_context.h",
+    "src/src/core/lib/security/credentials/alts/alts_credentials.cc",
+    "src/src/core/lib/security/credentials/alts/alts_credentials.h",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment.cc",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment.h",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc",
+    "src/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc",
+    "src/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc",
+    "src/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h",
+    "src/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc",
+    "src/src/core/lib/security/credentials/call_creds_util.cc",
+    "src/src/core/lib/security/credentials/call_creds_util.h",
+    "src/src/core/lib/security/credentials/channel_creds_registry.h",
+    "src/src/core/lib/security/credentials/channel_creds_registry_init.cc",
+    "src/src/core/lib/security/credentials/composite/composite_credentials.cc",
+    "src/src/core/lib/security/credentials/composite/composite_credentials.h",
+    "src/src/core/lib/security/credentials/credentials.cc",
+    "src/src/core/lib/security/credentials/credentials.h",
+    "src/src/core/lib/security/credentials/external/aws_external_account_credentials.cc",
+    "src/src/core/lib/security/credentials/external/aws_external_account_credentials.h",
+    "src/src/core/lib/security/credentials/external/aws_request_signer.cc",
+    "src/src/core/lib/security/credentials/external/aws_request_signer.h",
+    "src/src/core/lib/security/credentials/external/external_account_credentials.cc",
+    "src/src/core/lib/security/credentials/external/external_account_credentials.h",
+    "src/src/core/lib/security/credentials/external/file_external_account_credentials.cc",
+    "src/src/core/lib/security/credentials/external/file_external_account_credentials.h",
+    "src/src/core/lib/security/credentials/external/url_external_account_credentials.cc",
+    "src/src/core/lib/security/credentials/external/url_external_account_credentials.h",
+    "src/src/core/lib/security/credentials/fake/fake_credentials.cc",
+    "src/src/core/lib/security/credentials/fake/fake_credentials.h",
+    "src/src/core/lib/security/credentials/google_default/credentials_generic.cc",
+    "src/src/core/lib/security/credentials/google_default/google_default_credentials.cc",
+    "src/src/core/lib/security/credentials/google_default/google_default_credentials.h",
+    "src/src/core/lib/security/credentials/iam/iam_credentials.cc",
+    "src/src/core/lib/security/credentials/iam/iam_credentials.h",
+    "src/src/core/lib/security/credentials/insecure/insecure_credentials.cc",
+    "src/src/core/lib/security/credentials/insecure/insecure_credentials.h",
+    "src/src/core/lib/security/credentials/jwt/json_token.cc",
+    "src/src/core/lib/security/credentials/jwt/json_token.h",
+    "src/src/core/lib/security/credentials/jwt/jwt_credentials.cc",
+    "src/src/core/lib/security/credentials/jwt/jwt_credentials.h",
+    "src/src/core/lib/security/credentials/jwt/jwt_verifier.cc",
+    "src/src/core/lib/security/credentials/jwt/jwt_verifier.h",
+    "src/src/core/lib/security/credentials/local/local_credentials.cc",
+    "src/src/core/lib/security/credentials/local/local_credentials.h",
+    "src/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc",
+    "src/src/core/lib/security/credentials/oauth2/oauth2_credentials.h",
+    "src/src/core/lib/security/credentials/plugin/plugin_credentials.cc",
+    "src/src/core/lib/security/credentials/plugin/plugin_credentials.h",
+    "src/src/core/lib/security/credentials/ssl/ssl_credentials.cc",
+    "src/src/core/lib/security/credentials/ssl/ssl_credentials.h",
+    "src/src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.cc",
+    "src/src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.h",
+    "src/src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.cc",
+    "src/src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h",
+    "src/src/core/lib/security/credentials/tls/grpc_tls_certificate_verifier.cc",
+    "src/src/core/lib/security/credentials/tls/grpc_tls_certificate_verifier.h",
+    "src/src/core/lib/security/credentials/tls/grpc_tls_credentials_options.cc",
+    "src/src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h",
+    "src/src/core/lib/security/credentials/tls/tls_credentials.cc",
+    "src/src/core/lib/security/credentials/tls/tls_credentials.h",
+    "src/src/core/lib/security/credentials/tls/tls_utils.cc",
+    "src/src/core/lib/security/credentials/tls/tls_utils.h",
+    "src/src/core/lib/security/credentials/xds/xds_credentials.cc",
+    "src/src/core/lib/security/credentials/xds/xds_credentials.h",
+    "src/src/core/lib/security/security_connector/alts/alts_security_connector.cc",
+    "src/src/core/lib/security/security_connector/alts/alts_security_connector.h",
+    "src/src/core/lib/security/security_connector/fake/fake_security_connector.cc",
+    "src/src/core/lib/security/security_connector/fake/fake_security_connector.h",
+    "src/src/core/lib/security/security_connector/insecure/insecure_security_connector.cc",
+    "src/src/core/lib/security/security_connector/insecure/insecure_security_connector.h",
+    "src/src/core/lib/security/security_connector/load_system_roots.h",
+    "src/src/core/lib/security/security_connector/load_system_roots_fallback.cc",
+    "src/src/core/lib/security/security_connector/load_system_roots_supported.cc",
+    "src/src/core/lib/security/security_connector/load_system_roots_supported.h",
+    "src/src/core/lib/security/security_connector/local/local_security_connector.cc",
+    "src/src/core/lib/security/security_connector/local/local_security_connector.h",
+    "src/src/core/lib/security/security_connector/security_connector.cc",
+    "src/src/core/lib/security/security_connector/security_connector.h",
+    "src/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc",
+    "src/src/core/lib/security/security_connector/ssl/ssl_security_connector.h",
+    "src/src/core/lib/security/security_connector/ssl_utils.cc",
+    "src/src/core/lib/security/security_connector/ssl_utils.h",
+    "src/src/core/lib/security/security_connector/ssl_utils_config.cc",
+    "src/src/core/lib/security/security_connector/ssl_utils_config.h",
+    "src/src/core/lib/security/security_connector/tls/tls_security_connector.cc",
+    "src/src/core/lib/security/security_connector/tls/tls_security_connector.h",
+    "src/src/core/lib/security/transport/auth_filters.h",
+    "src/src/core/lib/security/transport/client_auth_filter.cc",
+    "src/src/core/lib/security/transport/secure_endpoint.cc",
+    "src/src/core/lib/security/transport/secure_endpoint.h",
+    "src/src/core/lib/security/transport/security_handshaker.cc",
+    "src/src/core/lib/security/transport/security_handshaker.h",
+    "src/src/core/lib/security/transport/server_auth_filter.cc",
+    "src/src/core/lib/security/transport/tsi_error.cc",
+    "src/src/core/lib/security/transport/tsi_error.h",
+    "src/src/core/lib/security/util/json_util.cc",
+    "src/src/core/lib/security/util/json_util.h",
+    "src/src/core/lib/service_config/service_config.h",
+    "src/src/core/lib/service_config/service_config_call_data.h",
+    "src/src/core/lib/service_config/service_config_impl.cc",
+    "src/src/core/lib/service_config/service_config_impl.h",
+    "src/src/core/lib/service_config/service_config_parser.cc",
+    "src/src/core/lib/service_config/service_config_parser.h",
+    "src/src/core/lib/slice/b64.cc",
+    "src/src/core/lib/slice/b64.h",
+    "src/src/core/lib/slice/percent_encoding.cc",
+    "src/src/core/lib/slice/percent_encoding.h",
+    "src/src/core/lib/slice/slice.cc",
+    "src/src/core/lib/slice/slice.h",
+    "src/src/core/lib/slice/slice_buffer.cc",
+    "src/src/core/lib/slice/slice_buffer.h",
+    "src/src/core/lib/slice/slice_internal.h",
+    "src/src/core/lib/slice/slice_refcount.cc",
+    "src/src/core/lib/slice/slice_refcount.h",
+    "src/src/core/lib/slice/slice_string_helpers.cc",
+    "src/src/core/lib/slice/slice_string_helpers.h",
+    "src/src/core/lib/surface/api_trace.cc",
+    "src/src/core/lib/surface/api_trace.h",
+    "src/src/core/lib/surface/builtins.cc",
+    "src/src/core/lib/surface/builtins.h",
+    "src/src/core/lib/surface/byte_buffer.cc",
+    "src/src/core/lib/surface/byte_buffer_reader.cc",
+    "src/src/core/lib/surface/call.cc",
+    "src/src/core/lib/surface/call.h",
+    "src/src/core/lib/surface/call_details.cc",
+    "src/src/core/lib/surface/call_log_batch.cc",
+    "src/src/core/lib/surface/call_test_only.h",
+    "src/src/core/lib/surface/call_trace.cc",
+    "src/src/core/lib/surface/call_trace.h",
+    "src/src/core/lib/surface/channel.cc",
+    "src/src/core/lib/surface/channel.h",
+    "src/src/core/lib/surface/channel_init.cc",
+    "src/src/core/lib/surface/channel_init.h",
+    "src/src/core/lib/surface/channel_ping.cc",
+    "src/src/core/lib/surface/channel_stack_type.cc",
+    "src/src/core/lib/surface/channel_stack_type.h",
+    "src/src/core/lib/surface/completion_queue.cc",
+    "src/src/core/lib/surface/completion_queue.h",
+    "src/src/core/lib/surface/completion_queue_factory.cc",
+    "src/src/core/lib/surface/completion_queue_factory.h",
+    "src/src/core/lib/surface/event_string.cc",
+    "src/src/core/lib/surface/event_string.h",
+    "src/src/core/lib/surface/init.cc",
+    "src/src/core/lib/surface/init.h",
+    "src/src/core/lib/surface/init_internally.cc",
+    "src/src/core/lib/surface/init_internally.h",
+    "src/src/core/lib/surface/lame_client.cc",
+    "src/src/core/lib/surface/lame_client.h",
+    "src/src/core/lib/surface/metadata_array.cc",
+    "src/src/core/lib/surface/server.cc",
+    "src/src/core/lib/surface/server.h",
+    "src/src/core/lib/surface/validate_metadata.cc",
+    "src/src/core/lib/surface/validate_metadata.h",
+    "src/src/core/lib/surface/version.cc",
+    "src/src/core/lib/transport/bdp_estimator.cc",
+    "src/src/core/lib/transport/bdp_estimator.h",
+    "src/src/core/lib/transport/connectivity_state.cc",
+    "src/src/core/lib/transport/connectivity_state.h",
+    "src/src/core/lib/transport/error_utils.cc",
+    "src/src/core/lib/transport/error_utils.h",
+    "src/src/core/lib/transport/handshaker.cc",
+    "src/src/core/lib/transport/handshaker.h",
+    "src/src/core/lib/transport/handshaker_factory.h",
+    "src/src/core/lib/transport/handshaker_registry.cc",
+    "src/src/core/lib/transport/handshaker_registry.h",
+    "src/src/core/lib/transport/http2_errors.h",
+    "src/src/core/lib/transport/http_connect_handshaker.cc",
+    "src/src/core/lib/transport/http_connect_handshaker.h",
+    "src/src/core/lib/transport/metadata_batch.cc",
+    "src/src/core/lib/transport/metadata_batch.h",
+    "src/src/core/lib/transport/parsed_metadata.cc",
+    "src/src/core/lib/transport/parsed_metadata.h",
+    "src/src/core/lib/transport/pid_controller.cc",
+    "src/src/core/lib/transport/pid_controller.h",
+    "src/src/core/lib/transport/status_conversion.cc",
+    "src/src/core/lib/transport/status_conversion.h",
+    "src/src/core/lib/transport/tcp_connect_handshaker.cc",
+    "src/src/core/lib/transport/tcp_connect_handshaker.h",
+    "src/src/core/lib/transport/timeout_encoding.cc",
+    "src/src/core/lib/transport/timeout_encoding.h",
+    "src/src/core/lib/transport/transport.cc",
+    "src/src/core/lib/transport/transport.h",
+    "src/src/core/lib/transport/transport_fwd.h",
+    "src/src/core/lib/transport/transport_impl.h",
+    "src/src/core/lib/transport/transport_op_string.cc",
+    "src/src/core/lib/uri/uri_parser.cc",
+    "src/src/core/lib/uri/uri_parser.h",
+    "src/src/core/plugin_registry/grpc_plugin_registry.cc",
+    "src/src/core/plugin_registry/grpc_plugin_registry_extra.cc",
+    "src/src/core/tsi/alts/crypt/aes_gcm.cc",
+    "src/src/core/tsi/alts/crypt/gsec.cc",
+    "src/src/core/tsi/alts/crypt/gsec.h",
+    "src/src/core/tsi/alts/frame_protector/alts_counter.cc",
+    "src/src/core/tsi/alts/frame_protector/alts_counter.h",
+    "src/src/core/tsi/alts/frame_protector/alts_crypter.cc",
+    "src/src/core/tsi/alts/frame_protector/alts_crypter.h",
+    "src/src/core/tsi/alts/frame_protector/alts_frame_protector.cc",
+    "src/src/core/tsi/alts/frame_protector/alts_frame_protector.h",
+    "src/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc",
+    "src/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h",
+    "src/src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc",
+    "src/src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc",
+    "src/src/core/tsi/alts/frame_protector/frame_handler.cc",
+    "src/src/core/tsi/alts/frame_protector/frame_handler.h",
+    "src/src/core/tsi/alts/handshaker/alts_handshaker_client.cc",
+    "src/src/core/tsi/alts/handshaker/alts_handshaker_client.h",
+    "src/src/core/tsi/alts/handshaker/alts_shared_resource.cc",
+    "src/src/core/tsi/alts/handshaker/alts_shared_resource.h",
+    "src/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc",
+    "src/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h",
+    "src/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h",
+    "src/src/core/tsi/alts/handshaker/alts_tsi_utils.cc",
+    "src/src/core/tsi/alts/handshaker/alts_tsi_utils.h",
+    "src/src/core/tsi/alts/handshaker/transport_security_common_api.cc",
+    "src/src/core/tsi/alts/handshaker/transport_security_common_api.h",
+    "src/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc",
+    "src/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h",
+    "src/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc",
+    "src/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h",
+    "src/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h",
+    "src/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc",
+    "src/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h",
+    "src/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc",
+    "src/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h",
+    "src/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc",
+    "src/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h",
+    "src/src/core/tsi/fake_transport_security.cc",
+    "src/src/core/tsi/fake_transport_security.h",
+    "src/src/core/tsi/local_transport_security.cc",
+    "src/src/core/tsi/local_transport_security.h",
+    "src/src/core/tsi/ssl/key_logging/ssl_key_logging.cc",
+    "src/src/core/tsi/ssl/key_logging/ssl_key_logging.h",
+    "src/src/core/tsi/ssl/session_cache/ssl_session.h",
+    "src/src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc",
+    "src/src/core/tsi/ssl/session_cache/ssl_session_cache.cc",
+    "src/src/core/tsi/ssl/session_cache/ssl_session_cache.h",
+    "src/src/core/tsi/ssl/session_cache/ssl_session_openssl.cc",
+    "src/src/core/tsi/ssl_transport_security.cc",
+    "src/src/core/tsi/ssl_transport_security.h",
+    "src/src/core/tsi/ssl_transport_security_utils.cc",
+    "src/src/core/tsi/ssl_transport_security_utils.h",
+    "src/src/core/tsi/ssl_types.h",
+    "src/src/core/tsi/transport_security.cc",
+    "src/src/core/tsi/transport_security.h",
+    "src/src/core/tsi/transport_security_grpc.cc",
+    "src/src/core/tsi/transport_security_grpc.h",
+    "src/src/core/tsi/transport_security_interface.h",
+    "src/third_party/xxhash/xxhash.h",
+  ]
+  public_deps = [
+    ":absl_cleanup_cleanup",
+    ":absl_container_flat_hash_map",
+    ":absl_container_flat_hash_set",
+    ":absl_container_inlined_vector",
+    ":absl_functional_any_invocable",
+    ":absl_functional_bind_front",
+    ":absl_functional_function_ref",
+    ":absl_hash_hash",
+    ":absl_meta_type_traits",
+    ":absl_status_statusor",
+    ":absl_types_span",
+    ":absl_utility_utility",
+    ":address_sorting",
+    ":boringssl",
+    ":gpr",
+    ":re2",
+    ":upb",
+    "../../gn:zlib",
+  ]
+  public_configs = [ "..:grpc_internal_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("grpc_unsecure") {
+  sources = [
+    "src/src/core/ext/filters/census/grpc_context.cc",
+    "src/src/core/ext/filters/channel_idle/channel_idle_filter.cc",
+    "src/src/core/ext/filters/channel_idle/channel_idle_filter.h",
+    "src/src/core/ext/filters/channel_idle/idle_filter_state.cc",
+    "src/src/core/ext/filters/channel_idle/idle_filter_state.h",
+    "src/src/core/ext/filters/client_channel/backend_metric.cc",
+    "src/src/core/ext/filters/client_channel/backend_metric.h",
+    "src/src/core/ext/filters/client_channel/backup_poller.cc",
+    "src/src/core/ext/filters/client_channel/backup_poller.h",
+    "src/src/core/ext/filters/client_channel/channel_connectivity.cc",
+    "src/src/core/ext/filters/client_channel/client_channel.cc",
+    "src/src/core/ext/filters/client_channel/client_channel.h",
+    "src/src/core/ext/filters/client_channel/client_channel_channelz.cc",
+    "src/src/core/ext/filters/client_channel/client_channel_channelz.h",
+    "src/src/core/ext/filters/client_channel/client_channel_factory.cc",
+    "src/src/core/ext/filters/client_channel/client_channel_factory.h",
+    "src/src/core/ext/filters/client_channel/client_channel_plugin.cc",
+    "src/src/core/ext/filters/client_channel/client_channel_service_config.cc",
+    "src/src/core/ext/filters/client_channel/client_channel_service_config.h",
+    "src/src/core/ext/filters/client_channel/config_selector.cc",
+    "src/src/core/ext/filters/client_channel/config_selector.h",
+    "src/src/core/ext/filters/client_channel/connector.h",
+    "src/src/core/ext/filters/client_channel/dynamic_filters.cc",
+    "src/src/core/ext/filters/client_channel/dynamic_filters.h",
+    "src/src/core/ext/filters/client_channel/global_subchannel_pool.cc",
+    "src/src/core/ext/filters/client_channel/global_subchannel_pool.h",
+    "src/src/core/ext/filters/client_channel/health/health_check_client.cc",
+    "src/src/core/ext/filters/client_channel/health/health_check_client.h",
+    "src/src/core/ext/filters/client_channel/http_proxy.cc",
+    "src/src/core/ext/filters/client_channel/http_proxy.h",
+    "src/src/core/ext/filters/client_channel/lb_call_state_internal.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/address_filtering.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/address_filtering.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/backend_metric_data.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/child_policy_handler.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/child_policy_handler.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_balancer_addresses.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_balancer_addresses.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/oob_backend_metric.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/oob_backend_metric.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/oob_backend_metric_internal.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/outlier_detection/outlier_detection.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/outlier_detection/outlier_detection.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/priority/priority.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/ring_hash/ring_hash.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/rls/rls.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/weighted_round_robin/static_stride_scheduler.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/weighted_round_robin/static_stride_scheduler.h",
+    "src/src/core/ext/filters/client_channel/lb_policy/weighted_round_robin/weighted_round_robin.cc",
+    "src/src/core/ext/filters/client_channel/lb_policy/weighted_target/weighted_target.cc",
+    "src/src/core/ext/filters/client_channel/local_subchannel_pool.cc",
+    "src/src/core/ext/filters/client_channel/local_subchannel_pool.h",
+    "src/src/core/ext/filters/client_channel/resolver/binder/binder_resolver.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.cc",
+    "src/src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h",
+    "src/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc",
+    "src/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc",
+    "src/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h",
+    "src/src/core/ext/filters/client_channel/resolver/polling_resolver.cc",
+    "src/src/core/ext/filters/client_channel/resolver/polling_resolver.h",
+    "src/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc",
+    "src/src/core/ext/filters/client_channel/retry_filter.cc",
+    "src/src/core/ext/filters/client_channel/retry_filter.h",
+    "src/src/core/ext/filters/client_channel/retry_service_config.cc",
+    "src/src/core/ext/filters/client_channel/retry_service_config.h",
+    "src/src/core/ext/filters/client_channel/retry_throttle.cc",
+    "src/src/core/ext/filters/client_channel/retry_throttle.h",
+    "src/src/core/ext/filters/client_channel/service_config_channel_arg_filter.cc",
+    "src/src/core/ext/filters/client_channel/subchannel.cc",
+    "src/src/core/ext/filters/client_channel/subchannel.h",
+    "src/src/core/ext/filters/client_channel/subchannel_interface_internal.h",
+    "src/src/core/ext/filters/client_channel/subchannel_pool_interface.cc",
+    "src/src/core/ext/filters/client_channel/subchannel_pool_interface.h",
+    "src/src/core/ext/filters/client_channel/subchannel_stream_client.cc",
+    "src/src/core/ext/filters/client_channel/subchannel_stream_client.h",
+    "src/src/core/ext/filters/deadline/deadline_filter.cc",
+    "src/src/core/ext/filters/deadline/deadline_filter.h",
+    "src/src/core/ext/filters/fault_injection/fault_injection_filter.cc",
+    "src/src/core/ext/filters/fault_injection/fault_injection_filter.h",
+    "src/src/core/ext/filters/fault_injection/fault_injection_service_config_parser.cc",
+    "src/src/core/ext/filters/fault_injection/fault_injection_service_config_parser.h",
+    "src/src/core/ext/filters/http/client/http_client_filter.cc",
+    "src/src/core/ext/filters/http/client/http_client_filter.h",
+    "src/src/core/ext/filters/http/client_authority_filter.cc",
+    "src/src/core/ext/filters/http/client_authority_filter.h",
+    "src/src/core/ext/filters/http/http_filters_plugin.cc",
+    "src/src/core/ext/filters/http/message_compress/compression_filter.cc",
+    "src/src/core/ext/filters/http/message_compress/compression_filter.h",
+    "src/src/core/ext/filters/http/server/http_server_filter.cc",
+    "src/src/core/ext/filters/http/server/http_server_filter.h",
+    "src/src/core/ext/filters/message_size/message_size_filter.cc",
+    "src/src/core/ext/filters/message_size/message_size_filter.h",
+    "src/src/core/ext/transport/chttp2/client/chttp2_connector.cc",
+    "src/src/core/ext/transport/chttp2/client/chttp2_connector.h",
+    "src/src/core/ext/transport/chttp2/server/chttp2_server.cc",
+    "src/src/core/ext/transport/chttp2/server/chttp2_server.h",
+    "src/src/core/ext/transport/chttp2/transport/bin_decoder.cc",
+    "src/src/core/ext/transport/chttp2/transport/bin_decoder.h",
+    "src/src/core/ext/transport/chttp2/transport/bin_encoder.cc",
+    "src/src/core/ext/transport/chttp2/transport/bin_encoder.h",
+    "src/src/core/ext/transport/chttp2/transport/chttp2_transport.cc",
+    "src/src/core/ext/transport/chttp2/transport/chttp2_transport.h",
+    "src/src/core/ext/transport/chttp2/transport/context_list.cc",
+    "src/src/core/ext/transport/chttp2/transport/context_list.h",
+    "src/src/core/ext/transport/chttp2/transport/decode_huff.cc",
+    "src/src/core/ext/transport/chttp2/transport/decode_huff.h",
+    "src/src/core/ext/transport/chttp2/transport/flow_control.cc",
+    "src/src/core/ext/transport/chttp2/transport/flow_control.h",
+    "src/src/core/ext/transport/chttp2/transport/frame.h",
+    "src/src/core/ext/transport/chttp2/transport/frame_data.cc",
+    "src/src/core/ext/transport/chttp2/transport/frame_data.h",
+    "src/src/core/ext/transport/chttp2/transport/frame_goaway.cc",
+    "src/src/core/ext/transport/chttp2/transport/frame_goaway.h",
+    "src/src/core/ext/transport/chttp2/transport/frame_ping.cc",
+    "src/src/core/ext/transport/chttp2/transport/frame_ping.h",
+    "src/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc",
+    "src/src/core/ext/transport/chttp2/transport/frame_rst_stream.h",
+    "src/src/core/ext/transport/chttp2/transport/frame_settings.cc",
+    "src/src/core/ext/transport/chttp2/transport/frame_settings.h",
+    "src/src/core/ext/transport/chttp2/transport/frame_window_update.cc",
+    "src/src/core/ext/transport/chttp2/transport/frame_window_update.h",
+    "src/src/core/ext/transport/chttp2/transport/hpack_constants.h",
+    "src/src/core/ext/transport/chttp2/transport/hpack_encoder.cc",
+    "src/src/core/ext/transport/chttp2/transport/hpack_encoder.h",
+    "src/src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc",
+    "src/src/core/ext/transport/chttp2/transport/hpack_encoder_table.h",
+    "src/src/core/ext/transport/chttp2/transport/hpack_parser.cc",
+    "src/src/core/ext/transport/chttp2/transport/hpack_parser.h",
+    "src/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc",
+    "src/src/core/ext/transport/chttp2/transport/hpack_parser_table.h",
+    "src/src/core/ext/transport/chttp2/transport/http2_settings.cc",
+    "src/src/core/ext/transport/chttp2/transport/http2_settings.h",
+    "src/src/core/ext/transport/chttp2/transport/http_trace.cc",
+    "src/src/core/ext/transport/chttp2/transport/http_trace.h",
+    "src/src/core/ext/transport/chttp2/transport/huffsyms.cc",
+    "src/src/core/ext/transport/chttp2/transport/huffsyms.h",
+    "src/src/core/ext/transport/chttp2/transport/internal.h",
+    "src/src/core/ext/transport/chttp2/transport/parsing.cc",
+    "src/src/core/ext/transport/chttp2/transport/stream_lists.cc",
+    "src/src/core/ext/transport/chttp2/transport/stream_map.cc",
+    "src/src/core/ext/transport/chttp2/transport/stream_map.h",
+    "src/src/core/ext/transport/chttp2/transport/varint.cc",
+    "src/src/core/ext/transport/chttp2/transport/varint.h",
+    "src/src/core/ext/transport/chttp2/transport/writing.cc",
+    "src/src/core/ext/transport/inproc/inproc_plugin.cc",
+    "src/src/core/ext/transport/inproc/inproc_transport.cc",
+    "src/src/core/ext/transport/inproc/inproc_transport.h",
+    "src/src/core/ext/upb-generated/google/api/annotations.upb.c",
+    "src/src/core/ext/upb-generated/google/api/annotations.upb.h",
+    "src/src/core/ext/upb-generated/google/api/http.upb.c",
+    "src/src/core/ext/upb-generated/google/api/http.upb.h",
+    "src/src/core/ext/upb-generated/google/protobuf/any.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/any.upb.h",
+    "src/src/core/ext/upb-generated/google/protobuf/descriptor.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/descriptor.upb.h",
+    "src/src/core/ext/upb-generated/google/protobuf/duration.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/duration.upb.h",
+    "src/src/core/ext/upb-generated/google/protobuf/empty.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/empty.upb.h",
+    "src/src/core/ext/upb-generated/google/protobuf/struct.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/struct.upb.h",
+    "src/src/core/ext/upb-generated/google/protobuf/timestamp.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/timestamp.upb.h",
+    "src/src/core/ext/upb-generated/google/protobuf/wrappers.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/wrappers.upb.h",
+    "src/src/core/ext/upb-generated/google/rpc/status.upb.c",
+    "src/src/core/ext/upb-generated/google/rpc/status.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/altscontext.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/altscontext.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/handshaker.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/handshaker.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/transport_security_common.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/transport_security_common.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/health/v1/health.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/health/v1/health.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/lb/v1/load_balancer.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/lb/v1/load_balancer.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/lookup/v1/rls.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/lookup/v1/rls.upb.h",
+    "src/src/core/ext/upb-generated/validate/validate.upb.c",
+    "src/src/core/ext/upb-generated/validate/validate.upb.h",
+    "src/src/core/ext/upb-generated/xds/data/orca/v3/orca_load_report.upb.c",
+    "src/src/core/ext/upb-generated/xds/data/orca/v3/orca_load_report.upb.h",
+    "src/src/core/ext/upb-generated/xds/service/orca/v3/orca.upb.c",
+    "src/src/core/ext/upb-generated/xds/service/orca/v3/orca.upb.h",
+    "src/src/core/lib/address_utils/parse_address.cc",
+    "src/src/core/lib/address_utils/parse_address.h",
+    "src/src/core/lib/address_utils/sockaddr_utils.cc",
+    "src/src/core/lib/address_utils/sockaddr_utils.h",
+    "src/src/core/lib/avl/avl.h",
+    "src/src/core/lib/backoff/backoff.cc",
+    "src/src/core/lib/backoff/backoff.h",
+    "src/src/core/lib/channel/call_finalization.h",
+    "src/src/core/lib/channel/call_tracer.h",
+    "src/src/core/lib/channel/channel_args.cc",
+    "src/src/core/lib/channel/channel_args.h",
+    "src/src/core/lib/channel/channel_args_preconditioning.cc",
+    "src/src/core/lib/channel/channel_args_preconditioning.h",
+    "src/src/core/lib/channel/channel_fwd.h",
+    "src/src/core/lib/channel/channel_stack.cc",
+    "src/src/core/lib/channel/channel_stack.h",
+    "src/src/core/lib/channel/channel_stack_builder.cc",
+    "src/src/core/lib/channel/channel_stack_builder.h",
+    "src/src/core/lib/channel/channel_stack_builder_impl.cc",
+    "src/src/core/lib/channel/channel_stack_builder_impl.h",
+    "src/src/core/lib/channel/channel_trace.cc",
+    "src/src/core/lib/channel/channel_trace.h",
+    "src/src/core/lib/channel/channelz.cc",
+    "src/src/core/lib/channel/channelz.h",
+    "src/src/core/lib/channel/channelz_registry.cc",
+    "src/src/core/lib/channel/channelz_registry.h",
+    "src/src/core/lib/channel/connected_channel.cc",
+    "src/src/core/lib/channel/connected_channel.h",
+    "src/src/core/lib/channel/context.h",
+    "src/src/core/lib/channel/promise_based_filter.cc",
+    "src/src/core/lib/channel/promise_based_filter.h",
+    "src/src/core/lib/channel/status_util.cc",
+    "src/src/core/lib/channel/status_util.h",
+    "src/src/core/lib/compression/compression.cc",
+    "src/src/core/lib/compression/compression_internal.cc",
+    "src/src/core/lib/compression/compression_internal.h",
+    "src/src/core/lib/compression/message_compress.cc",
+    "src/src/core/lib/compression/message_compress.h",
+    "src/src/core/lib/config/core_configuration.cc",
+    "src/src/core/lib/config/core_configuration.h",
+    "src/src/core/lib/debug/event_log.cc",
+    "src/src/core/lib/debug/event_log.h",
+    "src/src/core/lib/debug/histogram_view.cc",
+    "src/src/core/lib/debug/histogram_view.h",
+    "src/src/core/lib/debug/stats.cc",
+    "src/src/core/lib/debug/stats.h",
+    "src/src/core/lib/debug/stats_data.cc",
+    "src/src/core/lib/debug/stats_data.h",
+    "src/src/core/lib/debug/trace.cc",
+    "src/src/core/lib/debug/trace.h",
+    "src/src/core/lib/event_engine/channel_args_endpoint_config.cc",
+    "src/src/core/lib/event_engine/channel_args_endpoint_config.h",
+    "src/src/core/lib/event_engine/common_closures.h",
+    "src/src/core/lib/event_engine/default_event_engine.cc",
+    "src/src/core/lib/event_engine/default_event_engine.h",
+    "src/src/core/lib/event_engine/default_event_engine_factory.cc",
+    "src/src/core/lib/event_engine/default_event_engine_factory.h",
+    "src/src/core/lib/event_engine/event_engine.cc",
+    "src/src/core/lib/event_engine/executor/executor.h",
+    "src/src/core/lib/event_engine/forkable.cc",
+    "src/src/core/lib/event_engine/forkable.h",
+    "src/src/core/lib/event_engine/handle_containers.h",
+    "src/src/core/lib/event_engine/memory_allocator.cc",
+    "src/src/core/lib/event_engine/poller.h",
+    "src/src/core/lib/event_engine/posix.h",
+    "src/src/core/lib/event_engine/posix_engine/ev_epoll1_linux.cc",
+    "src/src/core/lib/event_engine/posix_engine/ev_epoll1_linux.h",
+    "src/src/core/lib/event_engine/posix_engine/ev_poll_posix.cc",
+    "src/src/core/lib/event_engine/posix_engine/ev_poll_posix.h",
+    "src/src/core/lib/event_engine/posix_engine/event_poller.h",
+    "src/src/core/lib/event_engine/posix_engine/event_poller_posix_default.cc",
+    "src/src/core/lib/event_engine/posix_engine/event_poller_posix_default.h",
+    "src/src/core/lib/event_engine/posix_engine/internal_errqueue.cc",
+    "src/src/core/lib/event_engine/posix_engine/internal_errqueue.h",
+    "src/src/core/lib/event_engine/posix_engine/lockfree_event.cc",
+    "src/src/core/lib/event_engine/posix_engine/lockfree_event.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_endpoint.cc",
+    "src/src/core/lib/event_engine/posix_engine/posix_endpoint.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine.cc",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_closure.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_listener.cc",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_listener.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_listener_utils.cc",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_listener_utils.h",
+    "src/src/core/lib/event_engine/posix_engine/tcp_socket_utils.cc",
+    "src/src/core/lib/event_engine/posix_engine/tcp_socket_utils.h",
+    "src/src/core/lib/event_engine/posix_engine/timer.cc",
+    "src/src/core/lib/event_engine/posix_engine/timer.h",
+    "src/src/core/lib/event_engine/posix_engine/timer_heap.cc",
+    "src/src/core/lib/event_engine/posix_engine/timer_heap.h",
+    "src/src/core/lib/event_engine/posix_engine/timer_manager.cc",
+    "src/src/core/lib/event_engine/posix_engine/timer_manager.h",
+    "src/src/core/lib/event_engine/posix_engine/traced_buffer_list.cc",
+    "src/src/core/lib/event_engine/posix_engine/traced_buffer_list.h",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_eventfd.cc",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_eventfd.h",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_pipe.cc",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_pipe.h",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_posix.h",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_posix_default.cc",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_posix_default.h",
+    "src/src/core/lib/event_engine/resolved_address.cc",
+    "src/src/core/lib/event_engine/resolved_address_internal.h",
+    "src/src/core/lib/event_engine/shim.cc",
+    "src/src/core/lib/event_engine/shim.h",
+    "src/src/core/lib/event_engine/slice.cc",
+    "src/src/core/lib/event_engine/slice_buffer.cc",
+    "src/src/core/lib/event_engine/tcp_socket_utils.cc",
+    "src/src/core/lib/event_engine/tcp_socket_utils.h",
+    "src/src/core/lib/event_engine/thread_pool.cc",
+    "src/src/core/lib/event_engine/thread_pool.h",
+    "src/src/core/lib/event_engine/time_util.cc",
+    "src/src/core/lib/event_engine/time_util.h",
+    "src/src/core/lib/event_engine/trace.cc",
+    "src/src/core/lib/event_engine/trace.h",
+    "src/src/core/lib/event_engine/utils.cc",
+    "src/src/core/lib/event_engine/utils.h",
+    "src/src/core/lib/event_engine/windows/iocp.cc",
+    "src/src/core/lib/event_engine/windows/iocp.h",
+    "src/src/core/lib/event_engine/windows/win_socket.cc",
+    "src/src/core/lib/event_engine/windows/win_socket.h",
+    "src/src/core/lib/event_engine/windows/windows_endpoint.cc",
+    "src/src/core/lib/event_engine/windows/windows_endpoint.h",
+    "src/src/core/lib/event_engine/windows/windows_engine.cc",
+    "src/src/core/lib/event_engine/windows/windows_engine.h",
+    "src/src/core/lib/experiments/config.cc",
+    "src/src/core/lib/experiments/config.h",
+    "src/src/core/lib/experiments/experiments.cc",
+    "src/src/core/lib/experiments/experiments.h",
+    "src/src/core/lib/gpr/spinlock.h",
+    "src/src/core/lib/gprpp/atomic_utils.h",
+    "src/src/core/lib/gprpp/bitset.h",
+    "src/src/core/lib/gprpp/chunked_vector.h",
+    "src/src/core/lib/gprpp/cpp_impl_of.h",
+    "src/src/core/lib/gprpp/dual_ref_counted.h",
+    "src/src/core/lib/gprpp/load_file.cc",
+    "src/src/core/lib/gprpp/load_file.h",
+    "src/src/core/lib/gprpp/manual_constructor.h",
+    "src/src/core/lib/gprpp/match.h",
+    "src/src/core/lib/gprpp/notification.h",
+    "src/src/core/lib/gprpp/orphanable.h",
+    "src/src/core/lib/gprpp/overload.h",
+    "src/src/core/lib/gprpp/packed_table.h",
+    "src/src/core/lib/gprpp/per_cpu.h",
+    "src/src/core/lib/gprpp/ref_counted.h",
+    "src/src/core/lib/gprpp/ref_counted_ptr.h",
+    "src/src/core/lib/gprpp/single_set_ptr.h",
+    "src/src/core/lib/gprpp/sorted_pack.h",
+    "src/src/core/lib/gprpp/status_helper.cc",
+    "src/src/core/lib/gprpp/status_helper.h",
+    "src/src/core/lib/gprpp/table.h",
+    "src/src/core/lib/gprpp/time.cc",
+    "src/src/core/lib/gprpp/time.h",
+    "src/src/core/lib/gprpp/time_averaged_stats.cc",
+    "src/src/core/lib/gprpp/time_averaged_stats.h",
+    "src/src/core/lib/gprpp/unique_type_name.h",
+    "src/src/core/lib/gprpp/validation_errors.cc",
+    "src/src/core/lib/gprpp/validation_errors.h",
+    "src/src/core/lib/gprpp/work_serializer.cc",
+    "src/src/core/lib/gprpp/work_serializer.h",
+    "src/src/core/lib/handshaker/proxy_mapper.h",
+    "src/src/core/lib/handshaker/proxy_mapper_registry.cc",
+    "src/src/core/lib/handshaker/proxy_mapper_registry.h",
+    "src/src/core/lib/http/format_request.cc",
+    "src/src/core/lib/http/format_request.h",
+    "src/src/core/lib/http/httpcli.cc",
+    "src/src/core/lib/http/httpcli.h",
+    "src/src/core/lib/http/parser.cc",
+    "src/src/core/lib/http/parser.h",
+    "src/src/core/lib/iomgr/block_annotate.h",
+    "src/src/core/lib/iomgr/buffer_list.cc",
+    "src/src/core/lib/iomgr/buffer_list.h",
+    "src/src/core/lib/iomgr/call_combiner.cc",
+    "src/src/core/lib/iomgr/call_combiner.h",
+    "src/src/core/lib/iomgr/cfstream_handle.cc",
+    "src/src/core/lib/iomgr/cfstream_handle.h",
+    "src/src/core/lib/iomgr/closure.cc",
+    "src/src/core/lib/iomgr/closure.h",
+    "src/src/core/lib/iomgr/combiner.cc",
+    "src/src/core/lib/iomgr/combiner.h",
+    "src/src/core/lib/iomgr/dualstack_socket_posix.cc",
+    "src/src/core/lib/iomgr/dynamic_annotations.h",
+    "src/src/core/lib/iomgr/endpoint.cc",
+    "src/src/core/lib/iomgr/endpoint.h",
+    "src/src/core/lib/iomgr/endpoint_cfstream.cc",
+    "src/src/core/lib/iomgr/endpoint_cfstream.h",
+    "src/src/core/lib/iomgr/endpoint_pair.h",
+    "src/src/core/lib/iomgr/endpoint_pair_posix.cc",
+    "src/src/core/lib/iomgr/endpoint_pair_windows.cc",
+    "src/src/core/lib/iomgr/error.cc",
+    "src/src/core/lib/iomgr/error.h",
+    "src/src/core/lib/iomgr/error_cfstream.cc",
+    "src/src/core/lib/iomgr/error_cfstream.h",
+    "src/src/core/lib/iomgr/ev_apple.cc",
+    "src/src/core/lib/iomgr/ev_apple.h",
+    "src/src/core/lib/iomgr/ev_epoll1_linux.cc",
+    "src/src/core/lib/iomgr/ev_epoll1_linux.h",
+    "src/src/core/lib/iomgr/ev_poll_posix.cc",
+    "src/src/core/lib/iomgr/ev_poll_posix.h",
+    "src/src/core/lib/iomgr/ev_posix.cc",
+    "src/src/core/lib/iomgr/ev_posix.h",
+    "src/src/core/lib/iomgr/ev_windows.cc",
+    "src/src/core/lib/iomgr/event_engine_shims/closure.cc",
+    "src/src/core/lib/iomgr/event_engine_shims/closure.h",
+    "src/src/core/lib/iomgr/event_engine_shims/endpoint.cc",
+    "src/src/core/lib/iomgr/event_engine_shims/endpoint.h",
+    "src/src/core/lib/iomgr/event_engine_shims/tcp_client.cc",
+    "src/src/core/lib/iomgr/event_engine_shims/tcp_client.h",
+    "src/src/core/lib/iomgr/exec_ctx.cc",
+    "src/src/core/lib/iomgr/exec_ctx.h",
+    "src/src/core/lib/iomgr/executor.cc",
+    "src/src/core/lib/iomgr/executor.h",
+    "src/src/core/lib/iomgr/fork_posix.cc",
+    "src/src/core/lib/iomgr/fork_windows.cc",
+    "src/src/core/lib/iomgr/gethostname.h",
+    "src/src/core/lib/iomgr/gethostname_fallback.cc",
+    "src/src/core/lib/iomgr/gethostname_host_name_max.cc",
+    "src/src/core/lib/iomgr/gethostname_sysconf.cc",
+    "src/src/core/lib/iomgr/grpc_if_nametoindex.h",
+    "src/src/core/lib/iomgr/grpc_if_nametoindex_posix.cc",
+    "src/src/core/lib/iomgr/grpc_if_nametoindex_unsupported.cc",
+    "src/src/core/lib/iomgr/internal_errqueue.cc",
+    "src/src/core/lib/iomgr/internal_errqueue.h",
+    "src/src/core/lib/iomgr/iocp_windows.cc",
+    "src/src/core/lib/iomgr/iocp_windows.h",
+    "src/src/core/lib/iomgr/iomgr.cc",
+    "src/src/core/lib/iomgr/iomgr.h",
+    "src/src/core/lib/iomgr/iomgr_fwd.h",
+    "src/src/core/lib/iomgr/iomgr_internal.cc",
+    "src/src/core/lib/iomgr/iomgr_internal.h",
+    "src/src/core/lib/iomgr/iomgr_posix.cc",
+    "src/src/core/lib/iomgr/iomgr_posix_cfstream.cc",
+    "src/src/core/lib/iomgr/iomgr_windows.cc",
+    "src/src/core/lib/iomgr/load_file.cc",
+    "src/src/core/lib/iomgr/load_file.h",
+    "src/src/core/lib/iomgr/lockfree_event.cc",
+    "src/src/core/lib/iomgr/lockfree_event.h",
+    "src/src/core/lib/iomgr/nameser.h",
+    "src/src/core/lib/iomgr/polling_entity.cc",
+    "src/src/core/lib/iomgr/polling_entity.h",
+    "src/src/core/lib/iomgr/pollset.cc",
+    "src/src/core/lib/iomgr/pollset.h",
+    "src/src/core/lib/iomgr/pollset_set.cc",
+    "src/src/core/lib/iomgr/pollset_set.h",
+    "src/src/core/lib/iomgr/pollset_set_windows.cc",
+    "src/src/core/lib/iomgr/pollset_set_windows.h",
+    "src/src/core/lib/iomgr/pollset_windows.cc",
+    "src/src/core/lib/iomgr/pollset_windows.h",
+    "src/src/core/lib/iomgr/port.h",
+    "src/src/core/lib/iomgr/python_util.h",
+    "src/src/core/lib/iomgr/resolve_address.cc",
+    "src/src/core/lib/iomgr/resolve_address.h",
+    "src/src/core/lib/iomgr/resolve_address_impl.h",
+    "src/src/core/lib/iomgr/resolve_address_posix.cc",
+    "src/src/core/lib/iomgr/resolve_address_posix.h",
+    "src/src/core/lib/iomgr/resolve_address_windows.cc",
+    "src/src/core/lib/iomgr/resolve_address_windows.h",
+    "src/src/core/lib/iomgr/resolved_address.h",
+    "src/src/core/lib/iomgr/sockaddr.h",
+    "src/src/core/lib/iomgr/sockaddr_posix.h",
+    "src/src/core/lib/iomgr/sockaddr_utils_posix.cc",
+    "src/src/core/lib/iomgr/sockaddr_windows.h",
+    "src/src/core/lib/iomgr/socket_factory_posix.cc",
+    "src/src/core/lib/iomgr/socket_factory_posix.h",
+    "src/src/core/lib/iomgr/socket_mutator.cc",
+    "src/src/core/lib/iomgr/socket_mutator.h",
+    "src/src/core/lib/iomgr/socket_utils.h",
+    "src/src/core/lib/iomgr/socket_utils_common_posix.cc",
+    "src/src/core/lib/iomgr/socket_utils_linux.cc",
+    "src/src/core/lib/iomgr/socket_utils_posix.cc",
+    "src/src/core/lib/iomgr/socket_utils_posix.h",
+    "src/src/core/lib/iomgr/socket_utils_windows.cc",
+    "src/src/core/lib/iomgr/socket_windows.cc",
+    "src/src/core/lib/iomgr/socket_windows.h",
+    "src/src/core/lib/iomgr/systemd_utils.cc",
+    "src/src/core/lib/iomgr/systemd_utils.h",
+    "src/src/core/lib/iomgr/tcp_client.cc",
+    "src/src/core/lib/iomgr/tcp_client.h",
+    "src/src/core/lib/iomgr/tcp_client_cfstream.cc",
+    "src/src/core/lib/iomgr/tcp_client_posix.cc",
+    "src/src/core/lib/iomgr/tcp_client_posix.h",
+    "src/src/core/lib/iomgr/tcp_client_windows.cc",
+    "src/src/core/lib/iomgr/tcp_posix.cc",
+    "src/src/core/lib/iomgr/tcp_posix.h",
+    "src/src/core/lib/iomgr/tcp_server.cc",
+    "src/src/core/lib/iomgr/tcp_server.h",
+    "src/src/core/lib/iomgr/tcp_server_posix.cc",
+    "src/src/core/lib/iomgr/tcp_server_utils_posix.h",
+    "src/src/core/lib/iomgr/tcp_server_utils_posix_common.cc",
+    "src/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc",
+    "src/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc",
+    "src/src/core/lib/iomgr/tcp_server_windows.cc",
+    "src/src/core/lib/iomgr/tcp_windows.cc",
+    "src/src/core/lib/iomgr/tcp_windows.h",
+    "src/src/core/lib/iomgr/timer.cc",
+    "src/src/core/lib/iomgr/timer.h",
+    "src/src/core/lib/iomgr/timer_generic.cc",
+    "src/src/core/lib/iomgr/timer_generic.h",
+    "src/src/core/lib/iomgr/timer_heap.cc",
+    "src/src/core/lib/iomgr/timer_heap.h",
+    "src/src/core/lib/iomgr/timer_manager.cc",
+    "src/src/core/lib/iomgr/timer_manager.h",
+    "src/src/core/lib/iomgr/unix_sockets_posix.cc",
+    "src/src/core/lib/iomgr/unix_sockets_posix.h",
+    "src/src/core/lib/iomgr/unix_sockets_posix_noop.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_eventfd.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_nospecial.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_pipe.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_pipe.h",
+    "src/src/core/lib/iomgr/wakeup_fd_posix.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_posix.h",
+    "src/src/core/lib/json/json.h",
+    "src/src/core/lib/json/json_args.h",
+    "src/src/core/lib/json/json_channel_args.h",
+    "src/src/core/lib/json/json_object_loader.cc",
+    "src/src/core/lib/json/json_object_loader.h",
+    "src/src/core/lib/json/json_reader.cc",
+    "src/src/core/lib/json/json_writer.cc",
+    "src/src/core/lib/load_balancing/lb_policy.cc",
+    "src/src/core/lib/load_balancing/lb_policy.h",
+    "src/src/core/lib/load_balancing/lb_policy_factory.h",
+    "src/src/core/lib/load_balancing/lb_policy_registry.cc",
+    "src/src/core/lib/load_balancing/lb_policy_registry.h",
+    "src/src/core/lib/load_balancing/subchannel_interface.h",
+    "src/src/core/lib/promise/activity.cc",
+    "src/src/core/lib/promise/activity.h",
+    "src/src/core/lib/promise/arena_promise.h",
+    "src/src/core/lib/promise/context.h",
+    "src/src/core/lib/promise/detail/basic_join.h",
+    "src/src/core/lib/promise/detail/basic_seq.h",
+    "src/src/core/lib/promise/detail/promise_factory.h",
+    "src/src/core/lib/promise/detail/promise_like.h",
+    "src/src/core/lib/promise/detail/status.h",
+    "src/src/core/lib/promise/detail/switch.h",
+    "src/src/core/lib/promise/exec_ctx_wakeup_scheduler.h",
+    "src/src/core/lib/promise/if.h",
+    "src/src/core/lib/promise/interceptor_list.h",
+    "src/src/core/lib/promise/intra_activity_waiter.h",
+    "src/src/core/lib/promise/latch.h",
+    "src/src/core/lib/promise/loop.h",
+    "src/src/core/lib/promise/map.h",
+    "src/src/core/lib/promise/pipe.h",
+    "src/src/core/lib/promise/poll.h",
+    "src/src/core/lib/promise/promise.h",
+    "src/src/core/lib/promise/race.h",
+    "src/src/core/lib/promise/seq.h",
+    "src/src/core/lib/promise/sleep.cc",
+    "src/src/core/lib/promise/sleep.h",
+    "src/src/core/lib/promise/trace.cc",
+    "src/src/core/lib/promise/trace.h",
+    "src/src/core/lib/promise/try_join.h",
+    "src/src/core/lib/promise/try_seq.h",
+    "src/src/core/lib/resolver/resolver.cc",
+    "src/src/core/lib/resolver/resolver.h",
+    "src/src/core/lib/resolver/resolver_factory.h",
+    "src/src/core/lib/resolver/resolver_registry.cc",
+    "src/src/core/lib/resolver/resolver_registry.h",
+    "src/src/core/lib/resolver/server_address.cc",
+    "src/src/core/lib/resolver/server_address.h",
+    "src/src/core/lib/resource_quota/api.cc",
+    "src/src/core/lib/resource_quota/api.h",
+    "src/src/core/lib/resource_quota/arena.cc",
+    "src/src/core/lib/resource_quota/arena.h",
+    "src/src/core/lib/resource_quota/memory_quota.cc",
+    "src/src/core/lib/resource_quota/memory_quota.h",
+    "src/src/core/lib/resource_quota/periodic_update.cc",
+    "src/src/core/lib/resource_quota/periodic_update.h",
+    "src/src/core/lib/resource_quota/resource_quota.cc",
+    "src/src/core/lib/resource_quota/resource_quota.h",
+    "src/src/core/lib/resource_quota/thread_quota.cc",
+    "src/src/core/lib/resource_quota/thread_quota.h",
+    "src/src/core/lib/resource_quota/trace.cc",
+    "src/src/core/lib/resource_quota/trace.h",
+    "src/src/core/lib/security/authorization/authorization_engine.h",
+    "src/src/core/lib/security/authorization/authorization_policy_provider.h",
+    "src/src/core/lib/security/authorization/authorization_policy_provider_vtable.cc",
+    "src/src/core/lib/security/authorization/evaluate_args.cc",
+    "src/src/core/lib/security/authorization/evaluate_args.h",
+    "src/src/core/lib/security/authorization/grpc_server_authz_filter.cc",
+    "src/src/core/lib/security/authorization/grpc_server_authz_filter.h",
+    "src/src/core/lib/security/certificate_provider/certificate_provider_factory.h",
+    "src/src/core/lib/security/certificate_provider/certificate_provider_registry.cc",
+    "src/src/core/lib/security/certificate_provider/certificate_provider_registry.h",
+    "src/src/core/lib/security/context/security_context.cc",
+    "src/src/core/lib/security/context/security_context.h",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment.cc",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment.h",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc",
+    "src/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc",
+    "src/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc",
+    "src/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h",
+    "src/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc",
+    "src/src/core/lib/security/credentials/call_creds_util.cc",
+    "src/src/core/lib/security/credentials/call_creds_util.h",
+    "src/src/core/lib/security/credentials/channel_creds_registry.h",
+    "src/src/core/lib/security/credentials/composite/composite_credentials.cc",
+    "src/src/core/lib/security/credentials/composite/composite_credentials.h",
+    "src/src/core/lib/security/credentials/credentials.cc",
+    "src/src/core/lib/security/credentials/credentials.h",
+    "src/src/core/lib/security/credentials/fake/fake_credentials.cc",
+    "src/src/core/lib/security/credentials/fake/fake_credentials.h",
+    "src/src/core/lib/security/credentials/insecure/insecure_credentials.cc",
+    "src/src/core/lib/security/credentials/insecure/insecure_credentials.h",
+    "src/src/core/lib/security/credentials/plugin/plugin_credentials.cc",
+    "src/src/core/lib/security/credentials/plugin/plugin_credentials.h",
+    "src/src/core/lib/security/credentials/tls/tls_utils.cc",
+    "src/src/core/lib/security/credentials/tls/tls_utils.h",
+    "src/src/core/lib/security/security_connector/fake/fake_security_connector.cc",
+    "src/src/core/lib/security/security_connector/fake/fake_security_connector.h",
+    "src/src/core/lib/security/security_connector/insecure/insecure_security_connector.cc",
+    "src/src/core/lib/security/security_connector/insecure/insecure_security_connector.h",
+    "src/src/core/lib/security/security_connector/load_system_roots.h",
+    "src/src/core/lib/security/security_connector/load_system_roots_fallback.cc",
+    "src/src/core/lib/security/security_connector/load_system_roots_supported.cc",
+    "src/src/core/lib/security/security_connector/load_system_roots_supported.h",
+    "src/src/core/lib/security/security_connector/security_connector.cc",
+    "src/src/core/lib/security/security_connector/security_connector.h",
+    "src/src/core/lib/security/transport/auth_filters.h",
+    "src/src/core/lib/security/transport/client_auth_filter.cc",
+    "src/src/core/lib/security/transport/secure_endpoint.cc",
+    "src/src/core/lib/security/transport/secure_endpoint.h",
+    "src/src/core/lib/security/transport/security_handshaker.cc",
+    "src/src/core/lib/security/transport/security_handshaker.h",
+    "src/src/core/lib/security/transport/server_auth_filter.cc",
+    "src/src/core/lib/security/transport/tsi_error.cc",
+    "src/src/core/lib/security/transport/tsi_error.h",
+    "src/src/core/lib/security/util/json_util.cc",
+    "src/src/core/lib/security/util/json_util.h",
+    "src/src/core/lib/service_config/service_config.h",
+    "src/src/core/lib/service_config/service_config_call_data.h",
+    "src/src/core/lib/service_config/service_config_impl.cc",
+    "src/src/core/lib/service_config/service_config_impl.h",
+    "src/src/core/lib/service_config/service_config_parser.cc",
+    "src/src/core/lib/service_config/service_config_parser.h",
+    "src/src/core/lib/slice/b64.cc",
+    "src/src/core/lib/slice/b64.h",
+    "src/src/core/lib/slice/percent_encoding.cc",
+    "src/src/core/lib/slice/percent_encoding.h",
+    "src/src/core/lib/slice/slice.cc",
+    "src/src/core/lib/slice/slice.h",
+    "src/src/core/lib/slice/slice_buffer.cc",
+    "src/src/core/lib/slice/slice_buffer.h",
+    "src/src/core/lib/slice/slice_internal.h",
+    "src/src/core/lib/slice/slice_refcount.cc",
+    "src/src/core/lib/slice/slice_refcount.h",
+    "src/src/core/lib/slice/slice_string_helpers.cc",
+    "src/src/core/lib/slice/slice_string_helpers.h",
+    "src/src/core/lib/surface/api_trace.cc",
+    "src/src/core/lib/surface/api_trace.h",
+    "src/src/core/lib/surface/builtins.cc",
+    "src/src/core/lib/surface/builtins.h",
+    "src/src/core/lib/surface/byte_buffer.cc",
+    "src/src/core/lib/surface/byte_buffer_reader.cc",
+    "src/src/core/lib/surface/call.cc",
+    "src/src/core/lib/surface/call.h",
+    "src/src/core/lib/surface/call_details.cc",
+    "src/src/core/lib/surface/call_log_batch.cc",
+    "src/src/core/lib/surface/call_test_only.h",
+    "src/src/core/lib/surface/call_trace.cc",
+    "src/src/core/lib/surface/call_trace.h",
+    "src/src/core/lib/surface/channel.cc",
+    "src/src/core/lib/surface/channel.h",
+    "src/src/core/lib/surface/channel_init.cc",
+    "src/src/core/lib/surface/channel_init.h",
+    "src/src/core/lib/surface/channel_ping.cc",
+    "src/src/core/lib/surface/channel_stack_type.cc",
+    "src/src/core/lib/surface/channel_stack_type.h",
+    "src/src/core/lib/surface/completion_queue.cc",
+    "src/src/core/lib/surface/completion_queue.h",
+    "src/src/core/lib/surface/completion_queue_factory.cc",
+    "src/src/core/lib/surface/completion_queue_factory.h",
+    "src/src/core/lib/surface/event_string.cc",
+    "src/src/core/lib/surface/event_string.h",
+    "src/src/core/lib/surface/init.cc",
+    "src/src/core/lib/surface/init.h",
+    "src/src/core/lib/surface/init_internally.cc",
+    "src/src/core/lib/surface/init_internally.h",
+    "src/src/core/lib/surface/lame_client.cc",
+    "src/src/core/lib/surface/lame_client.h",
+    "src/src/core/lib/surface/metadata_array.cc",
+    "src/src/core/lib/surface/server.cc",
+    "src/src/core/lib/surface/server.h",
+    "src/src/core/lib/surface/validate_metadata.cc",
+    "src/src/core/lib/surface/validate_metadata.h",
+    "src/src/core/lib/surface/version.cc",
+    "src/src/core/lib/transport/bdp_estimator.cc",
+    "src/src/core/lib/transport/bdp_estimator.h",
+    "src/src/core/lib/transport/connectivity_state.cc",
+    "src/src/core/lib/transport/connectivity_state.h",
+    "src/src/core/lib/transport/error_utils.cc",
+    "src/src/core/lib/transport/error_utils.h",
+    "src/src/core/lib/transport/handshaker.cc",
+    "src/src/core/lib/transport/handshaker.h",
+    "src/src/core/lib/transport/handshaker_factory.h",
+    "src/src/core/lib/transport/handshaker_registry.cc",
+    "src/src/core/lib/transport/handshaker_registry.h",
+    "src/src/core/lib/transport/http2_errors.h",
+    "src/src/core/lib/transport/http_connect_handshaker.cc",
+    "src/src/core/lib/transport/http_connect_handshaker.h",
+    "src/src/core/lib/transport/metadata_batch.cc",
+    "src/src/core/lib/transport/metadata_batch.h",
+    "src/src/core/lib/transport/parsed_metadata.cc",
+    "src/src/core/lib/transport/parsed_metadata.h",
+    "src/src/core/lib/transport/pid_controller.cc",
+    "src/src/core/lib/transport/pid_controller.h",
+    "src/src/core/lib/transport/status_conversion.cc",
+    "src/src/core/lib/transport/status_conversion.h",
+    "src/src/core/lib/transport/tcp_connect_handshaker.cc",
+    "src/src/core/lib/transport/tcp_connect_handshaker.h",
+    "src/src/core/lib/transport/timeout_encoding.cc",
+    "src/src/core/lib/transport/timeout_encoding.h",
+    "src/src/core/lib/transport/transport.cc",
+    "src/src/core/lib/transport/transport.h",
+    "src/src/core/lib/transport/transport_fwd.h",
+    "src/src/core/lib/transport/transport_impl.h",
+    "src/src/core/lib/transport/transport_op_string.cc",
+    "src/src/core/lib/uri/uri_parser.cc",
+    "src/src/core/lib/uri/uri_parser.h",
+    "src/src/core/plugin_registry/grpc_plugin_registry.cc",
+    "src/src/core/plugin_registry/grpc_plugin_registry_noextra.cc",
+    "src/src/core/tsi/alts/handshaker/transport_security_common_api.cc",
+    "src/src/core/tsi/alts/handshaker/transport_security_common_api.h",
+    "src/src/core/tsi/fake_transport_security.cc",
+    "src/src/core/tsi/fake_transport_security.h",
+    "src/src/core/tsi/local_transport_security.cc",
+    "src/src/core/tsi/local_transport_security.h",
+    "src/src/core/tsi/transport_security.cc",
+    "src/src/core/tsi/transport_security.h",
+    "src/src/core/tsi/transport_security_grpc.cc",
+    "src/src/core/tsi/transport_security_grpc.h",
+    "src/src/core/tsi/transport_security_interface.h",
+    "src/third_party/xxhash/xxhash.h",
+  ]
+  public_deps = [
+    ":absl_cleanup_cleanup",
+    ":absl_container_flat_hash_map",
+    ":absl_container_flat_hash_set",
+    ":absl_container_inlined_vector",
+    ":absl_functional_any_invocable",
+    ":absl_functional_bind_front",
+    ":absl_functional_function_ref",
+    ":absl_hash_hash",
+    ":absl_meta_type_traits",
+    ":absl_status_statusor",
+    ":absl_types_span",
+    ":absl_utility_utility",
+    ":address_sorting",
+    ":gpr",
+    ":upb",
+  ]
+  public_configs = [ "..:grpc_internal_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+static_library("grpc++") {
+  sources = [
+    "src/src/core/ext/transport/binder/client/binder_connector.cc",
+    "src/src/core/ext/transport/binder/client/binder_connector.h",
+    "src/src/core/ext/transport/binder/client/channel_create.cc",
+    "src/src/core/ext/transport/binder/client/channel_create_impl.cc",
+    "src/src/core/ext/transport/binder/client/channel_create_impl.h",
+    "src/src/core/ext/transport/binder/client/connection_id_generator.cc",
+    "src/src/core/ext/transport/binder/client/connection_id_generator.h",
+    "src/src/core/ext/transport/binder/client/endpoint_binder_pool.cc",
+    "src/src/core/ext/transport/binder/client/endpoint_binder_pool.h",
+    "src/src/core/ext/transport/binder/client/jni_utils.cc",
+    "src/src/core/ext/transport/binder/client/jni_utils.h",
+    "src/src/core/ext/transport/binder/client/security_policy_setting.cc",
+    "src/src/core/ext/transport/binder/client/security_policy_setting.h",
+    "src/src/core/ext/transport/binder/security_policy/binder_security_policy.cc",
+    "src/src/core/ext/transport/binder/server/binder_server.cc",
+    "src/src/core/ext/transport/binder/server/binder_server.h",
+    "src/src/core/ext/transport/binder/server/binder_server_credentials.cc",
+    "src/src/core/ext/transport/binder/transport/binder_stream.h",
+    "src/src/core/ext/transport/binder/transport/binder_transport.cc",
+    "src/src/core/ext/transport/binder/transport/binder_transport.h",
+    "src/src/core/ext/transport/binder/utils/binder_auto_utils.h",
+    "src/src/core/ext/transport/binder/utils/ndk_binder.cc",
+    "src/src/core/ext/transport/binder/utils/ndk_binder.h",
+    "src/src/core/ext/transport/binder/utils/transport_stream_receiver.h",
+    "src/src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc",
+    "src/src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h",
+    "src/src/core/ext/transport/binder/wire_format/binder.h",
+    "src/src/core/ext/transport/binder/wire_format/binder_android.cc",
+    "src/src/core/ext/transport/binder/wire_format/binder_android.h",
+    "src/src/core/ext/transport/binder/wire_format/binder_constants.cc",
+    "src/src/core/ext/transport/binder/wire_format/binder_constants.h",
+    "src/src/core/ext/transport/binder/wire_format/transaction.cc",
+    "src/src/core/ext/transport/binder/wire_format/transaction.h",
+    "src/src/core/ext/transport/binder/wire_format/wire_reader.h",
+    "src/src/core/ext/transport/binder/wire_format/wire_reader_impl.cc",
+    "src/src/core/ext/transport/binder/wire_format/wire_reader_impl.h",
+    "src/src/core/ext/transport/binder/wire_format/wire_writer.cc",
+    "src/src/core/ext/transport/binder/wire_format/wire_writer.h",
+    "src/src/cpp/client/channel_cc.cc",
+    "src/src/cpp/client/client_callback.cc",
+    "src/src/cpp/client/client_context.cc",
+    "src/src/cpp/client/client_interceptor.cc",
+    "src/src/cpp/client/create_channel.cc",
+    "src/src/cpp/client/create_channel_internal.cc",
+    "src/src/cpp/client/create_channel_internal.h",
+    "src/src/cpp/client/create_channel_posix.cc",
+    "src/src/cpp/client/insecure_credentials.cc",
+    "src/src/cpp/client/secure_credentials.cc",
+    "src/src/cpp/client/secure_credentials.h",
+    "src/src/cpp/client/xds_credentials.cc",
+    "src/src/cpp/common/alarm.cc",
+    "src/src/cpp/common/auth_property_iterator.cc",
+    "src/src/cpp/common/channel_arguments.cc",
+    "src/src/cpp/common/channel_filter.cc",
+    "src/src/cpp/common/channel_filter.h",
+    "src/src/cpp/common/completion_queue_cc.cc",
+    "src/src/cpp/common/resource_quota_cc.cc",
+    "src/src/cpp/common/rpc_method.cc",
+    "src/src/cpp/common/secure_auth_context.cc",
+    "src/src/cpp/common/secure_auth_context.h",
+    "src/src/cpp/common/secure_channel_arguments.cc",
+    "src/src/cpp/common/secure_create_auth_context.cc",
+    "src/src/cpp/common/tls_certificate_provider.cc",
+    "src/src/cpp/common/tls_certificate_verifier.cc",
+    "src/src/cpp/common/tls_credentials_options.cc",
+    "src/src/cpp/common/validate_service_config.cc",
+    "src/src/cpp/common/version_cc.cc",
+    "src/src/cpp/server/async_generic_service.cc",
+    "src/src/cpp/server/channel_argument_option.cc",
+    "src/src/cpp/server/create_default_thread_pool.cc",
+    "src/src/cpp/server/dynamic_thread_pool.h",
+    "src/src/cpp/server/external_connection_acceptor_impl.cc",
+    "src/src/cpp/server/external_connection_acceptor_impl.h",
+    "src/src/cpp/server/health/default_health_check_service.cc",
+    "src/src/cpp/server/health/default_health_check_service.h",
+    "src/src/cpp/server/health/health_check_service.cc",
+    "src/src/cpp/server/health/health_check_service_server_builder_option.cc",
+    "src/src/cpp/server/insecure_server_credentials.cc",
+    "src/src/cpp/server/orca/call_metric_recorder.cc",
+    "src/src/cpp/server/secure_server_credentials.cc",
+    "src/src/cpp/server/secure_server_credentials.h",
+    "src/src/cpp/server/server_builder.cc",
+    "src/src/cpp/server/server_callback.cc",
+    "src/src/cpp/server/server_cc.cc",
+    "src/src/cpp/server/server_context.cc",
+    "src/src/cpp/server/server_posix.cc",
+    "src/src/cpp/server/thread_pool_interface.h",
+    "src/src/cpp/server/xds_server_credentials.cc",
+    "src/src/cpp/thread_manager/thread_manager.cc",
+    "src/src/cpp/thread_manager/thread_manager.h",
+    "src/src/cpp/util/byte_buffer_cc.cc",
+    "src/src/cpp/util/status.cc",
+    "src/src/cpp/util/string_ref.cc",
+    "src/src/cpp/util/time_cc.cc",
+  ]
+  public_deps = [ ":grpc" ]
+  public_configs = [ "..:grpc_internal_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("grpc++_alts") {
+  sources = [
+    "src/src/cpp/common/alts_context.cc",
+    "src/src/cpp/common/alts_util.cc",
+  ]
+  public_deps = [ ":grpc++" ]
+  public_configs = [ "..:grpc_internal_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("grpc++_error_details") {
+  sources = [ "src/src/cpp/util/error_details.cc" ]
+  public_deps = [ ":grpc++" ]
+  public_configs = [ "..:grpc_internal_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("grpc++_unsecure") {
+  sources = [
+    "src/src/cpp/client/channel_cc.cc",
+    "src/src/cpp/client/client_callback.cc",
+    "src/src/cpp/client/client_context.cc",
+    "src/src/cpp/client/client_interceptor.cc",
+    "src/src/cpp/client/create_channel.cc",
+    "src/src/cpp/client/create_channel_internal.cc",
+    "src/src/cpp/client/create_channel_internal.h",
+    "src/src/cpp/client/create_channel_posix.cc",
+    "src/src/cpp/client/insecure_credentials.cc",
+    "src/src/cpp/common/alarm.cc",
+    "src/src/cpp/common/channel_arguments.cc",
+    "src/src/cpp/common/channel_filter.cc",
+    "src/src/cpp/common/channel_filter.h",
+    "src/src/cpp/common/completion_queue_cc.cc",
+    "src/src/cpp/common/insecure_create_auth_context.cc",
+    "src/src/cpp/common/resource_quota_cc.cc",
+    "src/src/cpp/common/rpc_method.cc",
+    "src/src/cpp/common/validate_service_config.cc",
+    "src/src/cpp/common/version_cc.cc",
+    "src/src/cpp/server/async_generic_service.cc",
+    "src/src/cpp/server/channel_argument_option.cc",
+    "src/src/cpp/server/create_default_thread_pool.cc",
+    "src/src/cpp/server/dynamic_thread_pool.h",
+    "src/src/cpp/server/external_connection_acceptor_impl.cc",
+    "src/src/cpp/server/external_connection_acceptor_impl.h",
+    "src/src/cpp/server/health/default_health_check_service.cc",
+    "src/src/cpp/server/health/default_health_check_service.h",
+    "src/src/cpp/server/health/health_check_service.cc",
+    "src/src/cpp/server/health/health_check_service_server_builder_option.cc",
+    "src/src/cpp/server/insecure_server_credentials.cc",
+    "src/src/cpp/server/orca/call_metric_recorder.cc",
+    "src/src/cpp/server/server_builder.cc",
+    "src/src/cpp/server/server_callback.cc",
+    "src/src/cpp/server/server_cc.cc",
+    "src/src/cpp/server/server_context.cc",
+    "src/src/cpp/server/server_posix.cc",
+    "src/src/cpp/server/thread_pool_interface.h",
+    "src/src/cpp/thread_manager/thread_manager.cc",
+    "src/src/cpp/thread_manager/thread_manager.h",
+    "src/src/cpp/util/byte_buffer_cc.cc",
+    "src/src/cpp/util/status.cc",
+    "src/src/cpp/util/string_ref.cc",
+    "src/src/cpp/util/time_cc.cc",
+  ]
+  public_deps = [ ":grpc_unsecure" ]
+  public_configs = [ "..:grpc_internal_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("grpc_authorization_provider") {
+  sources = [
+    "src/src/core/ext/filters/client_channel/lb_policy/backend_metric_data.h",
+    "src/src/core/ext/upb-generated/google/protobuf/any.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/any.upb.h",
+    "src/src/core/ext/upb-generated/google/rpc/status.upb.c",
+    "src/src/core/ext/upb-generated/google/rpc/status.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/altscontext.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/altscontext.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/handshaker.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/handshaker.upb.h",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/transport_security_common.upb.c",
+    "src/src/core/ext/upb-generated/src/proto/grpc/gcp/transport_security_common.upb.h",
+    "src/src/core/lib/address_utils/parse_address.cc",
+    "src/src/core/lib/address_utils/parse_address.h",
+    "src/src/core/lib/address_utils/sockaddr_utils.cc",
+    "src/src/core/lib/address_utils/sockaddr_utils.h",
+    "src/src/core/lib/avl/avl.h",
+    "src/src/core/lib/channel/call_finalization.h",
+    "src/src/core/lib/channel/call_tracer.h",
+    "src/src/core/lib/channel/channel_args.cc",
+    "src/src/core/lib/channel/channel_args.h",
+    "src/src/core/lib/channel/channel_args_preconditioning.cc",
+    "src/src/core/lib/channel/channel_args_preconditioning.h",
+    "src/src/core/lib/channel/channel_fwd.h",
+    "src/src/core/lib/channel/channel_stack.cc",
+    "src/src/core/lib/channel/channel_stack.h",
+    "src/src/core/lib/channel/channel_stack_builder.cc",
+    "src/src/core/lib/channel/channel_stack_builder.h",
+    "src/src/core/lib/channel/channel_stack_builder_impl.cc",
+    "src/src/core/lib/channel/channel_stack_builder_impl.h",
+    "src/src/core/lib/channel/channel_trace.cc",
+    "src/src/core/lib/channel/channel_trace.h",
+    "src/src/core/lib/channel/channelz.cc",
+    "src/src/core/lib/channel/channelz.h",
+    "src/src/core/lib/channel/channelz_registry.cc",
+    "src/src/core/lib/channel/channelz_registry.h",
+    "src/src/core/lib/channel/connected_channel.cc",
+    "src/src/core/lib/channel/connected_channel.h",
+    "src/src/core/lib/channel/context.h",
+    "src/src/core/lib/channel/promise_based_filter.cc",
+    "src/src/core/lib/channel/promise_based_filter.h",
+    "src/src/core/lib/channel/status_util.cc",
+    "src/src/core/lib/channel/status_util.h",
+    "src/src/core/lib/compression/compression.cc",
+    "src/src/core/lib/compression/compression_internal.cc",
+    "src/src/core/lib/compression/compression_internal.h",
+    "src/src/core/lib/compression/message_compress.cc",
+    "src/src/core/lib/compression/message_compress.h",
+    "src/src/core/lib/config/core_configuration.cc",
+    "src/src/core/lib/config/core_configuration.h",
+    "src/src/core/lib/debug/event_log.cc",
+    "src/src/core/lib/debug/event_log.h",
+    "src/src/core/lib/debug/histogram_view.cc",
+    "src/src/core/lib/debug/histogram_view.h",
+    "src/src/core/lib/debug/stats.cc",
+    "src/src/core/lib/debug/stats.h",
+    "src/src/core/lib/debug/stats_data.cc",
+    "src/src/core/lib/debug/stats_data.h",
+    "src/src/core/lib/debug/trace.cc",
+    "src/src/core/lib/debug/trace.h",
+    "src/src/core/lib/event_engine/channel_args_endpoint_config.cc",
+    "src/src/core/lib/event_engine/channel_args_endpoint_config.h",
+    "src/src/core/lib/event_engine/common_closures.h",
+    "src/src/core/lib/event_engine/default_event_engine.cc",
+    "src/src/core/lib/event_engine/default_event_engine.h",
+    "src/src/core/lib/event_engine/default_event_engine_factory.cc",
+    "src/src/core/lib/event_engine/default_event_engine_factory.h",
+    "src/src/core/lib/event_engine/event_engine.cc",
+    "src/src/core/lib/event_engine/executor/executor.h",
+    "src/src/core/lib/event_engine/forkable.cc",
+    "src/src/core/lib/event_engine/forkable.h",
+    "src/src/core/lib/event_engine/handle_containers.h",
+    "src/src/core/lib/event_engine/memory_allocator.cc",
+    "src/src/core/lib/event_engine/poller.h",
+    "src/src/core/lib/event_engine/posix.h",
+    "src/src/core/lib/event_engine/posix_engine/ev_epoll1_linux.cc",
+    "src/src/core/lib/event_engine/posix_engine/ev_epoll1_linux.h",
+    "src/src/core/lib/event_engine/posix_engine/ev_poll_posix.cc",
+    "src/src/core/lib/event_engine/posix_engine/ev_poll_posix.h",
+    "src/src/core/lib/event_engine/posix_engine/event_poller.h",
+    "src/src/core/lib/event_engine/posix_engine/event_poller_posix_default.cc",
+    "src/src/core/lib/event_engine/posix_engine/event_poller_posix_default.h",
+    "src/src/core/lib/event_engine/posix_engine/internal_errqueue.cc",
+    "src/src/core/lib/event_engine/posix_engine/internal_errqueue.h",
+    "src/src/core/lib/event_engine/posix_engine/lockfree_event.cc",
+    "src/src/core/lib/event_engine/posix_engine/lockfree_event.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_endpoint.cc",
+    "src/src/core/lib/event_engine/posix_engine/posix_endpoint.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine.cc",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_closure.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_listener.cc",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_listener.h",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_listener_utils.cc",
+    "src/src/core/lib/event_engine/posix_engine/posix_engine_listener_utils.h",
+    "src/src/core/lib/event_engine/posix_engine/tcp_socket_utils.cc",
+    "src/src/core/lib/event_engine/posix_engine/tcp_socket_utils.h",
+    "src/src/core/lib/event_engine/posix_engine/timer.cc",
+    "src/src/core/lib/event_engine/posix_engine/timer.h",
+    "src/src/core/lib/event_engine/posix_engine/timer_heap.cc",
+    "src/src/core/lib/event_engine/posix_engine/timer_heap.h",
+    "src/src/core/lib/event_engine/posix_engine/timer_manager.cc",
+    "src/src/core/lib/event_engine/posix_engine/timer_manager.h",
+    "src/src/core/lib/event_engine/posix_engine/traced_buffer_list.cc",
+    "src/src/core/lib/event_engine/posix_engine/traced_buffer_list.h",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_eventfd.cc",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_eventfd.h",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_pipe.cc",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_pipe.h",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_posix.h",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_posix_default.cc",
+    "src/src/core/lib/event_engine/posix_engine/wakeup_fd_posix_default.h",
+    "src/src/core/lib/event_engine/resolved_address.cc",
+    "src/src/core/lib/event_engine/resolved_address_internal.h",
+    "src/src/core/lib/event_engine/shim.cc",
+    "src/src/core/lib/event_engine/shim.h",
+    "src/src/core/lib/event_engine/slice.cc",
+    "src/src/core/lib/event_engine/slice_buffer.cc",
+    "src/src/core/lib/event_engine/tcp_socket_utils.cc",
+    "src/src/core/lib/event_engine/tcp_socket_utils.h",
+    "src/src/core/lib/event_engine/thread_pool.cc",
+    "src/src/core/lib/event_engine/thread_pool.h",
+    "src/src/core/lib/event_engine/time_util.cc",
+    "src/src/core/lib/event_engine/time_util.h",
+    "src/src/core/lib/event_engine/trace.cc",
+    "src/src/core/lib/event_engine/trace.h",
+    "src/src/core/lib/event_engine/utils.cc",
+    "src/src/core/lib/event_engine/utils.h",
+    "src/src/core/lib/event_engine/windows/iocp.cc",
+    "src/src/core/lib/event_engine/windows/iocp.h",
+    "src/src/core/lib/event_engine/windows/win_socket.cc",
+    "src/src/core/lib/event_engine/windows/win_socket.h",
+    "src/src/core/lib/event_engine/windows/windows_endpoint.cc",
+    "src/src/core/lib/event_engine/windows/windows_endpoint.h",
+    "src/src/core/lib/event_engine/windows/windows_engine.cc",
+    "src/src/core/lib/event_engine/windows/windows_engine.h",
+    "src/src/core/lib/experiments/config.cc",
+    "src/src/core/lib/experiments/config.h",
+    "src/src/core/lib/experiments/experiments.cc",
+    "src/src/core/lib/experiments/experiments.h",
+    "src/src/core/lib/gpr/spinlock.h",
+    "src/src/core/lib/gprpp/atomic_utils.h",
+    "src/src/core/lib/gprpp/bitset.h",
+    "src/src/core/lib/gprpp/chunked_vector.h",
+    "src/src/core/lib/gprpp/cpp_impl_of.h",
+    "src/src/core/lib/gprpp/dual_ref_counted.h",
+    "src/src/core/lib/gprpp/load_file.cc",
+    "src/src/core/lib/gprpp/load_file.h",
+    "src/src/core/lib/gprpp/manual_constructor.h",
+    "src/src/core/lib/gprpp/match.h",
+    "src/src/core/lib/gprpp/notification.h",
+    "src/src/core/lib/gprpp/orphanable.h",
+    "src/src/core/lib/gprpp/overload.h",
+    "src/src/core/lib/gprpp/packed_table.h",
+    "src/src/core/lib/gprpp/per_cpu.h",
+    "src/src/core/lib/gprpp/ref_counted.h",
+    "src/src/core/lib/gprpp/ref_counted_ptr.h",
+    "src/src/core/lib/gprpp/sorted_pack.h",
+    "src/src/core/lib/gprpp/status_helper.cc",
+    "src/src/core/lib/gprpp/status_helper.h",
+    "src/src/core/lib/gprpp/table.h",
+    "src/src/core/lib/gprpp/time.cc",
+    "src/src/core/lib/gprpp/time.h",
+    "src/src/core/lib/gprpp/time_averaged_stats.cc",
+    "src/src/core/lib/gprpp/time_averaged_stats.h",
+    "src/src/core/lib/gprpp/unique_type_name.h",
+    "src/src/core/lib/gprpp/validation_errors.cc",
+    "src/src/core/lib/gprpp/validation_errors.h",
+    "src/src/core/lib/gprpp/work_serializer.cc",
+    "src/src/core/lib/gprpp/work_serializer.h",
+    "src/src/core/lib/handshaker/proxy_mapper.h",
+    "src/src/core/lib/handshaker/proxy_mapper_registry.cc",
+    "src/src/core/lib/handshaker/proxy_mapper_registry.h",
+    "src/src/core/lib/iomgr/block_annotate.h",
+    "src/src/core/lib/iomgr/buffer_list.cc",
+    "src/src/core/lib/iomgr/buffer_list.h",
+    "src/src/core/lib/iomgr/call_combiner.cc",
+    "src/src/core/lib/iomgr/call_combiner.h",
+    "src/src/core/lib/iomgr/cfstream_handle.cc",
+    "src/src/core/lib/iomgr/cfstream_handle.h",
+    "src/src/core/lib/iomgr/closure.cc",
+    "src/src/core/lib/iomgr/closure.h",
+    "src/src/core/lib/iomgr/combiner.cc",
+    "src/src/core/lib/iomgr/combiner.h",
+    "src/src/core/lib/iomgr/dualstack_socket_posix.cc",
+    "src/src/core/lib/iomgr/dynamic_annotations.h",
+    "src/src/core/lib/iomgr/endpoint.cc",
+    "src/src/core/lib/iomgr/endpoint.h",
+    "src/src/core/lib/iomgr/endpoint_cfstream.cc",
+    "src/src/core/lib/iomgr/endpoint_cfstream.h",
+    "src/src/core/lib/iomgr/endpoint_pair.h",
+    "src/src/core/lib/iomgr/endpoint_pair_posix.cc",
+    "src/src/core/lib/iomgr/endpoint_pair_windows.cc",
+    "src/src/core/lib/iomgr/error.cc",
+    "src/src/core/lib/iomgr/error.h",
+    "src/src/core/lib/iomgr/error_cfstream.cc",
+    "src/src/core/lib/iomgr/error_cfstream.h",
+    "src/src/core/lib/iomgr/ev_apple.cc",
+    "src/src/core/lib/iomgr/ev_apple.h",
+    "src/src/core/lib/iomgr/ev_epoll1_linux.cc",
+    "src/src/core/lib/iomgr/ev_epoll1_linux.h",
+    "src/src/core/lib/iomgr/ev_poll_posix.cc",
+    "src/src/core/lib/iomgr/ev_poll_posix.h",
+    "src/src/core/lib/iomgr/ev_posix.cc",
+    "src/src/core/lib/iomgr/ev_posix.h",
+    "src/src/core/lib/iomgr/ev_windows.cc",
+    "src/src/core/lib/iomgr/event_engine_shims/closure.cc",
+    "src/src/core/lib/iomgr/event_engine_shims/closure.h",
+    "src/src/core/lib/iomgr/event_engine_shims/endpoint.cc",
+    "src/src/core/lib/iomgr/event_engine_shims/endpoint.h",
+    "src/src/core/lib/iomgr/event_engine_shims/tcp_client.cc",
+    "src/src/core/lib/iomgr/event_engine_shims/tcp_client.h",
+    "src/src/core/lib/iomgr/exec_ctx.cc",
+    "src/src/core/lib/iomgr/exec_ctx.h",
+    "src/src/core/lib/iomgr/executor.cc",
+    "src/src/core/lib/iomgr/executor.h",
+    "src/src/core/lib/iomgr/fork_posix.cc",
+    "src/src/core/lib/iomgr/fork_windows.cc",
+    "src/src/core/lib/iomgr/gethostname.h",
+    "src/src/core/lib/iomgr/gethostname_fallback.cc",
+    "src/src/core/lib/iomgr/gethostname_host_name_max.cc",
+    "src/src/core/lib/iomgr/gethostname_sysconf.cc",
+    "src/src/core/lib/iomgr/grpc_if_nametoindex.h",
+    "src/src/core/lib/iomgr/grpc_if_nametoindex_posix.cc",
+    "src/src/core/lib/iomgr/grpc_if_nametoindex_unsupported.cc",
+    "src/src/core/lib/iomgr/internal_errqueue.cc",
+    "src/src/core/lib/iomgr/internal_errqueue.h",
+    "src/src/core/lib/iomgr/iocp_windows.cc",
+    "src/src/core/lib/iomgr/iocp_windows.h",
+    "src/src/core/lib/iomgr/iomgr.cc",
+    "src/src/core/lib/iomgr/iomgr.h",
+    "src/src/core/lib/iomgr/iomgr_fwd.h",
+    "src/src/core/lib/iomgr/iomgr_internal.cc",
+    "src/src/core/lib/iomgr/iomgr_internal.h",
+    "src/src/core/lib/iomgr/iomgr_posix.cc",
+    "src/src/core/lib/iomgr/iomgr_posix_cfstream.cc",
+    "src/src/core/lib/iomgr/iomgr_windows.cc",
+    "src/src/core/lib/iomgr/load_file.cc",
+    "src/src/core/lib/iomgr/load_file.h",
+    "src/src/core/lib/iomgr/lockfree_event.cc",
+    "src/src/core/lib/iomgr/lockfree_event.h",
+    "src/src/core/lib/iomgr/nameser.h",
+    "src/src/core/lib/iomgr/polling_entity.cc",
+    "src/src/core/lib/iomgr/polling_entity.h",
+    "src/src/core/lib/iomgr/pollset.cc",
+    "src/src/core/lib/iomgr/pollset.h",
+    "src/src/core/lib/iomgr/pollset_set.cc",
+    "src/src/core/lib/iomgr/pollset_set.h",
+    "src/src/core/lib/iomgr/pollset_set_windows.cc",
+    "src/src/core/lib/iomgr/pollset_set_windows.h",
+    "src/src/core/lib/iomgr/pollset_windows.cc",
+    "src/src/core/lib/iomgr/pollset_windows.h",
+    "src/src/core/lib/iomgr/port.h",
+    "src/src/core/lib/iomgr/python_util.h",
+    "src/src/core/lib/iomgr/resolve_address.cc",
+    "src/src/core/lib/iomgr/resolve_address.h",
+    "src/src/core/lib/iomgr/resolve_address_impl.h",
+    "src/src/core/lib/iomgr/resolve_address_posix.cc",
+    "src/src/core/lib/iomgr/resolve_address_posix.h",
+    "src/src/core/lib/iomgr/resolve_address_windows.cc",
+    "src/src/core/lib/iomgr/resolve_address_windows.h",
+    "src/src/core/lib/iomgr/resolved_address.h",
+    "src/src/core/lib/iomgr/sockaddr.h",
+    "src/src/core/lib/iomgr/sockaddr_posix.h",
+    "src/src/core/lib/iomgr/sockaddr_utils_posix.cc",
+    "src/src/core/lib/iomgr/sockaddr_windows.h",
+    "src/src/core/lib/iomgr/socket_factory_posix.cc",
+    "src/src/core/lib/iomgr/socket_factory_posix.h",
+    "src/src/core/lib/iomgr/socket_mutator.cc",
+    "src/src/core/lib/iomgr/socket_mutator.h",
+    "src/src/core/lib/iomgr/socket_utils.h",
+    "src/src/core/lib/iomgr/socket_utils_common_posix.cc",
+    "src/src/core/lib/iomgr/socket_utils_linux.cc",
+    "src/src/core/lib/iomgr/socket_utils_posix.cc",
+    "src/src/core/lib/iomgr/socket_utils_posix.h",
+    "src/src/core/lib/iomgr/socket_utils_windows.cc",
+    "src/src/core/lib/iomgr/socket_windows.cc",
+    "src/src/core/lib/iomgr/socket_windows.h",
+    "src/src/core/lib/iomgr/systemd_utils.cc",
+    "src/src/core/lib/iomgr/systemd_utils.h",
+    "src/src/core/lib/iomgr/tcp_client.cc",
+    "src/src/core/lib/iomgr/tcp_client.h",
+    "src/src/core/lib/iomgr/tcp_client_cfstream.cc",
+    "src/src/core/lib/iomgr/tcp_client_posix.cc",
+    "src/src/core/lib/iomgr/tcp_client_posix.h",
+    "src/src/core/lib/iomgr/tcp_client_windows.cc",
+    "src/src/core/lib/iomgr/tcp_posix.cc",
+    "src/src/core/lib/iomgr/tcp_posix.h",
+    "src/src/core/lib/iomgr/tcp_server.cc",
+    "src/src/core/lib/iomgr/tcp_server.h",
+    "src/src/core/lib/iomgr/tcp_server_posix.cc",
+    "src/src/core/lib/iomgr/tcp_server_utils_posix.h",
+    "src/src/core/lib/iomgr/tcp_server_utils_posix_common.cc",
+    "src/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc",
+    "src/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc",
+    "src/src/core/lib/iomgr/tcp_server_windows.cc",
+    "src/src/core/lib/iomgr/tcp_windows.cc",
+    "src/src/core/lib/iomgr/tcp_windows.h",
+    "src/src/core/lib/iomgr/timer.cc",
+    "src/src/core/lib/iomgr/timer.h",
+    "src/src/core/lib/iomgr/timer_generic.cc",
+    "src/src/core/lib/iomgr/timer_generic.h",
+    "src/src/core/lib/iomgr/timer_heap.cc",
+    "src/src/core/lib/iomgr/timer_heap.h",
+    "src/src/core/lib/iomgr/timer_manager.cc",
+    "src/src/core/lib/iomgr/timer_manager.h",
+    "src/src/core/lib/iomgr/unix_sockets_posix.cc",
+    "src/src/core/lib/iomgr/unix_sockets_posix.h",
+    "src/src/core/lib/iomgr/unix_sockets_posix_noop.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_eventfd.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_nospecial.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_pipe.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_pipe.h",
+    "src/src/core/lib/iomgr/wakeup_fd_posix.cc",
+    "src/src/core/lib/iomgr/wakeup_fd_posix.h",
+    "src/src/core/lib/json/json.h",
+    "src/src/core/lib/json/json_reader.cc",
+    "src/src/core/lib/json/json_writer.cc",
+    "src/src/core/lib/load_balancing/lb_policy.cc",
+    "src/src/core/lib/load_balancing/lb_policy.h",
+    "src/src/core/lib/load_balancing/lb_policy_factory.h",
+    "src/src/core/lib/load_balancing/lb_policy_registry.cc",
+    "src/src/core/lib/load_balancing/lb_policy_registry.h",
+    "src/src/core/lib/load_balancing/subchannel_interface.h",
+    "src/src/core/lib/matchers/matchers.cc",
+    "src/src/core/lib/matchers/matchers.h",
+    "src/src/core/lib/promise/activity.cc",
+    "src/src/core/lib/promise/activity.h",
+    "src/src/core/lib/promise/arena_promise.h",
+    "src/src/core/lib/promise/context.h",
+    "src/src/core/lib/promise/detail/basic_join.h",
+    "src/src/core/lib/promise/detail/basic_seq.h",
+    "src/src/core/lib/promise/detail/promise_factory.h",
+    "src/src/core/lib/promise/detail/promise_like.h",
+    "src/src/core/lib/promise/detail/status.h",
+    "src/src/core/lib/promise/detail/switch.h",
+    "src/src/core/lib/promise/exec_ctx_wakeup_scheduler.h",
+    "src/src/core/lib/promise/if.h",
+    "src/src/core/lib/promise/interceptor_list.h",
+    "src/src/core/lib/promise/intra_activity_waiter.h",
+    "src/src/core/lib/promise/loop.h",
+    "src/src/core/lib/promise/map.h",
+    "src/src/core/lib/promise/pipe.h",
+    "src/src/core/lib/promise/poll.h",
+    "src/src/core/lib/promise/promise.h",
+    "src/src/core/lib/promise/race.h",
+    "src/src/core/lib/promise/seq.h",
+    "src/src/core/lib/promise/trace.cc",
+    "src/src/core/lib/promise/trace.h",
+    "src/src/core/lib/promise/try_join.h",
+    "src/src/core/lib/promise/try_seq.h",
+    "src/src/core/lib/resolver/resolver.cc",
+    "src/src/core/lib/resolver/resolver.h",
+    "src/src/core/lib/resolver/resolver_factory.h",
+    "src/src/core/lib/resolver/resolver_registry.cc",
+    "src/src/core/lib/resolver/resolver_registry.h",
+    "src/src/core/lib/resolver/server_address.cc",
+    "src/src/core/lib/resolver/server_address.h",
+    "src/src/core/lib/resource_quota/api.cc",
+    "src/src/core/lib/resource_quota/api.h",
+    "src/src/core/lib/resource_quota/arena.cc",
+    "src/src/core/lib/resource_quota/arena.h",
+    "src/src/core/lib/resource_quota/memory_quota.cc",
+    "src/src/core/lib/resource_quota/memory_quota.h",
+    "src/src/core/lib/resource_quota/periodic_update.cc",
+    "src/src/core/lib/resource_quota/periodic_update.h",
+    "src/src/core/lib/resource_quota/resource_quota.cc",
+    "src/src/core/lib/resource_quota/resource_quota.h",
+    "src/src/core/lib/resource_quota/thread_quota.cc",
+    "src/src/core/lib/resource_quota/thread_quota.h",
+    "src/src/core/lib/resource_quota/trace.cc",
+    "src/src/core/lib/resource_quota/trace.h",
+    "src/src/core/lib/security/authorization/authorization_engine.h",
+    "src/src/core/lib/security/authorization/authorization_policy_provider.h",
+    "src/src/core/lib/security/authorization/authorization_policy_provider_vtable.cc",
+    "src/src/core/lib/security/authorization/evaluate_args.cc",
+    "src/src/core/lib/security/authorization/evaluate_args.h",
+    "src/src/core/lib/security/authorization/grpc_authorization_engine.cc",
+    "src/src/core/lib/security/authorization/grpc_authorization_engine.h",
+    "src/src/core/lib/security/authorization/grpc_authorization_policy_provider.cc",
+    "src/src/core/lib/security/authorization/grpc_authorization_policy_provider.h",
+    "src/src/core/lib/security/authorization/grpc_server_authz_filter.cc",
+    "src/src/core/lib/security/authorization/grpc_server_authz_filter.h",
+    "src/src/core/lib/security/authorization/matchers.cc",
+    "src/src/core/lib/security/authorization/matchers.h",
+    "src/src/core/lib/security/authorization/rbac_policy.cc",
+    "src/src/core/lib/security/authorization/rbac_policy.h",
+    "src/src/core/lib/security/authorization/rbac_translator.cc",
+    "src/src/core/lib/security/authorization/rbac_translator.h",
+    "src/src/core/lib/security/certificate_provider/certificate_provider_factory.h",
+    "src/src/core/lib/security/certificate_provider/certificate_provider_registry.cc",
+    "src/src/core/lib/security/certificate_provider/certificate_provider_registry.h",
+    "src/src/core/lib/security/context/security_context.cc",
+    "src/src/core/lib/security/context/security_context.h",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment.cc",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment.h",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc",
+    "src/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc",
+    "src/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc",
+    "src/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc",
+    "src/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h",
+    "src/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc",
+    "src/src/core/lib/security/credentials/call_creds_util.cc",
+    "src/src/core/lib/security/credentials/call_creds_util.h",
+    "src/src/core/lib/security/credentials/channel_creds_registry.h",
+    "src/src/core/lib/security/credentials/composite/composite_credentials.cc",
+    "src/src/core/lib/security/credentials/composite/composite_credentials.h",
+    "src/src/core/lib/security/credentials/credentials.cc",
+    "src/src/core/lib/security/credentials/credentials.h",
+    "src/src/core/lib/security/credentials/plugin/plugin_credentials.cc",
+    "src/src/core/lib/security/credentials/plugin/plugin_credentials.h",
+    "src/src/core/lib/security/credentials/tls/tls_utils.cc",
+    "src/src/core/lib/security/credentials/tls/tls_utils.h",
+    "src/src/core/lib/security/security_connector/load_system_roots.h",
+    "src/src/core/lib/security/security_connector/load_system_roots_fallback.cc",
+    "src/src/core/lib/security/security_connector/load_system_roots_supported.cc",
+    "src/src/core/lib/security/security_connector/load_system_roots_supported.h",
+    "src/src/core/lib/security/security_connector/security_connector.cc",
+    "src/src/core/lib/security/security_connector/security_connector.h",
+    "src/src/core/lib/security/transport/auth_filters.h",
+    "src/src/core/lib/security/transport/client_auth_filter.cc",
+    "src/src/core/lib/security/transport/secure_endpoint.cc",
+    "src/src/core/lib/security/transport/secure_endpoint.h",
+    "src/src/core/lib/security/transport/security_handshaker.cc",
+    "src/src/core/lib/security/transport/security_handshaker.h",
+    "src/src/core/lib/security/transport/server_auth_filter.cc",
+    "src/src/core/lib/security/transport/tsi_error.cc",
+    "src/src/core/lib/security/transport/tsi_error.h",
+    "src/src/core/lib/security/util/json_util.cc",
+    "src/src/core/lib/security/util/json_util.h",
+    "src/src/core/lib/service_config/service_config.h",
+    "src/src/core/lib/service_config/service_config_call_data.h",
+    "src/src/core/lib/service_config/service_config_parser.cc",
+    "src/src/core/lib/service_config/service_config_parser.h",
+    "src/src/core/lib/slice/b64.cc",
+    "src/src/core/lib/slice/b64.h",
+    "src/src/core/lib/slice/percent_encoding.cc",
+    "src/src/core/lib/slice/percent_encoding.h",
+    "src/src/core/lib/slice/slice.cc",
+    "src/src/core/lib/slice/slice.h",
+    "src/src/core/lib/slice/slice_buffer.cc",
+    "src/src/core/lib/slice/slice_buffer.h",
+    "src/src/core/lib/slice/slice_internal.h",
+    "src/src/core/lib/slice/slice_refcount.cc",
+    "src/src/core/lib/slice/slice_refcount.h",
+    "src/src/core/lib/slice/slice_string_helpers.cc",
+    "src/src/core/lib/slice/slice_string_helpers.h",
+    "src/src/core/lib/surface/api_trace.cc",
+    "src/src/core/lib/surface/api_trace.h",
+    "src/src/core/lib/surface/builtins.cc",
+    "src/src/core/lib/surface/builtins.h",
+    "src/src/core/lib/surface/byte_buffer.cc",
+    "src/src/core/lib/surface/byte_buffer_reader.cc",
+    "src/src/core/lib/surface/call.cc",
+    "src/src/core/lib/surface/call.h",
+    "src/src/core/lib/surface/call_details.cc",
+    "src/src/core/lib/surface/call_log_batch.cc",
+    "src/src/core/lib/surface/call_test_only.h",
+    "src/src/core/lib/surface/call_trace.cc",
+    "src/src/core/lib/surface/call_trace.h",
+    "src/src/core/lib/surface/channel.cc",
+    "src/src/core/lib/surface/channel.h",
+    "src/src/core/lib/surface/channel_init.cc",
+    "src/src/core/lib/surface/channel_init.h",
+    "src/src/core/lib/surface/channel_ping.cc",
+    "src/src/core/lib/surface/channel_stack_type.cc",
+    "src/src/core/lib/surface/channel_stack_type.h",
+    "src/src/core/lib/surface/completion_queue.cc",
+    "src/src/core/lib/surface/completion_queue.h",
+    "src/src/core/lib/surface/completion_queue_factory.cc",
+    "src/src/core/lib/surface/completion_queue_factory.h",
+    "src/src/core/lib/surface/event_string.cc",
+    "src/src/core/lib/surface/event_string.h",
+    "src/src/core/lib/surface/init.h",
+    "src/src/core/lib/surface/init_internally.cc",
+    "src/src/core/lib/surface/init_internally.h",
+    "src/src/core/lib/surface/lame_client.cc",
+    "src/src/core/lib/surface/lame_client.h",
+    "src/src/core/lib/surface/metadata_array.cc",
+    "src/src/core/lib/surface/server.cc",
+    "src/src/core/lib/surface/server.h",
+    "src/src/core/lib/surface/validate_metadata.cc",
+    "src/src/core/lib/surface/validate_metadata.h",
+    "src/src/core/lib/surface/version.cc",
+    "src/src/core/lib/transport/connectivity_state.cc",
+    "src/src/core/lib/transport/connectivity_state.h",
+    "src/src/core/lib/transport/error_utils.cc",
+    "src/src/core/lib/transport/error_utils.h",
+    "src/src/core/lib/transport/handshaker.cc",
+    "src/src/core/lib/transport/handshaker.h",
+    "src/src/core/lib/transport/handshaker_factory.h",
+    "src/src/core/lib/transport/handshaker_registry.cc",
+    "src/src/core/lib/transport/handshaker_registry.h",
+    "src/src/core/lib/transport/http2_errors.h",
+    "src/src/core/lib/transport/metadata_batch.cc",
+    "src/src/core/lib/transport/metadata_batch.h",
+    "src/src/core/lib/transport/parsed_metadata.cc",
+    "src/src/core/lib/transport/parsed_metadata.h",
+    "src/src/core/lib/transport/status_conversion.cc",
+    "src/src/core/lib/transport/status_conversion.h",
+    "src/src/core/lib/transport/timeout_encoding.cc",
+    "src/src/core/lib/transport/timeout_encoding.h",
+    "src/src/core/lib/transport/transport.cc",
+    "src/src/core/lib/transport/transport.h",
+    "src/src/core/lib/transport/transport_fwd.h",
+    "src/src/core/lib/transport/transport_impl.h",
+    "src/src/core/lib/transport/transport_op_string.cc",
+    "src/src/core/lib/uri/uri_parser.cc",
+    "src/src/core/lib/uri/uri_parser.h",
+    "src/src/core/tsi/alts/handshaker/transport_security_common_api.cc",
+    "src/src/core/tsi/alts/handshaker/transport_security_common_api.h",
+    "src/src/core/tsi/transport_security.cc",
+    "src/src/core/tsi/transport_security.h",
+    "src/src/core/tsi/transport_security_grpc.cc",
+    "src/src/core/tsi/transport_security_grpc.h",
+    "src/src/core/tsi/transport_security_interface.h",
+  ]
+  public_deps = [
+    ":absl_cleanup_cleanup",
+    ":absl_container_flat_hash_map",
+    ":absl_container_flat_hash_set",
+    ":absl_container_inlined_vector",
+    ":absl_functional_any_invocable",
+    ":absl_functional_function_ref",
+    ":absl_hash_hash",
+    ":absl_meta_type_traits",
+    ":absl_status_statusor",
+    ":absl_types_span",
+    ":absl_utility_utility",
+    ":gpr",
+    ":re2",
+    ":upb",
+  ]
+  public_configs = [ "..:grpc_internal_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+source_set("grpc_plugin_support") {
+  sources = [
+    "src/src/compiler/config.h",
+    "src/src/compiler/config_protobuf.h",
+    "src/src/compiler/cpp_generator.cc",
+    "src/src/compiler/cpp_generator.h",
+    "src/src/compiler/cpp_generator_helpers.h",
+    "src/src/compiler/cpp_plugin.h",
+    "src/src/compiler/csharp_generator.cc",
+    "src/src/compiler/csharp_generator.h",
+    "src/src/compiler/csharp_generator_helpers.h",
+    "src/src/compiler/generator_helpers.h",
+    "src/src/compiler/node_generator.cc",
+    "src/src/compiler/node_generator.h",
+    "src/src/compiler/node_generator_helpers.h",
+    "src/src/compiler/objective_c_generator.cc",
+    "src/src/compiler/objective_c_generator.h",
+    "src/src/compiler/objective_c_generator_helpers.h",
+    "src/src/compiler/php_generator.cc",
+    "src/src/compiler/php_generator.h",
+    "src/src/compiler/php_generator_helpers.h",
+    "src/src/compiler/protobuf_plugin.h",
+    "src/src/compiler/python_generator.cc",
+    "src/src/compiler/python_generator.h",
+    "src/src/compiler/python_generator_helpers.h",
+    "src/src/compiler/python_private_generator.h",
+    "src/src/compiler/ruby_generator.cc",
+    "src/src/compiler/ruby_generator.h",
+    "src/src/compiler/ruby_generator_helpers-inl.h",
+    "src/src/compiler/ruby_generator_map-inl.h",
+    "src/src/compiler/ruby_generator_string-inl.h",
+    "src/src/compiler/schema_interface.h",
+  ]
+  public_deps = [ "..:protoc_lib" ]
+  public_configs = [ "..:grpc_internal_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
+
+executable("grpc_cpp_plugin") {
+  sources = [ "src/src/compiler/cpp_plugin.cc" ]
+  public_deps = [ ":grpc_plugin_support" ]
+  public_configs = [ "..:grpc_internal_config" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}
diff --git a/docs/contributing/perfetto-in-the-press.md b/docs/contributing/perfetto-in-the-press.md
index 40a7578..108849a 100644
--- a/docs/contributing/perfetto-in-the-press.md
+++ b/docs/contributing/perfetto-in-the-press.md
@@ -15,4 +15,5 @@
 experience. We will discuss how we instrument our Snapchat app such that we can
 have the necessary signals for explainability. Additionally, we will describe
 how we incorporate tracing into our development process from local debugging,
-to performance tests and finally in production."
\ No newline at end of file
+to performance tests and finally in production."
+- [Microsoft: Perfetto tooling for analyzing Android, Linux, and Chromium browser performance](https://devblogs.microsoft.com/performance-diagnostics/perfetto-tooling-for-analyzing-android-linux-and-chromium-browser-performance-microsoft-performance-tools-linux-android/)
diff --git a/docs/faq.md b/docs/faq.md
index 29ba71b..7a05913 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -1,23 +1,48 @@
 # Frequently Asked Questions
-This page contains some common questions that the Perfetto team is asked
-and their answers.
-
-- [Frequently Asked Questions](#frequently-asked-questions)
-  - [How do I open trace in UI from command line?](#how-do-i-open-trace-in-ui-from-command-line)
 
 ## How do I open trace in UI from command line?
+
 When collecting traces from the command line, a convenient way to open traces
-is to use the [open_trace_in_ui script](/tools/open_trace_in_ui).
+is to use the [open\_trace\_in\_ui script](/tools/open_trace_in_ui).
 
 This can be used as follows:
+
 ```sh
 curl -OL https://github.com/google/perfetto/raw/master/tools/open_trace_in_ui
 chmod +x open_trace_in_ui
 ./open_trace_in_ui -i /path/to/trace
 ```
 
-If you already have a Perfetto checkout, the first steps can be skipped.
+If you already have a Perfetto checkout, the first two steps can be skipped.
 From the Perfetto root, run:
+
 ```sh
 tools/open_trace_in_ui -i /path/to/trace
-```
\ No newline at end of file
+```
+
+## Incorrectly displayed overlapping events in JSON trace
+
+NOTE: JSON is considered a legacy trace format and is supported on a best-effort
+basis.
+
+The Perfetto UI and trace processor do support overlapping B/E/X events, in
+compliance with the
+[JSON spec](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#heading=h.nso4gcezn7n1).
+As stated in the spec, events are only allowed to perfecty nest.
+
+Users are recommended to emit
+[TrackEvent](/docs/instrumentation/track-events.md)
+instead, Perfetto's native trace format. See
+[this guide](/docs/reference/synthetic-track-event.md) for how common JSON
+events can be represented using
+TrackEvent.
+
+## How can I use Perfetto tooling without instrumenting my program?
+A common problem is that users want to use Perfetto analysis and visualization
+tooling but they don't want to instrument their program. This can be because
+Perfetto is not a good fit for their use-case or because they may already have
+an existing tracing system.
+
+The recommended approach for this is to emit Perfetto's native TrackEvent proto
+format. A reference guide for this is available
+[here](/docs/reference/synthetic-track-event.md).
diff --git a/docs/images/synthetic-track-event-counter.png b/docs/images/synthetic-track-event-counter.png
new file mode 100644
index 0000000..fc659d5
--- /dev/null
+++ b/docs/images/synthetic-track-event-counter.png
Binary files differ
diff --git a/docs/images/synthetic-track-event-flow.png b/docs/images/synthetic-track-event-flow.png
new file mode 100644
index 0000000..e0a20ed
--- /dev/null
+++ b/docs/images/synthetic-track-event-flow.png
Binary files differ
diff --git a/docs/images/synthetic-track-event-process.png b/docs/images/synthetic-track-event-process.png
new file mode 100644
index 0000000..db1379e
--- /dev/null
+++ b/docs/images/synthetic-track-event-process.png
Binary files differ
diff --git a/docs/images/synthetic-track-event-thread.png b/docs/images/synthetic-track-event-thread.png
new file mode 100644
index 0000000..d247662
--- /dev/null
+++ b/docs/images/synthetic-track-event-thread.png
Binary files differ
diff --git a/docs/reference/synthetic-track-event.md b/docs/reference/synthetic-track-event.md
new file mode 100644
index 0000000..1052d77
--- /dev/null
+++ b/docs/reference/synthetic-track-event.md
@@ -0,0 +1,375 @@
+# Writing TrackEvent Protos Synthetically
+This page acts as a reference guide to synthetically generate TrackEvent,
+Perfetto's native protobuf based tracing format. This allows using Perfetto's
+analysis and visualzation without using collecting traces using the Perfetto
+SDK.
+
+TrackEvent protos can be manually written using the
+[official protobuf library](https://protobuf.dev/reference/) or any other
+protobuf-compatible library. To be language-agnostic, the rest of this page
+will show examples using the
+[text format](https://protobuf.dev/reference/protobuf/textformat-spec/)
+representation of protobufs.
+
+## Thread-scoped (sync) slices
+NOTE: in the legacy JSON tracing format, this section correspond to B/E/I/X
+events with the associated M (metadata) events.
+
+Thread scoped slices are used to trace execution of functions on a single
+thread. As only one function runs on a single thread over time, this requires
+that child slices nest perfectly inside parent slices and do not partially
+overlap.
+
+![Thread track event in UI](/docs/images/synthetic-track-event-thread.png)
+
+This is corresponds to the following protos:
+```
+# Emit this packet once *before* you emit the first event for this process.
+packet: {
+  track_descriptor: {
+    uuid: 894893984                     # 64-bit random number.
+    process: {
+      pid: 1234                         # PID for your process.
+      process_name: "My process name"
+    }
+  }
+}
+
+# Emit this packet once *before* you emit the first event for this thread.
+packet: {
+  track_descriptor: {
+    uuid: 49083589894                   # 64-bit random number.
+    parent_uuid: 894893984              # UUID from above.
+    thread: {
+      pid: 1234                         # PID for your process.
+      tid: 5678                         # TID for your thread.
+      thread_name: "My thread name"
+    }
+  }
+}
+
+# The events for this thread.
+packet: {
+  timestamp: 200
+  track_event: {
+    type: TYPE_SLICE_BEGIN
+    track_uuid: 49083589894             # Same random number from above.
+    name: "My special parent"
+  }
+  trusted_packet_sequence_id: 3903809   # Generate *once*, use throughout.
+}
+packet: {
+  timestamp: 250
+  track_event: {
+    type: TYPE_SLICE_BEGIN
+    track_uuid: 49083589894
+    name: "My special child"
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet {
+  timestamp: 285
+  track_event {
+    type: TYPE_INSTANT
+    track_uuid: 49083589894
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet: {
+  timestamp: 290
+  track_event: {
+    type: TYPE_SLICE_END
+    track_uuid: 49083589894
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet: {
+  timestamp: 300
+  track_event: {
+    type: TYPE_SLICE_END
+    track_uuid: 49083589894
+  }
+  trusted_packet_sequence_id: 3903809
+}
+```
+
+## Process-scoped (async) slices
+NOTE: in the legacy JSON tracing format, this section corresponds to b/e/n
+events with the associated M (metadata) events.
+
+Process-scoped slices are useful to trace execution of a "piece of work" across
+multiple threads of a process. A process-scoped slice can start on a thread
+A and end on a thread B. Examples include work submitted to thread pools
+and coroutines.
+
+Process tracks can be named corresponding to the executor and can also have
+child slices in an identical way to thread-scoped slices. Importantly, this
+means slices on a single track must **strictly nest** inside each other
+without overlapping.
+
+As separating each track in the UI can cause a lot of clutter, the UI
+visually merges process tracks with the same name in each process. Note that
+this **does not** change the data model (e.g. in trace processor
+tracks remain separated) as this is simply a visual grouping.
+
+![Process track event in UI](/docs/images/synthetic-track-event-process.png)
+
+This is corresponds to the following protos:
+```
+# The first track associated with this process.
+packet {
+  track_descriptor {
+    uuid: 48948                         # 64-bit random number.
+    name: "My special track"
+    process {
+      pid: 1234                         # PID for your process
+      process_name: "My process name"
+    }
+  }
+}
+# The events for the first track.
+packet {
+  timestamp: 200
+  track_event {
+    type: TYPE_SLICE_BEGIN
+    track_uuid: 48948                   # Same random number from above.
+    name: "My special parent A"
+  }
+  trusted_packet_sequence_id: 3903809   # Generate *once*, use throughout.
+}
+packet {
+  timestamp: 250
+  track_event {
+    type: TYPE_SLICE_BEGIN
+    track_uuid: 48948
+    name: "My special child"
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet {
+  timestamp: 290
+  track_event {
+    type: TYPE_SLICE_END
+    track_uuid: 48948
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet {
+  timestamp: 300
+  track_event {
+    type: TYPE_SLICE_END
+    track_uuid: 48948
+  }
+  trusted_packet_sequence_id: 3903809
+}
+
+# The second track associated with this process. Note how we make the above
+# track the "parent" of this track: this means that this track also is
+# associated to the same process. Note further this shows as the same visual
+# track in the UI but remains separate in the trace and data model. Emitting
+# these events on a separate track is necessary because these events overlap
+# *without* nesting with the above events.
+packet {
+  track_descriptor {
+      uuid: 2390190934                  # 64-bit random number.
+      name: "My special track"
+      parent_uuid: 48948
+  }
+}
+# The events for the second track.
+packet {
+  timestamp: 230
+  track_event {
+    type: TYPE_SLICE_BEGIN
+    track_uuid: 2390190934              # Same random number from above.
+    name: "My special parent A"
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet {
+  timestamp: 260
+  track_event {
+    type: TYPE_SLICE_BEGIN
+    track_uuid: 2390190934
+    name: "My special child"
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet {
+  timestamp: 270
+  track_event {
+    type: TYPE_SLICE_END
+    track_uuid: 2390190934
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet {
+  timestamp: 295
+  track_event {
+    type: TYPE_SLICE_END
+    track_uuid: 2390190934
+  }
+  trusted_packet_sequence_id: 3903809
+}
+```
+
+## Flows
+NOTE: in the legacy JSON tracing format, this section correspond to s/t/f
+events.
+
+Flows allow connecting any number of slices with arrows. The semantic meaning
+of the arrow varies across different applications but most commonly it is used
+to track work passing between threads or processes: e.g. the UI thread asks a
+background thread to do some work and notify when the result is available.
+
+NOTE: a single flow *cannot* fork ands imply represents a single stream of
+arrows from one slice to the next. See [this](https://source.chromium.org/chromium/chromium/src/+/main:third_party/perfetto/protos/perfetto/trace/perfetto_trace.proto;drc=ba05b783d9c29fe334a02913cf157ea1d415d37c;l=9604) comment for information.
+
+![TrackEvent flows in UI](/docs/images/synthetic-track-event-flow.png)
+
+```
+# The main thread of the process.
+packet {
+  track_descriptor {
+    uuid: 93094
+    thread {
+        pid: 100
+        tid: 100
+        thread_name: "Main thread"
+    }
+  }
+}
+packet {
+  timestamp: 200
+  track_event {
+    type: TYPE_SLICE_BEGIN
+    track_uuid: 93094
+    name: "Request generation"
+    flow_ids: 1055895987                  # Random number used to track work
+                                          # across threads/processes.
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet {
+  timestamp: 300
+  track_event {
+    type: TYPE_SLICE_END
+    track_uuid: 93094
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet {
+  timestamp: 400
+  track_event {
+    type: TYPE_SLICE_BEGIN
+    track_uuid: 93094
+    name: "Process background result"
+    flow_ids: 1055895987                  # Same as above.
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet {
+  timestamp: 500
+  track_event {
+    type: TYPE_SLICE_END
+    track_uuid: 93094
+  }
+  trusted_packet_sequence_id: 3903809
+}
+
+# The background thread of the process.
+packet {
+  track_descriptor {
+    uuid: 40489498
+    thread {
+      pid: 100
+      tid: 101
+      thread_name: "Background thread"
+    }
+  }
+}
+packet {
+  timestamp: 310
+  track_event {
+    type: TYPE_SLICE_BEGIN
+    track_uuid: 40489498
+    name: "Background work"
+    flow_ids: 1055895987                  # Same as above.
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet {
+  timestamp: 385
+  track_event {
+    type: TYPE_SLICE_END
+    track_uuid: 40489498
+  }
+  trusted_packet_sequence_id: 3903809
+}
+```
+
+## Counters
+NOTE: in the legacy JSON tracing format, this section correspond to C events.
+
+Counters are useful to represent continuous values which change with time.
+Common examples include CPU frequency, memory usage, battery charge etc.
+
+![TrackEvent counter in UI](/docs/images/synthetic-track-event-counter.png)
+
+This corresponds to the following protos:
+```
+# Counter track scoped to a process.
+packet {
+  track_descriptor {
+    uuid: 1388
+    process {
+      pid: 1024
+      process_name: "MySpecialProcess"
+    }
+  }
+}
+packet {
+  track_descriptor {
+    uuid: 4489498
+    parent_uuid: 1388
+    name: "My special counter"
+    counter {}
+  }
+}
+packet {
+  timestamp: 200
+  track_event {
+    type: TYPE_COUNTER
+    track_uuid: 4489498
+    counter_value: 34567    # Value at start
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet {
+  timestamp: 250
+  track_event {
+    type: TYPE_COUNTER
+    track_uuid: 4489498
+    counter_value: 67890    # Value goes up
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet {
+  timestamp: 300
+  track_event {
+    type: TYPE_COUNTER
+    track_uuid: 4489498
+    counter_value: 12345   # Value goes down
+  }
+  trusted_packet_sequence_id: 3903809
+}
+packet {
+  timestamp: 400
+  track_event {
+    type: TYPE_COUNTER
+    track_uuid: 4489498
+    counter_value: 12345   # Final value
+  }
+  trusted_packet_sequence_id: 3903809
+}
+```
diff --git a/docs/toc.md b/docs/toc.md
index 1d37192..bc96bbc 100644
--- a/docs/toc.md
+++ b/docs/toc.md
@@ -67,6 +67,7 @@
   * [Trace Packet proto](reference/trace-packet-proto.autogen)
   * [perfetto cmdline](reference/perfetto-cli.md)
   * [heap_profile cmdline](reference/heap_profile-cli.md)
+  * [Synthetic TrackEvent](reference/synthetic-track-event.md)
 
 * [Contributing](#)
     * [Getting started](contributing/getting-started.md)
diff --git a/docs/tracing-101.md b/docs/tracing-101.md
index 61ea985..ef80452 100644
--- a/docs/tracing-101.md
+++ b/docs/tracing-101.md
@@ -1,9 +1,6 @@
 # Tracing 101
-*This page provides a birds-eye view of performance analysis using Perfetto.
-The aim is for to orient even people who have no idea what "tracing" is. You
-should walk away from this page with a good understanding of the available
-capabilities in Perfetto and how they can be used to improve performance of a
-system.*
+*This page provides a birds-eye view of performance analysis.
+The aim is to orient people who have no idea what "tracing" is.*
 
 ## Introduction to...
 ### Performance
@@ -148,7 +145,7 @@
 systems. It helps identify the changes they can make to improve performance
 and verify the impact of those changes.
 
-NOTE: In Perfetto, since profiles and traces can be collected simultaneously, 
+NOTE: In Perfetto, since profiles and traces can be collected simultaneously,
 we call everything a "trace" even if it may contain (only) profiling data
 inside.
 
diff --git a/examples/README.md b/examples/README.md
index cc39a16..09a6c90 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -3,3 +3,4 @@
 This directory contains examples for integrating Perfetto into your projects.
 
 - Example applications using the [Perfetto SDK](sdk/README.md).
+- Example applications using the [Perfetto shared library](shared_lib/README.md).
diff --git a/examples/shared_lib/BUILD.gn b/examples/shared_lib/BUILD.gn
new file mode 100644
index 0000000..ff9dc55
--- /dev/null
+++ b/examples/shared_lib/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# On windows C11 (required for atomics) is not really supported, so we can't
+# really build a C executable.
+assert(!is_win)
+
+executable("example_shlib_data_source") {
+  testonly = true
+  deps = [
+    "../../gn:default_deps",
+    "../../src/shared_lib:libperfetto_c",
+  ]
+  sources = [ "example_shlib_data_source.c" ]
+}
diff --git a/examples/shared_lib/README.md b/examples/shared_lib/README.md
new file mode 100644
index 0000000..a28e4ff
--- /dev/null
+++ b/examples/shared_lib/README.md
@@ -0,0 +1,4 @@
+# Perfetto C SDK example project
+
+This directory contains example programs that link with the perfetto dynamic
+tracing library. The interfaces (API and ABI) are not yet styable.
diff --git a/examples/shared_lib/example_shlib_data_source.c b/examples/shared_lib/example_shlib_data_source.c
new file mode 100644
index 0000000..9955d0e
--- /dev/null
+++ b/examples/shared_lib/example_shlib_data_source.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <threads.h>
+#include <time.h>
+
+#include "perfetto/public/data_source.h"
+#include "perfetto/public/producer.h"
+#include "perfetto/public/protos/trace/test_event.pzc.h"
+#include "perfetto/public/protos/trace/trace_packet.pzc.h"
+
+static struct PerfettoDs custom = PERFETTO_DS_INIT();
+
+int main(void) {
+  struct PerfettoProducerInitArgs args = {0};
+  args.backends = PERFETTO_BACKEND_SYSTEM;
+  PerfettoProducerInit(args);
+
+  PerfettoDsRegister(&custom, "com.example.custom_data_source",
+                     PerfettoDsNoCallbacks());
+
+  for (;;) {
+    PERFETTO_DS_TRACE(custom, ctx) {
+      struct PerfettoDsRootTracePacket root;
+      PerfettoDsTracerPacketBegin(&ctx, &root);
+
+      perfetto_protos_TracePacket_set_timestamp(&root.msg, 42);
+      {
+        struct perfetto_protos_TestEvent for_testing;
+        perfetto_protos_TracePacket_begin_for_testing(&root.msg, &for_testing);
+
+        perfetto_protos_TestEvent_set_cstr_str(&for_testing,
+                                               "This is a long string");
+        {
+          struct perfetto_protos_TestEvent_TestPayload payload;
+          perfetto_protos_TestEvent_begin_payload(&for_testing, &payload);
+
+          for (int i = 0; i < 1000; i++) {
+            perfetto_protos_TestEvent_TestPayload_set_cstr_str(&payload,
+                                                               "nested");
+          }
+          perfetto_protos_TestEvent_end_payload(&for_testing, &payload);
+        }
+        perfetto_protos_TracePacket_end_for_testing(&root.msg, &for_testing);
+      }
+      PerfettoDsTracerPacketEnd(&ctx, &root);
+    }
+    thrd_sleep(&(struct timespec){.tv_sec = 1}, NULL);
+  }
+}
diff --git a/gn/BUILD.gn b/gn/BUILD.gn
index 0130ce7..192ede8 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -275,13 +275,8 @@
     "GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
   ]
   cflags = []
-  if (is_clang || !is_win) {
-    cflags += [
-      "-Wno-unknown-warning-option",
-      "-Wno-deprecated",
-      "-Wno-undef",
-      "-Wno-zero-as-null-pointer-constant",
-    ]
+  if (!is_clang || !is_win) {
+    cflags += [ "-Wno-deprecated-declarations" ]
   }
   if (is_clang && is_win) {
     cflags += [
@@ -292,6 +287,8 @@
       "-Wno-undefined-reinterpret-cast",
       "-Wno-inconsistent-missing-destructor-override",
       "-Wno-unused-parameter",
+      "-Wno-shadow-field-in-constructor",
+      "-Wno-zero-as-null-pointer-constant",
     ]
   }
 
diff --git a/gn/perfetto.gni b/gn/perfetto.gni
index 04c07ca..b778d88 100644
--- a/gn/perfetto.gni
+++ b/gn/perfetto.gni
@@ -307,6 +307,12 @@
   # typically handles only itanium mangling.
   enable_perfetto_llvm_demangle =
       enable_perfetto_trace_processor && perfetto_build_standalone
+
+  # Enables gRPC in the Perfetto codebase. gRPC significantly increases build
+  # times and the general footprint of Perfetto. As it only required for
+  # cloud trace processor and even then only to build the final ready-to-ship
+  # binary, don't enable this by default.
+  enable_perfetto_grpc = false
 }
 
 declare_args() {
diff --git a/gn/proto_library.gni b/gn/proto_library.gni
index d1794d5..f69d3ce 100644
--- a/gn/proto_library.gni
+++ b/gn/proto_library.gni
@@ -204,6 +204,40 @@
   }
 }
 
+# Generates .grpc.{h,cc} stubs for services defined in .proto files.
+# We require explicit opt-in as gRPC is very heavyweight so we do not
+# want accidental dependencies on this.
+if (enable_perfetto_grpc) {
+  template("perfetto_grpc_library") {
+    proto_library(target_name) {
+      proto_in_dir = perfetto_root_path
+      proto_out_dir = perfetto_root_path
+      propagate_imports_configs = false
+
+      perfetto_root_path = perfetto_root_path
+      generate_cc = false
+      generate_python = false
+      generator_plugin_label =
+          "$perfetto_root_path/buildtools/grpc:grpc_cpp_plugin"
+      generator_plugin_suffix = ".grpc.pb"
+      deps = [ "$perfetto_root_path/buildtools/grpc:grpc++" ]
+      public_configs = [ "$perfetto_root_path/buildtools:grpc_gen_config" ]
+      if (defined(invoker.deps)) {
+        deps += invoker.deps
+      }
+      forward_variables_from(invoker,
+                             [
+                               "defines",
+                               "extra_configs",
+                               "include_dirs",
+                               "sources",
+                               "testonly",
+                               "visibility",
+                             ])
+    }
+  }
+}
+
 # The template used everywhere in the codebase.
 template("perfetto_proto_library") {
   if (defined(invoker.proto_generators)) {
diff --git a/gn/standalone/BUILD.gn b/gn/standalone/BUILD.gn
index bd21589..1f58eb5 100644
--- a/gn/standalone/BUILD.gn
+++ b/gn/standalone/BUILD.gn
@@ -84,14 +84,15 @@
       "-Wno-reserved-identifier",
       "-Wno-unknown-sanitizers",
       "-Wno-unknown-warning-option",
+      "-Wno-unsafe-buffer-usage",
     ]
   } else if (!is_clang && !is_win) {
     # Use return std::move(...) for compatibility with old GCC compilers.
-    cflags += [ "-Wno-redundant-move" ]
+    cflags_cc = [ "-Wno-redundant-move" ]
 
     # Use after free detection in GCC is still not good enough: it still fails
     # on very obvious false-positives in trace processor.
-    cflags += [ "-Wno-use-after-free" ]
+    cflags_cc += [ "-Wno-use-after-free" ]
   }
 }
 
@@ -110,40 +111,8 @@
   }
 }
 
-config("c++11") {
-  if (!perfetto_cpp11_until_q1_2023) {
-    visibility = []
-  }
-
-  # C++11 is the default on Windows.
-  if (!is_win) {
-    cflags_cc = [ "-std=c++11" ]
-  } else if (is_win && !is_clang) {
-    # Enable standards-conforming compiler behavior.
-    cflags_cc = [ "/permissive-" ]
-  }
-  defines = [ "PERFETTO_ALLOW_SUB_CPP17" ]
-}
-
-# Used in buildtools dependencies for standalone builds.
-config("c++14") {
-  if (perfetto_cpp11_until_q1_2023) {
-    visibility = [ "//buildtools:llvm_demangle" ]
-  } else {
-    visibility = []
-  }
-  if (is_win) {
-    cflags_cc = [ "/std:c++14" ]
-  } else {
-    cflags_cc = [ "-std=c++14" ]
-  }
-}
-
 # Used in buildtools dependencies for standalone builds.
 config("c++17") {
-  if (perfetto_cpp11_until_q1_2023) {
-    visibility = [ "//buildtools:libunwindstack" ]
-  }
   if (is_win) {
     cflags_cc = [ "/std:c++17" ]
   } else {
diff --git a/gn/standalone/BUILDCONFIG.gn b/gn/standalone/BUILDCONFIG.gn
index 772161b..3a1119d 100644
--- a/gn/standalone/BUILDCONFIG.gn
+++ b/gn/standalone/BUILDCONFIG.gn
@@ -17,7 +17,6 @@
   is_clang = true
   is_system_compiler = false
   is_lto = false
-  perfetto_cpp11_until_q1_2023 = false
 
   # This is defined here because it's needed below for determining the value of
   # |is_cross_compiling|.
@@ -72,14 +71,9 @@
   "//gn/standalone:visibility_hidden",
   "//gn/standalone/libc++:config",
   "//gn/standalone/sanitizers:sanitizers_cflags",
+  "//gn/standalone:c++17",
 ]
 
-if (perfetto_cpp11_until_q1_2023) {
-  default_configs += [ "//gn/standalone:c++11" ]
-} else {
-  default_configs += [ "//gn/standalone:c++17" ]
-}
-
 if (is_win) {
   default_configs += [ "//gn/standalone:win32_lean_and_mean" ]
 }
diff --git a/include/perfetto/base/compiler.h b/include/perfetto/base/compiler.h
index bf1b823..7a7a091 100644
--- a/include/perfetto/base/compiler.h
+++ b/include/perfetto/base/compiler.h
@@ -33,16 +33,6 @@
 #define PERFETTO_IS_AT_LEAST_CPP17() 0
 #endif
 
-#if !PERFETTO_IS_AT_LEAST_CPP17() && !defined(PERFETTO_ALLOW_SUB_CPP17)
-#error Perfetto is exploring a switch to C++17 in v34 (Feb 2023). During this \
-transitionary period, we are throwing an error when compiling Perfetto \
-with a standard less than C++17. Please reach out to \
-perfetto-dev@googlegroups.com if you have objections or thoughts on \
-this move. To continue compiling this release of Perfetto with \
-C++11/14, specify the define PERFETTO_ALLOW_SUB_CPP17. \
-*Note*: this define *will* stop working in v34 (Feb 2023).
-#endif
-
 // __has_attribute is supported only by clang and recent versions of GCC.
 // Add a layer to wrap the __has_attribute macro.
 #if defined(__has_attribute)
diff --git a/include/perfetto/base/time.h b/include/perfetto/base/time.h
index 5849d3e..9913f3c 100644
--- a/include/perfetto/base/time.h
+++ b/include/perfetto/base/time.h
@@ -183,6 +183,10 @@
   return std::chrono::duration_cast<TimeSeconds>(GetBootTimeNs());
 }
 
+inline TimeMillis GetBootTimeMs() {
+  return std::chrono::duration_cast<TimeMillis>(GetBootTimeNs());
+}
+
 inline TimeMillis GetWallTimeMs() {
   return std::chrono::duration_cast<TimeMillis>(GetWallTimeNs());
 }
diff --git a/include/perfetto/ext/base/file_utils.h b/include/perfetto/ext/base/file_utils.h
index 7c7b8e0..d2412d0 100644
--- a/include/perfetto/ext/base/file_utils.h
+++ b/include/perfetto/ext/base/file_utils.h
@@ -27,7 +27,6 @@
 #include "perfetto/base/export.h"
 #include "perfetto/base/status.h"
 #include "perfetto/ext/base/scoped_file.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/utils.h"
 
 namespace perfetto {
@@ -94,9 +93,6 @@
 base::Status ListFilesRecursive(const std::string& dir_path,
                                 std::vector<std::string>& output);
 
-// Returns the size of the file at `path` or nullopt in case of error.
-Optional<size_t> GetFileSize(const std::string& path);
-
 }  // namespace base
 }  // namespace perfetto
 
diff --git a/include/perfetto/ext/base/periodic_task.h b/include/perfetto/ext/base/periodic_task.h
index b948322..e58eb01 100644
--- a/include/perfetto/ext/base/periodic_task.h
+++ b/include/perfetto/ext/base/periodic_task.h
@@ -31,14 +31,17 @@
 // A periodic task utility class. It wraps the logic necessary to do periodic
 // tasks using a TaskRunner, taking care of subtleties like ensuring that
 // outstanding tasks are cancelled after reset/dtor.
-// Tasks are aligned on wall time, this is to ensure that when using multiple
-// periodic tasks, they happen at the same time, minimizing wakeups.
+// Tasks are aligned on wall time (unless they are |one_shot|). This is to
+// ensure that when using multiple periodic tasks, they happen at the same time,
+// minimizing context switches.
 // On Linux/Android it also supports suspend-aware mode (via timerfd). On other
 // operating systems it falls back to PostDelayedTask, which is not
 // suspend-aware.
 // TODO(primiano): this should probably become a periodic timer scheduler, so we
 // can use one FD for everything rather than one FD per task. For now we take
 // the hit of a FD-per-task to keep this low-risk.
+// TODO(primiano): consider renaming this class to TimerTask. When |one_shot|
+// is set, the "Periodic" part of the class name becomes a lie.
 class PeriodicTask {
  public:
   explicit PeriodicTask(base::TaskRunner*);
@@ -49,6 +52,7 @@
     std::function<void()> task = nullptr;
     bool start_first_task_immediately = false;
     bool use_suspend_aware_timer = false;
+    bool one_shot = false;
   };
 
   void Start(Args);
diff --git a/include/perfetto/ext/base/status_or.h b/include/perfetto/ext/base/status_or.h
index ee0df07..7067295 100644
--- a/include/perfetto/ext/base/status_or.h
+++ b/include/perfetto/ext/base/status_or.h
@@ -32,6 +32,10 @@
 template <typename T>
 class StatusOr {
  public:
+  // Matches naming of declarations in similar types e.g. std::optional,
+  // std::variant.
+  using value_type = T;
+
   // Intentionally implicit to allow idomatic usage (e.g. returning value/status
   // from base::StatusOr returning function).
   StatusOr(base::Status status) : StatusOr(std::move(status), base::nullopt) {
diff --git a/include/perfetto/ext/base/string_utils.h b/include/perfetto/ext/base/string_utils.h
index bca19c8..828d214 100644
--- a/include/perfetto/ext/base/string_utils.h
+++ b/include/perfetto/ext/base/string_utils.h
@@ -211,6 +211,7 @@
   std::string ToStdString() const { return std::string(buf_, len_); }
   const char* c_str() const { return buf_; }
   size_t len() const { return len_; }
+  char* mutable_data() { return buf_; }
 
  private:
   char buf_[N];
diff --git a/include/perfetto/ext/base/threading/BUILD.gn b/include/perfetto/ext/base/threading/BUILD.gn
index ab12670..cb11115 100644
--- a/include/perfetto/ext/base/threading/BUILD.gn
+++ b/include/perfetto/ext/base/threading/BUILD.gn
@@ -15,7 +15,10 @@
 import("../../../../../gn/perfetto.gni")
 
 source_set("threading") {
-  sources = [ "thread_pool.h" ]
+  sources = [
+    "channel.h",
+    "thread_pool.h",
+  ]
   deps = [
     "..:base",
     "../../../../../gn:default_deps",
diff --git a/include/perfetto/ext/base/threading/channel.h b/include/perfetto/ext/base/threading/channel.h
new file mode 100644
index 0000000..d117ff0
--- /dev/null
+++ b/include/perfetto/ext/base/threading/channel.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_THREADING_CHANNEL_H_
+#define INCLUDE_PERFETTO_EXT_BASE_THREADING_CHANNEL_H_
+
+#include <mutex>
+
+#include "perfetto/base/compiler.h"
+#include "perfetto/base/platform_handle.h"
+#include "perfetto/ext/base/circular_queue.h"
+#include "perfetto/ext/base/event_fd.h"
+#include "perfetto/ext/base/optional.h"
+
+namespace perfetto {
+namespace base {
+
+// Unidirectional conduit used to send values between threads with a fixed-sized
+// buffer in-between.
+//
+// When a channel is read from when empty or written to when full, the operation
+// will not succeed and the caller can choose to a) abandon the operation,
+// or b) use |read_fd| or |write_fd| (as appropriate) which will be become
+// "ready" (i.e. base::TaskRunner watches will fire) when the operation would
+// succeed.
+//
+// A channel is very similar to a Unix pipe except with the values being sent
+// a) not needing to be serializable b) data does not go through the kernel.
+template <typename T>
+class Channel {
+ public:
+  struct ReadResult {
+    ReadResult(base::Optional<T> _item, bool _is_closed)
+        : item(std::move(_item)), is_closed(_is_closed) {}
+
+    bool operator==(const ReadResult& res) const {
+      return item == res.item && is_closed == res.is_closed;
+    }
+
+    // The item read from the channel or base::nullopt if the channel is empty.
+    // If so, callers can use |read_fd| to be notified when a read operation
+    // would succeed.
+    base::Optional<T> item;
+
+    // Indicates the channel is closed. Readers can continue to read from the
+    // channel and any buffered elements will be correctly returned. Moreover,
+    // any future reads will also have |is_closed| == true and |read_fd| will be
+    // ready forever.
+    //
+    // Once a ReadResult is returned with |item| == base::nullopt and
+    // |is_closed| == true, no further values will ever be returned.
+    bool is_closed;
+  };
+  struct WriteResult {
+    WriteResult(bool _success, bool _is_closed)
+        : success(std::move(_success)), is_closed(_is_closed) {}
+
+    bool operator==(const WriteResult& res) const {
+      return success == res.success && is_closed == res.is_closed;
+    }
+
+    // Returns whether the write to the channel was successful. If this is
+    // false, callers can use |write_fd| to be notified when future writes
+    // would succeed. Note that callers should also check |is_closed| as another
+    // writer may have closed the channel.
+    bool success;
+
+    // Indicates that the channel is closed. If this value is true, |success|
+    // will be |false| Moreover, any further writes will continue to return
+    // |success| == false, |is_closed| == true and |write_fd| will be ready
+    // forever.
+    bool is_closed;
+  };
+
+  // Creates a channel with a capacity at least as large as |capacity_hint|. The
+  // capacity *must* be greater than zero.
+  //
+  // Note that it's possible that a capacity > |capacity_hint| will be chosen:
+  // it is implementation defined when this might happen.
+  explicit Channel(uint32_t capacity_hint) : elements_(capacity_hint) {
+    PERFETTO_DCHECK(capacity_hint > 0);
+
+    // It's very important that we make sure |write_fd| is ready to avoid
+    // deadlocks.
+    write_fd_.Notify();
+  }
+
+  // Attempts to read from the channel and returns the result of the attempt.
+  // See |ReadResult| for more information on the result.
+  PERFETTO_WARN_UNUSED_RESULT ReadResult ReadNonBlocking() {
+    std::lock_guard<std::mutex> lock(mutex_);
+    if (elements_.empty()) {
+      return ReadResult(base::nullopt, is_closed_);
+    }
+    if (elements_.capacity() == elements_.size()) {
+      write_fd_.Notify();
+    }
+    T value = std::move(elements_.front());
+    elements_.pop_front();
+    if (!is_closed_ && elements_.empty()) {
+      read_fd_.Clear();
+    }
+    return ReadResult(std::move(value), is_closed_);
+  }
+
+  // Attempts to write to the channel and returns the result of the attempt.
+  // See |WriteResult| for more information on the result.
+  //
+  // IMPORTANT: if this function returns |success| == false, |element| *will
+  // not* be modified. This allows the caller to try again with the same value.
+  PERFETTO_WARN_UNUSED_RESULT WriteResult WriteNonBlocking(T&& element) {
+    std::lock_guard<std::mutex> lock(mutex_);
+    if (is_closed_) {
+      return WriteResult{false, true};
+    }
+    if (elements_.size() == elements_.capacity()) {
+      return WriteResult{false, false};
+    }
+    if (elements_.empty()) {
+      read_fd_.Notify();
+    }
+    elements_.emplace_back(std::move(element));
+    if (elements_.size() == elements_.capacity()) {
+      write_fd_.Clear();
+    }
+    return WriteResult{true, false};
+  }
+
+  // Closes the channel for to any further writes.
+  //
+  // Note: this function will make both |read_fd| and |write_fd| ready to
+  // avoid deadlocks. Callers should correctly handle |is_closed| being
+  // false from |ReadNonBlocking| and |WriteNonBlocking| to stop watching the
+  // fds to avoid poll returning immediately.
+  //
+  // We prefer this behaviour as it's a lot more obvious something is wrong when
+  // it spins and takes 100% CPU rather than silently deadlocking.
+  void Close() {
+    std::lock_guard<std::mutex> lock(mutex_);
+    is_closed_ = true;
+
+    // Make both fds ready to avoid deadlocks.
+    read_fd_.Notify();
+    write_fd_.Notify();
+  }
+
+  // Notification FD for when |ReadNonBlocking| would succeed. Can be useful to
+  // pass to AddFileDescriptorWatch to read data from the channel.
+  base::PlatformHandle read_fd() const { return read_fd_.fd(); }
+
+  // Notification FD for when |WriteNonBlocking| would succeed. Can be useful to
+  // pass to AddFileDescriptorWatch to send data through the channel.
+  base::PlatformHandle write_fd() const { return write_fd_.fd(); }
+
+ private:
+  std::mutex mutex_;
+  base::CircularQueue<T> elements_;
+  bool is_closed_ = false;
+
+  base::EventFd read_fd_;
+  base::EventFd write_fd_;
+};
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_THREADING_CHANNEL_H_
diff --git a/include/perfetto/protozero/BUILD.gn b/include/perfetto/protozero/BUILD.gn
index c99f7fd..4c6c08f 100644
--- a/include/perfetto/protozero/BUILD.gn
+++ b/include/perfetto/protozero/BUILD.gn
@@ -20,6 +20,7 @@
     "cpp_message_obj.h",
     "field.h",
     "field_writer.h",
+    "gen_field_helpers.h",
     "message.h",
     "message_arena.h",
     "message_handle.h",
diff --git a/include/perfetto/protozero/gen_field_helpers.h b/include/perfetto/protozero/gen_field_helpers.h
new file mode 100644
index 0000000..ad3dee8
--- /dev/null
+++ b/include/perfetto/protozero/gen_field_helpers.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PROTOZERO_GEN_FIELD_HELPERS_H_
+#define INCLUDE_PERFETTO_PROTOZERO_GEN_FIELD_HELPERS_H_
+
+#include "perfetto/protozero/message.h"
+#include "perfetto/protozero/proto_decoder.h"
+#include "perfetto/protozero/proto_utils.h"
+#include "perfetto/protozero/scattered_heap_buffer.h"
+
+namespace protozero {
+namespace internal {
+namespace gen_helpers {
+
+// This file implements some helpers used by the protobuf generated code in the
+// .gen.cc files.
+//
+// The .gen.cc generated protobuf implementation (as opposed to the .pbzero.h
+// implementation) is not zero-copy and is not supposed to be used in fast
+// paths, so most of these helpers are designed to reduce binary size.
+
+void DeserializeString(const protozero::Field& field, std::string* dst);
+
+// Read packed repeated elements (serialized as `wire_type`) from `field` into
+// the `*dst` vector. Returns false if some bytes of `field` could not be
+// interpreted correctly as `wire_type`.
+template <proto_utils::ProtoWireType wire_type, typename CppType>
+bool DeserializePackedRepeated(const protozero::Field& field,
+                               std::vector<CppType>* dst) {
+  bool parse_error = false;
+  for (::protozero::PackedRepeatedFieldIterator<wire_type, CppType> rep(
+           field.data(), field.size(), &parse_error);
+       rep; ++rep) {
+    dst->emplace_back(*rep);
+  }
+  return !parse_error;
+}
+
+extern template bool
+DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt, uint64_t>(
+    const protozero::Field& field,
+    std::vector<uint64_t>* dst);
+
+extern template bool
+DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt, int64_t>(
+    const protozero::Field& field,
+    std::vector<int64_t>* dst);
+
+extern template bool
+DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt, uint32_t>(
+    const protozero::Field& field,
+    std::vector<uint32_t>* dst);
+
+extern template bool
+DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt, int32_t>(
+    const protozero::Field& field,
+    std::vector<int32_t>* dst);
+
+// Serializers for different type of fields
+
+void SerializeTinyVarInt(uint32_t field_id, bool value, Message* msg);
+
+template <typename T>
+void SerializeExtendedVarInt(uint32_t field_id, T value, Message* msg) {
+  msg->AppendVarInt(field_id, value);
+}
+
+extern template void SerializeExtendedVarInt<uint64_t>(uint32_t field_id,
+                                                       uint64_t value,
+                                                       Message* msg);
+
+extern template void SerializeExtendedVarInt<uint32_t>(uint32_t field_id,
+                                                       uint32_t value,
+                                                       Message* msg);
+
+template <typename T>
+void SerializeVarInt(uint32_t field_id, T value, Message* msg) {
+  SerializeExtendedVarInt(
+      field_id, proto_utils::ExtendValueForVarIntSerialization(value), msg);
+}
+
+template <typename T>
+void SerializeSignedVarInt(uint32_t field_id, T value, Message* msg) {
+  SerializeVarInt(field_id, proto_utils::ZigZagEncode(value), msg);
+}
+
+template <typename T>
+void SerializeFixed(uint32_t field_id, T value, Message* msg) {
+  msg->AppendFixed(field_id, value);
+}
+
+extern template void SerializeFixed<double>(uint32_t field_id,
+                                            double value,
+                                            Message* msg);
+
+extern template void SerializeFixed<float>(uint32_t field_id,
+                                           float value,
+                                           Message* msg);
+
+extern template void SerializeFixed<uint64_t>(uint32_t field_id,
+                                              uint64_t value,
+                                              Message* msg);
+
+extern template void SerializeFixed<int64_t>(uint32_t field_id,
+                                             int64_t value,
+                                             Message* msg);
+
+extern template void SerializeFixed<uint32_t>(uint32_t field_id,
+                                              uint32_t value,
+                                              Message* msg);
+
+extern template void SerializeFixed<int32_t>(uint32_t field_id,
+                                             int32_t value,
+                                             Message* msg);
+
+void SerializeString(uint32_t field_id, const std::string& value, Message* msg);
+
+void SerializeUnknownFields(const std::string& unknown_fields, Message* msg);
+
+// Wrapper around HeapBuffered that avoids inlining.
+class MessageSerializer {
+ public:
+  MessageSerializer();
+  ~MessageSerializer();
+
+  Message* get() { return msg_.get(); }
+  std::vector<uint8_t> SerializeAsArray();
+  std::string SerializeAsString();
+
+ private:
+  HeapBuffered<Message> msg_;
+};
+
+}  // namespace gen_helpers
+}  // namespace internal
+}  // namespace protozero
+
+#endif  // INCLUDE_PERFETTO_PROTOZERO_GEN_FIELD_HELPERS_H_
diff --git a/include/perfetto/protozero/proto_utils.h b/include/perfetto/protozero/proto_utils.h
index eafd6ce..2896b3d 100644
--- a/include/perfetto/protozero/proto_utils.h
+++ b/include/perfetto/protozero/proto_utils.h
@@ -178,7 +178,9 @@
 }
 
 template <typename T>
-inline uint8_t* WriteVarInt(T value, uint8_t* target) {
+auto ExtendValueForVarIntSerialization(T value) -> typename std::make_unsigned<
+    typename std::conditional<std::is_unsigned<T>::value, T, int64_t>::type>::
+    type {
   // If value is <= 0 we must first sign extend to int64_t (see [1]).
   // Finally we always cast to an unsigned value to to avoid arithmetic
   // (sign expanding) shifts in the while loop.
@@ -198,6 +200,13 @@
   MaybeExtendedType extended_value = static_cast<MaybeExtendedType>(value);
   UnsignedType unsigned_value = static_cast<UnsignedType>(extended_value);
 
+  return unsigned_value;
+}
+
+template <typename T>
+inline uint8_t* WriteVarInt(T value, uint8_t* target) {
+  auto unsigned_value = ExtendValueForVarIntSerialization(value);
+
   while (unsigned_value >= 0x80) {
     *target++ = static_cast<uint8_t>(unsigned_value) | 0x80;
     unsigned_value >>= 7;
diff --git a/include/perfetto/public/BUILD.gn b/include/perfetto/public/BUILD.gn
index ae2c91d..422187c 100644
--- a/include/perfetto/public/BUILD.gn
+++ b/include/perfetto/public/BUILD.gn
@@ -18,6 +18,21 @@
 }
 
 source_set("protozero") {
-  sources = [ "stream_writer.h" ]
-  public_deps = [ "./abi:protozero" ]
+  sources = [ "pb_utils.h" ]
+}
+
+source_set("public") {
+  sources = [
+    "data_source.h",
+    "pb_macros.h",
+    "pb_msg.h",
+    "producer.h",
+    "stream_writer.h",
+  ]
+  public_deps = [
+    ":base",
+    ":protozero",
+    "./abi:public",
+    "./protos",
+  ]
 }
diff --git a/include/perfetto/public/abi/BUILD.gn b/include/perfetto/public/abi/BUILD.gn
index f17098b..9f90a27 100644
--- a/include/perfetto/public/abi/BUILD.gn
+++ b/include/perfetto/public/abi/BUILD.gn
@@ -13,9 +13,20 @@
 # limitations under the License.
 
 source_set("base") {
-  sources = [ "export.h" ]
+  sources = [
+    "atomic.h",
+    "export.h",
+  ]
 }
 
-source_set("protozero") {
-  sources = [ "stream_writer_abi.h" ]
+source_set("public") {
+  sources = [
+    "backend_type.h",
+    "data_source_abi.h",
+    "heap_buffer.h",
+    "producer.h",
+    "stream_writer_abi.h",
+    "tracing_session_abi.h",
+  ]
+  public_deps = [ ":base" ]
 }
diff --git a/include/perfetto/public/abi/atomic.h b/include/perfetto/public/abi/atomic.h
new file mode 100644
index 0000000..5506f8b
--- /dev/null
+++ b/include/perfetto/public/abi/atomic.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PUBLIC_ABI_ATOMIC_H_
+#define INCLUDE_PERFETTO_PUBLIC_ABI_ATOMIC_H_
+
+// Problem: C++11 and C11 use a different syntax for atomics and the C11 syntax
+// is not supported in C++11.
+//
+// This header bridges the gap.
+//
+// This assumes that C++11 atomics are binary compatible with C11 atomics. While
+// this is technically not required by the standards, reasonable compilers
+// appear to guarantee this.
+
+#ifdef __cplusplus
+#include <atomic>
+#else
+#include <stdatomic.h>
+#endif
+
+#ifdef __cplusplus
+#define PERFETTO_ATOMIC(TYPE) std::atomic<TYPE>
+#else
+#define PERFETTO_ATOMIC(TYPE) _Atomic(TYPE)
+#endif
+
+#ifdef __cplusplus
+#define PERFETTO_ATOMIC_LOAD std::atomic_load
+#define PERFETTO_ATOMIC_LOAD_EXPLICIT std::atomic_load_explicit
+#define PERFETTO_ATOMIC_STORE std::atomic_store
+#define PERFETTO_ATOMIC_STORE_EXPLICIT std::atomic_store_explicit
+
+#define PERFETTO_MEMORY_ORDER_ACQ_REL std::memory_order_acq_rel
+#define PERFETTO_MEMORY_ORDER_ACQUIRE std::memory_order_acquire
+#define PERFETTO_MEMORY_ORDER_CONSUME std::memory_order_consume
+#define PERFETTO_MEMORY_ORDER_RELAXED std::memory_order_relaxed
+#define PERFETTO_MEMORY_ORDER_RELEASE std::memory_order_release
+#define PERFETTO_MEMORY_ORDER_SEQ_CST std::memory_order_seq_cst
+#else
+#define PERFETTO_ATOMIC_LOAD atomic_load
+#define PERFETTO_ATOMIC_LOAD_EXPLICIT atomic_load_explicit
+#define PERFETTO_ATOMIC_STORE atomic_store
+#define PERFETTO_ATOMIC_STORE_EXPLICIT atomic_store_explicit
+
+#define PERFETTO_MEMORY_ORDER_ACQ_REL memory_order_acq_rel
+#define PERFETTO_MEMORY_ORDER_ACQUIRE memory_order_acquire
+#define PERFETTO_MEMORY_ORDER_CONSUME memory_order_consume
+#define PERFETTO_MEMORY_ORDER_RELAXED memory_order_relaxed
+#define PERFETTO_MEMORY_ORDER_RELEASE memory_order_release
+#define PERFETTO_MEMORY_ORDER_SEQ_CST memory_order_seq_cst
+#endif
+
+#endif  // INCLUDE_PERFETTO_PUBLIC_ABI_ATOMIC_H_
diff --git a/include/perfetto/public/abi/backend_type.h b/include/perfetto/public/abi/backend_type.h
new file mode 100644
index 0000000..fe5fe48
--- /dev/null
+++ b/include/perfetto/public/abi/backend_type.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PUBLIC_ABI_BACKEND_TYPE_H_
+#define INCLUDE_PERFETTO_PUBLIC_ABI_BACKEND_TYPE_H_
+
+#include <stdint.h>
+
+enum {
+  // The in-process tracing backend. Keeps trace buffers in the process memory.
+  PERFETTO_BACKEND_IN_PROCESS = (1 << 0),
+
+  // The system tracing backend. Connects to the system tracing service (e.g.
+  // on Linux/Android/Mac uses a named UNIX socket).
+  PERFETTO_BACKEND_SYSTEM = (1 << 1),
+};
+
+// Or-combination of one or more of the above PERFETTO_BACKEND_ flags.
+typedef uint32_t PerfettoBackendTypes;
+
+#endif  // INCLUDE_PERFETTO_PUBLIC_ABI_BACKEND_TYPE_H_
diff --git a/include/perfetto/public/abi/data_source_abi.h b/include/perfetto/public/abi/data_source_abi.h
new file mode 100644
index 0000000..3a31650
--- /dev/null
+++ b/include/perfetto/public/abi/data_source_abi.h
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PUBLIC_ABI_DATA_SOURCE_ABI_H_
+#define INCLUDE_PERFETTO_PUBLIC_ABI_DATA_SOURCE_ABI_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "perfetto/public/abi/atomic.h"
+#include "perfetto/public/abi/export.h"
+#include "perfetto/public/abi/stream_writer_abi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Internal representation of a data source type.
+struct PerfettoDsImpl;
+
+// Internal thread local state of a data source type.
+struct PerfettoDsTlsImpl;
+
+// Internal thread local state of a data source instance used for tracing.
+struct PerfettoDsTracerImpl;
+
+// A global atomic boolean that's always false.
+extern PERFETTO_SDK_EXPORT PERFETTO_ATOMIC(bool) perfetto_atomic_false;
+
+// There can be more than one data source instance for each data source type.
+// This index identifies one of them.
+typedef uint32_t PerfettoDsInstanceIndex;
+
+// Creates a data source type.
+//
+// The data source type needs to be registered later with
+// PerfettoDsImplRegister().
+PERFETTO_SDK_EXPORT struct PerfettoDsImpl* PerfettoDsImplCreate(void);
+
+// Called when a data source instance of a specific type is created. `ds_config`
+// points to a serialized perfetto.protos.DataSourceConfig message,
+// `ds_config_size` bytes long. `user_arg` is the value passed to
+// PerfettoDsSetCbUserArg().
+typedef void* (*PerfettoDsOnSetupCb)(PerfettoDsInstanceIndex inst_id,
+                                     void* ds_config,
+                                     size_t ds_config_size,
+                                     void* user_arg);
+
+// Called when tracing starts for a data source instance. `user_arg` is the
+// value passed to PerfettoDsSetCbUserArg(). `inst_ctx` is the return
+// value of PerfettoDsOnSetupCb.
+typedef void (*PerfettoDsOnStartCb)(PerfettoDsInstanceIndex inst_id,
+                                    void* user_arg,
+                                    void* inst_ctx);
+
+// Internal handle used to perform operations from the OnStop callback.
+struct PerfettoDsOnStopArgs;
+
+// Internal handle used to signal when the data source stop operation is
+// complete.
+struct PerfettoDsAsyncStopper;
+
+// Tells the tracing service to postpone the stopping of a data source instance.
+// The returned handle can be used to signal the tracing service when the data
+// source instance can be stopped.
+PERFETTO_SDK_EXPORT struct PerfettoDsAsyncStopper* PerfettoDsOnStopArgsPostpone(
+    struct PerfettoDsOnStopArgs*);
+
+// Tells the tracing service to stop a data source instance (whose stop
+// operation was previously postponed with PerfettoDsOnStopArgsPostpone).
+PERFETTO_SDK_EXPORT void PerfettoDsStopDone(struct PerfettoDsAsyncStopper*);
+
+// Called when tracing stops for a data source instance. `user_arg` is the value
+// passed to PerfettoDsSetCbUserArg(). `inst_ctx` is the return value of
+// PerfettoDsOnSetupCb. `args` can be used to postpone stopping this data source
+// instance.
+typedef void (*PerfettoDsOnStopCb)(PerfettoDsInstanceIndex inst_id,
+                                   void* user_arg,
+                                   void* inst_ctx,
+                                   struct PerfettoDsOnStopArgs* args);
+
+// Creates custom state (either thread local state or incremental state) for
+// instance `inst_id`. `user_arg` is the value passed to
+// PerfettoDsSetCbUserArg().
+typedef void* (*PerfettoDsOnCreateCustomState)(
+    PerfettoDsInstanceIndex inst_id,
+    struct PerfettoDsTracerImpl* tracer,
+    void* user_arg);
+
+// Deletes the previously created custom state `obj`.
+typedef void (*PerfettoDsOnDeleteCustomState)(void* obj);
+
+// Setters for callbacks: can not be called after PerfettoDsImplRegister().
+
+PERFETTO_SDK_EXPORT void PerfettoDsSetOnSetupCallback(struct PerfettoDsImpl*,
+                                                      PerfettoDsOnSetupCb);
+
+PERFETTO_SDK_EXPORT void PerfettoDsSetOnStartCallback(struct PerfettoDsImpl*,
+                                                      PerfettoDsOnStartCb);
+
+PERFETTO_SDK_EXPORT void PerfettoDsSetOnStopCallback(struct PerfettoDsImpl*,
+                                                     PerfettoDsOnStopCb);
+
+// Callbacks for custom per instance thread local state.
+PERFETTO_SDK_EXPORT void PerfettoDsSetOnCreateTls(
+    struct PerfettoDsImpl*,
+    PerfettoDsOnCreateCustomState);
+
+PERFETTO_SDK_EXPORT void PerfettoDsSetOnDeleteTls(
+    struct PerfettoDsImpl*,
+    PerfettoDsOnDeleteCustomState);
+
+// Callbacks for custom per instance thread local incremental state.
+PERFETTO_SDK_EXPORT void PerfettoDsSetOnCreateIncr(
+    struct PerfettoDsImpl*,
+    PerfettoDsOnCreateCustomState);
+
+PERFETTO_SDK_EXPORT void PerfettoDsSetOnDeleteIncr(
+    struct PerfettoDsImpl*,
+    PerfettoDsOnDeleteCustomState);
+
+// Stores the `user_arg` that's going to be passed later to the callbacks for
+// this data source type.
+PERFETTO_SDK_EXPORT void PerfettoDsSetCbUserArg(struct PerfettoDsImpl*,
+                                                void* user_arg);
+
+// Registers the `*ds_impl` data source type.
+//
+// `ds_impl` must be obtained via a call to `PerfettoDsImplCreate()`.
+//
+// `**enabled_ptr` will be set to true when the data source type has been
+// enabled.
+//
+// `descriptor` should point to a serialized
+// perfetto.protos.DataSourceDescriptor message, `descriptor_size` bytes long.
+//
+// Returns `true` in case of success, `false` in case of failure (in which case
+// `ds_impl is invalid`).
+PERFETTO_SDK_EXPORT bool PerfettoDsImplRegister(struct PerfettoDsImpl* ds_impl,
+                                                PERFETTO_ATOMIC(bool) *
+                                                    *enabled_ptr,
+                                                const void* descriptor,
+                                                size_t descriptor_size);
+
+// Updates the descriptor the `*ds_impl` data source type.
+//
+// `descriptor` should point to a serialized
+// perfetto.protos.DataSourceDescriptor message, `descriptor_size` bytes long.
+PERFETTO_SDK_EXPORT void PerfettoDsImplUpdateDescriptor(
+    struct PerfettoDsImpl* ds_impl,
+    const void* descriptor,
+    size_t descriptor_size);
+
+// Tries to get the `inst_ctx` returned by PerfettoDsOnSetupCb() for the
+// instance with index `inst_id`.
+//
+// If successful, returns a non-null pointer and acquires a lock, which must be
+// released with PerfettoDsImplReleaseInstanceLocked.
+//
+// If unsuccessful (because the instance was destroyed in the meantime) or if
+// PerfettoDsOnSetupCb() returned a null value, returns null and does not
+// acquire any lock.
+PERFETTO_SDK_EXPORT void* PerfettoDsImplGetInstanceLocked(
+    struct PerfettoDsImpl* ds_impl,
+    PerfettoDsInstanceIndex inst_id);
+
+// Releases a lock previouly acquired by a PerfettoDsImplGetInstanceLocked()
+// call, which must have returned a non null value.
+PERFETTO_SDK_EXPORT void PerfettoDsImplReleaseInstanceLocked(
+    struct PerfettoDsImpl* ds_impl,
+    PerfettoDsInstanceIndex inst_id);
+
+// Gets the data source thread local instance custom state created by
+// the callback passed to `PerfettoDsSetOnCreateTls`.
+PERFETTO_SDK_EXPORT void* PerfettoDsImplGetCustomTls(
+    struct PerfettoDsImpl* ds_impl,
+    struct PerfettoDsTracerImpl* tracer,
+    PerfettoDsInstanceIndex inst_id);
+
+// Gets the data source thread local instance incremental state created by
+// the callback passed to `PerfettoDsSetOnCreateIncr`.
+PERFETTO_SDK_EXPORT void* PerfettoDsImplGetIncrementalState(
+    struct PerfettoDsImpl* ds_impl,
+    struct PerfettoDsTracerImpl* tracer,
+    PerfettoDsInstanceIndex inst_id);
+
+// Iterator for all the active instances (on this thread) of a data source type.
+struct PerfettoDsImplTracerIterator {
+  // Instance id.
+  PerfettoDsInstanceIndex inst_id;
+  // Caches a pointer to the internal thread local state of the data source
+  // type.
+  struct PerfettoDsTlsImpl* tls;
+  // Pointer to the object used to output trace packets. When nullptr, the
+  // iteration is over.
+  struct PerfettoDsTracerImpl* tracer;
+};
+
+// Start iterating over all the active instances of the data source type
+// (`ds_impl`).
+//
+// If the returned tracer is not nullptr, the user must continue the iteration
+// with PerfettoDsImplTraceIterateNext(), until it is. The iteration can
+// only be interrupted early by calling PerfettoDsImplTraceIterateBreak().
+PERFETTO_SDK_EXPORT struct PerfettoDsImplTracerIterator
+PerfettoDsImplTraceIterateBegin(struct PerfettoDsImpl* ds_impl);
+
+// Advances the iterator to the next active instance of the data source type
+// (`ds_impl`).
+//
+// The user must call PerfettoDsImplTraceIterateNext(), until it returns a
+// nullptr tracer. The iteration can only be interrupted early by calling
+// PerfettoDsImplTraceIterateBreak().
+PERFETTO_SDK_EXPORT void PerfettoDsImplTraceIterateNext(
+    struct PerfettoDsImpl* ds_impl,
+    struct PerfettoDsImplTracerIterator* iterator);
+
+// Prematurely interrupts iteration over all the active instances of the data
+// source type (`ds_impl`).
+PERFETTO_SDK_EXPORT void PerfettoDsImplTraceIterateBreak(
+    struct PerfettoDsImpl* ds_impl,
+    struct PerfettoDsImplTracerIterator* iterator);
+
+// Creates a new trace packet on `tracer`. Returns a stream writer that can be
+// used to write data to the packet. The caller must use
+// PerfettoDsTracerImplPacketEnd() when done.
+PERFETTO_SDK_EXPORT struct PerfettoStreamWriter PerfettoDsTracerImplPacketBegin(
+    struct PerfettoDsTracerImpl* tracer);
+
+// Signals that the trace packets created previously on `tracer` with
+// PerfettoDsTracerImplBeginPacket(), has been fully written.
+//
+// `writer` should point to the writer returned by
+// PerfettoDsTracerImplBeginPacket() and cannot be used anymore after this call.
+PERFETTO_SDK_EXPORT void PerfettoDsTracerImplPacketEnd(
+    struct PerfettoDsTracerImpl* tracer,
+    struct PerfettoStreamWriter* writer);
+
+// Called when a flush request is complete.
+typedef void (*PerfettoDsTracerOnFlushCb)(void* user_arg);
+
+// Forces a commit of the thread-local tracing data written so far to the
+// service.
+//
+// If `cb` is not NULL, it is called on a dedicated internal thread (with
+// `user_arg`), when flushing is complete. It may never be called (e.g. if the
+// tracing service disconnects).
+//
+// This is almost never required (tracing data is periodically committed as
+// trace pages are filled up) and has a non-negligible performance hit.
+PERFETTO_SDK_EXPORT void PerfettoDsTracerImplFlush(
+    struct PerfettoDsTracerImpl* tracer,
+    PerfettoDsTracerOnFlushCb cb,
+    void* user_arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // INCLUDE_PERFETTO_PUBLIC_ABI_DATA_SOURCE_ABI_H_
diff --git a/include/perfetto/public/abi/export.h b/include/perfetto/public/abi/export.h
index d11f5a2..734af85 100644
--- a/include/perfetto/public/abi/export.h
+++ b/include/perfetto/public/abi/export.h
@@ -26,10 +26,23 @@
 #endif
 
 // PERFETTO_SDK_EXPORT: Exports a symbol from the perfetto SDK shared library.
-#if defined(PERFETTO_IMPLEMENTATION)
+//
+// This is controlled by two defines (that likely come from the compiler command
+// line):
+// * PERFETTO_SDK_DISABLE_SHLIB_EXPORT: If this is defined, no export
+//   annotations are added. This might be useful when static linking.
+// * PERFETTO_SDK_SHLIB_IMPLEMENTATION: This must be defined when compiling the
+//   shared library itself (in order to export the symbols), but must be
+//   undefined when compiling objects that use the shared library (in order to
+//   import the symbols).
+#if !defined(PERFETTO_SDK_DISABLE_SHLIB_EXPORT)
+#if defined(PERFETTO_SHLIB_SDK_IMPLEMENTATION)
 #define PERFETTO_SDK_EXPORT PERFETTO_INTERNAL_DLL_EXPORT
 #else
 #define PERFETTO_SDK_EXPORT PERFETTO_INTERNAL_DLL_IMPORT
 #endif
+#else  // defined(PERFETTO_SDK_DISABLE_SHLIB_EXPORT)
+#define PERFETTO_SDK_EXPORT
+#endif  // defined(PERFETTO_SDK_DISABLE_SHLIB_EXPORT)
 
 #endif  // INCLUDE_PERFETTO_PUBLIC_ABI_EXPORT_H_
diff --git a/include/perfetto/public/abi/heap_buffer.h b/include/perfetto/public/abi/heap_buffer.h
new file mode 100644
index 0000000..811ed0b
--- /dev/null
+++ b/include/perfetto/public/abi/heap_buffer.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PUBLIC_ABI_HEAP_BUFFER_H_
+#define INCLUDE_PERFETTO_PUBLIC_ABI_HEAP_BUFFER_H_
+
+#include "perfetto/public/abi/stream_writer_abi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// A PerfettoHeapBuffer can be used to serialize protobuf data using the
+// PerfettoStreamWriter interface. Stores data on heap allocated buffers, which
+// can be read back with PerfettoHeapBufferCopyContent().
+
+struct PerfettoHeapBuffer;
+
+// Creates a PerfettoHeapBuffer. Takes a pointer to an (uninitialized)
+// PerfettoStreamWriter (owned by the caller). The stream writer can be user
+// later to serialize protobuf data.
+PERFETTO_SDK_EXPORT struct PerfettoHeapBuffer* PerfettoHeapBufferCreate(
+    struct PerfettoStreamWriter*);
+
+// Copies data from the heap buffer to `dst` (up to `size` bytes).
+PERFETTO_SDK_EXPORT void PerfettoHeapBufferCopyInto(
+    struct PerfettoHeapBuffer*,
+    struct PerfettoStreamWriter*,
+    void* dst,
+    size_t size);
+
+// Destroys the heap buffer.
+PERFETTO_SDK_EXPORT void PerfettoHeapBufferDestroy(
+    struct PerfettoHeapBuffer*,
+    struct PerfettoStreamWriter*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // INCLUDE_PERFETTO_PUBLIC_ABI_HEAP_BUFFER_H_
diff --git a/include/perfetto/public/abi/producer.h b/include/perfetto/public/abi/producer.h
new file mode 100644
index 0000000..f3e7caf
--- /dev/null
+++ b/include/perfetto/public/abi/producer.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PUBLIC_ABI_PRODUCER_H_
+#define INCLUDE_PERFETTO_PUBLIC_ABI_PRODUCER_H_
+
+#include <stdint.h>
+
+#include "perfetto/public/abi/export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Initializes the global system perfetto producer.
+PERFETTO_SDK_EXPORT void PerfettoProducerSystemInit(void);
+
+// Initializes the global in-process perfetto producer.
+PERFETTO_SDK_EXPORT void PerfettoProducerInProcessInit(void);
+
+// Initializes both the global in-process and system perfetto producer.
+PERFETTO_SDK_EXPORT void PerfettoProducerInProcessAndSystemInit(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // INCLUDE_PERFETTO_PUBLIC_ABI_PRODUCER_H_
diff --git a/include/perfetto/public/abi/tracing_session_abi.h b/include/perfetto/public/abi/tracing_session_abi.h
new file mode 100644
index 0000000..bb044d9
--- /dev/null
+++ b/include/perfetto/public/abi/tracing_session_abi.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PUBLIC_ABI_TRACING_SESSION_ABI_H_
+#define INCLUDE_PERFETTO_PUBLIC_ABI_TRACING_SESSION_ABI_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "perfetto/public/abi/backend_type.h"
+#include "perfetto/public/abi/export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Opaque pointer to the internal representation of a tracing session.
+struct PerfettoTracingSessionImpl;
+
+PERFETTO_SDK_EXPORT struct PerfettoTracingSessionImpl*
+PerfettoTracingSessionCreate(PerfettoBackendTypes backend);
+
+PERFETTO_SDK_EXPORT void PerfettoTracingSessionSetup(
+    struct PerfettoTracingSessionImpl*,
+    void* cfg_begin,
+    size_t cfg_len);
+
+PERFETTO_SDK_EXPORT void PerfettoTracingSessionStartAsync(
+    struct PerfettoTracingSessionImpl*);
+
+PERFETTO_SDK_EXPORT void PerfettoTracingSessionStartBlocking(
+    struct PerfettoTracingSessionImpl*);
+
+PERFETTO_SDK_EXPORT void PerfettoTracingSessionStopAsync(
+    struct PerfettoTracingSessionImpl*);
+
+PERFETTO_SDK_EXPORT void PerfettoTracingSessionStopBlocking(
+    struct PerfettoTracingSessionImpl*);
+
+// Called back to read pieces of tracing data. `data` points to a chunk of trace
+// data, `size` bytes long. `has_more` is true if there is more tracing data and
+// the callback will be invoked again.
+typedef void (*PerfettoTracingSessionReadCb)(struct PerfettoTracingSessionImpl*,
+                                             const void* data,
+                                             size_t size,
+                                             bool has_more,
+                                             void* user_arg);
+
+// Repeatedly calls cb with data from the tracing session. `user_arg` is passed
+// as is to the callback.
+PERFETTO_SDK_EXPORT void PerfettoTracingSessionReadTraceBlocking(
+    struct PerfettoTracingSessionImpl*,
+    PerfettoTracingSessionReadCb cb,
+    void* user_arg);
+
+PERFETTO_SDK_EXPORT void PerfettoTracingSessionDestroy(
+    struct PerfettoTracingSessionImpl*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // INCLUDE_PERFETTO_PUBLIC_ABI_TRACING_SESSION_ABI_H_
diff --git a/include/perfetto/public/compiler.h b/include/perfetto/public/compiler.h
index 4d8905b..3e38853 100644
--- a/include/perfetto/public/compiler.h
+++ b/include/perfetto/public/compiler.h
@@ -17,6 +17,8 @@
 #ifndef INCLUDE_PERFETTO_PUBLIC_COMPILER_H_
 #define INCLUDE_PERFETTO_PUBLIC_COMPILER_H_
 
+#include <stddef.h>
+
 #if defined(__GNUC__) || defined(__clang__)
 #define PERFETTO_LIKELY(_x) __builtin_expect(!!(_x), 1)
 #define PERFETTO_UNLIKELY(_x) __builtin_expect(!!(_x), 0)
@@ -33,4 +35,20 @@
 #define PERFETTO_STATIC_CAST(TYPE, VAL) ((TYPE)(VAL))
 #endif
 
+// PERFETTO_REINTERPRET_CAST(TYPE, VAL): avoids the -Wold-style-cast warning
+// when writing code that needs to be compiled as C and C++.
+#ifdef __cplusplus
+#define PERFETTO_REINTERPRET_CAST(TYPE, VAL) reinterpret_cast<TYPE>(VAL)
+#else
+#define PERFETTO_REINTERPRET_CAST(TYPE, VAL) ((TYPE)(VAL))
+#endif
+
+// PERFETTO_NULL: avoids the -Wzero-as-null-pointer-constant warning when
+// writing code that needs to be compiled as C and C++.
+#ifdef __cplusplus
+#define PERFETTO_NULL nullptr
+#else
+#define PERFETTO_NULL NULL
+#endif
+
 #endif  // INCLUDE_PERFETTO_PUBLIC_COMPILER_H_
diff --git a/include/perfetto/public/data_source.h b/include/perfetto/public/data_source.h
new file mode 100644
index 0000000..7f81334
--- /dev/null
+++ b/include/perfetto/public/data_source.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PUBLIC_DATA_SOURCE_H_
+#define INCLUDE_PERFETTO_PUBLIC_DATA_SOURCE_H_
+
+#include <malloc.h>
+#include <string.h>
+
+#include "perfetto/public/abi/atomic.h"
+#include "perfetto/public/abi/data_source_abi.h"
+#include "perfetto/public/compiler.h"
+#include "perfetto/public/pb_msg.h"
+#include "perfetto/public/pb_utils.h"
+#include "perfetto/public/protos/trace/trace_packet.pzc.h"
+
+// A data source type.
+struct PerfettoDs {
+  // Pointer to a (atomic) boolean, which is set to true if there is at
+  // least one enabled instance of this data source type.
+  PERFETTO_ATOMIC(bool) * enabled;
+  struct PerfettoDsImpl* impl;
+};
+
+// Initializes a PerfettoDs struct.
+#define PERFETTO_DS_INIT() \
+  { &perfetto_atomic_false, PERFETTO_NULL }
+
+// All the callbacks are optional and can be NULL if not needed.
+struct PerfettoDsCallbacks {
+  // Instance lifecycle callbacks:
+  PerfettoDsOnSetupCb on_setup_cb;
+  PerfettoDsOnStartCb on_start_cb;
+  PerfettoDsOnStopCb on_stop_cb;
+
+  // These are called to create/delete custom thread-local instance state, which
+  // can be accessed with PerfettoDsTracerImplGetCustomTls().
+  PerfettoDsOnCreateCustomState on_create_tls_cb;
+  PerfettoDsOnDeleteCustomState on_delete_tls_cb;
+
+  // These are called to create/delete custom thread-local instance incremental
+  // state. Incremental state may be cleared periodically by the tracing service
+  // and can be accessed with PerfettoDsTracerImplGetIncrementalState().
+  PerfettoDsOnCreateCustomState on_create_incr_cb;
+  PerfettoDsOnDeleteCustomState on_delete_incr_cb;
+
+  // Passed to all the callbacks as the `user_arg` param.
+  void* user_arg;
+};
+
+static inline struct PerfettoDsCallbacks PerfettoDsNoCallbacks(void) {
+  struct PerfettoDsCallbacks ret = {PERFETTO_NULL, PERFETTO_NULL, PERFETTO_NULL,
+                                    PERFETTO_NULL, PERFETTO_NULL, PERFETTO_NULL,
+                                    PERFETTO_NULL, PERFETTO_NULL};
+  return ret;
+}
+
+// Registers the data source type `ds`, named `data_source_name` with the global
+// perfetto producer.
+//
+// `callbacks` are called when certain events happen on the data source type.
+// PerfettoDsNoCallbacks() can be used if callbacks are not needed.
+//
+// TODO(ddiproietto): Accept the full DataSourceDescriptor, not just the
+// data_source_name
+static inline bool PerfettoDsRegister(struct PerfettoDs* ds,
+                                      const char* data_source_name,
+                                      struct PerfettoDsCallbacks callbacks) {
+  struct PerfettoDsImpl* ds_impl;
+  bool success;
+  // Build the DataSourceDescriptor protobuf message.
+  size_t data_source_name_len = strlen(data_source_name);
+  uint8_t* data_source_desc = PERFETTO_STATIC_CAST(
+      uint8_t*, malloc(data_source_name_len + PERFETTO_PB_VARINT_MAX_SIZE_32 +
+                       PERFETTO_PB_VARINT_MAX_SIZE_64));
+  uint8_t* write_ptr = data_source_desc;
+  const int32_t name_field_id = 1;  // perfetto.protos.DataSourceDescriptor.name
+  write_ptr = PerfettoPbWriteVarInt(
+      PerfettoPbMakeTag(name_field_id, PERFETTO_PB_WIRE_TYPE_DELIMITED),
+      write_ptr);
+  write_ptr = PerfettoPbWriteVarInt(data_source_name_len, write_ptr);
+  memcpy(write_ptr, data_source_name, data_source_name_len);
+  write_ptr += data_source_name_len;
+
+  ds_impl = PerfettoDsImplCreate();
+  if (callbacks.on_setup_cb) {
+    PerfettoDsSetOnSetupCallback(ds_impl, callbacks.on_setup_cb);
+  }
+  if (callbacks.on_start_cb) {
+    PerfettoDsSetOnStartCallback(ds_impl, callbacks.on_start_cb);
+  }
+  if (callbacks.on_stop_cb) {
+    PerfettoDsSetOnStopCallback(ds_impl, callbacks.on_stop_cb);
+  }
+  if (callbacks.on_create_tls_cb) {
+    PerfettoDsSetOnCreateTls(ds_impl, callbacks.on_create_tls_cb);
+  }
+  if (callbacks.on_delete_tls_cb) {
+    PerfettoDsSetOnDeleteTls(ds_impl, callbacks.on_delete_tls_cb);
+  }
+  if (callbacks.on_create_incr_cb) {
+    PerfettoDsSetOnCreateIncr(ds_impl, callbacks.on_create_incr_cb);
+  }
+  if (callbacks.on_delete_incr_cb) {
+    PerfettoDsSetOnDeleteIncr(ds_impl, callbacks.on_delete_incr_cb);
+  }
+  if (callbacks.user_arg) {
+    PerfettoDsSetCbUserArg(ds_impl, callbacks.user_arg);
+  }
+
+  success = PerfettoDsImplRegister(
+      ds_impl, &ds->enabled, data_source_desc,
+      PERFETTO_STATIC_CAST(size_t, write_ptr - data_source_desc));
+  free(data_source_desc);
+  if (!success) {
+    return false;
+  }
+  ds->impl = ds_impl;
+  return true;
+}
+
+// Iterator for all the active instances (on this thread) of a data source type.
+struct PerfettoDsTracerIterator {
+  struct PerfettoDsImplTracerIterator impl;
+};
+
+static inline struct PerfettoDsTracerIterator PerfettoDsTraceIterateBegin(
+    struct PerfettoDs* ds) {
+  struct PerfettoDsTracerIterator ret;
+  PERFETTO_ATOMIC(bool)* enabled = ds->enabled;
+  if (PERFETTO_LIKELY(!PERFETTO_ATOMIC_LOAD_EXPLICIT(
+          enabled, PERFETTO_MEMORY_ORDER_RELAXED))) {
+    // Tracing fast path: bail out immediately if the enabled flag is false.
+    ret.impl.tracer = PERFETTO_NULL;
+  } else {
+    // Else, make an ABI call to start iteration over the data source type
+    // active instances.
+    ret.impl = PerfettoDsImplTraceIterateBegin(ds->impl);
+  }
+  return ret;
+}
+
+static inline void PerfettoDsTraceIterateNext(
+    struct PerfettoDs* ds,
+    struct PerfettoDsTracerIterator* iterator) {
+  PerfettoDsImplTraceIterateNext(ds->impl, &iterator->impl);
+}
+
+static inline void PerfettoDsTraceIterateBreak(
+    struct PerfettoDs* ds,
+    struct PerfettoDsTracerIterator* iterator) {
+  if (iterator->impl.tracer) {
+    PerfettoDsImplTraceIterateBreak(ds->impl, &iterator->impl);
+  }
+}
+
+// For loop over the active instances of a data source type.
+//
+// `NAME` is the data source type (struct PerfettoDs).
+//
+// A local variable called `ITERATOR` will be instantiated. It can be used to
+// perform tracing on each instance.
+//
+// N.B. The iteration MUST NOT be interrupted early with `break`.
+// PERFETTO_DS_TRACE_BREAK should be used instead.
+#define PERFETTO_DS_TRACE(NAME, ITERATOR)         \
+  for (struct PerfettoDsTracerIterator ITERATOR = \
+           PerfettoDsTraceIterateBegin(&(NAME));  \
+       (ITERATOR).impl.tracer != NULL;            \
+       PerfettoDsTraceIterateNext(&(NAME), &(ITERATOR)))
+
+// Used to break the iteration in a PERFETTO_DS_TRACE loop.
+#define PERFETTO_DS_TRACE_BREAK(NAME, ITERATOR)      \
+  PerfettoDsTraceIterateBreak(&(NAME), &(ITERATOR)); \
+  break
+
+static inline void* PerfettoDsGetCustomTls(
+    struct PerfettoDs* ds,
+    struct PerfettoDsTracerIterator* iterator) {
+  return PerfettoDsImplGetCustomTls(ds->impl, iterator->impl.tracer,
+                                    iterator->impl.inst_id);
+}
+
+static inline void* PerfettoDsGetIncrementalState(
+    struct PerfettoDs* ds,
+    struct PerfettoDsTracerIterator* iterator) {
+  return PerfettoDsImplGetIncrementalState(ds->impl, iterator->impl.tracer,
+                                           iterator->impl.inst_id);
+}
+
+// Used to write a TracePacket on a data source instance. Stores the writer and
+// the TracePacket message.
+struct PerfettoDsRootTracePacket {
+  struct PerfettoPbMsgWriter writer;
+  struct perfetto_protos_TracePacket msg;
+};
+
+// Initializes `root` to write a new packet to the data source instance pointed
+// by `iterator`.
+static inline void PerfettoDsTracerPacketBegin(
+    struct PerfettoDsTracerIterator* iterator,
+    struct PerfettoDsRootTracePacket* root) {
+  root->writer.writer = PerfettoDsTracerImplPacketBegin(iterator->impl.tracer);
+  PerfettoPbMsgInit(&root->msg.msg, &root->writer);
+}
+
+// Finishes writing the packet pointed by `root` on the data source instance
+// pointer by `iterator`.
+static inline void PerfettoDsTracerPacketEnd(
+    struct PerfettoDsTracerIterator* iterator,
+    struct PerfettoDsRootTracePacket* root) {
+  PerfettoPbMsgFinalize(&root->msg.msg);
+  PerfettoDsTracerImplPacketEnd(iterator->impl.tracer, &root->writer.writer);
+}
+
+static inline void PerfettoDsTracerFlush(
+    struct PerfettoDsTracerIterator* iterator,
+    PerfettoDsTracerOnFlushCb cb,
+    void* ctx) {
+  PerfettoDsTracerImplFlush(iterator->impl.tracer, cb, ctx);
+}
+
+#endif  // INCLUDE_PERFETTO_PUBLIC_DATA_SOURCE_H_
diff --git a/include/perfetto/public/pb_macros.h b/include/perfetto/public/pb_macros.h
new file mode 100644
index 0000000..816f817
--- /dev/null
+++ b/include/perfetto/public/pb_macros.h
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PUBLIC_PB_MACROS_H_
+#define INCLUDE_PERFETTO_PUBLIC_PB_MACROS_H_
+
+#include "perfetto/public/compiler.h"  // IWYU pragma: export
+#include "perfetto/public/pb_msg.h"    // IWYU pragma: export
+#include "perfetto/public/pb_utils.h"  // IWYU pragma: export
+
+// This header contains macros that define types and accessors for protobuf
+// messages.
+//
+// Example usage:
+//
+// PERFETTO_PB_ENUM(perfetto_protos_BuiltinClock){
+//     PERFETTO_PB_ENUM_ENTRY(perfetto_protos_BUILTIN_CLOCK_UNKNOWN) = 0,
+//     PERFETTO_PB_ENUM_ENTRY(perfetto_protos_BUILTIN_CLOCK_REALTIME) = 1,
+//     PERFETTO_PB_ENUM_ENTRY(perfetto_protos_BUILTIN_CLOCK_REALTIME_COARSE)
+//         = 2,
+//     PERFETTO_PB_ENUM_ENTRY(perfetto_protos_BUILTIN_CLOCK_MONOTONIC) = 3,
+//     PERFETTO_PB_ENUM_ENTRY(perfetto_protos_BUILTIN_CLOCK_MONOTONIC_COARSE)
+//         = 4,
+//     PERFETTO_PB_ENUM_ENTRY(perfetto_protos_BUILTIN_CLOCK_MONOTONIC_RAW) = 5,
+//     PERFETTO_PB_ENUM_ENTRY(perfetto_protos_BUILTIN_CLOCK_BOOTTIME) = 6,
+//     PERFETTO_PB_ENUM_ENTRY(perfetto_protos_BUILTIN_CLOCK_MAX_ID) = 63,
+// };
+//
+// PERFETTO_PB_MSG(perfetto_protos_TraceConfig_BuiltinDataSource);
+// PERFETTO_PB_FIELD(perfetto_protos_TraceConfig_BuiltinDataSource,
+//                   VARINT,
+//                   perfetto_protos_BuiltinClock,
+//                   primary_trace_clock,
+//                   5);
+//
+// PERFETTO_PB_MSG(perfetto_protos_TraceConfig);
+// PERFETTO_PB_ENUM_IN_MSG(perfetto_protos_TraceConfig, LockdownModeOperation){
+//     PERFETTO_PB_ENUM_IN_MSG_ENTRY(perfetto_protos_TraceConfig,
+//         LOCKDOWN_UNCHANGED) = 0,
+//     PERFETTO_PB_ENUM_IN_MSG_ENTRY(perfetto_protos_TraceConfig,
+//         LOCKDOWN_CLEAR) = 1,
+//     PERFETTO_PB_ENUM_IN_MSG_ENTRY(perfetto_protos_TraceConfig,
+//         LOCKDOWN_SET) = 2,
+// };
+// PERFETTO_PB_FIELD(perfetto_protos_TraceConfig,
+//                   VARINT,
+//                   perfetto_protos_TraceConfig_LockdownModeOperation,
+//                   lockdown_mode,
+//                   5);
+//
+// PERFETTO_PB_MSG_DECL(perfetto_protos_DebugAnnotation);
+// PERFETTO_PB_MSG_DECL(perfetto_protos_TestEvent_TestPayload);
+//
+// PERFETTO_PB_MSG(perfetto_protos_TestEvent);
+// PERFETTO_PB_FIELD(perfetto_protos_TestEvent, STRING, const char*, str, 1);
+// PERFETTO_PB_FIELD(perfetto_protos_TestEvent, VARINT, uint32_t, seq_value, 2);
+// PERFETTO_PB_FIELD(perfetto_protos_TestEvent, VARINT, uint64_t, counter, 3);
+// PERFETTO_PB_FIELD(perfetto_protos_TestEvent,
+//                   MSG,
+//                   perfetto_protos_TestEvent_TestPayload,
+//                   payload,
+//                   5);
+//
+// PERFETTO_PB_MSG(perfetto_protos_TestEvent_TestPayload);
+// PERFETTO_PB_FIELD(perfetto_protos_TestEvent_TestPayload,
+//                   STRING,
+//                   const char*,
+//                   str,
+//                   1);
+// PERFETTO_PB_FIELD(perfetto_protos_TestEvent_TestPayload,
+//                   MSG,
+//                   perfetto_protos_DebugAnnotation,
+//                   debug_annotations,
+//                   7);
+
+#define PERFETTO_I_PB_CONCAT_4_(A, B, C, D) A##B##C##D
+#define PERFETTO_I_PB_CONCAT_4(A, B, C, D) PERFETTO_I_PB_CONCAT_4_(A, B, C, D)
+
+#define PERFETTO_I_PB_CONCAT_3_(A, B, C) A##B##C
+#define PERFETTO_I_PB_CONCAT_3(A, B, C) PERFETTO_I_PB_CONCAT_3_(A, B, C)
+
+#define PERFETTO_I_PB_SETTER_NAME(PROTO, NAME) \
+  PERFETTO_I_PB_CONCAT_3(PROTO, _set_, NAME)
+
+#define PERFETTO_I_PB_SETTER_CSTR_NAME(PROTO, NAME) \
+  PERFETTO_I_PB_CONCAT_3(PROTO, _set_cstr_, NAME)
+
+#define PERFETTO_I_PB_SETTER_BEGIN_NAME(PROTO, NAME) \
+  PERFETTO_I_PB_CONCAT_3(PROTO, _begin_, NAME)
+
+#define PERFETTO_I_PB_SETTER_END_NAME(PROTO, NAME) \
+  PERFETTO_I_PB_CONCAT_3(PROTO, _end_, NAME)
+
+#define PERFETTO_I_PB_NUM_FIELD_NAME(PROTO, NAME) \
+  PERFETTO_I_PB_CONCAT_4(PROTO, _, NAME, _field_number)
+
+#define PERFETTO_I_PB_GET_MSG(C_TYPE) PERFETTO_I_PB_CONCAT_3(C_TYPE, _, get_msg)
+
+#define PERFETTO_I_PB_FIELD_STRING(PROTO, C_TYPE, NAME, NUM)              \
+  static inline void PERFETTO_I_PB_SETTER_CSTR_NAME(PROTO, NAME)(         \
+      struct PROTO * msg, const char* value) {                            \
+    PerfettoPbMsgAppendCStrField(&msg->msg, NUM, value);                  \
+  }                                                                       \
+  static inline void PERFETTO_I_PB_SETTER_NAME(PROTO, NAME)(              \
+      struct PROTO * msg, const void* data, size_t len) {                 \
+    PerfettoPbMsgAppendType2Field(                                        \
+        &msg->msg, NUM, PERFETTO_STATIC_CAST(const uint8_t*, data), len); \
+  }                                                                       \
+  static inline void PERFETTO_I_PB_SETTER_BEGIN_NAME(PROTO, NAME)(        \
+      struct PROTO * msg, struct PerfettoPbMsg * nested) {                \
+    PerfettoPbMsgBeginNested(&msg->msg, nested, NUM);                     \
+  }                                                                       \
+  static inline void PERFETTO_I_PB_SETTER_END_NAME(PROTO, NAME)(          \
+      struct PROTO * msg, struct PerfettoPbMsg * nested) {                \
+    (void)nested;                                                         \
+    PerfettoPbMsgEndNested(&msg->msg);                                    \
+  }
+
+#define PERFETTO_I_PB_FIELD_VARINT(PROTO, C_TYPE, NAME, NUM)              \
+  static inline void PERFETTO_I_PB_SETTER_NAME(PROTO, NAME)(              \
+      struct PROTO * msg, C_TYPE value) {                                 \
+    PerfettoPbMsgAppendType0Field(&msg->msg, NUM,                         \
+                                  PERFETTO_STATIC_CAST(uint64_t, value)); \
+  }
+
+#define PERFETTO_I_PB_FIELD_ZIGZAG(PROTO, C_TYPE, NAME, NUM)            \
+  static inline void PERFETTO_I_PB_SETTER_NAME(PROTO, NAME)(            \
+      struct PROTO * msg, C_TYPE value) {                               \
+    uint64_t encoded =                                                  \
+        PerfettoPbZigZagEncode64(PERFETTO_STATIC_CAST(int64_t, value)); \
+    PerfettoPbMsgAppendType0Field(&msg->msg, NUM, encoded);             \
+  }
+
+#define PERFETTO_I_PB_FIELD_FIXED64(PROTO, C_TYPE, NAME, NUM) \
+  static inline void PERFETTO_I_PB_SETTER_NAME(PROTO, NAME)(  \
+      struct PROTO * msg, C_TYPE value) {                     \
+    uint64_t val;                                             \
+    memcpy(&val, &value, sizeof val);                         \
+    PerfettoPbMsgAppendFixed64Field(&msg->msg, NUM, val);     \
+  }
+
+#define PERFETTO_I_PB_FIELD_FIXED32(PROTO, C_TYPE, NAME, NUM) \
+  static inline void PERFETTO_I_PB_SETTER_NAME(PROTO, NAME)(  \
+      struct PROTO * msg, C_TYPE value) {                     \
+    uint32_t val;                                             \
+    memcpy(&val, &value, sizeof val);                         \
+    PerfettoPbMsgAppendFixed32Field(&msg->msg, NUM, val);     \
+  }
+
+#define PERFETTO_I_PB_FIELD_MSG(PROTO, C_TYPE, NAME, NUM)          \
+  static inline void PERFETTO_I_PB_SETTER_BEGIN_NAME(PROTO, NAME)( \
+      struct PROTO * msg, struct C_TYPE * nested) {                \
+    struct PerfettoPbMsg* nested_msg =                             \
+        PERFETTO_REINTERPRET_CAST(struct PerfettoPbMsg*, nested);  \
+    PerfettoPbMsgBeginNested(&msg->msg, nested_msg, NUM);          \
+  }                                                                \
+  static inline void PERFETTO_I_PB_SETTER_END_NAME(PROTO, NAME)(   \
+      struct PROTO * msg, struct C_TYPE * nested) {                \
+    (void)nested;                                                  \
+    PerfettoPbMsgEndNested(&msg->msg);                             \
+  }
+
+#define PERFETTO_I_PB_NUM_FIELD(PROTO, NAME, NUM) \
+  enum { PERFETTO_I_PB_NUM_FIELD_NAME(PROTO, NAME) = NUM }
+
+// Below are public macros that can be used to define protos. All the macros
+// above are just implementation details and can change at any time.
+
+// Defines the type for a protobuf message.
+// `PROTO` is the name of the message type. For nested messages, an underscore
+// should be used as a separator.
+#define PERFETTO_PB_MSG(PROTO) \
+  struct PROTO {               \
+    struct PerfettoPbMsg msg;  \
+  }
+
+// Declares the type for a protobuf message. Useful when a file references a
+// type (because it is used as type for a field), but doesn't need the full
+// definition.
+#define PERFETTO_PB_MSG_DECL(PROTO) struct PROTO
+
+// Defines accessors for a field of a message.
+// * `PROTO`: The message that contains this field. This should be the same
+//   identifier passed to PERFETTO_PB_MSG.
+// * `NAME`: The name of the field. It will be concatenated with `PROTO` to
+//   produce the name for the accessors.
+// * `NUM`: The numeric identifier for this field.
+// * `TYPE`: The protobuf type of the field. Possible options:
+//   * `VARINT`: For most integer (scalar and repeated non-packed) and enum
+//     field types. `CTYPE` is the corresponding C type of the field. Generates
+//     a single NAMESPACE_PROTO_set_NAME(CTYPE value accessor).
+//   * `ZIGZAG`: For sint* (scalar and repeated non-packed) field types. `CTYPE`
+//     is the corresponding C type of the field. Generates a single
+//     PROTO_set_NAME(struct PROTO*, CTYPE) value setter.
+//   * `FIXED32`: For fixed32, sfixed32 and float (scalar and repeated
+//     non-packed) field types. `CTYPE` can be uint32_t, int32_t or float.
+//     Generates a single PROTO_set_NAME(struct PROTO*, CTYPE) value setter.
+//   * `FIXED64`: For fixed64, sfixed64 or double (scalar and repeated
+//     non-packed) field types. `CTYPE` can be uint64_t or int64_t or double.
+//     Generates a single PROTO_set_NAME(struct PROTO*, CTYPE) value setter.
+//   * `MSG`: for nested (scalar and repeated) messages field types. `CTYPE` is
+//     the type of the nested message (full type, including the namespace).
+//     Generates
+//     `PROTO_begin_NAME(struct PROTO*, struct CTYPE* nested)` and
+//     `PROTO_end_NAME(struct PROTO*, struct CTYPE* nested)` that allows to
+//     begin and end a nested submessage.
+//   * `STRING`: for bytes, string and repeated packed field types. `CTYPE`
+//     should be `const char *`. Generates multiple accessors:
+//      * PROTO_set_cstr_NAME(struct PROTO*, const char*): Sets the value of the
+//        field by copying from a null terminated string.
+//      * PROTO_set_NAME(struct PROTO*, const void*, size_t): Sets the value of
+//        the field by copying from a buffer at an address with the specified
+//        size.
+//      * PROTO_begin_NAME(struct PROTO*, struct PerfettoPbMsg* nested) and
+//        PROTO_end_NAME(struct PROTO*, struct PerfettoPbMsg* nested):
+//        Begins (and ends) a nested submessage to allow users to generate part
+//        of the length delimited buffer piece by piece.
+#define PERFETTO_PB_FIELD(PROTO, TYPE, C_TYPE, NAME, NUM) \
+  PERFETTO_I_PB_FIELD_##TYPE(PROTO, C_TYPE, NAME, NUM)    \
+      PERFETTO_I_PB_NUM_FIELD(PROTO, NAME, NUM)
+
+// Defines an enum type nested inside a message (PROTO).
+#define PERFETTO_PB_ENUM_IN_MSG(PROTO, ENUM) \
+  enum PERFETTO_I_PB_CONCAT_3(PROTO, _, ENUM)
+
+// Defines an entry for an enum tpye nested inside a message.
+#define PERFETTO_PB_ENUM_IN_MSG_ENTRY(PROTO, NAME) \
+  PERFETTO_I_PB_CONCAT_3(PROTO, _, NAME)
+
+// Defines a global enum type.
+#define PERFETTO_PB_ENUM(ENUM) enum ENUM
+
+// Defines an entry for global enum type.
+#define PERFETTO_PB_ENUM_ENTRY(NAME) NAME
+
+#endif  // INCLUDE_PERFETTO_PUBLIC_PB_MACROS_H_
diff --git a/include/perfetto/public/pb_msg.h b/include/perfetto/public/pb_msg.h
new file mode 100644
index 0000000..0f201bb
--- /dev/null
+++ b/include/perfetto/public/pb_msg.h
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PUBLIC_PB_MSG_H_
+#define INCLUDE_PERFETTO_PUBLIC_PB_MSG_H_
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "perfetto/public/abi/stream_writer_abi.h"
+#include "perfetto/public/compiler.h"
+#include "perfetto/public/pb_utils.h"
+#include "perfetto/public/stream_writer.h"
+
+// The number of bytes reserved by this implementation to encode a protobuf type
+// 2 field size as var-int. Keep this in sync with kMessageLengthFieldSize in
+// proto_utils.h.
+#define PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE 4
+
+// Points to the memory used by a `PerfettoPbMsg` for writing.
+struct PerfettoPbMsgWriter {
+  struct PerfettoStreamWriter writer;
+};
+
+struct PerfettoPbMsg {
+  // Pointer to a non-aligned pre-reserved var-int slot of
+  // PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE bytes. If not NULL,
+  // protozero_length_buf_finalize() will write the size of proto-encoded
+  // message in the pointed memory region.
+  uint8_t* size_field;
+
+  // Current size of the buffer.
+  uint32_t size;
+
+  struct PerfettoPbMsgWriter* writer;
+
+  struct PerfettoPbMsg* nested;
+  struct PerfettoPbMsg* parent;
+};
+
+static inline void PerfettoPbMsgInit(struct PerfettoPbMsg* msg,
+                                     struct PerfettoPbMsgWriter* writer) {
+  msg->size_field = PERFETTO_NULL;
+  msg->size = 0;
+  msg->writer = writer;
+  msg->nested = PERFETTO_NULL;
+  msg->parent = PERFETTO_NULL;
+}
+
+static inline void PerfettoPbMsgPatch(struct PerfettoPbMsg* msg) {
+  static_assert(
+      PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE == PERFETTO_STREAM_WRITER_PATCH_SIZE,
+      "PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE doesn't match patch size");
+  msg->size_field =
+      PerfettoStreamWriterAnnotatePatch(&msg->writer->writer, msg->size_field);
+}
+
+static inline void PerfettoPbMsgPatchStack(struct PerfettoPbMsg* msg) {
+  uint8_t* const cur_range_end = msg->writer->writer.end;
+  uint8_t* const cur_range_begin = msg->writer->writer.begin;
+  while (msg && cur_range_begin <= msg->size_field &&
+         msg->size_field < cur_range_end) {
+    PerfettoPbMsgPatch(msg);
+    msg = msg->parent;
+  }
+}
+
+static inline void PerfettoPbMsgAppendBytes(struct PerfettoPbMsg* msg,
+                                            const uint8_t* begin,
+                                            size_t size) {
+  if (PERFETTO_UNLIKELY(
+          size > PerfettoStreamWriterAvailableBytes(&msg->writer->writer))) {
+    PerfettoPbMsgPatchStack(msg);
+  }
+  PerfettoStreamWriterAppendBytes(&msg->writer->writer, begin, size);
+  msg->size += size;
+}
+
+static inline void PerfettoPbMsgAppendByte(struct PerfettoPbMsg* msg,
+                                           uint8_t value) {
+  PerfettoPbMsgAppendBytes(msg, &value, 1);
+}
+
+static inline void PerfettoPbMsgAppendVarInt(struct PerfettoPbMsg* msg,
+                                             uint64_t value) {
+  uint8_t* buf_end;
+  uint8_t buf[PERFETTO_PB_VARINT_MAX_SIZE_64];
+  buf_end = PerfettoPbWriteVarInt(value, buf);
+
+  PerfettoPbMsgAppendBytes(msg, buf,
+                           PERFETTO_STATIC_CAST(size_t, buf_end - buf));
+}
+
+static inline void PerfettoPbMsgAppendType0Field(struct PerfettoPbMsg* msg,
+                                                 int32_t field_id,
+                                                 uint64_t value) {
+  uint8_t* buf_end;
+  uint8_t buf[PERFETTO_PB_VARINT_MAX_SIZE_64 + PERFETTO_PB_VARINT_MAX_SIZE_32];
+  buf_end = PerfettoPbWriteVarInt(
+      PerfettoPbMakeTag(field_id, PERFETTO_PB_WIRE_TYPE_VARINT), buf);
+  buf_end = PerfettoPbWriteVarInt(value, buf_end);
+
+  PerfettoPbMsgAppendBytes(msg, buf,
+                           PERFETTO_STATIC_CAST(size_t, buf_end - buf));
+}
+
+static inline void PerfettoPbMsgAppendType2Field(struct PerfettoPbMsg* msg,
+                                                 int32_t field_id,
+                                                 const uint8_t* data,
+                                                 size_t size) {
+  uint8_t* buf_end;
+  uint8_t buf[PERFETTO_PB_VARINT_MAX_SIZE_64 + PERFETTO_PB_VARINT_MAX_SIZE_32];
+  buf_end = PerfettoPbWriteVarInt(
+      PerfettoPbMakeTag(field_id, PERFETTO_PB_WIRE_TYPE_DELIMITED), buf);
+  buf_end =
+      PerfettoPbWriteVarInt(PERFETTO_STATIC_CAST(uint64_t, size), buf_end);
+  PerfettoPbMsgAppendBytes(msg, buf,
+                           PERFETTO_STATIC_CAST(size_t, buf_end - buf));
+
+  PerfettoPbMsgAppendBytes(msg, data, size);
+}
+
+static inline void PerfettoPbMsgAppendFixed32Field(struct PerfettoPbMsg* msg,
+                                                   int32_t field_id,
+                                                   uint32_t value) {
+  uint8_t* buf_end;
+  uint8_t buf[PERFETTO_PB_VARINT_MAX_SIZE_32 + 4];
+  buf_end = PerfettoPbWriteVarInt(
+      PerfettoPbMakeTag(field_id, PERFETTO_PB_WIRE_TYPE_FIXED32), buf);
+  buf_end[0] = PERFETTO_STATIC_CAST(uint8_t, value);
+  buf_end[1] = PERFETTO_STATIC_CAST(uint8_t, value >> 8);
+  buf_end[2] = PERFETTO_STATIC_CAST(uint8_t, value >> 16);
+  buf_end[3] = PERFETTO_STATIC_CAST(uint8_t, value >> 24);
+  buf_end += 4;
+
+  PerfettoPbMsgAppendBytes(msg, buf,
+                           PERFETTO_STATIC_CAST(size_t, buf_end - buf));
+}
+
+static inline void PerfettoPbMsgAppendFloatField(struct PerfettoPbMsg* msg,
+                                                 int32_t field_id,
+                                                 float value) {
+  uint32_t val;
+  memcpy(&val, &value, sizeof val);
+  PerfettoPbMsgAppendFixed32Field(msg, field_id, val);
+}
+
+static inline void PerfettoPbMsgAppendFixed64Field(struct PerfettoPbMsg* msg,
+                                                   int32_t field_id,
+                                                   uint64_t value) {
+  uint8_t* buf_end;
+  uint8_t buf[PERFETTO_PB_VARINT_MAX_SIZE_32 + 8];
+  buf_end = PerfettoPbWriteVarInt(
+      PerfettoPbMakeTag(field_id, PERFETTO_PB_WIRE_TYPE_FIXED64), buf);
+  buf_end[0] = PERFETTO_STATIC_CAST(uint8_t, value);
+  buf_end[1] = PERFETTO_STATIC_CAST(uint8_t, value >> 8);
+  buf_end[2] = PERFETTO_STATIC_CAST(uint8_t, value >> 16);
+  buf_end[3] = PERFETTO_STATIC_CAST(uint8_t, value >> 24);
+  buf_end[4] = PERFETTO_STATIC_CAST(uint8_t, value >> 32);
+  buf_end[5] = PERFETTO_STATIC_CAST(uint8_t, value >> 40);
+  buf_end[6] = PERFETTO_STATIC_CAST(uint8_t, value >> 48);
+  buf_end[7] = PERFETTO_STATIC_CAST(uint8_t, value >> 56);
+  buf_end += 8;
+
+  PerfettoPbMsgAppendBytes(msg, buf,
+                           PERFETTO_STATIC_CAST(size_t, buf_end - buf));
+}
+
+static inline void PerfettoPbMsgAppendDoubleField(struct PerfettoPbMsg* msg,
+                                                  int32_t field_id,
+                                                  double value) {
+  uint64_t val;
+  memcpy(&val, &value, sizeof val);
+  PerfettoPbMsgAppendFixed64Field(msg, field_id, val);
+}
+
+static inline void PerfettoPbMsgAppendCStrField(struct PerfettoPbMsg* msg,
+                                                int32_t field_id,
+                                                const char* c_str) {
+  PerfettoPbMsgAppendType2Field(
+      msg, field_id, PERFETTO_REINTERPRET_CAST(const uint8_t*, c_str),
+      strlen(c_str));
+}
+
+static inline void PerfettoPbMsgBeginNested(struct PerfettoPbMsg* parent,
+                                            struct PerfettoPbMsg* nested,
+                                            int32_t field_id) {
+  PerfettoPbMsgAppendVarInt(
+      parent, PerfettoPbMakeTag(field_id, PERFETTO_PB_WIRE_TYPE_DELIMITED));
+
+  PerfettoPbMsgInit(nested, parent->writer);
+  if (PERFETTO_UNLIKELY(
+          PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE >
+          PerfettoStreamWriterAvailableBytes(&parent->writer->writer))) {
+    PerfettoPbMsgPatchStack(parent);
+  }
+  nested->size_field = PERFETTO_REINTERPRET_CAST(
+      uint8_t*,
+      PerfettoStreamWriterReserveBytes(&nested->writer->writer,
+                                       PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE));
+  nested->parent = parent;
+  parent->size += PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE;
+  parent->nested = nested;
+}
+
+static inline size_t PerfettoPbMsgFinalize(struct PerfettoPbMsg* msg);
+
+static inline void PerfettoPbMsgEndNested(struct PerfettoPbMsg* parent) {
+  parent->size += PerfettoPbMsgFinalize(parent->nested);
+  parent->nested = PERFETTO_NULL;
+}
+
+static inline size_t PerfettoPbMsgFinalize(struct PerfettoPbMsg* msg) {
+  if (msg->nested)
+    PerfettoPbMsgEndNested(msg);
+
+  // Write the length of the nested message a posteriori, using a leading-zero
+  // redundant varint encoding.
+  if (msg->size_field) {
+    uint32_t size_to_write;
+    size_to_write = msg->size;
+    for (size_t i = 0; i < PROTOZERO_MESSAGE_LENGTH_FIELD_SIZE; i++) {
+      const uint8_t msb = (i < 3) ? 0x80 : 0;
+      msg->size_field[i] = (size_to_write & 0xFF) | msb;
+      size_to_write >>= 7;
+    }
+    msg->size_field = PERFETTO_NULL;
+  }
+
+  return msg->size;
+}
+
+#endif  // INCLUDE_PERFETTO_PUBLIC_PB_MSG_H_
diff --git a/include/perfetto/public/pb_utils.h b/include/perfetto/public/pb_utils.h
new file mode 100644
index 0000000..fa0d1e3
--- /dev/null
+++ b/include/perfetto/public/pb_utils.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PUBLIC_PB_UTILS_H_
+#define INCLUDE_PERFETTO_PUBLIC_PB_UTILS_H_
+
+#include <stdint.h>
+
+#include "perfetto/public/compiler.h"
+
+// Type of fields that can be found in a protobuf serialized message.
+enum PerfettoPbWireType {
+  PERFETTO_PB_WIRE_TYPE_VARINT = 0,
+  PERFETTO_PB_WIRE_TYPE_FIXED64 = 1,
+  PERFETTO_PB_WIRE_TYPE_DELIMITED = 2,
+  PERFETTO_PB_WIRE_TYPE_FIXED32 = 5,
+};
+
+// Creates a field tag, which encodes the field type and the field id.
+static inline uint32_t PerfettoPbMakeTag(int32_t field_id,
+                                         enum PerfettoPbWireType wire_type) {
+  return ((PERFETTO_STATIC_CAST(uint32_t, field_id)) << 3) |
+         PERFETTO_STATIC_CAST(uint32_t, wire_type);
+}
+
+enum {
+  // Maximum bytes size of a 64-bit integer encoded as a VarInt.
+  PERFETTO_PB_VARINT_MAX_SIZE_64 = 10,
+  // Maximum bytes size of a 32-bit integer encoded as a VarInt.
+  PERFETTO_PB_VARINT_MAX_SIZE_32 = 5,
+};
+
+// Encodes `value` as a VarInt into `*dst`.
+//
+// `dst` must point into a buffer big enough to represent `value`:
+// PERFETTO_PB_VARINT_MAX_SIZE_* can help.
+static inline uint8_t* PerfettoPbWriteVarInt(uint64_t value, uint8_t* dst) {
+  uint8_t byte;
+  while (value >= 0x80) {
+    byte = (value & 0x7f) | 0x80;
+    *dst++ = byte;
+    value >>= 7;
+  }
+  byte = value & 0x7f;
+  *dst++ = byte;
+
+  return dst;
+}
+
+#endif  // INCLUDE_PERFETTO_PUBLIC_PB_UTILS_H_
diff --git a/include/perfetto/public/producer.h b/include/perfetto/public/producer.h
new file mode 100644
index 0000000..a85cf78
--- /dev/null
+++ b/include/perfetto/public/producer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PUBLIC_PRODUCER_H_
+#define INCLUDE_PERFETTO_PUBLIC_PRODUCER_H_
+
+#include "perfetto/public/abi/backend_type.h"
+#include "perfetto/public/abi/producer.h"
+
+// Arguments for PerfettoProducerInit. This struct is not ABI-stable, fields can
+// be added and rearranged.
+struct PerfettoProducerInitArgs {
+  // Bitwise-or of backends that should be enabled.
+  PerfettoBackendTypes backends;
+};
+
+// Initializes the global perfetto producer.
+static inline void PerfettoProducerInit(struct PerfettoProducerInitArgs args) {
+  if (args.backends & PERFETTO_BACKEND_IN_PROCESS &&
+      args.backends & PERFETTO_BACKEND_SYSTEM) {
+    PerfettoProducerInProcessAndSystemInit();
+  } else if (args.backends & PERFETTO_BACKEND_IN_PROCESS) {
+    PerfettoProducerInProcessInit();
+  } else if (args.backends & PERFETTO_BACKEND_SYSTEM) {
+    PerfettoProducerSystemInit();
+  }
+}
+
+#endif  // INCLUDE_PERFETTO_PUBLIC_PRODUCER_H_
diff --git a/include/perfetto/public/protos/BUILD.gn b/include/perfetto/public/protos/BUILD.gn
new file mode 100644
index 0000000..97a4800
--- /dev/null
+++ b/include/perfetto/public/protos/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source_set("protos") {
+  sources = [
+    "trace/test_event.pzc.h",
+    "trace/trace_packet.pzc.h",
+  ]
+}
diff --git a/include/perfetto/public/protos/trace/test_event.pzc.h b/include/perfetto/public/protos/trace/test_event.pzc.h
new file mode 100644
index 0000000..2706934
--- /dev/null
+++ b/include/perfetto/public/protos/trace/test_event.pzc.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PUBLIC_PROTOS_TRACE_TEST_EVENT_PZC_H_
+#define INCLUDE_PERFETTO_PUBLIC_PROTOS_TRACE_TEST_EVENT_PZC_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "perfetto/public/pb_macros.h"
+
+PERFETTO_PB_MSG_DECL(perfetto_protos_DebugAnnotation);
+PERFETTO_PB_MSG_DECL(perfetto_protos_TestEvent_TestPayload);
+
+PERFETTO_PB_MSG(perfetto_protos_TestEvent);
+PERFETTO_PB_FIELD(perfetto_protos_TestEvent, STRING, const char*, str, 1);
+PERFETTO_PB_FIELD(perfetto_protos_TestEvent, VARINT, uint32_t, seq_value, 2);
+PERFETTO_PB_FIELD(perfetto_protos_TestEvent, VARINT, uint64_t, counter, 3);
+PERFETTO_PB_FIELD(perfetto_protos_TestEvent, VARINT, bool, is_last, 4);
+PERFETTO_PB_FIELD(perfetto_protos_TestEvent,
+                  MSG,
+                  perfetto_protos_TestEvent_TestPayload,
+                  payload,
+                  5);
+
+PERFETTO_PB_MSG(perfetto_protos_TestEvent_TestPayload);
+PERFETTO_PB_FIELD(perfetto_protos_TestEvent_TestPayload,
+                  STRING,
+                  const char*,
+                  str,
+                  1);
+PERFETTO_PB_FIELD(perfetto_protos_TestEvent_TestPayload,
+                  MSG,
+                  perfetto_protos_TestEvent_TestPayload,
+                  nested,
+                  2);
+PERFETTO_PB_FIELD(perfetto_protos_TestEvent_TestPayload,
+                  STRING,
+                  const char*,
+                  single_string,
+                  4);
+PERFETTO_PB_FIELD(perfetto_protos_TestEvent_TestPayload,
+                  VARINT,
+                  int32_t,
+                  single_int,
+                  5);
+PERFETTO_PB_FIELD(perfetto_protos_TestEvent_TestPayload,
+                  VARINT,
+                  int32_t,
+                  repeated_ints,
+                  6);
+PERFETTO_PB_FIELD(perfetto_protos_TestEvent_TestPayload,
+                  VARINT,
+                  uint32_t,
+                  remaining_nesting_depth,
+                  3);
+PERFETTO_PB_FIELD(perfetto_protos_TestEvent_TestPayload,
+                  MSG,
+                  perfetto_protos_DebugAnnotation,
+                  debug_annotations,
+                  7);
+
+#endif  // INCLUDE_PERFETTO_PUBLIC_PROTOS_TRACE_TEST_EVENT_PZC_H_
diff --git a/include/perfetto/public/protos/trace/trace_packet.pzc.h b/include/perfetto/public/protos/trace/trace_packet.pzc.h
new file mode 100644
index 0000000..8c0efe4
--- /dev/null
+++ b/include/perfetto/public/protos/trace/trace_packet.pzc.h
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_PUBLIC_PROTOS_TRACE_TRACE_PACKET_PZC_H_
+#define INCLUDE_PERFETTO_PUBLIC_PROTOS_TRACE_TRACE_PACKET_PZC_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "perfetto/public/pb_macros.h"
+
+PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidCameraFrameEvent);
+PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidCameraSessionStats);
+PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidEnergyEstimationBreakdown);
+PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidGameInterventionList);
+PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidLogPacket);
+PERFETTO_PB_MSG_DECL(perfetto_protos_AndroidSystemProperty);
+PERFETTO_PB_MSG_DECL(perfetto_protos_BatteryCounters);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ChromeBenchmarkMetadata);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ChromeEventBundle);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ChromeMetadataPacket);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ClockSnapshot);
+PERFETTO_PB_MSG_DECL(perfetto_protos_CpuInfo);
+PERFETTO_PB_MSG_DECL(perfetto_protos_DeobfuscationMapping);
+PERFETTO_PB_MSG_DECL(perfetto_protos_EntityStateResidency);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ExtensionDescriptor);
+PERFETTO_PB_MSG_DECL(perfetto_protos_FrameTimelineEvent);
+PERFETTO_PB_MSG_DECL(perfetto_protos_FtraceEventBundle);
+PERFETTO_PB_MSG_DECL(perfetto_protos_FtraceStats);
+PERFETTO_PB_MSG_DECL(perfetto_protos_GpuCounterEvent);
+PERFETTO_PB_MSG_DECL(perfetto_protos_GpuLog);
+PERFETTO_PB_MSG_DECL(perfetto_protos_GpuMemTotalEvent);
+PERFETTO_PB_MSG_DECL(perfetto_protos_GpuRenderStageEvent);
+PERFETTO_PB_MSG_DECL(perfetto_protos_GraphicsFrameEvent);
+PERFETTO_PB_MSG_DECL(perfetto_protos_HeapGraph);
+PERFETTO_PB_MSG_DECL(perfetto_protos_InitialDisplayState);
+PERFETTO_PB_MSG_DECL(perfetto_protos_InodeFileMap);
+PERFETTO_PB_MSG_DECL(perfetto_protos_InternedData);
+PERFETTO_PB_MSG_DECL(perfetto_protos_MemoryTrackerSnapshot);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ModuleSymbols);
+PERFETTO_PB_MSG_DECL(perfetto_protos_NetworkPacketEvent);
+PERFETTO_PB_MSG_DECL(perfetto_protos_PackagesList);
+PERFETTO_PB_MSG_DECL(perfetto_protos_PerfSample);
+PERFETTO_PB_MSG_DECL(perfetto_protos_PerfettoMetatrace);
+PERFETTO_PB_MSG_DECL(perfetto_protos_PowerRails);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ProcessDescriptor);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ProcessStats);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ProcessTree);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ProfilePacket);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ProfiledFrameSymbols);
+PERFETTO_PB_MSG_DECL(perfetto_protos_SmapsPacket);
+PERFETTO_PB_MSG_DECL(perfetto_protos_StatsdAtom);
+PERFETTO_PB_MSG_DECL(perfetto_protos_StreamingAllocation);
+PERFETTO_PB_MSG_DECL(perfetto_protos_StreamingFree);
+PERFETTO_PB_MSG_DECL(perfetto_protos_StreamingProfilePacket);
+PERFETTO_PB_MSG_DECL(perfetto_protos_SysStats);
+PERFETTO_PB_MSG_DECL(perfetto_protos_SystemInfo);
+PERFETTO_PB_MSG_DECL(perfetto_protos_TestEvent);
+PERFETTO_PB_MSG_DECL(perfetto_protos_ThreadDescriptor);
+PERFETTO_PB_MSG_DECL(perfetto_protos_TraceConfig);
+PERFETTO_PB_MSG_DECL(perfetto_protos_TracePacketDefaults);
+PERFETTO_PB_MSG_DECL(perfetto_protos_TraceStats);
+PERFETTO_PB_MSG_DECL(perfetto_protos_TraceUuid);
+PERFETTO_PB_MSG_DECL(perfetto_protos_TracingServiceEvent);
+PERFETTO_PB_MSG_DECL(perfetto_protos_TrackDescriptor);
+PERFETTO_PB_MSG_DECL(perfetto_protos_TrackEvent);
+PERFETTO_PB_MSG_DECL(perfetto_protos_TrackEventRangeOfInterest);
+PERFETTO_PB_MSG_DECL(perfetto_protos_TranslationTable);
+PERFETTO_PB_MSG_DECL(perfetto_protos_Trigger);
+PERFETTO_PB_MSG_DECL(perfetto_protos_UiState);
+PERFETTO_PB_MSG_DECL(perfetto_protos_VulkanApiEvent);
+PERFETTO_PB_MSG_DECL(perfetto_protos_VulkanMemoryEvent);
+
+PERFETTO_PB_ENUM_IN_MSG(perfetto_protos_TracePacket, SequenceFlags){
+    PERFETTO_PB_ENUM_IN_MSG_ENTRY(perfetto_protos_TracePacket,
+                                  SEQ_UNSPECIFIED) = 0,
+    PERFETTO_PB_ENUM_IN_MSG_ENTRY(perfetto_protos_TracePacket,
+                                  SEQ_INCREMENTAL_STATE_CLEARED) = 1,
+    PERFETTO_PB_ENUM_IN_MSG_ENTRY(perfetto_protos_TracePacket,
+                                  SEQ_NEEDS_INCREMENTAL_STATE) = 2,
+};
+
+PERFETTO_PB_MSG(perfetto_protos_TracePacket);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket, VARINT, uint64_t, timestamp, 8);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  VARINT,
+                  uint32_t,
+                  timestamp_clock_id,
+                  58);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ProcessTree,
+                  process_tree,
+                  2);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ProcessStats,
+                  process_stats,
+                  9);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_InodeFileMap,
+                  inode_file_map,
+                  4);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ChromeEventBundle,
+                  chrome_events,
+                  5);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ClockSnapshot,
+                  clock_snapshot,
+                  6);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_SysStats,
+                  sys_stats,
+                  7);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_TrackEvent,
+                  track_event,
+                  11);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_TraceUuid,
+                  trace_uuid,
+                  89);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_TraceConfig,
+                  trace_config,
+                  33);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_FtraceStats,
+                  ftrace_stats,
+                  34);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_TraceStats,
+                  trace_stats,
+                  35);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ProfilePacket,
+                  profile_packet,
+                  37);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_StreamingAllocation,
+                  streaming_allocation,
+                  74);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_StreamingFree,
+                  streaming_free,
+                  75);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_BatteryCounters,
+                  battery,
+                  38);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_PowerRails,
+                  power_rails,
+                  40);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_AndroidLogPacket,
+                  android_log,
+                  39);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_SystemInfo,
+                  system_info,
+                  45);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_Trigger,
+                  trigger,
+                  46);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_PackagesList,
+                  packages_list,
+                  47);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ChromeBenchmarkMetadata,
+                  chrome_benchmark_metadata,
+                  48);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_PerfettoMetatrace,
+                  perfetto_metatrace,
+                  49);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ChromeMetadataPacket,
+                  chrome_metadata,
+                  51);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_GpuCounterEvent,
+                  gpu_counter_event,
+                  52);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_GpuRenderStageEvent,
+                  gpu_render_stage_event,
+                  53);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_StreamingProfilePacket,
+                  streaming_profile_packet,
+                  54);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_HeapGraph,
+                  heap_graph,
+                  56);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_GraphicsFrameEvent,
+                  graphics_frame_event,
+                  57);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_VulkanMemoryEvent,
+                  vulkan_memory_event,
+                  62);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_GpuLog,
+                  gpu_log,
+                  63);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_VulkanApiEvent,
+                  vulkan_api_event,
+                  65);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_PerfSample,
+                  perf_sample,
+                  66);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_CpuInfo,
+                  cpu_info,
+                  67);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_SmapsPacket,
+                  smaps_packet,
+                  68);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_TracingServiceEvent,
+                  service_event,
+                  69);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_InitialDisplayState,
+                  initial_display_state,
+                  70);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_GpuMemTotalEvent,
+                  gpu_mem_total_event,
+                  71);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_MemoryTrackerSnapshot,
+                  memory_tracker_snapshot,
+                  73);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_FrameTimelineEvent,
+                  frame_timeline_event,
+                  76);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_AndroidEnergyEstimationBreakdown,
+                  android_energy_estimation_breakdown,
+                  77);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_UiState,
+                  ui_state,
+                  78);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_AndroidCameraFrameEvent,
+                  android_camera_frame_event,
+                  80);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_AndroidCameraSessionStats,
+                  android_camera_session_stats,
+                  81);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_TranslationTable,
+                  translation_table,
+                  82);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_AndroidGameInterventionList,
+                  android_game_intervention_list,
+                  83);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_StatsdAtom,
+                  statsd_atom,
+                  84);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_AndroidSystemProperty,
+                  android_system_property,
+                  86);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_EntityStateResidency,
+                  entity_state_residency,
+                  91);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ProfiledFrameSymbols,
+                  profiled_frame_symbols,
+                  55);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ModuleSymbols,
+                  module_symbols,
+                  61);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_DeobfuscationMapping,
+                  deobfuscation_mapping,
+                  64);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_TrackDescriptor,
+                  track_descriptor,
+                  60);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ProcessDescriptor,
+                  process_descriptor,
+                  43);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ThreadDescriptor,
+                  thread_descriptor,
+                  44);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_FtraceEventBundle,
+                  ftrace_events,
+                  1);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  STRING,
+                  const char*,
+                  synchronization_marker,
+                  36);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  STRING,
+                  const char*,
+                  compressed_packets,
+                  50);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_ExtensionDescriptor,
+                  extension_descriptor,
+                  72);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_NetworkPacketEvent,
+                  network_packet,
+                  88);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_TrackEventRangeOfInterest,
+                  track_event_range_of_interest,
+                  90);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_TestEvent,
+                  for_testing,
+                  900);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket, VARINT, int32_t, trusted_uid, 3);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  VARINT,
+                  uint32_t,
+                  trusted_packet_sequence_id,
+                  10);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  VARINT,
+                  int32_t,
+                  trusted_pid,
+                  79);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_InternedData,
+                  interned_data,
+                  12);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  VARINT,
+                  uint32_t,
+                  sequence_flags,
+                  13);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  VARINT,
+                  bool,
+                  incremental_state_cleared,
+                  41);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  MSG,
+                  perfetto_protos_TracePacketDefaults,
+                  trace_packet_defaults,
+                  59);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  VARINT,
+                  bool,
+                  previous_packet_dropped,
+                  42);
+PERFETTO_PB_FIELD(perfetto_protos_TracePacket,
+                  VARINT,
+                  bool,
+                  first_packet_on_sequence,
+                  87);
+
+#endif  // INCLUDE_PERFETTO_PUBLIC_PROTOS_TRACE_TRACE_PACKET_PZC_H_
diff --git a/include/perfetto/trace_processor/ref_counted.h b/include/perfetto/trace_processor/ref_counted.h
index 4eb27c9..d753151 100644
--- a/include/perfetto/trace_processor/ref_counted.h
+++ b/include/perfetto/trace_processor/ref_counted.h
@@ -88,6 +88,25 @@
   // This case is really the move-assignment operator=(&&).
   void reset(T* new_obj) { *this = RefPtr<T>(new_obj); }
 
+  // Releases the pointer owned by this RefPtr *without* decrementing the
+  // refcount. Callers *must* call |FromReleasedUnsafe| at a later date with
+  // this pointer to avoid memory leaks.
+  T* ReleaseUnsafe() {
+    PERFETTO_DCHECK(ptr_->refcount_ > 0);
+    auto* old_ptr = ptr_;
+    ptr_ = nullptr;
+    return old_ptr;
+  }
+
+  // Creates a RefPtr from a pointer returned by |ReleaseUnsafe|. Passing a
+  // pointer from any other source results in undefined behaviour.
+  static RefPtr<T> FromReleasedUnsafe(T* ptr) {
+    PERFETTO_DCHECK(ptr->refcount_ > 0);
+    RefPtr<T> res;
+    res.ptr_ = ptr;
+    return res;
+  }
+
   // Move operators.
   RefPtr(RefPtr&& move_from) noexcept {
     ptr_ = move_from.ptr_;
@@ -119,6 +138,10 @@
   bool operator==(const RefPtr<U>& rhs) const {
     return ptr_ == rhs.ptr_;
   }
+  template <typename U>
+  bool operator!=(const RefPtr<U>& rhs) const {
+    return !(*this == rhs);
+  }
 
   T* get() const { return ptr_; }
   T* operator->() const { return ptr_; }
diff --git a/include/perfetto/trace_processor/trace_blob_view.h b/include/perfetto/trace_processor/trace_blob_view.h
index f140866..3de4d7b 100644
--- a/include/perfetto/trace_processor/trace_blob_view.h
+++ b/include/perfetto/trace_processor/trace_blob_view.h
@@ -45,7 +45,7 @@
 //  - TraceBlob: writable, move-only, single-instance.
 //  - TraceBlobView: readable, copyable, multiple-instances can hold onto
 //                   (different sub-slices of) the same refcounted TraceBlob.
-class TraceBlobView {
+class alignas(8) TraceBlobView {
  public:
   // Takes ownership of the passed |blob|.
   static constexpr size_t kWholeBlob = std::numeric_limits<size_t>::max();
@@ -64,6 +64,11 @@
     blob_.reset(new TraceBlob(std::move(blob)));
   }
 
+  TraceBlobView(RefPtr<TraceBlob> blob, size_t offset, uint32_t length)
+      : blob_(std::move(blob)), data_(blob_->data() + offset), length_(length) {
+    PERFETTO_DCHECK(offset + length_ <= blob_->size());
+  }
+
   // Trivial empty ctor.
   TraceBlobView() : data_(nullptr), length_(0) {}
 
@@ -105,9 +110,10 @@
   bool operator!=(const TraceBlobView& rhs) const { return !(*this == rhs); }
 
   const uint8_t* data() const { return data_; }
-  // TODO(primiano): normalize length() vs size() usage.
+  size_t offset() const { return static_cast<size_t>(data_ - blob_->data()); }
   size_t length() const { return length_; }
   size_t size() const { return length_; }
+  RefPtr<TraceBlob> blob() const { return blob_; }
 
  private:
   TraceBlobView(RefPtr<TraceBlob> blob, const uint8_t* data, uint32_t length)
diff --git a/include/perfetto/tracing/BUILD.gn b/include/perfetto/tracing/BUILD.gn
index f73dafd..21abc8e 100644
--- a/include/perfetto/tracing/BUILD.gn
+++ b/include/perfetto/tracing/BUILD.gn
@@ -38,6 +38,7 @@
     "internal/checked_scope.h",
     "internal/compile_time_hash.h",
     "internal/data_source_internal.h",
+    "internal/data_source_type.h",
     "internal/in_process_tracing_backend.h",
     "internal/interceptor_trace_writer.h",
     "internal/system_tracing_backend.h",
diff --git a/include/perfetto/tracing/data_source.h b/include/perfetto/tracing/data_source.h
index acf892c..30f2010 100644
--- a/include/perfetto/tracing/data_source.h
+++ b/include/perfetto/tracing/data_source.h
@@ -31,15 +31,12 @@
 #include <memory>
 #include <mutex>
 
-#include "perfetto/base/build_config.h"
-#include "perfetto/base/compiler.h"
-#include "perfetto/base/export.h"
-#include "perfetto/protozero/message.h"
 #include "perfetto/protozero/message_handle.h"
 #include "perfetto/tracing/buffer_exhausted_policy.h"
 #include "perfetto/tracing/core/forward_decls.h"
 #include "perfetto/tracing/internal/basic_types.h"
 #include "perfetto/tracing/internal/data_source_internal.h"
+#include "perfetto/tracing/internal/data_source_type.h"
 #include "perfetto/tracing/internal/tracing_muxer.h"
 #include "perfetto/tracing/locked_handle.h"
 #include "perfetto/tracing/trace_writer_base.h"
@@ -143,9 +140,7 @@
 
 struct DefaultDataSourceTraits {
   // |IncrementalStateType| can optionally be used store custom per-sequence
-  // incremental data (e.g., interning tables). It should have a Clear() method
-  // for when incremental state needs to be cleared. See
-  // TraceContext::GetIncrementalState().
+  // incremental data (e.g., interning tables).
   using IncrementalStateType = void;
   // |TlsStateType| can optionally be used to store custom per-sequence
   // session data, which is not reset when incremental state is cleared
@@ -171,12 +166,12 @@
 };
 
 // Templated base class meant to be derived by embedders to create a custom data
-// source. DataSourceType must be the type of the derived class itself, e.g.:
-// class MyDataSource : public DataSourceBase<MyDataSource> {...}.
+// source. DerivedDataSource must be the type of the derived class itself, e.g.:
+// class MyDataSource : public DataSource<MyDataSource> {...}.
 //
 // |DataSourceTraits| allows customizing the behavior of the data source. See
 // |DefaultDataSourceTraits|.
-template <typename DataSourceType,
+template <typename DerivedDataSource,
           typename DataSourceTraits = DefaultDataSourceTraits>
 class DataSource : public DataSourceBase {
   struct DefaultTracePointTraits;
@@ -265,14 +260,14 @@
     // immediately before calling this. The caller is supposed to check for its
     // validity before using it. After checking, the handle is guaranteed to
     // remain valid until the handle goes out of scope.
-    LockedHandle<DataSourceType> GetDataSourceLocked() const {
-      auto* internal_state = static_state_.TryGet(instance_index_);
+    LockedHandle<DerivedDataSource> GetDataSourceLocked() const {
+      auto* internal_state = type_.static_state()->TryGet(instance_index_);
       if (!internal_state)
-        return LockedHandle<DataSourceType>();
+        return LockedHandle<DerivedDataSource>();
       std::unique_lock<std::recursive_mutex> lock(internal_state->lock);
-      return LockedHandle<DataSourceType>(
+      return LockedHandle<DerivedDataSource>(
           std::move(lock),
-          static_cast<DataSourceType*>(internal_state->data_source.get()));
+          static_cast<DerivedDataSource*>(internal_state->data_source.get()));
     }
 
     // Post-condition: returned ptr will be non-null.
@@ -283,15 +278,8 @@
     }
 
     typename DataSourceTraits::IncrementalStateType* GetIncrementalState() {
-      // Recreate incremental state data if it has been reset by the service.
-      if (tls_inst_->incremental_state_generation !=
-          static_state_.incremental_state_generation.load(
-              std::memory_order_relaxed)) {
-        tls_inst_->incremental_state.reset();
-        CreateIncrementalState(tls_inst_);
-      }
-      return reinterpret_cast<typename DataSourceTraits::IncrementalStateType*>(
-          tls_inst_->incremental_state.get());
+      return static_cast<typename DataSourceTraits::IncrementalStateType*>(
+          type_.GetIncrementalState(tls_inst_, instance_index_));
     }
 
    private:
@@ -334,7 +322,8 @@
                                 {}) PERFETTO_ALWAYS_INLINE {
     // |instances| is a per-class bitmap that tells:
     // 1. If the data source is enabled at all.
-    // 2. The index of the slot within |static_state_| that holds the instance
+    // 2. The index of the slot within
+    //    internal::DataSourceStaticState::instances that holds the instance
     //    state. In turn this allows to map the data source to the tracing
     //    session and buffers.
     // memory_order_relaxed is okay because:
@@ -361,128 +350,27 @@
   // GetActiveInstances| to make it possible to use custom storage for
   // the data source enabled state. This is, for example, used by TrackEvent to
   // implement per-tracing category enabled states.
-  //
-  // TODO(primiano): all the stuff below should be outlined from the trace
-  // point. Or at least we should have some compile-time traits like
-  // kOptimizeBinarySize / kOptimizeTracingLatency.
   template <typename Traits = DefaultTracePointTraits, typename Lambda>
   static void TraceWithInstances(
-      uint32_t instances,
+      uint32_t cached_instances,
       Lambda tracing_fn,
       typename Traits::TracePointData trace_point_data = {}) {
-    PERFETTO_DCHECK(instances);
-    constexpr auto kMaxDataSourceInstances = internal::kMaxDataSourceInstances;
+    PERFETTO_DCHECK(cached_instances);
 
-    // See tracing_muxer.h for the structure of the TLS.
-    if (PERFETTO_UNLIKELY(!tls_state_)) {
-      // If the TLS hasn't been obtained yet, it's possible that this thread
-      // hasn't observed the initialization of global state like the muxer yet.
-      // To ensure that the thread "sees" the effects of such initialization,
-      // we have to reload |instances| with an acquire fence, ensuring that any
-      // initialization performed before instances was updated is visible
-      // in this thread.
-      instances &= Traits::GetActiveInstances(trace_point_data)
-                       ->load(std::memory_order_acquire);
-      if (!instances)
-        return;
-      tls_state_ = GetOrCreateDataSourceTLS(&static_state_);
-    }
-
-    // |tls_state_| is valid, which means that the current thread must have
-    // observed the initialization of the muxer, and obtaining it without a
-    // fence is safe.
-    auto* tracing_impl = internal::TracingMuxer::Get();
-
-    // Avoid re-entering the trace point recursively.
-    if (PERFETTO_UNLIKELY(tls_state_->root_tls->is_in_trace_point))
+    if (!type_.TracePrologue<DataSourceTraits, Traits>(
+            &tls_state_, &cached_instances, trace_point_data)) {
       return;
-    internal::ScopedReentrancyAnnotator scoped_annotator(*tls_state_->root_tls);
-
-    // TracingTLS::generation is a global monotonic counter that is incremented
-    // every time a tracing session is stopped. We use that as a signal to force
-    // a slow-path garbage collection of all the trace writers for the current
-    // thread and to destroy the ones that belong to tracing sessions that have
-    // ended. This is to avoid having too many TraceWriter instances alive, each
-    // holding onto one chunk of the shared memory buffer.
-    // Rationale why memory_order_relaxed should be fine:
-    // - The TraceWriter object that we use is always constructed and destructed
-    //   on the current thread. There is no risk of accessing a half-initialized
-    //   TraceWriter (which would be really bad).
-    // - In the worst case, in the case of a race on the generation check, we
-    //   might end up using a TraceWriter for the same data source that belongs
-    //   to a stopped session. This is not really wrong, as we don't give any
-    //   guarantee on the global atomicity of the stop. In the worst case the
-    //   service will reject the data commit if this arrives too late.
-
-    if (PERFETTO_UNLIKELY(
-            tls_state_->root_tls->generation !=
-            tracing_impl->generation(std::memory_order_relaxed))) {
-      // Will update root_tls->generation.
-      tracing_impl->DestroyStoppedTraceWritersForCurrentThread();
     }
 
-    for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
-      internal::DataSourceState* instance_state =
-          static_state_.TryGetCached(instances, i);
-      if (!instance_state)
-        continue;
-
-      // Even if we passed the check above, the DataSourceInstance might be
-      // still destroyed concurrently while this code runs. The code below is
-      // designed to deal with such race, as follows:
-      // - We don't access the user-defined data source instance state. The only
-      //   bits of state we use are |backend_id| and |buffer_id|.
-      // - Beyond those two integers, we access only the TraceWriter here. The
-      //   TraceWriter is always safe because it lives on the TLS.
-      // - |instance_state| is backed by static storage, so the pointer is
-      //   always valid, even after the data source instance is destroyed.
-      // - In the case of a race-on-destruction, we'll still see the latest
-      //   backend_id and buffer_id and in the worst case keep trying writing
-      //   into the tracing shared memory buffer after stopped. But this isn't
-      //   really any worse than the case of the stop IPC being delayed by the
-      //   kernel scheduler. The tracing service is robust against data commit
-      //   attemps made after tracing is stopped.
-      // There is a theoretical race that would case the wrong behavior w.r.t
-      // writing data in the wrong buffer, but it's so rare that we ignore it:
-      // if the data source is stopped and started kMaxDataSourceInstances
-      // times (so that the same id is recycled) while we are in this function,
-      // we might end up reusing the old data source's backend_id and buffer_id
-      // for the new one, because we don't see the generation change past this
-      // point. But stopping and starting tracing (even once) takes so much
-      // handshaking to make this extremely unrealistic.
-
-      auto& tls_inst = tls_state_->per_instance[i];
-      if (PERFETTO_UNLIKELY(!tls_inst.trace_writer)) {
-        // Here we need an acquire barrier, which matches the release-store made
-        // by TracingMuxerImpl::SetupDataSource(), to ensure that the backend_id
-        // and buffer_id are consistent.
-        instances &= Traits::GetActiveInstances(trace_point_data)
-                         ->load(std::memory_order_acquire);
-        instance_state = static_state_.TryGetCached(instances, i);
-        if (!instance_state || !instance_state->trace_lambda_enabled)
-          continue;
-        tls_inst.muxer_id_for_testing = instance_state->muxer_id_for_testing;
-        tls_inst.backend_id = instance_state->backend_id;
-        tls_inst.backend_connection_id = instance_state->backend_connection_id;
-        tls_inst.buffer_id = instance_state->buffer_id;
-        tls_inst.startup_target_buffer_reservation =
-            instance_state->startup_target_buffer_reservation.load(
-                std::memory_order_relaxed);
-        tls_inst.data_source_instance_id =
-            instance_state->data_source_instance_id;
-        tls_inst.is_intercepted = instance_state->interceptor_id != 0;
-        tls_inst.trace_writer = tracing_impl->CreateTraceWriter(
-            &static_state_, i, instance_state,
-            DataSourceType::kBufferExhaustedPolicy);
-        CreateIncrementalState(&tls_inst);
-        CreateDataSourceCustomTLS(TraceContext(&tls_inst, i));
-        // Even in the case of out-of-IDs, SharedMemoryArbiterImpl returns a
-        // NullTraceWriter. The returned pointer should never be null.
-        assert(tls_inst.trace_writer);
-      }
-
-      tracing_fn(TraceContext(&tls_inst, i));
+    for (internal::DataSourceType::InstancesIterator it =
+             type_.BeginIteration<Traits>(cached_instances, tls_state_,
+                                          trace_point_data);
+         it.instance;
+         type_.NextIteration<Traits>(&it, tls_state_, trace_point_data)) {
+      tracing_fn(TraceContext(it.instance, it.i));
     }
+
+    type_.TraceEpilogue(tls_state_);
   }
 
   // Registers the data source on all tracing backends, including ones that
@@ -500,25 +388,29 @@
                        const Args&... constructor_args) {
     // Silences -Wunused-variable warning in case the trace method is not used
     // by the translation unit that declares the data source.
-    (void)static_state_;
+    (void)type_;
     (void)tls_state_;
 
     auto factory = [constructor_args...]() {
       return std::unique_ptr<DataSourceBase>(
-          new DataSourceType(constructor_args...));
+          new DerivedDataSource(constructor_args...));
     };
-    auto* tracing_impl = internal::TracingMuxer::Get();
     internal::DataSourceParams params{
-        DataSourceType::kSupportsMultipleInstances,
-        DataSourceType::kRequiresCallbacksUnderLock};
-    return tracing_impl->RegisterDataSource(descriptor, factory, params,
-                                            &static_state_);
+        DerivedDataSource::kSupportsMultipleInstances,
+        DerivedDataSource::kRequiresCallbacksUnderLock};
+    return type_.Register(
+        descriptor, factory, params, DerivedDataSource::kBufferExhaustedPolicy,
+        GetCreateTlsFn(
+            static_cast<typename DataSourceTraits::TlsStateType*>(nullptr)),
+        GetCreateIncrementalStateFn(
+            static_cast<typename DataSourceTraits::IncrementalStateType*>(
+                nullptr)),
+        nullptr);
   }
 
   // Updates the data source descriptor.
   static void UpdateDescriptor(const DataSourceDescriptor& descriptor) {
-    auto* tracing_impl = internal::TracingMuxer::Get();
-    tracing_impl->UpdateDataSourceDescriptor(descriptor, &static_state_);
+    type_.UpdateDescriptor(descriptor);
   }
 
  private:
@@ -538,80 +430,59 @@
     // implement per-category enabled states.
     struct TracePointData {};
     static constexpr std::atomic<uint32_t>* GetActiveInstances(TracePointData) {
-      return &static_state_.valid_instances;
+      return type_.valid_instances();
     }
   };
 
-  // Create the user provided incremental state in the given thread-local
-  // storage. Note: The second parameter here is used to specialize the case
-  // where there is no incremental state type.
   template <typename T>
-  static void CreateIncrementalStateImpl(
+  static internal::DataSourceInstanceThreadLocalState::ObjectWithDeleter
+  CreateIncrementalState(internal::DataSourceInstanceThreadLocalState*,
+                         uint32_t,
+                         void*) {
+    return internal::DataSourceInstanceThreadLocalState::ObjectWithDeleter(
+        reinterpret_cast<void*>(new T()),
+        [](void* p) { delete reinterpret_cast<T*>(p); });
+  }
+
+  // The second parameter here is used to specialize the case where there is no
+  // incremental state type.
+  template <typename T>
+  static internal::DataSourceType::CreateIncrementalStateFn
+  GetCreateIncrementalStateFn(const T*) {
+    return &CreateIncrementalState<T>;
+  }
+
+  static internal::DataSourceType::CreateIncrementalStateFn
+  GetCreateIncrementalStateFn(const void*) {
+    return nullptr;
+  }
+
+  template <typename T>
+  static internal::DataSourceInstanceThreadLocalState::ObjectWithDeleter
+  CreateDataSourceCustomTls(
       internal::DataSourceInstanceThreadLocalState* tls_inst,
-      const T*) {
-    PERFETTO_DCHECK(!tls_inst->incremental_state);
-    tls_inst->incremental_state_generation =
-        static_state_.incremental_state_generation.load(
-            std::memory_order_relaxed);
-    tls_inst->incremental_state =
-        internal::DataSourceInstanceThreadLocalState::ObjectWithDeleter(
-            reinterpret_cast<void*>(new T()),
-            [](void* p) { delete reinterpret_cast<T*>(p); });
+      uint32_t instance_index,
+      void*) {
+    return internal::DataSourceInstanceThreadLocalState::ObjectWithDeleter(
+        reinterpret_cast<void*>(new T(TraceContext(tls_inst, instance_index))),
+        [](void* p) { delete reinterpret_cast<T*>(p); });
   }
 
-  static void CreateIncrementalStateImpl(
-      internal::DataSourceInstanceThreadLocalState*,
-      const void*) {}
-
-  static void CreateIncrementalState(
-      internal::DataSourceInstanceThreadLocalState* tls_inst) {
-    CreateIncrementalStateImpl(
-        tls_inst,
-        static_cast<typename DataSourceTraits::IncrementalStateType*>(nullptr));
-  }
-
-  // Create the user provided custom tls state in the given TraceContext's
-  // thread-local storage.  Note: The second parameter here is used to
-  // specialize the case where there is no incremental state type.
+  // The second parameter here is used to specialize the case where there is no
+  // tls state type.
   template <typename T>
-  static void CreateDataSourceCustomTLSImpl(const TraceContext& trace_context,
-                                            const T*) {
-    PERFETTO_DCHECK(!trace_context.tls_inst_->data_source_custom_tls);
-    trace_context.tls_inst_->data_source_custom_tls =
-        internal::DataSourceInstanceThreadLocalState::ObjectWithDeleter(
-            reinterpret_cast<void*>(new T(trace_context)),
-            [](void* p) { delete reinterpret_cast<T*>(p); });
+  static internal::DataSourceType::CreateCustomTlsFn GetCreateTlsFn(const T*) {
+    return &CreateDataSourceCustomTls<T>;
   }
 
-  static void CreateDataSourceCustomTLSImpl(const TraceContext&, const void*) {}
-
-  static void CreateDataSourceCustomTLS(const TraceContext& trace_context) {
-    CreateDataSourceCustomTLSImpl(
-        trace_context,
-        static_cast<typename DataSourceTraits::TlsStateType*>(nullptr));
+  static internal::DataSourceType::CreateCustomTlsFn GetCreateTlsFn(
+      const void*) {
+    return nullptr;
   }
 
-  // Note that the returned object is one per-thread per-data-source-type, NOT
-  // per data-source *instance*.
-  static internal::DataSourceThreadLocalState* GetOrCreateDataSourceTLS(
-      internal::DataSourceStaticState* static_state) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_IOS)
-    PERFETTO_FATAL("Data source TLS not supported on iOS, see b/158814068");
-#endif
-    auto* tracing_impl = internal::TracingMuxer::Get();
-    internal::TracingTLS* root_tls = tracing_impl->GetOrCreateTracingTLS();
-    internal::DataSourceThreadLocalState* ds_tls =
-        DataSourceTraits::GetDataSourceTLS(static_state, root_tls);
-    // We keep re-initializing as the initialization is idempotent and not worth
-    // the code for extra checks.
-    ds_tls->static_state = static_state;
-    assert(!ds_tls->root_tls || ds_tls->root_tls == root_tls);
-    ds_tls->root_tls = root_tls;
-    return ds_tls;
-  }
-
-  // Static state. Accessed by the static Trace() method fastpaths.
-  static internal::DataSourceStaticState static_state_;
+  // The type of this data source. Accessed by the static Trace() method
+  // fastpaths.
+  static internal::DataSourceType type_;
 
   // This TLS object is a cached raw pointer and has deliberately no destructor.
   // The Platform implementation is supposed to create and manage the lifetime
@@ -625,7 +496,7 @@
 
 // static
 template <typename T, typename D>
-internal::DataSourceStaticState DataSource<T, D>::static_state_;
+internal::DataSourceType DataSource<T, D>::type_;
 // static
 template <typename T, typename D>
 PERFETTO_THREAD_LOCAL internal::DataSourceThreadLocalState*
@@ -650,8 +521,8 @@
 // where a component specific export macro is used.
 #define PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS(attrs, ...) \
   template <>                                                              \
-  attrs perfetto::internal::DataSourceStaticState                          \
-      perfetto::DataSource<__VA_ARGS__>::static_state_
+  attrs perfetto::internal::DataSourceType                                 \
+      perfetto::DataSource<__VA_ARGS__>::type_
 
 // This macro must be used once for each data source in one source file to
 // allocate static storage for the data source's static state.
@@ -669,7 +540,7 @@
 // where a component specific export macro is used.
 #define PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS(attrs, ...) \
   template <>                                                             \
-  attrs perfetto::internal::DataSourceStaticState                         \
-      perfetto::DataSource<__VA_ARGS__>::static_state_ {}
+  attrs perfetto::internal::DataSourceType                                \
+      perfetto::DataSource<__VA_ARGS__>::type_ {}
 
 #endif  // INCLUDE_PERFETTO_TRACING_DATA_SOURCE_H_
diff --git a/include/perfetto/tracing/internal/basic_types.h b/include/perfetto/tracing/internal/basic_types.h
index 81f5353..85263dd 100644
--- a/include/perfetto/tracing/internal/basic_types.h
+++ b/include/perfetto/tracing/internal/basic_types.h
@@ -27,7 +27,7 @@
 // with the definition in tracing/core/basic_types.h
 using BufferId = uint16_t;
 
-// This is a direct index in the TracingMuxer::backends_ vector.
+// This is an id of a backend in the TracingMuxer::producer_backends_ list.
 // Backends are only added and never removed.
 using TracingBackendId = size_t;
 
diff --git a/include/perfetto/tracing/internal/data_source_internal.h b/include/perfetto/tracing/internal/data_source_internal.h
index 9d9c6a2..50b235c 100644
--- a/include/perfetto/tracing/internal/data_source_internal.h
+++ b/include/perfetto/tracing/internal/data_source_internal.h
@@ -59,7 +59,7 @@
   // Keep this flag as the first field. This allows the compiler to directly
   // dereference the DataSourceState* pointer in the trace fast-path without
   // doing extra pointr arithmetic.
-  bool trace_lambda_enabled = false;
+  std::atomic<bool> trace_lambda_enabled{false};
 
   // The overall TracingMuxerImpl instance id, which gets incremented by
   // ResetForTesting.
diff --git a/include/perfetto/tracing/internal/data_source_type.h b/include/perfetto/tracing/internal/data_source_type.h
new file mode 100644
index 0000000..5968223
--- /dev/null
+++ b/include/perfetto/tracing/internal/data_source_type.h
@@ -0,0 +1,316 @@
+#ifndef INCLUDE_PERFETTO_TRACING_INTERNAL_DATA_SOURCE_TYPE_H_
+#define INCLUDE_PERFETTO_TRACING_INTERNAL_DATA_SOURCE_TYPE_H_
+
+#include "perfetto/base/build_config.h"
+#include "perfetto/base/export.h"
+#include "perfetto/tracing/core/forward_decls.h"
+#include "perfetto/tracing/internal/data_source_internal.h"
+#include "perfetto/tracing/internal/tracing_muxer.h"
+
+namespace perfetto {
+namespace internal {
+
+// Represents a data source type (not an instance).
+//
+// All the static state of a DataSource<T> lives here (including
+// DataSourceStaticState).
+//
+// The C shared library API wrapper cannot use DataSource<T>, because it needs
+// to create new data source types at runtime, so it uses this directly.
+//
+// The main reason why this intermediate class exist is to decouple the
+// DataSourceStaticState from the specific DataSource<T>. The C API cannot
+// dynamically create template instances and it needs a way to decouple those at
+// runtime.
+class PERFETTO_EXPORT_COMPONENT DataSourceType {
+ public:
+  // Function pointer type used to create custom per instance thread local
+  // state.
+  using CreateCustomTlsFn =
+      DataSourceInstanceThreadLocalState::ObjectWithDeleter (*)(
+          DataSourceInstanceThreadLocalState* tls_inst,
+          uint32_t instance_index,
+          void* user_arg);
+  // Function pointer type used to create custom per instance thread local
+  // incremental state (which might be cleared periodically by the tracing
+  // service).
+  using CreateIncrementalStateFn =
+      DataSourceInstanceThreadLocalState::ObjectWithDeleter (*)(
+          DataSourceInstanceThreadLocalState* tls_inst,
+          uint32_t instance_index,
+          void* user_arg);
+
+  // Registers the data source type with the central tracing muxer.
+  // * `descriptor` is the data source protobuf descriptor.
+  // * `factory` is a std::function used to create instances of the data source
+  //   type.
+  // * `buffer_exhausted_policy` specifies what to do when the shared memory
+  //   buffer runs out of chunks.
+  // * `create_custom_tls_fn` and `create_incremental_state_fn` are function
+  //   pointers called to create custom state. They will receive `user_arg` as
+  //   an extra param.
+  bool Register(const DataSourceDescriptor& descriptor,
+                TracingMuxer::DataSourceFactory factory,
+                internal::DataSourceParams params,
+                BufferExhaustedPolicy buffer_exhausted_policy,
+                CreateCustomTlsFn create_custom_tls_fn,
+                CreateIncrementalStateFn create_incremental_state_fn,
+                void* user_arg) {
+    buffer_exhausted_policy_ = buffer_exhausted_policy;
+    create_custom_tls_fn_ = create_custom_tls_fn;
+    create_incremental_state_fn_ = create_incremental_state_fn;
+    user_arg_ = user_arg;
+    auto* tracing_impl = TracingMuxer::Get();
+    return tracing_impl->RegisterDataSource(descriptor, factory, params,
+                                            &state_);
+  }
+
+  // Updates the data source type descriptor.
+  void UpdateDescriptor(const DataSourceDescriptor& descriptor) {
+    auto* tracing_impl = TracingMuxer::Get();
+    tracing_impl->UpdateDataSourceDescriptor(descriptor, &state_);
+  }
+
+  // The beginning of a trace point.
+  //
+  // `tls_state` must point to a thread local variable that caches a pointer to
+  // an internal per data source type thread local state.
+  //
+  // `instances` must point to a copy of the current active instances for the
+  // data source type.
+  //
+  // `DataSourceTraits` can be used to customize the thread local storage used
+  // for the data source type.
+  //
+  // `TracePointTraits` and `trace_point_data` are customization point for
+  // getting the active instances bitmap.
+  //
+  // If this returns false, the trace point must be skipped.
+  template <typename DataSourceTraits, typename TracePointTraits>
+  bool TracePrologue(
+      DataSourceThreadLocalState** tls_state,
+      uint32_t* instances,
+      typename TracePointTraits::TracePointData trace_point_data) {
+    // See tracing_muxer.h for the structure of the TLS.
+    if (PERFETTO_UNLIKELY(!*tls_state)) {
+      *tls_state = GetOrCreateDataSourceTLS<DataSourceTraits>();
+      // If the TLS hasn't been obtained yet, it's possible that this thread
+      // hasn't observed the initialization of global state like the muxer yet.
+      // To ensure that the thread "sees" the effects of such initialization,
+      // we have to reload |instances| with an acquire fence, ensuring that any
+      // initialization performed before instances was updated is visible
+      // in this thread.
+      *instances &= TracePointTraits::GetActiveInstances(trace_point_data)
+                        ->load(std::memory_order_acquire);
+      if (!*instances)
+        return false;
+    }
+    auto* tracing_impl = TracingMuxer::Get();
+
+    // Avoid re-entering the trace point recursively.
+    if (PERFETTO_UNLIKELY((*tls_state)->root_tls->is_in_trace_point))
+      return false;
+
+    (*tls_state)->root_tls->is_in_trace_point = true;
+
+    // TracingTLS::generation is a global monotonic counter that is incremented
+    // every time a tracing session is stopped. We use that as a signal to force
+    // a slow-path garbage collection of all the trace writers for the current
+    // thread and to destroy the ones that belong to tracing sessions that have
+    // ended. This is to avoid having too many TraceWriter instances alive, each
+    // holding onto one chunk of the shared memory buffer.
+    // Rationale why memory_order_relaxed should be fine:
+    // - The TraceWriter object that we use is always constructed and destructed
+    //   on the current thread. There is no risk of accessing a half-initialized
+    //   TraceWriter (which would be really bad).
+    // - In the worst case, in the case of a race on the generation check, we
+    //   might end up using a TraceWriter for the same data source that belongs
+    //   to a stopped session. This is not really wrong, as we don't give any
+    //   guarantee on the global atomicity of the stop. In the worst case the
+    //   service will reject the data commit if this arrives too late.
+
+    if (PERFETTO_UNLIKELY(
+            (*tls_state)->root_tls->generation !=
+            tracing_impl->generation(std::memory_order_relaxed))) {
+      // Will update root_tls->generation.
+      tracing_impl->DestroyStoppedTraceWritersForCurrentThread();
+    }
+
+    return true;
+  }
+
+  // Must be called at the ending of a trace point that was not skipped.
+  void TraceEpilogue(DataSourceThreadLocalState* tls_state) {
+    tls_state->root_tls->is_in_trace_point = false;
+  }
+
+  struct InstancesIterator {
+    // A bitmap of the currenly active instances.
+    uint32_t cached_instances;
+    // The current instance index.
+    uint32_t i;
+    // The current instance. If this is `nullptr`, the iteration is over.
+    DataSourceInstanceThreadLocalState* instance;
+  };
+
+  // Returns an iterator to the active instances of this data source type.
+  //
+  // `cached_instances` is a copy of the bitmap of the active instances for this
+  // data source type (usually just a copy of ValidInstances(), but can be
+  // customized).
+  //
+  // `tls_state` is the thread local pointer obtained from TracePrologue.
+  //
+  // `TracePointTraits` and `trace_point_data` are customization point for
+  // getting the active instances bitmap.
+  template <typename TracePointTraits>
+  InstancesIterator BeginIteration(
+      uint32_t cached_instances,
+      DataSourceThreadLocalState* tls_state,
+      typename TracePointTraits::TracePointData trace_point_data)
+      PERFETTO_ALWAYS_INLINE {
+    InstancesIterator it{};
+    it.cached_instances = cached_instances;
+    FirstActiveInstance<TracePointTraits>(&it, tls_state, trace_point_data);
+    return it;
+  }
+
+  // Advances `*iterator` to point to the next active instance of this data
+  // source type.
+  //
+  // `tls_state` is the thread local pointer obtained from TracePrologue.
+  //
+  // `TracePointTraits` and `trace_point_data` are customization point for
+  // getting the active instances bitmap.
+  template <typename TracePointTraits>
+  void NextIteration(InstancesIterator* iterator,
+                     DataSourceThreadLocalState* tls_state,
+                     typename TracePointTraits::TracePointData trace_point_data)
+      PERFETTO_ALWAYS_INLINE {
+    iterator->i++;
+    FirstActiveInstance<TracePointTraits>(iterator, tls_state,
+                                          trace_point_data);
+  }
+
+  void* GetIncrementalState(
+      internal::DataSourceInstanceThreadLocalState* tls_inst,
+      uint32_t instance_index) {
+    // Recreate incremental state data if it has been reset by the service.
+    if (tls_inst->incremental_state_generation !=
+        static_state()->incremental_state_generation.load(
+            std::memory_order_relaxed)) {
+      tls_inst->incremental_state.reset();
+      CreateIncrementalState(tls_inst, instance_index);
+    }
+    return tls_inst->incremental_state.get();
+  }
+
+  std::atomic<uint32_t>* valid_instances() { return &state_.valid_instances; }
+
+  DataSourceStaticState* static_state() { return &state_; }
+
+ private:
+  void CreateIncrementalState(
+      internal::DataSourceInstanceThreadLocalState* tls_inst,
+      uint32_t instance_index) {
+    PERFETTO_DCHECK(create_incremental_state_fn_ != nullptr);
+    tls_inst->incremental_state =
+        create_incremental_state_fn_(tls_inst, instance_index, user_arg_);
+    tls_inst->incremental_state_generation =
+        static_state()->incremental_state_generation.load(
+            std::memory_order_relaxed);
+  }
+
+  void PopulateTlsInst(DataSourceInstanceThreadLocalState* tls_inst,
+                       DataSourceState* instance_state,
+                       uint32_t instance_index);
+
+  // Advances `*iterator` to the first active instance whose index is greater or
+  // equal than `iterator->i`.
+  template <typename TracePointTraits>
+  void FirstActiveInstance(
+      InstancesIterator* iterator,
+      DataSourceThreadLocalState* tls_state,
+      typename TracePointTraits::TracePointData trace_point_data) {
+    iterator->instance = nullptr;
+    for (; iterator->i < kMaxDataSourceInstances; iterator->i++) {
+      DataSourceState* instance_state =
+          state_.TryGetCached(iterator->cached_instances, iterator->i);
+      if (!instance_state)
+        continue;
+      // Even if we passed the check above, the DataSourceInstance might be
+      // still destroyed concurrently while this code runs. The code below is
+      // designed to deal with such race, as follows:
+      // - We don't access the user-defined data source instance state. The only
+      //   bits of state we use are |backend_id| and |buffer_id|.
+      // - Beyond those two integers, we access only the TraceWriter here. The
+      //   TraceWriter is always safe because it lives on the TLS.
+      // - |instance_state| is backed by static storage, so the pointer is
+      //   always valid, even after the data source instance is destroyed.
+      // - In the case of a race-on-destruction, we'll still see the latest
+      //   backend_id and buffer_id and in the worst case keep trying writing
+      //   into the tracing shared memory buffer after stopped. But this isn't
+      //   really any worse than the case of the stop IPC being delayed by the
+      //   kernel scheduler. The tracing service is robust against data commit
+      //   attemps made after tracing is stopped.
+      // There is a theoretical race that would case the wrong behavior w.r.t
+      // writing data in the wrong buffer, but it's so rare that we ignore it:
+      // if the data source is stopped and started kMaxDataSourceInstances
+      // times (so that the same id is recycled) while we are in this function,
+      // we might end up reusing the old data source's backend_id and buffer_id
+      // for the new one, because we don't see the generation change past this
+      // point. But stopping and starting tracing (even once) takes so much
+      // handshaking to make this extremely unrealistic.
+
+      auto& tls_inst = tls_state->per_instance[iterator->i];
+      if (PERFETTO_UNLIKELY(!tls_inst.trace_writer)) {
+        // Here we need an acquire barrier, which matches the release-store made
+        // by TracingMuxerImpl::SetupDataSource(), to ensure that the backend_id
+        // and buffer_id are consistent.
+        iterator->cached_instances &=
+            TracePointTraits::GetActiveInstances(trace_point_data)
+                ->load(std::memory_order_acquire);
+        instance_state =
+            state_.TryGetCached(iterator->cached_instances, iterator->i);
+        if (!instance_state || !instance_state->trace_lambda_enabled.load(
+                                   std::memory_order_relaxed))
+          continue;
+        PopulateTlsInst(&tls_inst, instance_state, iterator->i);
+      }
+      iterator->instance = &tls_inst;
+      break;
+    }
+  }
+
+  // Note that the returned object is one per-thread per-data-source-type, NOT
+  // per data-source *instance*.
+  template <typename DataSourceTraits>
+  DataSourceThreadLocalState* GetOrCreateDataSourceTLS() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_IOS)
+    PERFETTO_FATAL("Data source TLS not supported on iOS, see b/158814068");
+#endif
+    auto* tracing_impl = TracingMuxer::Get();
+    TracingTLS* root_tls = tracing_impl->GetOrCreateTracingTLS();
+    DataSourceThreadLocalState* ds_tls =
+        DataSourceTraits::GetDataSourceTLS(&state_, root_tls);
+    // We keep re-initializing as the initialization is idempotent and not worth
+    // the code for extra checks.
+    ds_tls->static_state = &state_;
+    assert(!ds_tls->root_tls || ds_tls->root_tls == root_tls);
+    ds_tls->root_tls = root_tls;
+    return ds_tls;
+  }
+
+  DataSourceStaticState state_;
+  BufferExhaustedPolicy buffer_exhausted_policy_{};
+  CreateCustomTlsFn create_custom_tls_fn_ = nullptr;
+  CreateIncrementalStateFn create_incremental_state_fn_ = nullptr;
+  // User defined pointer that carries extra content for the fn_ callbacks
+  // above. Only used in the C shared library.
+  void* user_arg_ = nullptr;
+};
+
+}  // namespace internal
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_DATA_SOURCE_TYPE_H_
diff --git a/include/perfetto/tracing/internal/system_tracing_backend.h b/include/perfetto/tracing/internal/system_tracing_backend.h
index ded54c5..c1ea664 100644
--- a/include/perfetto/tracing/internal/system_tracing_backend.h
+++ b/include/perfetto/tracing/internal/system_tracing_backend.h
@@ -28,43 +28,39 @@
 
 class Producer;
 
-// A built-in implementation of TracingBackend that connects to the system
-// tracing daemon (traced) via a UNIX socket using the perfetto built-in
-// proto-based IPC mechanism. Instantiated when the embedder calls
-// Tracing::Initialize(kSystemBackend). It allows to get app-traces fused
-// together with system traces, useful to correlate on the timeline system
-// events (e.g. scheduling slices from the kernel) with in-app events.
+// Built-in implementations of TracingProducerBackend and TracingConsumerBackend
+// that connect to the system tracing daemon (traced) via a UNIX socket using
+// the perfetto built-in proto-based IPC mechanism. Instantiated when the
+// embedder calls Tracing::Initialize(kSystemBackend). They allow to get
+// app-traces fused together with system traces, useful to correlate on the
+// timeline system events (e.g. scheduling slices from the kernel) with in-app
+// events.
 namespace internal {
 
-// Full backend (with producer and consumer)
-class PERFETTO_EXPORT_COMPONENT SystemTracingBackend : public TracingBackend {
+// Producer backend
+class PERFETTO_EXPORT_COMPONENT SystemProducerTracingBackend
+    : public TracingProducerBackend {
  public:
-  static TracingBackend* GetInstance();
+  static TracingProducerBackend* GetInstance();
 
-  // TracingBackend implementation.
   std::unique_ptr<ProducerEndpoint> ConnectProducer(
       const ConnectProducerArgs&) override;
-  std::unique_ptr<ConsumerEndpoint> ConnectConsumer(
-      const ConnectConsumerArgs&) override;
 
  private:
-  SystemTracingBackend();
+  SystemProducerTracingBackend();
 };
 
-// Producer only backend.
-class PERFETTO_EXPORT_COMPONENT SystemTracingProducerOnlyBackend
-    : public TracingBackend {
+// Consumer backend
+class PERFETTO_EXPORT_COMPONENT SystemConsumerTracingBackend
+    : public TracingConsumerBackend {
  public:
-  static TracingBackend* GetInstance();
+  static TracingConsumerBackend* GetInstance();
 
-  // TracingBackend implementation.
-  std::unique_ptr<ProducerEndpoint> ConnectProducer(
-      const ConnectProducerArgs&) override;
   std::unique_ptr<ConsumerEndpoint> ConnectConsumer(
       const ConnectConsumerArgs&) override;
 
  private:
-  SystemTracingProducerOnlyBackend();
+  SystemConsumerTracingBackend();
 };
 
 }  // namespace internal
diff --git a/include/perfetto/tracing/internal/tracing_tls.h b/include/perfetto/tracing/internal/tracing_tls.h
index b2340a4..dc3a7b6 100644
--- a/include/perfetto/tracing/internal/tracing_tls.h
+++ b/include/perfetto/tracing/internal/tracing_tls.h
@@ -81,6 +81,10 @@
   // handlers. See comment in TracingTLS::~TracingTLS().
   bool is_in_trace_point = false;
 
+  // Used inside a trace point (only one trace point per thread can be active at
+  // any time) to cache the instances bitmap.
+  uint32_t cached_instances = 0;
+
   // By default all data source instances have independent thread-local state
   // (see above).
   std::array<DataSourceThreadLocalState, kMaxDataSources> data_sources_tls{};
diff --git a/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index 7b1c87e..730b1e1 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -194,10 +194,11 @@
 
 // A generic track event data source which is instantiated once per track event
 // category namespace.
-template <typename DataSourceType, const TrackEventCategoryRegistry* Registry>
+template <typename DerivedDataSource,
+          const TrackEventCategoryRegistry* Registry>
 class TrackEventDataSource
-    : public DataSource<DataSourceType, TrackEventDataSourceTraits> {
-  using Base = DataSource<DataSourceType, TrackEventDataSourceTraits>;
+    : public DataSource<DerivedDataSource, TrackEventDataSourceTraits> {
+  using Base = DataSource<DerivedDataSource, TrackEventDataSourceTraits>;
 
  public:
   static constexpr bool kRequiresCallbacksUnderLock = false;
diff --git a/include/perfetto/tracing/internal/track_event_macros.h b/include/perfetto/tracing/internal/track_event_macros.h
index 63be387..3078f88 100644
--- a/include/perfetto/tracing/internal/track_event_macros.h
+++ b/include/perfetto/tracing/internal/track_event_macros.h
@@ -118,7 +118,7 @@
     namespace tns = PERFETTO_TRACK_EVENT_NAMESPACE;                            \
     /* Compute the category index outside the lambda to work around a */       \
     /* GCC 7 bug */                                                            \
-    static constexpr auto PERFETTO_UID(                                        \
+    constexpr auto PERFETTO_UID(                                               \
         kCatIndex_ADD_TO_PERFETTO_DEFINE_CATEGORIES_IF_FAILS_) =               \
         PERFETTO_GET_CATEGORY_INDEX(category);                                 \
     if (::PERFETTO_TRACK_EVENT_NAMESPACE::internal::IsDynamicCategory(         \
diff --git a/include/perfetto/tracing/tracing.h b/include/perfetto/tracing/tracing.h
index 73496d4..0601319 100644
--- a/include/perfetto/tracing/tracing.h
+++ b/include/perfetto/tracing/tracing.h
@@ -124,37 +124,33 @@
   bool supports_multiple_data_source_instances = true;
 
   // If this flag is set the default clock for taking timestamps is overridden
+  // with CLOCK_MONOTONIC (for use in Chrome).
+  bool use_monotonic_clock = false;
+
+  // If this flag is set the default clock for taking timestamps is overridden
   // with CLOCK_MONOTONIC_RAW on platforms that support it.
   bool use_monotonic_raw_clock = false;
 
   // This flag can be set to false in order to avoid enabling the system
   // consumer in Tracing::Initialize(), so that the linker can remove the unused
-  // consumer IPC implementation to reduce binary size. When this option is
-  // false, calling Tracing::NewTrace() on the system backend will fail. This
-  // setting only has an effect if kSystemBackend is specified in |backends|.
+  // consumer IPC implementation to reduce binary size. This setting only has an
+  // effect if kSystemBackend is specified in |backends|. When this option is
+  // false, Tracing::NewTrace() will instatiate the system backend only if
+  // explicitly specified as kSystemBackend: kUndefinedBackend will consider
+  // only already instantiated backends.
   bool enable_system_consumer = true;
 
  protected:
   friend class Tracing;
   friend class internal::TracingMuxerImpl;
 
-  // Used only by the DCHECK in tracing.cc, to check that the config is the
-  // same in case of re-initialization.
-  bool operator==(const TracingInitArgs& other) const {
-    return std::tie(backends, custom_backend, platform, shmem_size_hint_kb,
-                    shmem_page_size_hint_kb, in_process_backend_factory_,
-                    system_backend_factory_, dcheck_is_on_,
-                    enable_system_consumer) ==
-           std::tie(other.backends, other.custom_backend, other.platform,
-                    other.shmem_size_hint_kb, other.shmem_page_size_hint_kb,
-                    other.in_process_backend_factory_,
-                    other.system_backend_factory_, other.dcheck_is_on_,
-                    other.enable_system_consumer);
-  }
-
   using BackendFactoryFunction = TracingBackend* (*)();
+  using ProducerBackendFactoryFunction = TracingProducerBackend* (*)();
+  using ConsumerBackendFactoryFunction = TracingConsumerBackend* (*)();
+
   BackendFactoryFunction in_process_backend_factory_ = nullptr;
-  BackendFactoryFunction system_backend_factory_ = nullptr;
+  ProducerBackendFactoryFunction system_producer_backend_factory_ = nullptr;
+  ConsumerBackendFactoryFunction system_consumer_backend_factory_ = nullptr;
   bool dcheck_is_on_ = PERFETTO_DCHECK_IS_ON();
 };
 
@@ -162,7 +158,12 @@
 class PERFETTO_EXPORT_COMPONENT Tracing {
  public:
   // Initializes Perfetto with the given backends in the calling process and/or
-  // with a user-provided backend. No-op if called more than once.
+  // with a user-provided backend. It's possible to call this function more than
+  // once to initialize different backends. If a backend was already initialized
+  // the call will have no effect on it. All the members of `args` will be
+  // ignored in subsequent calls, except those require to initialize new
+  // backends (`backends`, `enable_system_consumer`, `shmem_size_hint_kb`,
+  // `shmem_page_size_hint_kb` and `shmem_batch_commits_duration_ms`).
   static inline void Initialize(const TracingInitArgs& args)
       PERFETTO_ALWAYS_INLINE {
     TracingInitArgs args_copy(args);
@@ -182,12 +183,11 @@
           &internal::InProcessTracingBackend::GetInstance;
     }
     if (args.backends & kSystemBackend) {
+      args_copy.system_producer_backend_factory_ =
+          &internal::SystemProducerTracingBackend::GetInstance;
       if (args.enable_system_consumer) {
-        args_copy.system_backend_factory_ =
-            &internal::SystemTracingBackend::GetInstance;
-      } else {
-        args_copy.system_backend_factory_ =
-            &internal::SystemTracingProducerOnlyBackend::GetInstance;
+        args_copy.system_consumer_backend_factory_ =
+            &internal::SystemConsumerTracingBackend::GetInstance;
       }
     }
     InitializeInternal(args_copy);
@@ -198,8 +198,30 @@
 
   // Start a new tracing session using the given tracing backend. Use
   // |kUnspecifiedBackend| to select an available backend automatically.
-  static std::unique_ptr<TracingSession> NewTrace(
-      BackendType = kUnspecifiedBackend);
+  static inline std::unique_ptr<TracingSession> NewTrace(
+      BackendType backend = kUnspecifiedBackend) PERFETTO_ALWAYS_INLINE {
+    // This code is inlined to allow dead-code elimination for unused consumer
+    // implementation. The logic behind it is the following:
+    // Nothing other than the code below references the GetInstance() method
+    // below. From a linker-graph viewpoint, those GetInstance() pull in many
+    // other pieces of the codebase (ConsumerOnlySystemTracingBackend pulls
+    // ConsumerIPCClient). Due to the inline, the compiler can see through the
+    // code and realize that some branches are always not taken. When that
+    // happens, no reference to the backends' GetInstance() is emitted and that
+    // allows the linker GC to get rid of the entire set of dependencies.
+    TracingConsumerBackend* (*system_backend_factory)();
+    system_backend_factory = nullptr;
+    // In case PERFETTO_IPC is disabled, a fake system backend is used, which
+    // always panics. NewTrace(kSystemBackend) should fail if PERFETTO_IPC is
+    // diabled, not panic.
+#if PERFETTO_BUILDFLAG(PERFETTO_IPC)
+    if (backend & kSystemBackend) {
+      system_backend_factory =
+          &internal::SystemConsumerTracingBackend::GetInstance;
+    }
+#endif
+    return NewTraceInternal(backend, system_backend_factory);
+  }
 
   // Shut down Perfetto, releasing any allocated OS resources (threads, files,
   // sockets, etc.). Note that Perfetto cannot be reinitialized again in the
@@ -291,6 +313,9 @@
 
  private:
   static void InitializeInternal(const TracingInitArgs&);
+  static std::unique_ptr<TracingSession> NewTraceInternal(
+      BackendType,
+      TracingConsumerBackend* (*system_backend_factory)());
 
   Tracing() = delete;
 };
diff --git a/include/perfetto/tracing/tracing_backend.h b/include/perfetto/tracing/tracing_backend.h
index 99b1e52..965d726 100644
--- a/include/perfetto/tracing/tracing_backend.h
+++ b/include/perfetto/tracing/tracing_backend.h
@@ -43,9 +43,10 @@
 class Producer;
 class ProducerEndpoint;
 
-class PERFETTO_EXPORT_COMPONENT TracingBackend {
+// Responsible for connecting to the producer.
+class PERFETTO_EXPORT_COMPONENT TracingProducerBackend {
  public:
-  virtual ~TracingBackend();
+  virtual ~TracingProducerBackend();
 
   // Connects a Producer instance and obtains a ProducerEndpoint, which is
   // essentially a 1:1 channel between one Producer and the Service.
@@ -77,6 +78,12 @@
 
   virtual std::unique_ptr<ProducerEndpoint> ConnectProducer(
       const ConnectProducerArgs&) = 0;
+};
+
+// Responsible for connecting to the consumer.
+class PERFETTO_EXPORT_COMPONENT TracingConsumerBackend {
+ public:
+  virtual ~TracingConsumerBackend();
 
   // As above, for the Consumer-side.
   struct ConnectConsumerArgs {
@@ -91,6 +98,12 @@
       const ConnectConsumerArgs&) = 0;
 };
 
+class PERFETTO_EXPORT_COMPONENT TracingBackend : public TracingProducerBackend,
+                                                 public TracingConsumerBackend {
+ public:
+  ~TracingBackend() override;
+};
+
 }  // namespace perfetto
 
 #endif  // INCLUDE_PERFETTO_TRACING_TRACING_BACKEND_H_
diff --git a/infra/ci/config.py b/infra/ci/config.py
index 7a10502..0f0f068 100755
--- a/infra/ci/config.py
+++ b/infra/ci/config.py
@@ -67,8 +67,7 @@
 JOB_CONFIGS = {
     'linux-clang-x86_64-debug': {
         'PERFETTO_TEST_GN_ARGS': 'is_debug=true is_hermetic_clang=false '
-                                 'non_hermetic_clang_stdlib="libc++" '
-                                 'perfetto_cpp11_until_q1_2023=true',
+                                 'non_hermetic_clang_stdlib="libc++"',
         'PERFETTO_TEST_SCRIPT': 'test/ci/linux_tests.sh',
     },
     'linux-clang-x86_64-tsan': {
diff --git a/infra/config/recipes.cfg b/infra/config/recipes.cfg
index 0b9c6c6..9057396 100644
--- a/infra/config/recipes.cfg
+++ b/infra/config/recipes.cfg
@@ -1,18 +1,19 @@
 {
   "api_version": 2,
   "deps": {
-    "recipe_engine": {
-      "branch": "refs/heads/master",
-      "revision": "2030661a4ff2a6b64b0651f2c44aabed8c71223f",
-      "url": "https://chromium.googlesource.com/infra/luci/recipes-py"
-    },
     "depot_tools": {
       "branch": "refs/heads/main",
-      "revision": "5345b34aaf50107d0a07146b9319ef19203f65a0",
+      "revision": "6b98cdcbc133ec6902e84da64617560b33f9febc",
       "url": "https://chromium.googlesource.com/chromium/tools/depot_tools.git"
+    },
+    "recipe_engine": {
+      "branch": "refs/heads/main",
+      "revision": "086386d9ca13cbcedba146391ff25241a4ec2dfd",
+      "url": "https://chromium.googlesource.com/infra/luci/recipes-py"
     }
   },
   "project_id": "perfetto",
   "recipes_path": "infra/luci",
+  "py3_only": true,
   "repo_name": "perfetto"
 }
diff --git a/infra/luci/PRESUBMIT.py b/infra/luci/PRESUBMIT.py
new file mode 100644
index 0000000..c1564ce
--- /dev/null
+++ b/infra/luci/PRESUBMIT.py
@@ -0,0 +1,38 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This line is 'magic' in that git-cl looks for it to decide whether to
+# use Python3 instead of Python2 when running the code in this file.
+USE_PYTHON3 = True
+
+
+def CommonChecks(input_api, output_api):
+  recipes_py = input_api.os_path.join(input_api.PresubmitLocalPath(),
+                                      'recipes.py')
+  return input_api.RunTests([
+      input_api.Command(
+          'Run recipe tests',
+          ['python3', recipes_py, 'test', 'run'],
+          {},
+          output_api.PresubmitError,
+      )
+  ])
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  return CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  return CommonChecks(input_api, output_api)
diff --git a/infra/luci/README.recipes.md b/infra/luci/README.recipes.md
index 0965828..36730a6 100644
--- a/infra/luci/README.recipes.md
+++ b/infra/luci/README.recipes.md
@@ -3,19 +3,21 @@
 ## Table of Contents
 
 **[Recipe Modules](#Recipe-Modules)**
-  * [macos_sdk](#recipe_modules-macos_sdk) &mdash; The `macos_sdk` module provides safe functions to access a semi-hermetic XCode installation.
-  * [windows_sdk](#recipe_modules-windows_sdk)
+  * [macos_sdk](#recipe_modules-macos_sdk) (Python3 ✅) &mdash; The `macos_sdk` module provides safe functions to access a semi-hermetic XCode installation.
+  * [windows_sdk](#recipe_modules-windows_sdk) (Python3 ✅)
 
 **[Recipes](#Recipes)**
-  * [macos_sdk:examples/full](#recipes-macos_sdk_examples_full)
-  * [perfetto](#recipes-perfetto) &mdash; Recipe for building Perfetto.
-  * [windows_sdk:examples/full](#recipes-windows_sdk_examples_full)
+  * [macos_sdk:examples/full](#recipes-macos_sdk_examples_full) (Python3 ✅)
+  * [perfetto](#recipes-perfetto) (Python3 ✅) &mdash; Recipe for building Perfetto.
+  * [windows_sdk:examples/full](#recipes-windows_sdk_examples_full) (Python3 ✅)
 ## Recipe Modules
 
 ### *recipe_modules* / [macos\_sdk](/infra/luci/recipe_modules/macos_sdk)
 
 [DEPS](/infra/luci/recipe_modules/macos_sdk/__init__.py#15): [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/step][recipe_engine/recipe_modules/step]
 
+PYTHON_VERSION_COMPATIBILITY: PY3
+
 The `macos_sdk` module provides safe functions to access a semi-hermetic
 XCode installation.
 
@@ -64,6 +66,8 @@
 
 [DEPS](/infra/luci/recipe_modules/windows_sdk/__init__.py#15): [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/step][recipe_engine/recipe_modules/step]
 
+PYTHON_VERSION_COMPATIBILITY: PY3
+
 #### **class [WindowsSDKApi](/infra/luci/recipe_modules/windows_sdk/api.py#20)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
 
 API for using Windows SDK distributed via CIPD.
@@ -82,35 +86,41 @@
 
 [DEPS](/infra/luci/recipe_modules/macos_sdk/examples/full.py#15): [macos\_sdk](#recipe_modules-macos_sdk), [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
 
+PYTHON_VERSION_COMPATIBILITY: PY3
+
 &mdash; **def [RunSteps](/infra/luci/recipe_modules/macos_sdk/examples/full.py#23)(api):**
 ### *recipes* / [perfetto](/infra/luci/recipes/perfetto.py)
 
 [DEPS](/infra/luci/recipes/perfetto.py#18): [depot\_tools/gsutil][depot_tools/recipe_modules/gsutil], [macos\_sdk](#recipe_modules-macos_sdk), [windows\_sdk](#recipe_modules-windows_sdk), [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/step][recipe_engine/recipe_modules/step]
 
+PYTHON_VERSION_COMPATIBILITY: PY3
+
 Recipe for building Perfetto.
 
-&mdash; **def [BuildForPlatform](/infra/luci/recipes/perfetto.py#138)(api, ctx, platform):**
+&mdash; **def [BuildForPlatform](/infra/luci/recipes/perfetto.py#130)(api, ctx, platform):**
 
-&mdash; **def [GnArgs](/infra/luci/recipes/perfetto.py#81)(platform):**
+&mdash; **def [GnArgs](/infra/luci/recipes/perfetto.py#73)(platform):**
 
-&mdash; **def [RunSteps](/infra/luci/recipes/perfetto.py#164)(api, repository):**
+&mdash; **def [RunSteps](/infra/luci/recipes/perfetto.py#156)(api, repository):**
 
-&mdash; **def [UploadArtifact](/infra/luci/recipes/perfetto.py#90)(api, ctx, platform, out_dir, artifact):**
+&mdash; **def [UploadArtifact](/infra/luci/recipes/perfetto.py#82)(api, ctx, platform, out_dir, artifact):**
 ### *recipes* / [windows\_sdk:examples/full](/infra/luci/recipe_modules/windows_sdk/examples/full.py)
 
 [DEPS](/infra/luci/recipe_modules/windows_sdk/examples/full.py#15): [windows\_sdk](#recipe_modules-windows_sdk), [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
 
+PYTHON_VERSION_COMPATIBILITY: PY3
+
 &mdash; **def [RunSteps](/infra/luci/recipe_modules/windows_sdk/examples/full.py#23)(api):**
 
-[depot_tools/recipe_modules/gsutil]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/5345b34aaf50107d0a07146b9319ef19203f65a0/recipes/README.recipes.md#recipe_modules-gsutil
-[recipe_engine/recipe_modules/buildbucket]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/2030661a4ff2a6b64b0651f2c44aabed8c71223f/README.recipes.md#recipe_modules-buildbucket
-[recipe_engine/recipe_modules/cipd]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/2030661a4ff2a6b64b0651f2c44aabed8c71223f/README.recipes.md#recipe_modules-cipd
-[recipe_engine/recipe_modules/context]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/2030661a4ff2a6b64b0651f2c44aabed8c71223f/README.recipes.md#recipe_modules-context
-[recipe_engine/recipe_modules/file]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/2030661a4ff2a6b64b0651f2c44aabed8c71223f/README.recipes.md#recipe_modules-file
-[recipe_engine/recipe_modules/json]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/2030661a4ff2a6b64b0651f2c44aabed8c71223f/README.recipes.md#recipe_modules-json
-[recipe_engine/recipe_modules/path]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/2030661a4ff2a6b64b0651f2c44aabed8c71223f/README.recipes.md#recipe_modules-path
-[recipe_engine/recipe_modules/platform]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/2030661a4ff2a6b64b0651f2c44aabed8c71223f/README.recipes.md#recipe_modules-platform
-[recipe_engine/recipe_modules/properties]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/2030661a4ff2a6b64b0651f2c44aabed8c71223f/README.recipes.md#recipe_modules-properties
-[recipe_engine/recipe_modules/raw_io]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/2030661a4ff2a6b64b0651f2c44aabed8c71223f/README.recipes.md#recipe_modules-raw_io
-[recipe_engine/recipe_modules/step]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/2030661a4ff2a6b64b0651f2c44aabed8c71223f/README.recipes.md#recipe_modules-step
-[recipe_engine/wkt/RecipeApi]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/2030661a4ff2a6b64b0651f2c44aabed8c71223f/recipe_engine/recipe_api.py#856
+[depot_tools/recipe_modules/gsutil]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/6b98cdcbc133ec6902e84da64617560b33f9febc/recipes/README.recipes.md#recipe_modules-gsutil
+[recipe_engine/recipe_modules/buildbucket]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/086386d9ca13cbcedba146391ff25241a4ec2dfd/README.recipes.md#recipe_modules-buildbucket
+[recipe_engine/recipe_modules/cipd]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/086386d9ca13cbcedba146391ff25241a4ec2dfd/README.recipes.md#recipe_modules-cipd
+[recipe_engine/recipe_modules/context]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/086386d9ca13cbcedba146391ff25241a4ec2dfd/README.recipes.md#recipe_modules-context
+[recipe_engine/recipe_modules/file]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/086386d9ca13cbcedba146391ff25241a4ec2dfd/README.recipes.md#recipe_modules-file
+[recipe_engine/recipe_modules/json]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/086386d9ca13cbcedba146391ff25241a4ec2dfd/README.recipes.md#recipe_modules-json
+[recipe_engine/recipe_modules/path]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/086386d9ca13cbcedba146391ff25241a4ec2dfd/README.recipes.md#recipe_modules-path
+[recipe_engine/recipe_modules/platform]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/086386d9ca13cbcedba146391ff25241a4ec2dfd/README.recipes.md#recipe_modules-platform
+[recipe_engine/recipe_modules/properties]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/086386d9ca13cbcedba146391ff25241a4ec2dfd/README.recipes.md#recipe_modules-properties
+[recipe_engine/recipe_modules/raw_io]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/086386d9ca13cbcedba146391ff25241a4ec2dfd/README.recipes.md#recipe_modules-raw_io
+[recipe_engine/recipe_modules/step]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/086386d9ca13cbcedba146391ff25241a4ec2dfd/README.recipes.md#recipe_modules-step
+[recipe_engine/wkt/RecipeApi]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/086386d9ca13cbcedba146391ff25241a4ec2dfd/recipe_engine/recipe_api.py#886
diff --git a/infra/luci/recipe_modules/windows_sdk/examples/full.expected/win.json b/infra/luci/recipe_modules/windows_sdk/examples/full.expected/win.json
index b325275..0f68f34 100644
--- a/infra/luci/recipe_modules/windows_sdk/examples/full.expected/win.json
+++ b/infra/luci/recipe_modules/windows_sdk/examples/full.expected/win.json
@@ -30,9 +30,9 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "RECIPE_MODULE[recipe_engine::json]\\resources\\read.py",
       "[CACHE]\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
       "/path/to/tmp/json"
     ],
diff --git a/infra/luci/recipes.py b/infra/luci/recipes.py
old mode 100644
new mode 100755
index 32e497c..642a90c
--- a/infra/luci/recipes.py
+++ b/infra/luci/recipes.py
@@ -6,12 +6,12 @@
 # We want to run python in unbuffered mode; however shebangs on linux grab the
 # entire rest of the shebang line as a single argument, leading to errors like:
 #
-#   /usr/bin/env: 'python -u': No such file or directory
+#   /usr/bin/env: 'python3 -u': No such file or directory
 #
 # This little shell hack is a triple-quoted noop in python, but in sh it
 # evaluates to re-exec'ing this script in unbuffered mode.
 # pylint: disable=pointless-string-statement
-''''exec python -u -- "$0" ${1+"$@"} # '''
+''''exec python3 -u -- "$0" ${1+"$@"} # '''
 # vi: syntax=python
 """Bootstrap script to clone and forward to the recipe engine tool.
 
@@ -19,7 +19,7 @@
 ** DO NOT MODIFY **
 *******************
 
-This is a copy of https://chromium.googlesource.com/infra/luci/recipes-py/+/master/recipes.py.
+This is a copy of https://chromium.googlesource.com/infra/luci/recipes-py/+/main/recipes.py.
 To fix bugs, fix in the googlesource repo then run the autoroller.
 """
 
@@ -33,6 +33,7 @@
 import sys
 
 from collections import namedtuple
+from io import open  # pylint: disable=redefined-builtin
 
 try:
   import urllib.parse as urlparse
@@ -44,7 +45,7 @@
 # url (str) - the url to the engine repo we want to use.
 # revision (str) - the git revision for the engine to get.
 # branch (str) - the branch to fetch for the engine as an absolute ref (e.g.
-#   refs/heads/master)
+#   refs/heads/main)
 EngineDep = namedtuple('EngineDep', 'url revision branch')
 
 
@@ -69,9 +70,12 @@
     recipes_path (str) - native path to where the recipes live inside of the
       current repo (i.e. the folder containing `recipes/` and/or
       `recipe_modules`)
+    py3_only (bool) - True if this repo has been marked as ONLY supporting
+      python3.
   """
-  with open(recipes_cfg_path, 'rU') as fh:
+  with open(recipes_cfg_path, 'r') as fh:
     pb = json.load(fh)
+  py3_only = pb.get('py3_only', False)
 
   try:
     if pb['api_version'] != 2:
@@ -84,7 +88,7 @@
     if not repo_name:
       repo_name = pb['project_id']
     if repo_name == 'recipe_engine':
-      return None, pb.get('recipes_path', '')
+      return None, pb.get('recipes_path', ''), py3_only
 
     engine = pb['deps']['recipe_engine']
 
@@ -94,7 +98,7 @@
           recipes_cfg_path)
 
     engine.setdefault('revision', '')
-    engine.setdefault('branch', 'refs/heads/master')
+    engine.setdefault('branch', 'refs/heads/main')
     recipes_path = pb.get('recipes_path', '')
 
     # TODO(iannucci): only support absolute refs
@@ -103,18 +107,17 @@
 
     recipes_path = os.path.join(repo_root,
                                 recipes_path.replace('/', os.path.sep))
-    return EngineDep(**engine), recipes_path
+    return EngineDep(**engine), recipes_path, py3_only
   except KeyError as ex:
-    raise MalformedRecipesCfg(ex.message, recipes_cfg_path)
+    raise MalformedRecipesCfg(str(ex), recipes_cfg_path)
 
 
 IS_WIN = sys.platform.startswith(('win', 'cygwin'))
 
 _BAT = '.bat' if IS_WIN else ''
 GIT = 'git' + _BAT
-VPYTHON = 'vpython' + _BAT
 CIPD = 'cipd' + _BAT
-REQUIRED_BINARIES = {GIT, VPYTHON, CIPD}
+REQUIRED_BINARIES = {GIT, CIPD}
 
 
 def _is_executable(path):
@@ -166,10 +169,14 @@
 
 
 def checkout_engine(engine_path, repo_root, recipes_cfg_path):
-  dep, recipes_path = parse(repo_root, recipes_cfg_path)
+  """Checks out the recipe_engine repo pinned in recipes.cfg.
+
+  Returns the path to the recipe engine repo and the py3_only boolean.
+  """
+  dep, recipes_path, py3_only = parse(repo_root, recipes_cfg_path)
   if dep is None:
     # we're running from the engine repo already!
-    return os.path.join(repo_root, recipes_path)
+    return os.path.join(repo_root, recipes_path), py3_only
 
   url = dep.url
 
@@ -194,10 +201,9 @@
                         stdout=NUL,
                         stderr=NUL)
       except subprocess.CalledProcessError:
-        _git_check_call(['fetch', url, branch],
+        _git_check_call(['fetch', '--quiet', url, branch],
                         cwd=engine_path,
-                        stdout=NUL,
-                        stderr=NUL)
+                        stdout=NUL)
 
     try:
       _git_check_call(['diff', '--quiet', revision], cwd=engine_path)
@@ -215,7 +221,7 @@
     # or things will get squirrely.
     _git_check_call(['clean', '-qxf'], cwd=engine_path)
 
-  return engine_path
+  return engine_path, py3_only
 
 
 def main():
@@ -241,16 +247,24 @@
     repo_root = os.path.abspath(repo_root).decode()
     recipes_cfg_path = os.path.join(repo_root, 'infra', 'config', 'recipes.cfg')
     args = ['--package', recipes_cfg_path] + args
+  engine_path, py3_only = checkout_engine(engine_override, repo_root,
+                                          recipes_cfg_path)
 
-  engine_path = checkout_engine(engine_override, repo_root, recipes_cfg_path)
+  using_py3 = py3_only or os.getenv('RECIPES_USE_PY3') == 'true'
+  vpython = ('vpython' + ('3' if using_py3 else '') + _BAT)
+  if not _is_on_path(vpython):
+    return 'Required binary is not found on PATH: %s' % vpython
 
-  argv = (
-      [VPYTHON, '-u',
-       os.path.join(engine_path, 'recipe_engine', 'main.py')] + args)
+  argv = ([
+      vpython,
+      '-u',
+      os.path.join(engine_path, 'recipe_engine', 'main.py'),
+  ] + args)
 
   if IS_WIN:
     # No real 'exec' on windows; set these signals to ignore so that they
     # propagate to our children but we still wait for the child process to quit.
+    import signal
     signal.signal(signal.SIGBREAK, signal.SIG_IGN)
     signal.signal(signal.SIGINT, signal.SIG_IGN)
     signal.signal(signal.SIGTERM, signal.SIG_IGN)
diff --git a/infra/luci/recipes/perfetto.expected/ci_android.json b/infra/luci/recipes/perfetto.expected/ci_android.json
index 3cad1f0..374c65d 100644
--- a/infra/luci/recipes/perfetto.expected/ci_android.json
+++ b/infra/luci/recipes/perfetto.expected/ci_android.json
@@ -5,7 +5,7 @@
   },
   {
     "cmd": [
-      "vpython",
+      "vpython3",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
@@ -16,6 +16,18 @@
       "[CACHE]/builder/perfetto"
     ],
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.ensure source dir",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -28,6 +40,18 @@
       "[CACHE]/builder/perfetto"
     ],
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.init",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -39,10 +63,22 @@
       "fetch",
       "--tags",
       "https://android.googlesource.com/platform/external/perfetto",
-      "refs/heads/master"
+      "refs/heads/main"
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.fetch",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -56,6 +92,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.checkout",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -69,6 +117,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.rev-parse",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -82,6 +142,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "build-deps"
   },
   {
@@ -97,6 +169,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"android\" target_cpu=\"arm\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -109,12 +193,25 @@
       "-C",
       "[CACHE]/builder/perfetto/out/android-arm",
       "trace_processor_shell",
+      "traceconv",
       "tracebox",
       "perfetto",
       "traced",
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -129,7 +226,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -141,6 +238,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm.Artifact upload.gsutil upload",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -161,6 +270,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm.Artifact upload.build perfetto/trace_processor_shell/android-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -186,6 +307,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm.Artifact upload.register perfetto/trace_processor_shell/android-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -201,7 +334,115 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "[CACHE]/builder/perfetto/out/android-arm/stripped/traceconv",
+      "gs://perfetto-luci-artifacts//android-arm/traceconv"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-arm.Artifact upload.gsutil upload (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-arm/traceconv@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-build",
+      "-pkg-def",
+      "{\"data\": [{\"file\": \"traceconv\"}], \"install_mode\": \"\", \"package\": \"perfetto/traceconv/android-arm\", \"root\": \"[CACHE]/builder/perfetto/out/android-arm/stripped\"}",
+      "-out",
+      "[CLEANUP]/traceconv-android-arm.cipd",
+      "-hash-algo",
+      "sha256",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-arm.Artifact upload.build perfetto/traceconv/android-arm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/android-arm\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-register",
+      "[CLEANUP]/traceconv-android-arm.cipd",
+      "-ref",
+      "latest",
+      "-tag",
+      "git_revision:",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-arm.Artifact upload.register perfetto/traceconv/android-arm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/android-arm\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@perfetto/traceconv/android-arm@https://chrome-infra-packages.appspot.com/p/perfetto/traceconv/android-arm/+/40-chars-fake-of-the-package-instance_id@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -213,7 +454,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-arm.Artifact upload.gsutil upload (2)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-arm.Artifact upload.gsutil upload (3)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-arm/tracebox@@@"
@@ -233,6 +486,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm.Artifact upload.build perfetto/tracebox/android-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -258,6 +523,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm.Artifact upload.register perfetto/tracebox/android-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -273,7 +550,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -285,7 +562,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-arm.Artifact upload.gsutil upload (3)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-arm.Artifact upload.gsutil upload (4)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-arm/perfetto@@@"
@@ -305,6 +594,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm.Artifact upload.build perfetto/perfetto/android-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -330,6 +631,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm.Artifact upload.register perfetto/perfetto/android-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -345,7 +658,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -357,7 +670,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-arm.Artifact upload.gsutil upload (4)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-arm.Artifact upload.gsutil upload (5)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-arm/traced@@@"
@@ -377,6 +702,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm.Artifact upload.build perfetto/traced/android-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -402,6 +739,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm.Artifact upload.register perfetto/traced/android-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -417,7 +766,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -429,7 +778,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-arm.Artifact upload.gsutil upload (5)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-arm.Artifact upload.gsutil upload (6)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-arm/traced_probes@@@"
@@ -449,6 +810,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm.Artifact upload.build perfetto/traced_probes/android-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -474,6 +847,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm.Artifact upload.register perfetto/traced_probes/android-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -500,6 +885,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"android\" target_cpu=\"arm64\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm64.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -512,12 +909,25 @@
       "-C",
       "[CACHE]/builder/perfetto/out/android-arm64",
       "trace_processor_shell",
+      "traceconv",
       "tracebox",
       "perfetto",
       "traced",
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm64.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -532,7 +942,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -544,6 +954,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm64.Artifact upload.gsutil upload",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -564,6 +986,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm64.Artifact upload.build perfetto/trace_processor_shell/android-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -589,6 +1023,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm64.Artifact upload.register perfetto/trace_processor_shell/android-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -604,7 +1050,115 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "[CACHE]/builder/perfetto/out/android-arm64/stripped/traceconv",
+      "gs://perfetto-luci-artifacts//android-arm64/traceconv"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-arm64.Artifact upload.gsutil upload (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-arm64/traceconv@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-build",
+      "-pkg-def",
+      "{\"data\": [{\"file\": \"traceconv\"}], \"install_mode\": \"\", \"package\": \"perfetto/traceconv/android-arm64\", \"root\": \"[CACHE]/builder/perfetto/out/android-arm64/stripped\"}",
+      "-out",
+      "[CLEANUP]/traceconv-android-arm64.cipd",
+      "-hash-algo",
+      "sha256",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-arm64.Artifact upload.build perfetto/traceconv/android-arm64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/android-arm64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-register",
+      "[CLEANUP]/traceconv-android-arm64.cipd",
+      "-ref",
+      "latest",
+      "-tag",
+      "git_revision:",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-arm64.Artifact upload.register perfetto/traceconv/android-arm64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/android-arm64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@perfetto/traceconv/android-arm64@https://chrome-infra-packages.appspot.com/p/perfetto/traceconv/android-arm64/+/40-chars-fake-of-the-package-instance_id@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -616,7 +1170,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-arm64.Artifact upload.gsutil upload (2)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-arm64.Artifact upload.gsutil upload (3)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-arm64/tracebox@@@"
@@ -636,6 +1202,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm64.Artifact upload.build perfetto/tracebox/android-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -661,6 +1239,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm64.Artifact upload.register perfetto/tracebox/android-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -676,7 +1266,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -688,7 +1278,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-arm64.Artifact upload.gsutil upload (3)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-arm64.Artifact upload.gsutil upload (4)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-arm64/perfetto@@@"
@@ -708,6 +1310,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm64.Artifact upload.build perfetto/perfetto/android-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -733,6 +1347,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm64.Artifact upload.register perfetto/perfetto/android-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -748,7 +1374,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -760,7 +1386,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-arm64.Artifact upload.gsutil upload (4)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-arm64.Artifact upload.gsutil upload (5)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-arm64/traced@@@"
@@ -780,6 +1418,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm64.Artifact upload.build perfetto/traced/android-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -805,6 +1455,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm64.Artifact upload.register perfetto/traced/android-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -820,7 +1482,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -832,7 +1494,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-arm64.Artifact upload.gsutil upload (5)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-arm64.Artifact upload.gsutil upload (6)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-arm64/traced_probes@@@"
@@ -852,6 +1526,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm64.Artifact upload.build perfetto/traced_probes/android-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -877,6 +1563,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-arm64.Artifact upload.register perfetto/traced_probes/android-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -903,6 +1601,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"android\" target_cpu=\"x86\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x86.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -915,12 +1625,25 @@
       "-C",
       "[CACHE]/builder/perfetto/out/android-x86",
       "trace_processor_shell",
+      "traceconv",
       "tracebox",
       "perfetto",
       "traced",
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x86.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -935,7 +1658,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -947,6 +1670,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x86.Artifact upload.gsutil upload",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -967,6 +1702,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x86.Artifact upload.build perfetto/trace_processor_shell/android-x86",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -992,6 +1739,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x86.Artifact upload.register perfetto/trace_processor_shell/android-x86",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1007,7 +1766,115 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "[CACHE]/builder/perfetto/out/android-x86/stripped/traceconv",
+      "gs://perfetto-luci-artifacts//android-x86/traceconv"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-x86.Artifact upload.gsutil upload (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-x86/traceconv@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-build",
+      "-pkg-def",
+      "{\"data\": [{\"file\": \"traceconv\"}], \"install_mode\": \"\", \"package\": \"perfetto/traceconv/android-x86\", \"root\": \"[CACHE]/builder/perfetto/out/android-x86/stripped\"}",
+      "-out",
+      "[CLEANUP]/traceconv-android-x86.cipd",
+      "-hash-algo",
+      "sha256",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-x86.Artifact upload.build perfetto/traceconv/android-x86",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/android-x86\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-register",
+      "[CLEANUP]/traceconv-android-x86.cipd",
+      "-ref",
+      "latest",
+      "-tag",
+      "git_revision:",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-x86.Artifact upload.register perfetto/traceconv/android-x86",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/android-x86\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@perfetto/traceconv/android-x86@https://chrome-infra-packages.appspot.com/p/perfetto/traceconv/android-x86/+/40-chars-fake-of-the-package-instance_id@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1019,7 +1886,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-x86.Artifact upload.gsutil upload (2)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-x86.Artifact upload.gsutil upload (3)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-x86/tracebox@@@"
@@ -1039,6 +1918,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x86.Artifact upload.build perfetto/tracebox/android-x86",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1064,6 +1955,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x86.Artifact upload.register perfetto/tracebox/android-x86",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1079,7 +1982,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1091,7 +1994,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-x86.Artifact upload.gsutil upload (3)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-x86.Artifact upload.gsutil upload (4)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-x86/perfetto@@@"
@@ -1111,6 +2026,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x86.Artifact upload.build perfetto/perfetto/android-x86",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1136,6 +2063,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x86.Artifact upload.register perfetto/perfetto/android-x86",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1151,7 +2090,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1163,7 +2102,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-x86.Artifact upload.gsutil upload (4)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-x86.Artifact upload.gsutil upload (5)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-x86/traced@@@"
@@ -1183,6 +2134,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x86.Artifact upload.build perfetto/traced/android-x86",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1208,6 +2171,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x86.Artifact upload.register perfetto/traced/android-x86",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1223,7 +2198,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1235,7 +2210,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-x86.Artifact upload.gsutil upload (5)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-x86.Artifact upload.gsutil upload (6)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-x86/traced_probes@@@"
@@ -1255,6 +2242,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x86.Artifact upload.build perfetto/traced_probes/android-x86",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1280,6 +2279,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x86.Artifact upload.register perfetto/traced_probes/android-x86",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1306,6 +2317,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"android\" target_cpu=\"x64\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x64.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -1318,12 +2341,25 @@
       "-C",
       "[CACHE]/builder/perfetto/out/android-x64",
       "trace_processor_shell",
+      "traceconv",
       "tracebox",
       "perfetto",
       "traced",
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x64.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -1338,7 +2374,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1350,6 +2386,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x64.Artifact upload.gsutil upload",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1370,6 +2418,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x64.Artifact upload.build perfetto/trace_processor_shell/android-x64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1395,6 +2455,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x64.Artifact upload.register perfetto/trace_processor_shell/android-x64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1410,7 +2482,115 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "[CACHE]/builder/perfetto/out/android-x64/stripped/traceconv",
+      "gs://perfetto-luci-artifacts//android-x64/traceconv"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-x64.Artifact upload.gsutil upload (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-x64/traceconv@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-build",
+      "-pkg-def",
+      "{\"data\": [{\"file\": \"traceconv\"}], \"install_mode\": \"\", \"package\": \"perfetto/traceconv/android-x64\", \"root\": \"[CACHE]/builder/perfetto/out/android-x64/stripped\"}",
+      "-out",
+      "[CLEANUP]/traceconv-android-x64.cipd",
+      "-hash-algo",
+      "sha256",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-x64.Artifact upload.build perfetto/traceconv/android-x64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/android-x64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-register",
+      "[CLEANUP]/traceconv-android-x64.cipd",
+      "-ref",
+      "latest",
+      "-tag",
+      "git_revision:",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-x64.Artifact upload.register perfetto/traceconv/android-x64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/android-x64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@perfetto/traceconv/android-x64@https://chrome-infra-packages.appspot.com/p/perfetto/traceconv/android-x64/+/40-chars-fake-of-the-package-instance_id@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1422,7 +2602,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-x64.Artifact upload.gsutil upload (2)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-x64.Artifact upload.gsutil upload (3)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-x64/tracebox@@@"
@@ -1442,6 +2634,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x64.Artifact upload.build perfetto/tracebox/android-x64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1467,6 +2671,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x64.Artifact upload.register perfetto/tracebox/android-x64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1482,7 +2698,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1494,7 +2710,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-x64.Artifact upload.gsutil upload (3)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-x64.Artifact upload.gsutil upload (4)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-x64/perfetto@@@"
@@ -1514,6 +2742,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x64.Artifact upload.build perfetto/perfetto/android-x64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1539,6 +2779,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x64.Artifact upload.register perfetto/perfetto/android-x64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1554,7 +2806,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1566,7 +2818,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-x64.Artifact upload.gsutil upload (4)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-x64.Artifact upload.gsutil upload (5)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-x64/traced@@@"
@@ -1586,6 +2850,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x64.Artifact upload.build perfetto/traced/android-x64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1611,6 +2887,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x64.Artifact upload.register perfetto/traced/android-x64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1626,7 +2914,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1638,7 +2926,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "android-x64.Artifact upload.gsutil upload (5)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "android-x64.Artifact upload.gsutil upload (6)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//android-x64/traced_probes@@@"
@@ -1658,6 +2958,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x64.Artifact upload.build perfetto/traced_probes/android-x64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1683,6 +2995,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "android-x64.Artifact upload.register perfetto/traced_probes/android-x64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
diff --git a/infra/luci/recipes/perfetto.expected/ci_linux.json b/infra/luci/recipes/perfetto.expected/ci_linux.json
index a6b9296..bfb27b3 100644
--- a/infra/luci/recipes/perfetto.expected/ci_linux.json
+++ b/infra/luci/recipes/perfetto.expected/ci_linux.json
@@ -5,7 +5,7 @@
   },
   {
     "cmd": [
-      "vpython",
+      "vpython3",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
@@ -16,6 +16,18 @@
       "[CACHE]/builder/perfetto"
     ],
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.ensure source dir",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -28,6 +40,18 @@
       "[CACHE]/builder/perfetto"
     ],
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.init",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -39,10 +63,22 @@
       "fetch",
       "--tags",
       "https://android.googlesource.com/platform/external/perfetto",
-      "refs/heads/master"
+      "refs/heads/main"
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.fetch",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -56,6 +92,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.checkout",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -69,6 +117,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.rev-parse",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -82,6 +142,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "build-deps"
   },
   {
@@ -97,6 +169,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"linux\" target_cpu=\"x64\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -116,6 +200,18 @@
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -130,7 +226,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -142,6 +238,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.gsutil upload",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -162,6 +270,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.build perfetto/trace_processor_shell/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -187,6 +307,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.register perfetto/trace_processor_shell/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -202,7 +334,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -214,6 +346,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.gsutil upload (2)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -234,6 +378,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.build perfetto/traceconv/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -259,6 +415,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.register perfetto/traceconv/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -274,7 +442,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -286,6 +454,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.gsutil upload (3)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -306,6 +486,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.build perfetto/tracebox/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -331,6 +523,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.register perfetto/tracebox/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -346,7 +550,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -358,6 +562,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.gsutil upload (4)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -378,6 +594,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.build perfetto/perfetto/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -403,6 +631,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.register perfetto/perfetto/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -418,7 +658,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -430,6 +670,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.gsutil upload (5)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -450,6 +702,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.build perfetto/traced/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -475,6 +739,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.register perfetto/traced/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -490,7 +766,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -502,6 +778,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.gsutil upload (6)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -522,6 +810,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.build perfetto/traced_probes/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -547,6 +847,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.register perfetto/traced_probes/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -573,6 +885,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"linux\" target_cpu=\"arm\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -585,12 +909,25 @@
       "-C",
       "[CACHE]/builder/perfetto/out/linux-arm",
       "trace_processor_shell",
+      "traceconv",
       "tracebox",
       "perfetto",
       "traced",
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -605,7 +942,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -617,6 +954,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.gsutil upload",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -637,6 +986,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.build perfetto/trace_processor_shell/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -662,6 +1023,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.register perfetto/trace_processor_shell/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -677,7 +1050,115 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "[CACHE]/builder/perfetto/out/linux-arm/stripped/traceconv",
+      "gs://perfetto-luci-artifacts//linux-arm/traceconv"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm.Artifact upload.gsutil upload (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//linux-arm/traceconv@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-build",
+      "-pkg-def",
+      "{\"data\": [{\"file\": \"traceconv\"}], \"install_mode\": \"\", \"package\": \"perfetto/traceconv/linux-arm\", \"root\": \"[CACHE]/builder/perfetto/out/linux-arm/stripped\"}",
+      "-out",
+      "[CLEANUP]/traceconv-linux-arm.cipd",
+      "-hash-algo",
+      "sha256",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm.Artifact upload.build perfetto/traceconv/linux-arm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/linux-arm\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-register",
+      "[CLEANUP]/traceconv-linux-arm.cipd",
+      "-ref",
+      "latest",
+      "-tag",
+      "git_revision:",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm.Artifact upload.register perfetto/traceconv/linux-arm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/linux-arm\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@perfetto/traceconv/linux-arm@https://chrome-infra-packages.appspot.com/p/perfetto/traceconv/linux-arm/+/40-chars-fake-of-the-package-instance_id@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -689,7 +1170,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm.Artifact upload.gsutil upload (2)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm.Artifact upload.gsutil upload (3)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//linux-arm/tracebox@@@"
@@ -709,6 +1202,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.build perfetto/tracebox/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -734,6 +1239,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.register perfetto/tracebox/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -749,7 +1266,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -761,7 +1278,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm.Artifact upload.gsutil upload (3)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm.Artifact upload.gsutil upload (4)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//linux-arm/perfetto@@@"
@@ -781,6 +1310,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.build perfetto/perfetto/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -806,6 +1347,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.register perfetto/perfetto/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -821,7 +1374,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -833,7 +1386,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm.Artifact upload.gsutil upload (4)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm.Artifact upload.gsutil upload (5)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//linux-arm/traced@@@"
@@ -853,6 +1418,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.build perfetto/traced/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -878,6 +1455,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.register perfetto/traced/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -893,7 +1482,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -905,7 +1494,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm.Artifact upload.gsutil upload (5)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm.Artifact upload.gsutil upload (6)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//linux-arm/traced_probes@@@"
@@ -925,6 +1526,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.build perfetto/traced_probes/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -950,6 +1563,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.register perfetto/traced_probes/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -976,6 +1601,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"linux\" target_cpu=\"arm64\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -988,12 +1625,25 @@
       "-C",
       "[CACHE]/builder/perfetto/out/linux-arm64",
       "trace_processor_shell",
+      "traceconv",
       "tracebox",
       "perfetto",
       "traced",
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -1008,7 +1658,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1020,6 +1670,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.gsutil upload",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1040,6 +1702,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.build perfetto/trace_processor_shell/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1065,6 +1739,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.register perfetto/trace_processor_shell/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1080,7 +1766,115 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "[CACHE]/builder/perfetto/out/linux-arm64/stripped/traceconv",
+      "gs://perfetto-luci-artifacts//linux-arm64/traceconv"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm64.Artifact upload.gsutil upload (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//linux-arm64/traceconv@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-build",
+      "-pkg-def",
+      "{\"data\": [{\"file\": \"traceconv\"}], \"install_mode\": \"\", \"package\": \"perfetto/traceconv/linux-arm64\", \"root\": \"[CACHE]/builder/perfetto/out/linux-arm64/stripped\"}",
+      "-out",
+      "[CLEANUP]/traceconv-linux-arm64.cipd",
+      "-hash-algo",
+      "sha256",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm64.Artifact upload.build perfetto/traceconv/linux-arm64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/linux-arm64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-register",
+      "[CLEANUP]/traceconv-linux-arm64.cipd",
+      "-ref",
+      "latest",
+      "-tag",
+      "git_revision:",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm64.Artifact upload.register perfetto/traceconv/linux-arm64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/linux-arm64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@perfetto/traceconv/linux-arm64@https://chrome-infra-packages.appspot.com/p/perfetto/traceconv/linux-arm64/+/40-chars-fake-of-the-package-instance_id@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1092,7 +1886,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm64.Artifact upload.gsutil upload (2)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm64.Artifact upload.gsutil upload (3)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//linux-arm64/tracebox@@@"
@@ -1112,6 +1918,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.build perfetto/tracebox/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1137,6 +1955,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.register perfetto/tracebox/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1152,7 +1982,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1164,7 +1994,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm64.Artifact upload.gsutil upload (3)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm64.Artifact upload.gsutil upload (4)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//linux-arm64/perfetto@@@"
@@ -1184,6 +2026,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.build perfetto/perfetto/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1209,6 +2063,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.register perfetto/perfetto/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1224,7 +2090,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1236,7 +2102,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm64.Artifact upload.gsutil upload (4)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm64.Artifact upload.gsutil upload (5)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//linux-arm64/traced@@@"
@@ -1256,6 +2134,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.build perfetto/traced/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1281,6 +2171,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.register perfetto/traced/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1296,7 +2198,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1308,7 +2210,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm64.Artifact upload.gsutil upload (5)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm64.Artifact upload.gsutil upload (6)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts//linux-arm64/traced_probes@@@"
@@ -1328,6 +2242,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.build perfetto/traced_probes/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1353,6 +2279,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.register perfetto/traced_probes/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
diff --git a/infra/luci/recipes/perfetto.expected/ci_mac.json b/infra/luci/recipes/perfetto.expected/ci_mac.json
index 07071d4..629b0ca 100644
--- a/infra/luci/recipes/perfetto.expected/ci_mac.json
+++ b/infra/luci/recipes/perfetto.expected/ci_mac.json
@@ -5,7 +5,7 @@
   },
   {
     "cmd": [
-      "vpython",
+      "vpython3",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
@@ -16,6 +16,18 @@
       "[CACHE]/builder/perfetto"
     ],
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.ensure source dir",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -28,6 +40,18 @@
       "[CACHE]/builder/perfetto"
     ],
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.init",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -39,10 +63,22 @@
       "fetch",
       "--tags",
       "https://android.googlesource.com/platform/external/perfetto",
-      "refs/heads/master"
+      "refs/heads/main"
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.fetch",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -56,6 +92,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.checkout",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -69,6 +117,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.rev-parse",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -81,6 +141,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "build-deps"
   },
   {
@@ -102,6 +174,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.ensure_installed",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
@@ -131,6 +215,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.install xcode",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -145,6 +241,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.select XCode",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -159,6 +267,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"mac\" target_cpu=\"x64\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -178,6 +298,18 @@
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -191,6 +323,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.reset XCode",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -205,7 +349,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -217,6 +361,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.gsutil upload",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -237,6 +393,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.build perfetto/trace_processor_shell/mac-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -262,6 +430,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.register perfetto/trace_processor_shell/mac-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -277,7 +457,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -289,6 +469,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.gsutil upload (2)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -309,6 +501,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.build perfetto/traceconv/mac-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -334,6 +538,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.register perfetto/traceconv/mac-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -349,7 +565,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -361,6 +577,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.gsutil upload (3)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -381,6 +609,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.build perfetto/tracebox/mac-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -406,6 +646,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.register perfetto/tracebox/mac-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -421,7 +673,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -433,6 +685,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.gsutil upload (4)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -453,6 +717,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.build perfetto/perfetto/mac-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -478,6 +754,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.register perfetto/perfetto/mac-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -493,7 +781,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -505,6 +793,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.gsutil upload (5)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -525,6 +825,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.build perfetto/traced/mac-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -550,6 +862,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.register perfetto/traced/mac-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -565,7 +889,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -577,6 +901,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.gsutil upload (6)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -597,6 +933,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.build perfetto/traced_probes/mac-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -622,6 +970,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-amd64.Artifact upload.register perfetto/traced_probes/mac-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -654,6 +1014,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.ensure_installed",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
@@ -683,6 +1055,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.install xcode",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -697,6 +1081,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.select XCode",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -711,6 +1107,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"mac\" target_cpu=\"arm64\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -730,6 +1138,18 @@
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -743,6 +1163,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.reset XCode",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -757,7 +1189,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -769,6 +1201,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.gsutil upload",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -789,6 +1233,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.build perfetto/trace_processor_shell/mac-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -814,6 +1270,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.register perfetto/trace_processor_shell/mac-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -829,7 +1297,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -841,6 +1309,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.gsutil upload (2)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -861,6 +1341,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.build perfetto/traceconv/mac-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -886,6 +1378,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.register perfetto/traceconv/mac-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -901,7 +1405,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -913,6 +1417,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.gsutil upload (3)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -933,6 +1449,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.build perfetto/tracebox/mac-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -958,6 +1486,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.register perfetto/tracebox/mac-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -973,7 +1513,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -985,6 +1525,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.gsutil upload (4)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1005,6 +1557,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.build perfetto/perfetto/mac-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1030,6 +1594,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.register perfetto/perfetto/mac-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1045,7 +1621,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1057,6 +1633,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.gsutil upload (5)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1077,6 +1665,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.build perfetto/traced/mac-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1102,6 +1702,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.register perfetto/traced/mac-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1117,7 +1729,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1129,6 +1741,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.gsutil upload (6)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1149,6 +1773,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.build perfetto/traced_probes/mac-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1174,6 +1810,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "mac-arm64.Artifact upload.register perfetto/traced_probes/mac-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
diff --git a/infra/luci/recipes/perfetto.expected/ci_tag.json b/infra/luci/recipes/perfetto.expected/ci_tag.json
index 099aacf..1ab974f 100644
--- a/infra/luci/recipes/perfetto.expected/ci_tag.json
+++ b/infra/luci/recipes/perfetto.expected/ci_tag.json
@@ -5,7 +5,7 @@
   },
   {
     "cmd": [
-      "vpython",
+      "vpython3",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
@@ -16,6 +16,18 @@
       "[CACHE]/builder/perfetto"
     ],
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.ensure source dir",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -28,6 +40,18 @@
       "[CACHE]/builder/perfetto"
     ],
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.init",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -43,6 +67,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.fetch",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -56,6 +92,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.checkout",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -69,6 +117,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.rev-parse",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -82,6 +142,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "build-deps"
   },
   {
@@ -97,6 +169,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"linux\" target_cpu=\"x64\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -116,6 +200,18 @@
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -130,7 +226,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -142,6 +238,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.gsutil upload",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -162,6 +270,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.build perfetto/trace_processor_shell/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -189,6 +309,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.register perfetto/trace_processor_shell/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -204,7 +336,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -216,6 +348,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.gsutil upload (2)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -236,6 +380,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.build perfetto/traceconv/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -263,6 +419,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.register perfetto/traceconv/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -278,7 +446,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -290,6 +458,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.gsutil upload (3)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -310,6 +490,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.build perfetto/tracebox/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -337,6 +529,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.register perfetto/tracebox/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -352,7 +556,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -364,6 +568,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.gsutil upload (4)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -384,6 +600,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.build perfetto/perfetto/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -411,6 +639,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.register perfetto/perfetto/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -426,7 +666,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -438,6 +678,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.gsutil upload (5)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -458,6 +710,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.build perfetto/traced/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -485,6 +749,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.register perfetto/traced/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -500,7 +776,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -512,6 +788,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.gsutil upload (6)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -532,6 +820,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.build perfetto/traced_probes/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -559,6 +859,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.Artifact upload.register perfetto/traced_probes/linux-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -585,6 +897,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"linux\" target_cpu=\"arm\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -597,12 +921,25 @@
       "-C",
       "[CACHE]/builder/perfetto/out/linux-arm",
       "trace_processor_shell",
+      "traceconv",
       "tracebox",
       "perfetto",
       "traced",
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -617,7 +954,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -629,6 +966,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.gsutil upload",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -649,6 +998,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.build perfetto/trace_processor_shell/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -676,6 +1037,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.register perfetto/trace_processor_shell/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -691,7 +1064,117 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "[CACHE]/builder/perfetto/out/linux-arm/stripped/traceconv",
+      "gs://perfetto-luci-artifacts/v13.0/linux-arm/traceconv"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm.Artifact upload.gsutil upload (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts/v13.0/linux-arm/traceconv@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-build",
+      "-pkg-def",
+      "{\"data\": [{\"file\": \"traceconv\"}], \"install_mode\": \"\", \"package\": \"perfetto/traceconv/linux-arm\", \"root\": \"[CACHE]/builder/perfetto/out/linux-arm/stripped\"}",
+      "-out",
+      "[CLEANUP]/traceconv-linux-arm.cipd",
+      "-hash-algo",
+      "sha256",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm.Artifact upload.build perfetto/traceconv/linux-arm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/linux-arm\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-register",
+      "[CLEANUP]/traceconv-linux-arm.cipd",
+      "-ref",
+      "latest",
+      "-tag",
+      "git_revision:",
+      "-tag",
+      "git_tag:v13.0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm.Artifact upload.register perfetto/traceconv/linux-arm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/linux-arm\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@perfetto/traceconv/linux-arm@https://chrome-infra-packages.appspot.com/p/perfetto/traceconv/linux-arm/+/40-chars-fake-of-the-package-instance_id@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -703,7 +1186,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm.Artifact upload.gsutil upload (2)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm.Artifact upload.gsutil upload (3)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts/v13.0/linux-arm/tracebox@@@"
@@ -723,6 +1218,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.build perfetto/tracebox/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -750,6 +1257,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.register perfetto/tracebox/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -765,7 +1284,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -777,7 +1296,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm.Artifact upload.gsutil upload (3)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm.Artifact upload.gsutil upload (4)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts/v13.0/linux-arm/perfetto@@@"
@@ -797,6 +1328,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.build perfetto/perfetto/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -824,6 +1367,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.register perfetto/perfetto/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -839,7 +1394,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -851,7 +1406,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm.Artifact upload.gsutil upload (4)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm.Artifact upload.gsutil upload (5)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts/v13.0/linux-arm/traced@@@"
@@ -871,6 +1438,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.build perfetto/traced/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -898,6 +1477,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.register perfetto/traced/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -913,7 +1504,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -925,7 +1516,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm.Artifact upload.gsutil upload (5)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm.Artifact upload.gsutil upload (6)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts/v13.0/linux-arm/traced_probes@@@"
@@ -945,6 +1548,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.build perfetto/traced_probes/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -972,6 +1587,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.Artifact upload.register perfetto/traced_probes/linux-arm",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -998,6 +1625,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"linux\" target_cpu=\"arm64\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -1010,12 +1649,25 @@
       "-C",
       "[CACHE]/builder/perfetto/out/linux-arm64",
       "trace_processor_shell",
+      "traceconv",
       "tracebox",
       "perfetto",
       "traced",
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -1030,7 +1682,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1042,6 +1694,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.gsutil upload",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1062,6 +1726,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.build perfetto/trace_processor_shell/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1089,6 +1765,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.register perfetto/trace_processor_shell/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1104,7 +1792,117 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "[CACHE]/builder/perfetto/out/linux-arm64/stripped/traceconv",
+      "gs://perfetto-luci-artifacts/v13.0/linux-arm64/traceconv"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm64.Artifact upload.gsutil upload (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts/v13.0/linux-arm64/traceconv@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-build",
+      "-pkg-def",
+      "{\"data\": [{\"file\": \"traceconv\"}], \"install_mode\": \"\", \"package\": \"perfetto/traceconv/linux-arm64\", \"root\": \"[CACHE]/builder/perfetto/out/linux-arm64/stripped\"}",
+      "-out",
+      "[CLEANUP]/traceconv-linux-arm64.cipd",
+      "-hash-algo",
+      "sha256",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm64.Artifact upload.build perfetto/traceconv/linux-arm64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/linux-arm64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "pkg-register",
+      "[CLEANUP]/traceconv-linux-arm64.cipd",
+      "-ref",
+      "latest",
+      "-tag",
+      "git_revision:",
+      "-tag",
+      "git_tag:v13.0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm64.Artifact upload.register perfetto/traceconv/linux-arm64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"package\": \"perfetto/traceconv/linux-arm64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@perfetto/traceconv/linux-arm64@https://chrome-infra-packages.appspot.com/p/perfetto/traceconv/linux-arm64/+/40-chars-fake-of-the-package-instance_id@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1116,7 +1914,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm64.Artifact upload.gsutil upload (2)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm64.Artifact upload.gsutil upload (3)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts/v13.0/linux-arm64/tracebox@@@"
@@ -1136,6 +1946,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.build perfetto/tracebox/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1163,6 +1985,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.register perfetto/tracebox/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1178,7 +2012,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1190,7 +2024,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm64.Artifact upload.gsutil upload (3)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm64.Artifact upload.gsutil upload (4)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts/v13.0/linux-arm64/perfetto@@@"
@@ -1210,6 +2056,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.build perfetto/perfetto/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1237,6 +2095,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.register perfetto/perfetto/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1252,7 +2122,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1264,7 +2134,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm64.Artifact upload.gsutil upload (4)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm64.Artifact upload.gsutil upload (5)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts/v13.0/linux-arm64/traced@@@"
@@ -1284,6 +2166,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.build perfetto/traced/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1311,6 +2205,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.register perfetto/traced/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1326,7 +2232,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
       "--",
@@ -1338,7 +2244,19 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
-    "name": "linux-arm64.Artifact upload.gsutil upload (5)",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "linux-arm64.Artifact upload.gsutil upload (6)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
       "@@@STEP_LINK@gsutil.upload@https://storage.cloud.google.com/perfetto-luci-artifacts/v13.0/linux-arm64/traced_probes@@@"
@@ -1358,6 +2276,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.build perfetto/traced_probes/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
@@ -1385,6 +2315,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.Artifact upload.register perfetto/traced_probes/linux-arm64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@",
diff --git a/infra/luci/recipes/perfetto.expected/ci_win.json b/infra/luci/recipes/perfetto.expected/ci_win.json
index 40f3cb7..9a7f1c6 100644
--- a/infra/luci/recipes/perfetto.expected/ci_win.json
+++ b/infra/luci/recipes/perfetto.expected/ci_win.json
@@ -5,7 +5,7 @@
   },
   {
     "cmd": [
-      "vpython",
+      "vpython3",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
       "--json-output",
@@ -16,6 +16,18 @@
       "[CACHE]\\builder\\perfetto"
     ],
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.ensure source dir",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -28,6 +40,18 @@
       "[CACHE]\\builder\\perfetto"
     ],
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.init",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -39,10 +63,22 @@
       "fetch",
       "--tags",
       "https://android.googlesource.com/platform/external/perfetto",
-      "refs/heads/master"
+      "refs/heads/main"
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.fetch",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -56,6 +92,18 @@
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.checkout",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -69,6 +117,18 @@
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.rev-parse",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -81,6 +141,18 @@
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "build-deps"
   },
   {
@@ -98,6 +170,18 @@
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "ensure_installed",
     "~followup_annotations": [
       "@@@STEP_LOG_LINE@json.output@{@@@",
@@ -115,13 +199,25 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
-      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "RECIPE_MODULE[recipe_engine::json]\\resources\\read.py",
       "[CACHE]\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "read SetEnv.x64.json",
     "~followup_annotations": [
       "@@@STEP_LOG_LINE@json.output@{@@@",
@@ -163,6 +259,18 @@
         "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "gn gen"
   },
   {
@@ -185,6 +293,18 @@
         "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
       ]
     },
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "ninja"
   },
   {
@@ -193,7 +313,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]\\resources\\gsutil_smart_retry.py",
       "--",
@@ -205,6 +325,18 @@
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "Artifact upload.gsutil upload",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
@@ -225,6 +357,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "Artifact upload.build perfetto/trace_processor_shell/windows-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
@@ -250,6 +394,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "Artifact upload.register perfetto/trace_processor_shell/windows-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
@@ -265,7 +421,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]\\resources\\gsutil_smart_retry.py",
       "--",
@@ -277,6 +433,18 @@
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "Artifact upload.gsutil upload (2)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
@@ -297,6 +465,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "Artifact upload.build perfetto/traceconv/windows-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
@@ -322,6 +502,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "Artifact upload.register perfetto/traceconv/windows-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
@@ -337,7 +529,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]\\resources\\gsutil_smart_retry.py",
       "--",
@@ -349,6 +541,18 @@
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "Artifact upload.gsutil upload (3)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
@@ -369,6 +573,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "Artifact upload.build perfetto/perfetto/windows-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
@@ -394,6 +610,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "Artifact upload.register perfetto/perfetto/windows-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
@@ -409,7 +637,7 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
       "RECIPE_MODULE[depot_tools::gsutil]\\resources\\gsutil_smart_retry.py",
       "--",
@@ -421,6 +649,18 @@
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "Artifact upload.gsutil upload (4)",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
@@ -441,6 +681,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "Artifact upload.build perfetto/traced/windows-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
@@ -466,6 +718,18 @@
       "/path/to/tmp/json"
     ],
     "cwd": "[CACHE]\\builder\\perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "Artifact upload.register perfetto/traced/windows-amd64",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
diff --git a/infra/luci/recipes/perfetto.expected/unofficial.json b/infra/luci/recipes/perfetto.expected/unofficial.json
index 5b86f34..4f72c69 100644
--- a/infra/luci/recipes/perfetto.expected/unofficial.json
+++ b/infra/luci/recipes/perfetto.expected/unofficial.json
@@ -5,7 +5,7 @@
   },
   {
     "cmd": [
-      "vpython",
+      "vpython3",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
@@ -16,6 +16,18 @@
       "[CACHE]/builder/perfetto"
     ],
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.ensure source dir",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -28,6 +40,18 @@
       "[CACHE]/builder/perfetto"
     ],
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.init",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -39,10 +63,22 @@
       "fetch",
       "--tags",
       "https://android.googlesource.com/platform/external/perfetto",
-      "refs/heads/master"
+      "refs/heads/main"
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.fetch",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -56,6 +92,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.checkout",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -69,6 +117,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "git.rev-parse",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -82,6 +142,18 @@
     ],
     "cwd": "[CACHE]/builder/perfetto",
     "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "build-deps"
   },
   {
@@ -97,6 +169,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"linux\" target_cpu=\"x64\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -116,6 +200,18 @@
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-amd64.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -134,6 +230,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"linux\" target_cpu=\"arm\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -146,12 +254,25 @@
       "-C",
       "[CACHE]/builder/perfetto/out/linux-arm",
       "trace_processor_shell",
+      "traceconv",
       "tracebox",
       "perfetto",
       "traced",
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -170,6 +291,18 @@
       "--args=is_debug=false monolithic_binaries=true target_os=\"linux\" target_cpu=\"arm64\""
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.gn gen",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
@@ -182,12 +315,25 @@
       "-C",
       "[CACHE]/builder/perfetto/out/linux-arm64",
       "trace_processor_shell",
+      "traceconv",
       "tracebox",
       "perfetto",
       "traced",
       "traced_probes"
     ],
     "cwd": "[CACHE]/builder/perfetto",
+    "luci_context": {
+      "realm": {
+        "name": "perfetto:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
     "name": "linux-arm64.ninja",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
diff --git a/infra/luci/recipes/perfetto.py b/infra/luci/recipes/perfetto.py
index 5df8c89..1a58179 100644
--- a/infra/luci/recipes/perfetto.py
+++ b/infra/luci/recipes/perfetto.py
@@ -176,7 +176,7 @@
       # Store information about the git revision and the tag if available.
       ctx.git_revision = api.step(
           'rev-parse', ['git', 'rev-parse', 'HEAD'],
-          stdout=api.raw_io.output()).stdout.strip()
+          stdout=api.raw_io.output_text()).stdout.strip()
       ctx.maybe_git_tag = ref.replace(
           'refs/tags/', '') if ref.startswith('refs/tags/') else None
 
diff --git a/protos/perfetto/common/BUILD.gn b/protos/perfetto/common/BUILD.gn
index 5ecc0de..31d4842 100644
--- a/protos/perfetto/common/BUILD.gn
+++ b/protos/perfetto/common/BUILD.gn
@@ -36,4 +36,10 @@
     "tracing_service_state.proto",
     "track_event_descriptor.proto",
   ]
+  proto_generators = [
+    "lite",
+    "zero",
+    "cpp",
+    "source_set",
+  ]
 }
diff --git a/protos/perfetto/config/data_source_config.proto b/protos/perfetto/config/data_source_config.proto
index 2bb9056..b15cc39 100644
--- a/protos/perfetto/config/data_source_config.proto
+++ b/protos/perfetto/config/data_source_config.proto
@@ -42,7 +42,7 @@
 import "protos/perfetto/config/system_info/system_info.proto";
 
 // The configuration that is passed to each data source when starting tracing.
-// Next id: 121
+// Next id: 123
 message DataSourceConfig {
   enum SessionInitiator {
     SESSION_INITIATOR_UNSPECIFIED = 0;
@@ -69,6 +69,10 @@
   // DO NOT SET in consumer as this will be overridden by the service.
   optional uint32 trace_duration_ms = 3;
 
+  // If true, |trace_duration_ms| should count also time in suspend. This
+  // is propagated from TraceConfig.prefer_suspend_clock_for_duration.
+  optional bool prefer_suspend_clock_for_duration = 122;
+
   // Set by the service to indicate how long it waits after StopDataSource.
   // DO NOT SET in consumer as this will be overridden by the service.
   optional uint32 stop_timeout_ms = 7;
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index 585a592..8ea2e4b 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -760,6 +760,10 @@
   // Provides a breakdown of energy estimation for various subsystem (e.g. GPU).
   // Available from Android S.
   optional bool collect_energy_estimation_breakdown = 4;
+
+  // Provides a breakdown of time in state for various subsystems.
+  // Available from Android U.
+  optional bool collect_entity_state_residency = 5;
 }
 
 // End of protos/perfetto/config/power/android_power_config.proto
@@ -2378,7 +2382,7 @@
 // Begin of protos/perfetto/config/data_source_config.proto
 
 // The configuration that is passed to each data source when starting tracing.
-// Next id: 121
+// Next id: 123
 message DataSourceConfig {
   enum SessionInitiator {
     SESSION_INITIATOR_UNSPECIFIED = 0;
@@ -2405,6 +2409,10 @@
   // DO NOT SET in consumer as this will be overridden by the service.
   optional uint32 trace_duration_ms = 3;
 
+  // If true, |trace_duration_ms| should count also time in suspend. This
+  // is propagated from TraceConfig.prefer_suspend_clock_for_duration.
+  optional bool prefer_suspend_clock_for_duration = 122;
+
   // Set by the service to indicate how long it waits after StopDataSource.
   // DO NOT SET in consumer as this will be overridden by the service.
   optional uint32 stop_timeout_ms = 7;
@@ -2522,7 +2530,7 @@
 // It contains the general config for the logging buffer(s) and the configs for
 // all the data source being enabled.
 //
-// Next id: 35.
+// Next id: 37.
 message TraceConfig {
   message BufferConfig {
     optional uint32 size_kb = 1;
@@ -2625,6 +2633,15 @@
   // TriggerConfig.trigger_timeout_ms instead.
   optional uint32 duration_ms = 3;
 
+  // If true, tries to use CLOCK_BOOTTIME for duration_ms rather than
+  // CLOCK_MONOTONIC (which doesn't count time in suspend). Supported only on
+  // Linux/Android, no-op on other platforms. This is used when dealing with
+  // long (e.g. 24h) traces, where suspend can inflate them to weeks of
+  // wall-time, making them more likely to hit device reboots (and hence loss).
+  // This option also changes consistently the semantic of
+  // TrigerConfig.stop_delay_ms.
+  optional bool prefer_suspend_clock_for_duration = 36;
+
   // This is set when --dropbox is passed to the Perfetto command line client
   // and enables guardrails that limit resource usage for traces requested
   // by statsd.
@@ -2791,6 +2808,8 @@
 
       // After a trigger is received either in START_TRACING or STOP_TRACING
       // mode then the trace will end |stop_delay_ms| after triggering.
+      // If |prefer_suspend_clock_for_duration| is set, the duration will be
+      // based on wall-clock, counting also time in suspend.
       optional uint32 stop_delay_ms = 3;
 
       // Limits the number of traces this trigger can start/stop in a rolling
diff --git a/protos/perfetto/config/power/android_power_config.proto b/protos/perfetto/config/power/android_power_config.proto
index 82c4bb2..dddcb28 100644
--- a/protos/perfetto/config/power/android_power_config.proto
+++ b/protos/perfetto/config/power/android_power_config.proto
@@ -43,4 +43,8 @@
   // Provides a breakdown of energy estimation for various subsystem (e.g. GPU).
   // Available from Android S.
   optional bool collect_energy_estimation_breakdown = 4;
+
+  // Provides a breakdown of time in state for various subsystems.
+  // Available from Android U.
+  optional bool collect_entity_state_residency = 5;
 }
diff --git a/protos/perfetto/config/trace_config.proto b/protos/perfetto/config/trace_config.proto
index 4338154..e7c9767 100644
--- a/protos/perfetto/config/trace_config.proto
+++ b/protos/perfetto/config/trace_config.proto
@@ -26,7 +26,7 @@
 // It contains the general config for the logging buffer(s) and the configs for
 // all the data source being enabled.
 //
-// Next id: 35.
+// Next id: 37.
 message TraceConfig {
   message BufferConfig {
     optional uint32 size_kb = 1;
@@ -129,6 +129,15 @@
   // TriggerConfig.trigger_timeout_ms instead.
   optional uint32 duration_ms = 3;
 
+  // If true, tries to use CLOCK_BOOTTIME for duration_ms rather than
+  // CLOCK_MONOTONIC (which doesn't count time in suspend). Supported only on
+  // Linux/Android, no-op on other platforms. This is used when dealing with
+  // long (e.g. 24h) traces, where suspend can inflate them to weeks of
+  // wall-time, making them more likely to hit device reboots (and hence loss).
+  // This option also changes consistently the semantic of
+  // TrigerConfig.stop_delay_ms.
+  optional bool prefer_suspend_clock_for_duration = 36;
+
   // This is set when --dropbox is passed to the Perfetto command line client
   // and enables guardrails that limit resource usage for traces requested
   // by statsd.
@@ -295,6 +304,8 @@
 
       // After a trigger is received either in START_TRACING or STOP_TRACING
       // mode then the trace will end |stop_delay_ms| after triggering.
+      // If |prefer_suspend_clock_for_duration| is set, the duration will be
+      // based on wall-clock, counting also time in suspend.
       optional uint32 stop_delay_ms = 3;
 
       // Limits the number of traces this trigger can start/stop in a rolling
diff --git a/protos/perfetto/trace/interned_data/interned_data.proto b/protos/perfetto/trace/interned_data/interned_data.proto
index eddd7b1..59103a7 100644
--- a/protos/perfetto/trace/interned_data/interned_data.proto
+++ b/protos/perfetto/trace/interned_data/interned_data.proto
@@ -53,7 +53,7 @@
 // emitted proactively in advance of referring to them in later packets.
 //
 // Next reserved id: 8 (up to 15).
-// Next id: 29.
+// Next id: 30.
 message InternedData {
   // TODO(eseckler): Replace iid fields inside interned messages with
   // map<iid, message> type fields in InternedData.
@@ -111,4 +111,7 @@
   // This is is NOT the real address. This is to avoid disclosing KASLR through
   // traces.
   repeated InternedString kernel_symbols = 26;
+
+  // Interned string values in the DebugAnnotation proto.
+  repeated InternedString debug_annotation_string_values = 29;
 }
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index a215592..6205554 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -760,6 +760,10 @@
   // Provides a breakdown of energy estimation for various subsystem (e.g. GPU).
   // Available from Android S.
   optional bool collect_energy_estimation_breakdown = 4;
+
+  // Provides a breakdown of time in state for various subsystems.
+  // Available from Android U.
+  optional bool collect_entity_state_residency = 5;
 }
 
 // End of protos/perfetto/config/power/android_power_config.proto
@@ -2378,7 +2382,7 @@
 // Begin of protos/perfetto/config/data_source_config.proto
 
 // The configuration that is passed to each data source when starting tracing.
-// Next id: 121
+// Next id: 123
 message DataSourceConfig {
   enum SessionInitiator {
     SESSION_INITIATOR_UNSPECIFIED = 0;
@@ -2405,6 +2409,10 @@
   // DO NOT SET in consumer as this will be overridden by the service.
   optional uint32 trace_duration_ms = 3;
 
+  // If true, |trace_duration_ms| should count also time in suspend. This
+  // is propagated from TraceConfig.prefer_suspend_clock_for_duration.
+  optional bool prefer_suspend_clock_for_duration = 122;
+
   // Set by the service to indicate how long it waits after StopDataSource.
   // DO NOT SET in consumer as this will be overridden by the service.
   optional uint32 stop_timeout_ms = 7;
@@ -2522,7 +2530,7 @@
 // It contains the general config for the logging buffer(s) and the configs for
 // all the data source being enabled.
 //
-// Next id: 35.
+// Next id: 37.
 message TraceConfig {
   message BufferConfig {
     optional uint32 size_kb = 1;
@@ -2625,6 +2633,15 @@
   // TriggerConfig.trigger_timeout_ms instead.
   optional uint32 duration_ms = 3;
 
+  // If true, tries to use CLOCK_BOOTTIME for duration_ms rather than
+  // CLOCK_MONOTONIC (which doesn't count time in suspend). Supported only on
+  // Linux/Android, no-op on other platforms. This is used when dealing with
+  // long (e.g. 24h) traces, where suspend can inflate them to weeks of
+  // wall-time, making them more likely to hit device reboots (and hence loss).
+  // This option also changes consistently the semantic of
+  // TrigerConfig.stop_delay_ms.
+  optional bool prefer_suspend_clock_for_duration = 36;
+
   // This is set when --dropbox is passed to the Perfetto command line client
   // and enables guardrails that limit resource usage for traces requested
   // by statsd.
@@ -2791,6 +2808,8 @@
 
       // After a trigger is received either in START_TRACING or STOP_TRACING
       // mode then the trace will end |stop_delay_ms| after triggering.
+      // If |prefer_suspend_clock_for_duration| is set, the duration will be
+      // based on wall-clock, counting also time in suspend.
       optional uint32 stop_delay_ms = 3;
 
       // Limits the number of traces this trigger can start/stop in a rolling
@@ -8656,7 +8675,7 @@
 //     }
 //   }
 //
-// Next ID: 17.
+// Next ID: 18.
 // Reserved ID: 15
 message DebugAnnotation {
   // Name fields are set only for dictionary entries.
@@ -8672,7 +8691,6 @@
     uint64 uint_value = 3;
     int64 int_value = 4;
     double double_value = 5;
-    string string_value = 6;
     // Pointers are stored in a separate type as the JSON output treats them
     // differently from other uint64 values.
     uint64 pointer_value = 7;
@@ -8683,6 +8701,11 @@
     // Legacy instrumentation may not support conversion of nested data to
     // NestedValue yet.
     string legacy_json_value = 9;
+
+    // interned and non-interned variants of strings.
+    string string_value = 6;
+    // Corresponds to |debug_annotation_string_values| field in InternedData.
+    uint64 string_value_iid = 17;
   }
 
   // Used to embed arbitrary proto messages (which are also typically used to
@@ -9850,7 +9873,7 @@
 // emitted proactively in advance of referring to them in later packets.
 //
 // Next reserved id: 8 (up to 15).
-// Next id: 29.
+// Next id: 30.
 message InternedData {
   // TODO(eseckler): Replace iid fields inside interned messages with
   // map<iid, message> type fields in InternedData.
@@ -9908,6 +9931,9 @@
   // This is is NOT the real address. This is to avoid disclosing KASLR through
   // traces.
   repeated InternedString kernel_symbols = 26;
+
+  // Interned string values in the DebugAnnotation proto.
+  repeated InternedString debug_annotation_string_values = 29;
 }
 
 // End of protos/perfetto/trace/interned_data/interned_data.proto
@@ -10184,6 +10210,48 @@
 
 // End of protos/perfetto/trace/power/android_energy_estimation_breakdown.proto
 
+// Begin of protos/perfetto/trace/power/android_entity_state_residency.proto
+
+message EntityStateResidency {
+  message PowerEntityState {
+    // Index corresponding to the entity
+    optional int32 entity_index = 1;
+
+    // Index corresponding to the state
+    optional int32 state_index = 2;
+
+    // Name of the entity
+    optional string entity_name = 3;
+
+    // Name of the state
+    optional string state_name = 4;
+  }
+
+  // This is only emitted at the beginning of the trace.
+  repeated PowerEntityState power_entity_state = 1;
+
+  message StateResidency {
+    // Index corresponding to PowerEntityState.entity_index
+    optional int32 entity_index = 1;
+
+    // Index corresponding to PowerEntityState.state_index
+    optional int32 state_index = 2;
+
+    // Time since boot that this entity has been in this state
+    optional uint64 total_time_in_state_ms = 3;
+
+    // Total number of times since boot that the entity has entered this state
+    optional uint64 total_state_entry_count = 4;
+
+    // Timestamp of the last time the entity entered this state
+    optional uint64 last_entry_timestamp_ms = 5;
+  }
+
+  repeated StateResidency residency = 2;
+}
+
+// End of protos/perfetto/trace/power/android_entity_state_residency.proto
+
 // Begin of protos/perfetto/trace/power/battery_counters.proto
 
 message BatteryCounters {
@@ -10204,6 +10272,12 @@
 
   // Battery name, emitted only on multiple batteries.
   optional string name = 5;
+
+  // Battery capacity in microwatt-hours(µWh).
+  optional int64 energy_counter_uwh = 6;
+
+  // Battery voltage in microvolts(µV).
+  optional int64 voltage_uv = 7;
 }
 
 // End of protos/perfetto/trace/power/battery_counters.proto
@@ -11704,7 +11778,7 @@
 // See the [Buffers and Dataflow](/docs/concepts/buffers.md) doc for details.
 //
 // Next reserved id: 14 (up to 15).
-// Next id: 91.
+// Next id: 92.
 message TracePacket {
   // The timestamp of the TracePacket.
   // By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on
@@ -11773,6 +11847,7 @@
     AndroidGameInterventionList android_game_intervention_list = 83;
     StatsdAtom statsd_atom = 84;
     AndroidSystemProperty android_system_property = 86;
+    EntityStateResidency entity_state_residency = 91;
 
     // Only used in profile packets.
     ProfiledFrameSymbols profiled_frame_symbols = 55;
diff --git a/protos/perfetto/trace/power/BUILD.gn b/protos/perfetto/trace/power/BUILD.gn
index ec595de..1d53a00 100644
--- a/protos/perfetto/trace/power/BUILD.gn
+++ b/protos/perfetto/trace/power/BUILD.gn
@@ -18,6 +18,7 @@
   deps = [ "../../common:@TYPE@" ]
   sources = [
     "android_energy_estimation_breakdown.proto",
+    "android_entity_state_residency.proto",
     "battery_counters.proto",
     "power_rails.proto",
   ]
diff --git a/protos/perfetto/trace/power/android_entity_state_residency.proto b/protos/perfetto/trace/power/android_entity_state_residency.proto
new file mode 100644
index 0000000..6ca767d
--- /dev/null
+++ b/protos/perfetto/trace/power/android_entity_state_residency.proto
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package perfetto.protos;
+
+message EntityStateResidency {
+  message PowerEntityState {
+    // Index corresponding to the entity
+    optional int32 entity_index = 1;
+
+    // Index corresponding to the state
+    optional int32 state_index = 2;
+
+    // Name of the entity
+    optional string entity_name = 3;
+
+    // Name of the state
+    optional string state_name = 4;
+  }
+
+  // This is only emitted at the beginning of the trace.
+  repeated PowerEntityState power_entity_state = 1;
+
+  message StateResidency {
+    // Index corresponding to PowerEntityState.entity_index
+    optional int32 entity_index = 1;
+
+    // Index corresponding to PowerEntityState.state_index
+    optional int32 state_index = 2;
+
+    // Time since boot that this entity has been in this state
+    optional uint64 total_time_in_state_ms = 3;
+
+    // Total number of times since boot that the entity has entered this state
+    optional uint64 total_state_entry_count = 4;
+
+    // Timestamp of the last time the entity entered this state
+    optional uint64 last_entry_timestamp_ms = 5;
+  }
+
+  repeated StateResidency residency = 2;
+}
diff --git a/protos/perfetto/trace/power/battery_counters.proto b/protos/perfetto/trace/power/battery_counters.proto
index ab1aaa8..bb1a23c 100644
--- a/protos/perfetto/trace/power/battery_counters.proto
+++ b/protos/perfetto/trace/power/battery_counters.proto
@@ -35,4 +35,10 @@
 
   // Battery name, emitted only on multiple batteries.
   optional string name = 5;
+
+  // Battery capacity in microwatt-hours(µWh).
+  optional int64 energy_counter_uwh = 6;
+
+  // Battery voltage in microvolts(µV).
+  optional int64 voltage_uv = 7;
 }
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index 1df67bd..c5c2de5 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -46,6 +46,7 @@
 import "protos/perfetto/trace/perfetto/perfetto_metatrace.proto";
 import "protos/perfetto/trace/perfetto/tracing_service_event.proto";
 import "protos/perfetto/trace/power/android_energy_estimation_breakdown.proto";
+import "protos/perfetto/trace/power/android_entity_state_residency.proto";
 import "protos/perfetto/trace/power/battery_counters.proto";
 import "protos/perfetto/trace/power/power_rails.proto";
 import "protos/perfetto/trace/statsd/statsd_atom.proto";
@@ -93,7 +94,7 @@
 // See the [Buffers and Dataflow](/docs/concepts/buffers.md) doc for details.
 //
 // Next reserved id: 14 (up to 15).
-// Next id: 91.
+// Next id: 92.
 message TracePacket {
   // The timestamp of the TracePacket.
   // By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on
@@ -162,6 +163,7 @@
     AndroidGameInterventionList android_game_intervention_list = 83;
     StatsdAtom statsd_atom = 84;
     AndroidSystemProperty android_system_property = 86;
+    EntityStateResidency entity_state_residency = 91;
 
     // Only used in profile packets.
     ProfiledFrameSymbols profiled_frame_symbols = 55;
diff --git a/protos/perfetto/trace/track_event/debug_annotation.proto b/protos/perfetto/trace/track_event/debug_annotation.proto
index 35a041d..6dba2ab 100644
--- a/protos/perfetto/trace/track_event/debug_annotation.proto
+++ b/protos/perfetto/trace/track_event/debug_annotation.proto
@@ -57,7 +57,7 @@
 //     }
 //   }
 //
-// Next ID: 17.
+// Next ID: 18.
 // Reserved ID: 15
 message DebugAnnotation {
   // Name fields are set only for dictionary entries.
@@ -73,7 +73,6 @@
     uint64 uint_value = 3;
     int64 int_value = 4;
     double double_value = 5;
-    string string_value = 6;
     // Pointers are stored in a separate type as the JSON output treats them
     // differently from other uint64 values.
     uint64 pointer_value = 7;
@@ -84,6 +83,11 @@
     // Legacy instrumentation may not support conversion of nested data to
     // NestedValue yet.
     string legacy_json_value = 9;
+
+    // interned and non-interned variants of strings.
+    string string_value = 6;
+    // Corresponds to |debug_annotation_string_values| field in InternedData.
+    uint64 string_value_iid = 17;
   }
 
   // Used to embed arbitrary proto messages (which are also typically used to
diff --git a/protos/perfetto/trace_processor/BUILD.gn b/protos/perfetto/trace_processor/BUILD.gn
index c8671b1..4ba70c7 100644
--- a/protos/perfetto/trace_processor/BUILD.gn
+++ b/protos/perfetto/trace_processor/BUILD.gn
@@ -17,10 +17,11 @@
 
 perfetto_proto_library("@TYPE@") {
   proto_generators = [
+    "lite",
     "zero",
     "source_set",
   ]
-  deps = [ "../common:zero" ]  # ../common:zero needed for descriptor.proto.
+  deps = [ "../common:@TYPE@" ]  # needed for descriptor.proto.
   sources = []
   foreach(source, trace_processor_protos) {
     sources += [ "$source.proto" ]
@@ -34,3 +35,10 @@
   ]
   sources = [ "metrics_impl.proto" ]
 }
+
+if (enable_perfetto_grpc) {
+  perfetto_grpc_library("cloud_trace_processor_grpc") {
+    deps = [ ":lite" ]
+    sources = [ "cloud_trace_processor.proto" ]
+  }
+}
diff --git a/protos/perfetto/trace_processor/cloud_trace_processor.proto b/protos/perfetto/trace_processor/cloud_trace_processor.proto
new file mode 100644
index 0000000..b009fe3
--- /dev/null
+++ b/protos/perfetto/trace_processor/cloud_trace_processor.proto
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+service CloudTraceProcessorWorkerService {}
\ No newline at end of file
diff --git a/protos/perfetto/trace_processor/proto_files.gni b/protos/perfetto/trace_processor/proto_files.gni
index b46da47..4c9ae21 100644
--- a/protos/perfetto/trace_processor/proto_files.gni
+++ b/protos/perfetto/trace_processor/proto_files.gni
@@ -15,6 +15,7 @@
 # This variable is used both by ./BUILD.gn (for the C++ proto codegen) and by
 # //ui/BUIlD.gn (for the TypeScript/JS proto codegen).
 trace_processor_protos = [
+  "cloud_trace_processor",
   "trace_processor",
   "metatrace_categories",
 ]
diff --git a/protos/third_party/chromium/chrome_track_event.proto b/protos/third_party/chromium/chrome_track_event.proto
index 911dbe0..0930dfe 100644
--- a/protos/third_party/chromium/chrome_track_event.proto
+++ b/protos/third_party/chromium/chrome_track_event.proto
@@ -948,7 +948,7 @@
     DIFF_NULL = 1;
     DIFF_TINT = 2;
     DIFF_TAB_COUNT = 3;
-    DIFF_OPTIONAL_BUTTON_DATA = 4;
+    DIFF_OPTIONAL_BUTTON = 4;
     DIFF_VISUAL_STATE = 5;
     DIFF_SECURITY_ICON = 6;
     DIFF_SHOWING_UPDATE_BADGE = 7;
@@ -956,9 +956,13 @@
     DIFF_PROGRESS = 9;
     DIFF_LOCATION_BAR_WIDTH = 10;
     DIFF_URL_TEXT = 11;
-    DIFF_HOME_BUTTON_COLOR = 12;
+    DIFF_HOME_BUTTON = 12;
     DIFF_TITLE_TEXT = 13;
     DIFF_CCT_ANIMATION = 14;
+    DIFF_BOOKMARK_BUTTON = 15;
+    DIFF_BACK_BUTTON = 16;
+    DIFF_FORWARD_BUTTON = 17;
+    DIFF_RELOAD_BUTTON = 18;
   }
 
   optional BlockCaptureReason block_capture_reason = 1;
@@ -1034,6 +1038,7 @@
   optional string stack_trace = 6;
   repeated V8StackFrame stack_frames = 7;
 }
+
 // Contains the meta information for high entropy events (like api calls)
 // that are to be traced for debugging in the context of identifiability study.
 message BlinkHighEntropyAPI {
@@ -1070,9 +1075,86 @@
   optional CalledJsApi called_api = 2;
 }
 
+// Contains information about a tab switch measurement.
+message TabSwitchMeasurement {
+  // Possible outcomes of a tab switch. Maps to
+  // blink::ContentToVisibleTimeReporter::TabSwitchResult.
+  enum Result {
+    RESULT_UNSPECIFIED = 0;
+    // A frame was successfully presented after a tab switch.
+    RESULT_SUCCESS = 1;
+    // Tab was hidden before a frame was presented after a tab switch.
+    RESULT_INCOMPLETE = 2;
+    // TabWasShown called twice for a frame without TabWasHidden between. Treat
+    // the first TabWasShown as an incomplete tab switch.
+    RESULT_MISSED_TAB_HIDE = 3;
+  }
+
+  // State of the target tab. Corresponds to the suffixes of the
+  // Browser.TabSwitchResult2.* histograms.
+  enum TabState {
+    STATE_UNSPECIFIED = 0;
+    // The tab has frames in the frame cache, which can be composited in the
+    // browser process.
+    STATE_WITH_SAVED_FRAMES = 1;
+    // The tab has no frames in the frame cache so must be rendered and
+    // composited in a renderer process.
+    STATE_LOADED_NO_SAVED_FRAMES = 2;
+    // The tab has no frames in the frame cache and is not fully loaded, so it
+    // must be reloaded before it can be rendered and composited in a renderer
+    // process.
+    STATE_NOT_LOADED_NO_SAVED_FRAMES = 3;
+  }
+
+  optional Result result = 1;
+  optional TabState tab_state = 2;
+}
+
+// Data about scroll deltas and offsets.
+// All values are in physical screen pixels.
+message ScrollDeltas {
+  // The trace_id of the current input.
+  optional int64 trace_id = 1;
+  // The original delta for current input. That is, this is the delta that
+  // the Browser receives from Android, but already scaled to the device's
+  // screen.
+  optional float original_delta_x = 2;
+  optional float original_delta_y = 3;
+  // The trace_id of the input that the current input is coalesced with.
+  // If input is not coalesced, this field is null.
+  optional int64 coalesced_to_trace_id = 4;
+  // The delta which arrived to compositor.
+  // This is the sum of all the inputs coalesced together
+  // with the resampler applied to them.
+  // This delta is used to calculate a |visual_offset|.
+  optional float provided_to_compositor_delta_x = 5;
+  optional float provided_to_compositor_delta_y = 6;
+  // The offset which compositor set.
+  optional int64 visual_offset_x = 7;
+  optional int64 visual_offset_y = 8;
+
+  // Data which GPU returns.
+  // Number of inputs which were shown together in one GPU frame.
+  optional int32 event_count_in_gpu_frame = 9;
+  // The trace_ids of inputs which were shown together in one GPU frame.
+  repeated int64 trace_ids_in_gpu_frame = 10;
+  // The sum of original deltas of inputs which were shown together in one GPU
+  // frame.
+  optional float original_delta_in_gpu_frame_y = 11;
+  // The sum of predicted deltas of inputs which were shown together in one GPU
+  // frame.
+  optional float predicted_delta_in_gpu_frame_y = 12;
+  // The array of original deltas of inputs which were shown together in one GPU
+  // frame.
+  repeated float segregated_original_deltas_in_gpu_frame_y = 13;
+  // The array of predicted deltas of inputs which were shown together in one
+  // GPU frame.
+  repeated float segregated_predicted_deltas_in_gpu_frame_y = 14;
+}
+
 message ChromeTrackEvent {
   // Extension range for Chrome: 1000-1999
-  // Next ID: 1046
+  // Next ID: 1048
   extend TrackEvent {
     optional ChromeAppState chrome_app_state = 1000;
 
@@ -1170,5 +1252,9 @@
     optional UkmPageLoadTimingUpdate ukm_page_load_timing_update = 1044;
 
     optional BlinkHighEntropyAPI high_entropy_api = 1045;
+
+    optional TabSwitchMeasurement tab_switch_measurement = 1046;
+
+    optional ScrollDeltas scroll_deltas = 1047;
   }
 }
diff --git a/python/generators/diff_tests/runner.py b/python/generators/diff_tests/runner.py
index dab06d1..c6eb14e 100644
--- a/python/generators/diff_tests/runner.py
+++ b/python/generators/diff_tests/runner.py
@@ -21,7 +21,7 @@
 import sys
 import tempfile
 from dataclasses import dataclass
-from typing import Dict, List, Tuple
+from typing import List, Tuple, Optional
 
 from google.protobuf import text_format
 from python.generators.diff_tests.testing import TestCase, TestType
@@ -63,7 +63,7 @@
   passed: bool
   stderr: str
   exit_code: int
-  perf_result: PerfResult
+  perf_result: Optional[PerfResult]
 
   def __init__(self, test: TestCase, gen_trace_path: str, cmd: List[str],
                expected_text: str, actual_text: str, stderr: str,
@@ -88,7 +88,10 @@
     actual_content = self.actual.replace('\r\n', '\n')
     self.passed = (expected_content == actual_content)
 
-    self.perf_result = PerfResult(self.test, perf_lines)
+    if self.exit_code == 0:
+      self.perf_result = PerfResult(self.test, perf_lines)
+    else:
+      self.perf_result = None
 
   def write_diff(self):
     expected_lines = self.expected.splitlines(True)
diff --git a/python/generators/diff_tests/testing.py b/python/generators/diff_tests/testing.py
index 30f69bd..aeef2b0 100644
--- a/python/generators/diff_tests/testing.py
+++ b/python/generators/diff_tests/testing.py
@@ -16,7 +16,7 @@
 import inspect
 import os
 from dataclasses import dataclass
-from typing import Dict, List, Union
+from typing import List, Union
 from enum import Enum
 import re
 
diff --git a/python/perfetto/trace_processor/api.py b/python/perfetto/trace_processor/api.py
index d884759..c21dd6f 100644
--- a/python/perfetto/trace_processor/api.py
+++ b/python/perfetto/trace_processor/api.py
@@ -367,7 +367,9 @@
     if hasattr(self, 'subprocess'):
       self.subprocess.kill()
       self.subprocess.wait()
-    self.http.conn.close()
+
+    if hasattr(self, 'http'):
+      self.http.conn.close()
 
   def __del__(self):
     self.close()
diff --git a/python/tools/cpu_profile.py b/python/tools/cpu_profile.py
index 46b258f..8968bb9 100644
--- a/python/tools/cpu_profile.py
+++ b/python/tools/cpu_profile.py
@@ -98,6 +98,25 @@
       metavar="DURATION",
       type=int,
       default=0)
+  # Profiling using hardware counters.
+  parser.add_argument(
+      "-e",
+      "--event",
+      help="Use the specified hardware counter event for sampling.",
+      metavar="EVENT",
+      action="append",
+      # See: '//perfetto/protos/perfetto/trace/perfetto_trace.proto'.
+      choices=['HW_CPU_CYCLES', 'HW_INSTRUCTIONS', 'HW_CACHE_REFERENCES',
+               'HW_CACHE_MISSES', 'HW_BRANCH_INSTRUCTIONS', 'HW_BRANCH_MISSES',
+               'HW_BUS_CYCLES', 'HW_STALLED_CYCLES_FRONTEND',
+               'HW_STALLED_CYCLES_BACKEND'],
+      default=[])
+  parser.add_argument(
+      "-k",
+      "--kernel-frames",
+      help="Collect kernel frames.  Default: false.",
+      action="store_true",
+      default=False)
   parser.add_argument(
       "-n",
       "--name",
@@ -136,8 +155,11 @@
       default=None)
 
   args = parser.parse_args()
-  if args.config is not None and args.name is not None:
-    sys.exit("--name/-n should not be provided when --config/-c is provided.")
+  if args.config is not None:
+    if args.name is not None:
+      sys.exit("--name/-n should not be specified with --config/-c.")
+    elif args.event is not None:
+      sys.exit("-e/--event should not be specified with --config/-c.")
   elif args.config is None and args.name is None:
     sys.exit("One of --names/-n or --config/-c is required.")
 
@@ -186,7 +208,7 @@
     except IOError as error:
       sys.exit("Unable to read config file: {}".format(error))
 
-  CONFIG_INDENT = '      '
+  CONFIG_INDENT = '          '
   CONFIG = textwrap.dedent('''\
   buffers {{
     size_kb: 2048
@@ -206,18 +228,6 @@
     }}
   }}
 
-  data_sources {{
-    config {{
-      name: "linux.perf"
-      target_buffer: 1
-      perf_event_config {{
-        all_cpus: true
-        sampling_frequency: {frequency}
-  {target_config}
-      }}
-    }}
-  }}
-
   duration_ms: {duration}
   write_into_file: true
   flush_timeout_ms: 30000
@@ -235,6 +245,35 @@
   target_config = "\n".join(
       [f'{CONFIG_INDENT}target_cmdline: "{p}"' for p in matching_processes])
 
+  events = args.event or ['SW_CPU_CLOCK']
+  for event in events:
+    CONFIG += (textwrap.dedent('''
+    data_sources {{
+      config {{
+        name: "linux.perf"
+        target_buffer: 1
+        perf_event_config {{
+          timebase {{
+            counter: %s
+            frequency: {frequency}
+            timestamp_clock: PERF_CLOCK_MONOTONIC
+          }}
+          callstack_sampling {{
+            scope {{
+    {target_config}
+            }}
+            kernel_frames: {kernel_config}
+          }}
+        }}
+      }}
+    }}
+    ''') % (event))
+
+  if args.kernel_frames:
+    kernel_config = "true"
+  else:
+    kernel_config = "false"
+
   if not args.print_config:
     print("Configured profiling for these processes:\n")
     for matching_process in matching_processes:
@@ -244,7 +283,8 @@
   config = CONFIG.format(
       frequency=args.frequency,
       duration=args.duration,
-      target_config=target_config)
+      target_config=target_config,
+      kernel_config=kernel_config)
 
   return config
 
diff --git a/src/android_internal/power_stats.cc b/src/android_internal/power_stats.cc
index f7badf6..2e3e956 100644
--- a/src/android_internal/power_stats.cc
+++ b/src/android_internal/power_stats.cc
@@ -53,6 +53,10 @@
                                      size_t* size_of_arr) = 0;
   virtual bool GetEnergyConsumed(EnergyEstimationBreakdown* breakdown,
                                  size_t* size_of_arr) = 0;
+  virtual bool GetPowerEntityStates(PowerEntityState* state,
+                                    size_t* size_of_arr) = 0;
+  virtual bool GetPowerEntityStateResidency(PowerEntityStateResidency* state,
+                                            size_t* size_of_arr) = 0;
   virtual ~PowerStatsDataProvider() = default;
 };
 
@@ -64,6 +68,10 @@
                              size_t* size_of_arr) override;
   bool GetEnergyConsumed(EnergyEstimationBreakdown* breakdown,
                          size_t* size_of_arr) override;
+  bool GetPowerEntityStates(PowerEntityState* state,
+                            size_t* size_of_arr) override;
+  bool GetPowerEntityStateResidency(PowerEntityStateResidency* state,
+                                    size_t* size_of_arr) override;
 
   PowerStatsHalDataProvider() = default;
   ~PowerStatsHalDataProvider() override = default;
@@ -84,6 +92,10 @@
                              size_t* size_of_arr) override;
   bool GetEnergyConsumed(EnergyEstimationBreakdown* breakdown,
                          size_t* size_of_arr) override;
+  bool GetPowerEntityStates(PowerEntityState* state,
+                            size_t* size_of_arr) override;
+  bool GetPowerEntityStateResidency(PowerEntityStateResidency* state,
+                                    size_t* size_of_arr) override;
 
   PowerStatsAidlDataProvider() = default;
   ~PowerStatsAidlDataProvider() override = default;
@@ -129,6 +141,16 @@
   return GetDataProvider()->GetEnergyConsumed(breakdown, size_of_arr);
 }
 
+bool GetPowerEntityStates(PowerEntityState* state, size_t* size_of_arr) {
+  return GetDataProvider()->GetPowerEntityStates(state, size_of_arr);
+}
+
+bool GetPowerEntityStateResidency(PowerEntityStateResidency* residency,
+                                  size_t* size_of_arr) {
+  return GetDataProvider()->GetPowerEntityStateResidency(residency,
+                                                         size_of_arr);
+}
+
 /*** Power Stats HAL Implemenation *******************************************/
 
 using android::hardware::hidl_vec;
@@ -218,6 +240,17 @@
   return false;
 }
 
+bool PowerStatsHalDataProvider::GetPowerEntityStates(PowerEntityState*,
+                                                     size_t*) {
+  return false;
+}
+
+bool PowerStatsHalDataProvider::GetPowerEntityStateResidency(
+    PowerEntityStateResidency*,
+    size_t*) {
+  return false;
+}
+
 /*** End of Power Stats HAL Implemenation *************************************/
 
 /*** Power Stats AIDL Implemenation *******************************************/
@@ -381,6 +414,95 @@
   }
   return true;
 }
+
+bool PowerStatsAidlDataProvider::GetPowerEntityStates(
+    PowerEntityState* entity_state,
+    size_t* size_of_arr) {
+  const size_t in_array_size = *size_of_arr;
+  *size_of_arr = 0;
+
+  aidl::IPowerStats* svc = MaybeGetService();
+  if (svc == nullptr) {
+    return false;
+  }
+
+  std::vector<aidl::PowerEntity> entities;
+  android::binder::Status status = svc->getPowerEntityInfo(&entities);
+
+  if (!status.isOk()) {
+    if (status.transactionError() == android::DEAD_OBJECT) {
+      // Service has died.  Reset it to attempt to acquire a new one next time.
+      ResetService();
+    }
+    return false;
+  }
+
+  // Iterate through all entities.
+  for (const auto& entity : entities) {
+    if (*size_of_arr >= in_array_size) {
+      break;
+    }
+
+    // Iterate through all states for this entity.
+    for (const auto& state : entity.states) {
+      if (*size_of_arr >= in_array_size) {
+        break;
+      }
+      auto& cur = entity_state[(*size_of_arr)++];
+      cur.entity_id = entity.id;
+      strlcpy(cur.entity_name, entity.name.c_str(), sizeof(cur.entity_name));
+      cur.state_id = state.id;
+      strlcpy(cur.state_name, state.name.c_str(), sizeof(cur.state_name));
+    }
+  }
+  return true;
+}
+
+bool PowerStatsAidlDataProvider::GetPowerEntityStateResidency(
+    PowerEntityStateResidency* residency,
+    size_t* size_of_arr) {
+  const size_t in_array_size = *size_of_arr;
+  *size_of_arr = 0;
+
+  aidl::IPowerStats* svc = MaybeGetService();
+  if (svc == nullptr) {
+    return false;
+  }
+
+  std::vector<int> ids;
+  std::vector<aidl::StateResidencyResult> entities;
+  android::binder::Status status = svc->getStateResidency(ids, &entities);
+
+  if (!status.isOk()) {
+    if (status.transactionError() == android::DEAD_OBJECT) {
+      // Service has died.  Reset it to attempt to acquire a new one next time.
+      ResetService();
+    }
+    return false;
+  }
+
+  // Iterate through all entities.
+  for (const auto& entity : entities) {
+    if (*size_of_arr >= in_array_size) {
+      break;
+    }
+
+    // Iterate through all states for this entity.
+    for (const auto& stateResidencyData : entity.stateResidencyData) {
+      if (*size_of_arr >= in_array_size) {
+        break;
+      }
+      auto& cur = residency[(*size_of_arr)++];
+      cur.entity_id = entity.id;
+      cur.state_id = stateResidencyData.id;
+      cur.total_time_in_state_ms = stateResidencyData.totalTimeInStateMs;
+      cur.total_state_entry_count = stateResidencyData.totalStateEntryCount;
+      cur.last_entry_timestamp_ms = stateResidencyData.lastEntryTimestampMs;
+    }
+  }
+  return true;
+}
+
 /*** End of Power Stats AIDL Implemenation ************************************/
 
 }  // namespace android_internal
diff --git a/src/android_internal/power_stats.h b/src/android_internal/power_stats.h
index 3225d29..46c78ae 100644
--- a/src/android_internal/power_stats.h
+++ b/src/android_internal/power_stats.h
@@ -81,6 +81,21 @@
   int64_t energy_uws;
 };
 
+struct PowerEntityState {
+  int32_t entity_id;
+  int32_t state_id;
+  char entity_name[64];
+  char state_name[64];
+};
+
+struct PowerEntityStateResidency {
+  int32_t entity_id;
+  int32_t state_id;
+  uint64_t total_time_in_state_ms;
+  uint64_t total_state_entry_count;
+  uint64_t last_entry_timestamp_ms;
+};
+
 extern "C" {
 
 // These functions are not thread safe unless specified otherwise.
@@ -101,6 +116,13 @@
 bool __attribute__((visibility("default")))
 GetEnergyConsumed(EnergyEstimationBreakdown* breakdown, size_t* size_of_arr);
 
+bool __attribute__((visibility("default")))
+GetPowerEntityStates(PowerEntityState* state, size_t* size_of_arr);
+
+bool __attribute__((visibility("default")))
+GetPowerEntityStateResidency(PowerEntityStateResidency* residency,
+                             size_t* size_of_arr);
+
 }  // extern "C"
 
 }  // namespace android_internal
diff --git a/src/base/file_utils.cc b/src/base/file_utils.cc
index 66ab5f6..9f70ad7 100644
--- a/src/base/file_utils.cc
+++ b/src/base/file_utils.cc
@@ -346,34 +346,5 @@
   return filename.substr(ext_idx);
 }
 
-base::Optional<size_t> GetFileSize(const std::string& file_path) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  HANDLE file =
-      CreateFileA(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
-                  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
-  if (file == INVALID_HANDLE_VALUE) {
-    return nullopt;
-  }
-  LARGE_INTEGER file_size;
-  file_size.QuadPart = 0;
-  BOOL ok = GetFileSizeEx(file, &file_size);
-  CloseHandle(file);
-  if (!ok) {
-    return nullopt;
-  }
-  return static_cast<size_t>(file_size.QuadPart);
-#else
-  base::ScopedFile fd(base::OpenFile(file_path, O_RDONLY | O_CLOEXEC));
-  if (!fd) {
-    return nullopt;
-  }
-  struct stat buf {};
-  if (fstat(*fd, &buf) == -1) {
-    return nullopt;
-  }
-  return static_cast<size_t>(buf.st_size);
-#endif
-}
-
 }  // namespace base
 }  // namespace perfetto
diff --git a/src/base/periodic_task.cc b/src/base/periodic_task.cc
index fcbc9de..eaeba30 100644
--- a/src/base/periodic_task.cc
+++ b/src/base/periodic_task.cc
@@ -33,34 +33,49 @@
 namespace base {
 
 namespace {
-base::ScopedPlatformHandle CreateTimerFd(uint32_t period_ms) {
+
+uint32_t GetNextDelayMs(const TimeMillis& now_ms,
+                        const PeriodicTask::Args& args) {
+  if (args.one_shot)
+    return args.period_ms;
+
+  return args.period_ms -
+         static_cast<uint32_t>(now_ms.count() % args.period_ms);
+}
+
+ScopedPlatformHandle CreateTimerFd(const PeriodicTask::Args& args) {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
     (PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && __ANDROID_API__ >= 19)
-  base::ScopedPlatformHandle tfd(
+  ScopedPlatformHandle tfd(
       timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK));
-  // The initial phase, aligned on wall clock.
-  uint32_t phase_ms =
-      period_ms -
-      static_cast<uint32_t>(base::GetBootTimeNs().count() % period_ms);
+  uint32_t phase_ms = GetNextDelayMs(GetBootTimeMs(), args);
+
   struct itimerspec its {};
   // The "1 +" is to make sure that we never pass a zero it_value in the
   // unlikely case of phase_ms being 0. That would cause the timer to be
   // considered disarmed by timerfd_settime.
   its.it_value.tv_sec = static_cast<time_t>(phase_ms / 1000u);
   its.it_value.tv_nsec = 1 + static_cast<long>((phase_ms % 1000u) * 1000000u);
-  its.it_interval.tv_sec = static_cast<time_t>(period_ms / 1000u);
-  its.it_interval.tv_nsec = static_cast<long>((period_ms % 1000u) * 1000000u);
+  if (args.one_shot) {
+    its.it_interval.tv_sec = 0;
+    its.it_interval.tv_nsec = 0;
+  } else {
+    const uint32_t period_ms = args.period_ms;
+    its.it_interval.tv_sec = static_cast<time_t>(period_ms / 1000u);
+    its.it_interval.tv_nsec = static_cast<long>((period_ms % 1000u) * 1000000u);
+  }
   if (timerfd_settime(*tfd, 0, &its, nullptr) < 0)
-    return base::ScopedPlatformHandle();
+    return ScopedPlatformHandle();
   return tfd;
 #else
-  base::ignore_result(period_ms);
-  return base::ScopedPlatformHandle();
+  ignore_result(args);
+  return ScopedPlatformHandle();
 #endif
 }
+
 }  // namespace
 
-PeriodicTask::PeriodicTask(base::TaskRunner* task_runner)
+PeriodicTask::PeriodicTask(TaskRunner* task_runner)
     : task_runner_(task_runner), weak_ptr_factory_(this) {}
 
 PeriodicTask::~PeriodicTask() {
@@ -77,7 +92,7 @@
   }
   args_ = std::move(args);
   if (args_.use_suspend_aware_timer) {
-    timer_fd_ = CreateTimerFd(args_.period_ms);
+    timer_fd_ = CreateTimerFd(args_);
     if (timer_fd_) {
       auto weak_this = weak_ptr_factory_.GetWeakPtr();
       task_runner_->AddFileDescriptorWatch(
@@ -99,9 +114,7 @@
   PERFETTO_DCHECK_THREAD(thread_checker_);
   PERFETTO_DCHECK(args_.period_ms > 0);
   PERFETTO_DCHECK(!timer_fd_);
-  uint32_t delay_ms =
-      args_.period_ms -
-      static_cast<uint32_t>(base::GetWallTimeMs().count() % args_.period_ms);
+  uint32_t delay_ms = GetNextDelayMs(GetWallTimeMs(), args_);
   auto weak_this = weak_ptr_factory_.GetWeakPtr();
   task_runner_->PostDelayedTask(
       std::bind(PeriodicTask::RunTaskAndPostNext, weak_this, generation_),
@@ -112,7 +125,7 @@
 // This function can be called in two ways (both from the TaskRunner):
 // 1. When using a timerfd, this task is registered as a FD watch.
 // 2. When using PostDelayedTask, this is the task posted on the TaskRunner.
-void PeriodicTask::RunTaskAndPostNext(base::WeakPtr<PeriodicTask> thiz,
+void PeriodicTask::RunTaskAndPostNext(WeakPtr<PeriodicTask> thiz,
                                       uint32_t generation) {
   if (!thiz || !thiz->args_.task || generation != thiz->generation_)
     return;  // Destroyed or Reset() in the meanwhile.
@@ -126,7 +139,7 @@
     // just need to read() it.
     uint64_t ignored = 0;
     errno = 0;
-    auto rsize = base::Read(*thiz->timer_fd_, &ignored, sizeof(&ignored));
+    auto rsize = Read(*thiz->timer_fd_, &ignored, sizeof(&ignored));
     if (rsize != sizeof(uint64_t)) {
       if (errno == EAGAIN)
         return;  // A spurious wakeup. Rare, but can happen, just ignore.
@@ -135,15 +148,21 @@
     }
 #endif
   }
+
+  // Create a copy of the task to deal with either:
+  // 1. one_shot causing a Reset().
+  // 2. task() invoking internally Reset().
+  // That would cause a reset of the args_.task itself, which would invalidate
+  // the task bind state while we are invoking it.
+  auto task = thiz->args_.task;
+
   // The repetition of the if() is to deal with the ResetTimerFd() case above.
-  if (!thiz->timer_fd_) {
+  if (thiz->args_.one_shot) {
+    thiz->Reset();
+  } else if (!thiz->timer_fd_) {
     thiz->PostNextTask();
   }
-  // Create a copy of the task in the unlikely event that the task ends up
-  // up destroying the PeriodicTask object or calling Reset() on it. That would
-  // cause a reset of the args_.task itself, which would invalidate the task
-  // bind state while we are invoking it.
-  auto task = thiz->args_.task;
+
   task();
 }
 
diff --git a/src/base/periodic_task_unittest.cc b/src/base/periodic_task_unittest.cc
index 4919720..9fae406 100644
--- a/src/base/periodic_task_unittest.cc
+++ b/src/base/periodic_task_unittest.cc
@@ -25,6 +25,9 @@
 #include <unistd.h>
 #endif
 
+#include <chrono>
+#include <thread>
+
 namespace perfetto {
 namespace base {
 
@@ -49,6 +52,26 @@
   EXPECT_EQ(num_callbacks, 3u);
 }
 
+TEST(PeriodicTaskTest, OneShot) {
+  TestTaskRunner task_runner;
+  PeriodicTask pt(&task_runner);
+  uint32_t num_callbacks = 0;
+  auto quit_closure = task_runner.CreateCheckpoint("one_shot_done");
+
+  PeriodicTask::Args args;
+  args.use_suspend_aware_timer = true;
+  args.one_shot = true;
+  args.period_ms = 1;
+  args.task = [&] {
+    ASSERT_EQ(++num_callbacks, 1u);
+    quit_closure();
+  };
+  pt.Start(std::move(args));
+  std::this_thread::sleep_for(std::chrono::milliseconds(3));
+  task_runner.RunUntilCheckpoint("one_shot_done");
+  EXPECT_EQ(num_callbacks, 1u);
+}
+
 // Call Reset() from a callback, ensure no further calls are made.
 TEST(PeriodicTaskTest, ResetFromCallback) {
   TestTaskRunner task_runner;
diff --git a/src/base/threading/BUILD.gn b/src/base/threading/BUILD.gn
index ba709e7..b1e01e3 100644
--- a/src/base/threading/BUILD.gn
+++ b/src/base/threading/BUILD.gn
@@ -28,6 +28,8 @@
     "../../../gn:default_deps",
     "../../../gn:gtest_and_gmock",
   ]
-
-  sources = [ "thread_pool_unittest.cc" ]
+  sources = [
+    "channel_unittest.cc",
+    "thread_pool_unittest.cc",
+  ]
 }
diff --git a/src/base/threading/channel_unittest.cc b/src/base/threading/channel_unittest.cc
new file mode 100644
index 0000000..7acb355
--- /dev/null
+++ b/src/base/threading/channel_unittest.cc
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/ext/base/threading/channel.h"
+#include <array>
+#include <memory>
+
+#include "perfetto/base/platform_handle.h"
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/utils.h"
+#include "test/gtest_and_gmock.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#include <synchapi.h>
+#else
+#include <poll.h>
+#endif
+
+namespace perfetto {
+namespace base {
+namespace {
+
+using ReadResult = Channel<int>::ReadResult;
+using WriteResult = Channel<int>::WriteResult;
+
+bool IsReady(base::PlatformHandle fd) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  std::array<base::PlatformHandle, 1> poll_fds{fd};
+  DWORD ret =
+      WaitForMultipleObjects(static_cast<DWORD>(poll_fds.size()), &poll_fds[0],
+                             /*bWaitAll=*/false, 0);
+  PERFETTO_CHECK(ret == WAIT_TIMEOUT || ret == 0);
+  return ret == 0;
+#else
+  std::array<struct pollfd, 1> poll_fds;
+  poll_fds[0].fd = fd;
+  poll_fds[0].events = POLLIN | POLLHUP;
+  poll_fds[0].revents = 0;
+
+  int ret = PERFETTO_EINTR(
+      poll(&poll_fds[0], static_cast<nfds_t>(poll_fds.size()), 0));
+  PERFETTO_CHECK(ret == 0 || ret == 1);
+  return ret == 1;
+#endif
+}
+
+TEST(ChannelUnittest, SingleElementBuffer) {
+  Channel<int> channel(1);
+  ASSERT_TRUE(IsReady(channel.write_fd()));
+  ASSERT_FALSE(IsReady(channel.read_fd()));
+
+  ASSERT_EQ(channel.WriteNonBlocking(100), WriteResult(true, false));
+  ASSERT_EQ(channel.WriteNonBlocking(101), WriteResult(false, false));
+
+  ASSERT_FALSE(IsReady(channel.write_fd()));
+  ASSERT_TRUE(IsReady(channel.read_fd()));
+
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(100, false));
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, false));
+
+  ASSERT_TRUE(IsReady(channel.write_fd()));
+  ASSERT_FALSE(IsReady(channel.read_fd()));
+}
+
+TEST(ChannelUnittest, MultiElementBuffer) {
+  Channel<int> channel(2);
+  ASSERT_TRUE(IsReady(channel.write_fd()));
+  ASSERT_FALSE(IsReady(channel.read_fd()));
+
+  ASSERT_EQ(channel.WriteNonBlocking(100), WriteResult(true, false));
+  ASSERT_TRUE(IsReady(channel.write_fd()));
+  ASSERT_TRUE(IsReady(channel.read_fd()));
+
+  ASSERT_EQ(channel.WriteNonBlocking(101), WriteResult(true, false));
+  ASSERT_FALSE(IsReady(channel.write_fd()));
+  ASSERT_TRUE(IsReady(channel.read_fd()));
+
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(100, false));
+  ASSERT_TRUE(IsReady(channel.write_fd()));
+  ASSERT_TRUE(IsReady(channel.read_fd()));
+
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(101, false));
+  ASSERT_TRUE(IsReady(channel.write_fd()));
+  ASSERT_FALSE(IsReady(channel.read_fd()));
+
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, false));
+  ASSERT_TRUE(IsReady(channel.write_fd()));
+  ASSERT_FALSE(IsReady(channel.read_fd()));
+}
+
+TEST(ChannelUnittest, CloseEmptyChannel) {
+  Channel<int> channel(1);
+
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, false));
+  ASSERT_FALSE(IsReady(channel.read_fd()));
+
+  channel.Close();
+
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, true));
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, true));
+
+  ASSERT_TRUE(IsReady(channel.read_fd()));
+  ASSERT_TRUE(IsReady(channel.read_fd()));
+}
+
+TEST(ChannelUnittest, WriteDoesNotMoveIfFalse) {
+  Channel<std::unique_ptr<int>> channel(1);
+
+  std::unique_ptr<int> first(new int(100));
+  int* first_ptr = first.get();
+  ASSERT_EQ(channel.WriteNonBlocking(std::move(first)),
+            Channel<std::unique_ptr<int>>::WriteResult(true, false));
+  ASSERT_EQ(first.get(), nullptr);
+
+  std::unique_ptr<int> second(new int(101));
+  ASSERT_EQ(channel.WriteNonBlocking(std::move(second)),
+            Channel<std::unique_ptr<int>>::WriteResult(false, false));
+  ASSERT_NE(second.get(), nullptr);
+  ASSERT_EQ(*second, 101);
+
+  auto res = channel.ReadNonBlocking();
+  ASSERT_EQ(res.item->get(), first_ptr);
+}
+
+TEST(ChannelUnittest, ReadAfterClose) {
+  Channel<int> channel(1);
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, false));
+  ASSERT_EQ(channel.WriteNonBlocking(100), WriteResult(true, false));
+  channel.Close();
+
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(100, true));
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, true));
+}
+
+TEST(ChannelUnittest, WriteAfterClose) {
+  Channel<int> channel(1);
+  ASSERT_EQ(channel.WriteNonBlocking(100), WriteResult(true, false));
+  ASSERT_EQ(channel.WriteNonBlocking(101), WriteResult(false, false));
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(100, false));
+  channel.Close();
+
+  ASSERT_EQ(channel.WriteNonBlocking(101), WriteResult(false, true));
+}
+
+TEST(ChannelUnittest, EmptyClosedChannel) {
+  Channel<int> channel(1);
+  ASSERT_FALSE(IsReady(channel.read_fd()));
+  ASSERT_TRUE(IsReady(channel.write_fd()));
+  channel.Close();
+  ASSERT_TRUE(IsReady(channel.write_fd()));
+  ASSERT_TRUE(IsReady(channel.write_fd()));
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, true));
+  ASSERT_TRUE(IsReady(channel.write_fd()));
+  ASSERT_TRUE(IsReady(channel.read_fd()));
+}
+
+TEST(ChannelUnittest, FullClosedChannel) {
+  Channel<int> channel(1);
+  ASSERT_FALSE(IsReady(channel.read_fd()));
+  ASSERT_EQ(channel.WriteNonBlocking(100), WriteResult(true, false));
+  ASSERT_TRUE(IsReady(channel.read_fd()));
+  ASSERT_FALSE(IsReady(channel.write_fd()));
+  channel.Close();
+  ASSERT_TRUE(IsReady(channel.write_fd()));
+
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(100, true));
+  ASSERT_TRUE(IsReady(channel.write_fd()));
+  ASSERT_TRUE(IsReady(channel.read_fd()));
+}
+
+}  // namespace
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/perfetto_cmd/BUILD.gn b/src/perfetto_cmd/BUILD.gn
index d402e1f..70c47fc 100644
--- a/src/perfetto_cmd/BUILD.gn
+++ b/src/perfetto_cmd/BUILD.gn
@@ -55,6 +55,7 @@
     "../../protos/perfetto/common:cpp",
     "../../protos/perfetto/config:cpp",
     "../../protos/perfetto/config/ftrace:cpp",
+    "../../protos/perfetto/config/sys_stats:cpp",
     "../android_stats",
     "../base",
     "../base:version",
diff --git a/src/perfetto_cmd/config.cc b/src/perfetto_cmd/config.cc
index 56a8f06..1313dcf 100644
--- a/src/perfetto_cmd/config.cc
+++ b/src/perfetto_cmd/config.cc
@@ -24,6 +24,7 @@
 #include "perfetto/tracing/core/trace_config.h"
 
 #include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
+#include "protos/perfetto/config/sys_stats/sys_stats_config.gen.h"
 
 namespace perfetto {
 namespace {
@@ -132,6 +133,18 @@
       frame_timeline->mutable_config()->set_name(
           "android.surfaceflinger.frametimeline");
     }
+
+    // For the disk category, add the diskstat data source
+    // to figure out disk io statistics.
+    if (category == "disk") {
+      protos::gen::SysStatsConfig cfg;
+      cfg.set_diskstat_period_ms(1000);
+
+      auto* sys_stats_ds = config->add_data_sources();
+      sys_stats_ds->mutable_config()->set_name("linux.sys_stats");
+      sys_stats_ds->mutable_config()->set_sys_stats_config_raw(
+          cfg.SerializeAsString());
+    }
   }
 
   config->set_duration_ms(static_cast<unsigned int>(duration_ms));
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index 25bdfbd..be35ea9 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -202,10 +202,10 @@
 
 PerfettoCmd::~PerfettoCmd() {
   PERFETTO_DCHECK(g_perfetto_cmd == this);
+  g_perfetto_cmd = nullptr;
   if (ctrl_c_handler_installed_) {
     task_runner_.RemoveFileDescriptorWatch(ctrl_c_evt_.fd());
   }
-  g_perfetto_cmd = nullptr;
 }
 
 void PerfettoCmd::PrintUsage(const char* argv0) {
@@ -881,11 +881,14 @@
     LogTriggerEvents(PerfettoTriggerAtom::kCmdTrigger, triggers_to_activate_);
 
     bool finished_with_success = false;
+    auto weak_this = weak_factory_.GetWeakPtr();
     TriggerProducer producer(
         &task_runner_,
-        [this, &finished_with_success](bool success) {
+        [weak_this, &finished_with_success](bool success) {
+          if (!weak_this)
+            return;
           finished_with_success = success;
-          task_runner_.Quit();
+          weak_this->task_runner_.Quit();
         },
         &triggers_to_activate_);
     task_runner_.Run();
@@ -1037,6 +1040,10 @@
   }
 
   // Failsafe mechanism to avoid waiting indefinitely if the service hangs.
+  // Note: when using prefer_suspend_clock_for_duration the actual duration
+  // might be < expected_duration_ms_ measured in in wall time. But this is fine
+  // because the resulting timeout will be conservative (it will be accurate
+  // if the device never suspends, and will be more lax if it does).
   if (expected_duration_ms_) {
     uint32_t trace_timeout = expected_duration_ms_ + 60000 +
                              trace_config_->flush_timeout_ms() +
@@ -1208,13 +1215,18 @@
       return;
     g_perfetto_cmd->SignalCtrlC();
   });
-  task_runner_.AddFileDescriptorWatch(ctrl_c_evt_.fd(), [this] {
+  auto weak_this = weak_factory_.GetWeakPtr();
+  task_runner_.AddFileDescriptorWatch(ctrl_c_evt_.fd(), [weak_this] {
+    if (!weak_this)
+      return;
     PERFETTO_LOG("SIGINT/SIGTERM received: disabling tracing.");
-    ctrl_c_evt_.Clear();
-    consumer_endpoint_->Flush(0, [this](bool flush_success) {
+    weak_this->ctrl_c_evt_.Clear();
+    weak_this->consumer_endpoint_->Flush(0, [weak_this](bool flush_success) {
+      if (!weak_this)
+        return;
       if (!flush_success)
         PERFETTO_ELOG("Final flush unsuccessful.");
-      consumer_endpoint_->DisableTracing();
+      weak_this->consumer_endpoint_->DisableTracing();
     });
   });
 }
@@ -1249,10 +1261,13 @@
   PERFETTO_DCHECK(trace_config_->write_into_file());
 
   if (stop_trace_once_attached_) {
-    consumer_endpoint_->Flush(0, [this](bool flush_success) {
+    auto weak_this = weak_factory_.GetWeakPtr();
+    consumer_endpoint_->Flush(0, [weak_this](bool flush_success) {
+      if (!weak_this)
+        return;
       if (!flush_success)
         PERFETTO_ELOG("Final flush unsuccessful.");
-      consumer_endpoint_->DisableTracing();
+      weak_this->consumer_endpoint_->DisableTracing();
     });
   }
 }
diff --git a/src/perfetto_cmd/perfetto_cmd.h b/src/perfetto_cmd/perfetto_cmd.h
index b64159e..842ddcc 100644
--- a/src/perfetto_cmd/perfetto_cmd.h
+++ b/src/perfetto_cmd/perfetto_cmd.h
@@ -29,6 +29,7 @@
 #include "perfetto/ext/base/pipe.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/unix_task_runner.h"
+#include "perfetto/ext/base/weak_ptr.h"
 #include "perfetto/ext/tracing/core/consumer.h"
 #include "perfetto/ext/tracing/ipc/consumer_ipc_client.h"
 #include "src/android_stats/perfetto_atoms.h"
@@ -162,6 +163,7 @@
   // How long we expect to trace for or 0 if the trace is indefinite.
   uint32_t expected_duration_ms_ = 0;
   bool trace_data_timeout_armed_ = false;
+  base::WeakPtrFactory<PerfettoCmd> weak_factory_{this};
 };
 
 }  // namespace perfetto
diff --git a/src/profiling/common/producer_support.cc b/src/profiling/common/producer_support.cc
index 8ab0b42..a160149 100644
--- a/src/profiling/common/producer_support.cc
+++ b/src/profiling/common/producer_support.cc
@@ -110,10 +110,25 @@
     return true;
   }
 
+  bool trusted_initiator = ds_config.session_initiator() ==
+                           DataSourceConfig::SESSION_INITIATOR_TRUSTED_SYSTEM;
+
   uint64_t uid_without_profile = uid % kAidUserOffset;
   uint64_t uid_for_lookup = 0;
-  if (uid_without_profile >= kAidAppStart &&
-      uid_without_profile <= kAidAppEnd) {
+  if (uid_without_profile < kAidAppStart) {
+    // Platform processes are considered profileable by the platform itself.
+    // This includes platform UIDs from other profiles, e.g. "u10_system".
+    // It's possible that this is an app (e.g. com.android.settings runs as
+    // AID_SYSTEM), but we will skip checking packages.list for the profileable
+    // manifest flags, as running under a platform UID is considered sufficient.
+    // Minor consequence: shell cannot profile platform apps, even if their
+    // manifest flags opt into profiling from shell. Resolving this would
+    // require definitively disambiguating native processes from apps if both
+    // can run as the same platform UID.
+    return trusted_initiator;
+
+  } else if (uid_without_profile >= kAidAppStart &&
+             uid_without_profile <= kAidAppEnd) {
     // normal app
     uid_for_lookup = uid_without_profile;
 
@@ -133,8 +148,6 @@
     // even be the package in which the service was defined).
     // TODO(rsavitski): find a way for the platform to tell native services
     // about isolated<->app relations.
-    bool trusted_initiator = ds_config.session_initiator() ==
-                             DataSourceConfig::SESSION_INITIATOR_TRUSTED_SYSTEM;
     return trusted_initiator &&
            AllPackagesProfileableByTrustedInitiator(packages_list_path);
 
diff --git a/src/profiling/symbolizer/BUILD.gn b/src/profiling/symbolizer/BUILD.gn
index 94feb3e..c81dbb1 100644
--- a/src/profiling/symbolizer/BUILD.gn
+++ b/src/profiling/symbolizer/BUILD.gn
@@ -24,6 +24,9 @@
     "breakpad_symbolizer.cc",
     "breakpad_symbolizer.h",
     "elf.h",
+    "filesystem.h",
+    "filesystem_posix.cc",
+    "filesystem_windows.cc",
     "local_symbolizer.cc",
     "local_symbolizer.h",
     "scoped_read_mmap.h",
diff --git a/src/profiling/symbolizer/filesystem.h b/src/profiling/symbolizer/filesystem.h
new file mode 100644
index 0000000..d9cf87c
--- /dev/null
+++ b/src/profiling/symbolizer/filesystem.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_PROFILING_SYMBOLIZER_FILESYSTEM_H_
+#define SRC_PROFILING_SYMBOLIZER_FILESYSTEM_H_
+
+#include "src/profiling/symbolizer/local_symbolizer.h"
+
+namespace perfetto {
+namespace profiling {
+
+using FileCallback = std::function<void(const char*, size_t)>;
+size_t GetFileSize(const std::string& file_path);
+bool WalkDirectories(std::vector<std::string> dirs, FileCallback fn);
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_SYMBOLIZER_FILESYSTEM_H_
diff --git a/src/profiling/symbolizer/filesystem_posix.cc b/src/profiling/symbolizer/filesystem_posix.cc
new file mode 100644
index 0000000..cc983e2
--- /dev/null
+++ b/src/profiling/symbolizer/filesystem_posix.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/profiling/symbolizer/filesystem.h"
+
+#include "perfetto/base/build_config.h"
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
+#include <fts.h>
+#include <sys/stat.h>
+#endif
+
+#include <string>
+
+#include "perfetto/ext/base/file_utils.h"
+
+namespace perfetto {
+namespace profiling {
+#if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
+bool WalkDirectories(std::vector<std::string> dirs, FileCallback fn) {
+  std::vector<char*> dir_cstrs;
+  dir_cstrs.reserve(dirs.size());
+  for (std::string& dir : dirs)
+    dir_cstrs.emplace_back(&dir[0]);
+  dir_cstrs.push_back(nullptr);
+  base::ScopedResource<FTS*, fts_close, nullptr> fts(
+      fts_open(&dir_cstrs[0], FTS_LOGICAL | FTS_NOCHDIR, nullptr));
+  if (!fts) {
+    PERFETTO_PLOG("fts_open");
+    return false;
+  }
+  FTSENT* ent;
+  while ((ent = fts_read(*fts))) {
+    if (ent->fts_info & FTS_F)
+      fn(ent->fts_path, static_cast<size_t>(ent->fts_statp->st_size));
+  }
+  return true;
+}
+
+size_t GetFileSize(const std::string& file_path) {
+  base::ScopedFile fd(base::OpenFile(file_path, O_RDONLY | O_CLOEXEC));
+  if (!fd) {
+    PERFETTO_PLOG("Failed to get file size %s", file_path.c_str());
+    return 0;
+  }
+  struct stat buf;
+  if (fstat(*fd, &buf) == -1) {
+    return 0;
+  }
+  return static_cast<size_t>(buf.st_size);
+}
+#else
+bool WalkDirectories(std::vector<std::string>, FileCallback) {
+  return false;
+}
+size_t GetFileSize(const std::string&) {
+  return 0;
+}
+#endif
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
diff --git a/src/profiling/symbolizer/filesystem_windows.cc b/src/profiling/symbolizer/filesystem_windows.cc
new file mode 100644
index 0000000..04cd685
--- /dev/null
+++ b/src/profiling/symbolizer/filesystem_windows.cc
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/profiling/symbolizer/filesystem.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+#include <Windows.h>
+
+namespace perfetto {
+namespace profiling {
+
+bool WalkDirectories(std::vector<std::string> dirs, FileCallback fn) {
+  std::vector<std::string> sub_dirs;
+  for (const std::string& dir : dirs) {
+    WIN32_FIND_DATAA file;
+    HANDLE fh = FindFirstFileA((dir + "\\*").c_str(), &file);
+    if (fh != INVALID_HANDLE_VALUE) {
+      do {
+        std::string file_path = dir + "\\" + file.cFileName;
+        if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+          if (strcmp(file.cFileName, ".") != 0 &&
+              strcmp(file.cFileName, "..") != 0) {
+            sub_dirs.push_back(file_path);
+          }
+        } else {
+          ULARGE_INTEGER size;
+          size.HighPart = file.nFileSizeHigh;
+          size.LowPart = file.nFileSizeLow;
+          fn(file_path.c_str(), size.QuadPart);
+        }
+      } while (FindNextFileA(fh, &file));
+    }
+    FindClose(fh);
+  }
+  if (!sub_dirs.empty()) {
+    WalkDirectories(sub_dirs, fn);
+  }
+  return true;
+}
+
+size_t GetFileSize(const std::string& file_path) {
+  HANDLE file =
+      CreateFileA(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
+                  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+  if (file == INVALID_HANDLE_VALUE) {
+    PERFETTO_PLOG("Failed to get file size %s", file_path.c_str());
+    return 0;
+  }
+  LARGE_INTEGER file_size;
+  file_size.QuadPart = 0;
+  if (!GetFileSizeEx(file, &file_size)) {
+    PERFETTO_PLOG("Failed to get file size %s", file_path.c_str());
+  }
+  CloseHandle(file);
+  return static_cast<size_t>(file_size.QuadPart);
+}
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
diff --git a/src/profiling/symbolizer/local_symbolizer.cc b/src/profiling/symbolizer/local_symbolizer.cc
index e37cee7..ce11a40 100644
--- a/src/profiling/symbolizer/local_symbolizer.cc
+++ b/src/profiling/symbolizer/local_symbolizer.cc
@@ -32,6 +32,7 @@
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "src/profiling/symbolizer/elf.h"
+#include "src/profiling/symbolizer/filesystem.h"
 #include "src/profiling/symbolizer/scoped_read_mmap.h"
 
 namespace perfetto {
@@ -216,36 +217,31 @@
   uint64_t load_bias;
 };
 
-base::Optional<BuildIdAndLoadBias> GetBuildIdAndLoadBias(
-    const std::string& fname) {
-  base::Optional<size_t> size = base::GetFileSize(fname);
-  if (!size.has_value()) {
-    PERFETTO_PLOG("Failed to get file size %s", fname.c_str());
-    return base::nullopt;
-  }
+base::Optional<BuildIdAndLoadBias> GetBuildIdAndLoadBias(const char* fname,
+                                                         size_t size) {
   static_assert(EI_CLASS > EI_MAG3, "mem[EI_MAG?] accesses are in range.");
-  if (*size <= EI_CLASS)
+  if (size <= EI_CLASS)
     return base::nullopt;
-  ScopedReadMmap map(fname.c_str(), *size);
+  ScopedReadMmap map(fname, size);
   if (!map.IsValid()) {
     PERFETTO_PLOG("mmap");
     return base::nullopt;
   }
   char* mem = static_cast<char*>(*map);
 
-  if (!IsElf(mem, *size))
+  if (!IsElf(mem, size))
     return base::nullopt;
 
   base::Optional<std::string> build_id;
   base::Optional<uint64_t> load_bias;
   switch (mem[EI_CLASS]) {
     case ELFCLASS32:
-      build_id = GetBuildId<Elf32>(mem, *size);
-      load_bias = GetLoadBias<Elf32>(mem, *size);
+      build_id = GetBuildId<Elf32>(mem, size);
+      load_bias = GetLoadBias<Elf32>(mem, size);
       break;
     case ELFCLASS64:
-      build_id = GetBuildId<Elf64>(mem, *size);
-      load_bias = GetLoadBias<Elf64>(mem, *size);
+      build_id = GetBuildId<Elf64>(mem, size);
+      load_bias = GetLoadBias<Elf64>(mem, size);
       break;
     default:
       return base::nullopt;
@@ -256,48 +252,35 @@
   return base::nullopt;
 }
 
-bool StartsWithElfMagic(const std::string& fname) {
-  base::ScopedFile fd(base::OpenFile(fname, O_RDONLY));
-  char magic[EI_MAG3 + 1];
-  if (!fd) {
-    PERFETTO_PLOG("Failed to open %s", fname.c_str());
-    return false;
-  }
-  ssize_t rd = base::Read(*fd, &magic, sizeof(magic));
-  if (rd != sizeof(magic)) {
-    PERFETTO_PLOG("Failed to read %s", fname.c_str());
-    return false;
-  }
-  if (!IsElf(magic, static_cast<size_t>(rd))) {
-    PERFETTO_DLOG("%s not an ELF.", fname.c_str());
-    return false;
-  }
-  return true;
-}
-
 std::map<std::string, FoundBinary> BuildIdIndex(std::vector<std::string> dirs) {
   std::map<std::string, FoundBinary> result;
-  for (const std::string& dir : dirs) {
-    std::vector<std::string> files;
-    base::Status status = base::ListFilesRecursive(dir, files);
-    if (!status.ok()) {
-      PERFETTO_PLOG("Failed to list directory %s", dir.c_str());
-      continue;
-    }
-    for (const std::string& basename : files) {
-      std::string fname = dir + "/" + basename;
-      if (!StartsWithElfMagic(fname)) {
-        continue;
+  WalkDirectories(std::move(dirs), [&result](const char* fname, size_t size) {
+    char magic[EI_MAG3 + 1];
+    // Scope file access. On windows OpenFile opens an exclusive lock.
+    // This lock needs to be released before mapping the file.
+    {
+      base::ScopedFile fd(base::OpenFile(fname, O_RDONLY));
+      if (!fd) {
+        PERFETTO_PLOG("Failed to open %s", fname);
+        return;
       }
-      base::Optional<BuildIdAndLoadBias> build_id_and_load_bias =
-          GetBuildIdAndLoadBias(fname);
-      if (build_id_and_load_bias) {
-        result.emplace(build_id_and_load_bias->build_id,
-                       FoundBinary{fname, build_id_and_load_bias->load_bias});
+      ssize_t rd = base::Read(*fd, &magic, sizeof(magic));
+      if (rd != sizeof(magic)) {
+        PERFETTO_PLOG("Failed to read %s", fname);
+        return;
+      }
+      if (!IsElf(magic, static_cast<size_t>(rd))) {
+        PERFETTO_DLOG("%s not an ELF.", fname);
+        return;
       }
     }
-  }
-
+    base::Optional<BuildIdAndLoadBias> build_id_and_load_bias =
+        GetBuildIdAndLoadBias(fname, size);
+    if (build_id_and_load_bias) {
+      result.emplace(build_id_and_load_bias->build_id,
+                     FoundBinary{fname, build_id_and_load_bias->load_bias});
+    }
+  });
   return result;
 }
 
@@ -368,8 +351,15 @@
   if (!base::FileExists(symbol_file)) {
     return base::nullopt;
   }
+  // Openfile opens the file with an exclusive lock on windows.
+  size_t size = GetFileSize(symbol_file);
+
+  if (size == 0) {
+    return base::nullopt;
+  }
+
   base::Optional<BuildIdAndLoadBias> build_id_and_load_bias =
-      GetBuildIdAndLoadBias(symbol_file);
+      GetBuildIdAndLoadBias(symbol_file.c_str(), size);
   if (!build_id_and_load_bias)
     return base::nullopt;
   if (build_id_and_load_bias->build_id != build_id) {
diff --git a/src/profiling/symbolizer/local_symbolizer_unittest.cc b/src/profiling/symbolizer/local_symbolizer_unittest.cc
index cb0c11d..919e3ff 100644
--- a/src/profiling/symbolizer/local_symbolizer_unittest.cc
+++ b/src/profiling/symbolizer/local_symbolizer_unittest.cc
@@ -28,6 +28,12 @@
 #include "src/profiling/symbolizer/local_symbolizer.h"
 #include "src/profiling/symbolizer/subprocess.h"
 
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <unistd.h>
+#endif
+
 namespace perfetto {
 namespace profiling {
 namespace {
@@ -140,7 +146,13 @@
   return std::string(reinterpret_cast<const char*>(&e), sizeof e);
 }
 
-TEST(LocalBinaryIndexerTest, SimpleTree) {
+#if defined(MEMORY_SANITIZER)
+// fts_read() causes some error under msan.
+#define NOMSAN_SimpleTree DISABLED_SimpleTree
+#else
+#define NOMSAN_SimpleTree SimpleTree
+#endif
+TEST(LocalBinaryIndexerTest, NOMSAN_SimpleTree) {
   base::TmpDirTree tmp;
   tmp.AddDir("dir1");
   tmp.AddFile("dir1/elf1", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
@@ -154,14 +166,91 @@
   base::Optional<FoundBinary> bin1 =
       indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
   ASSERT_TRUE(bin1.has_value());
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  EXPECT_EQ(bin1.value().file_name, tmp.path() + "/dir1\\elf1");
+#else
   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/dir1/elf1");
+#endif
+  base::Optional<FoundBinary> bin2 =
+      indexer.FindBinary("", "BBBBBBBBBBBBBBBBBBBB");
+  ASSERT_TRUE(bin2.has_value());
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  EXPECT_EQ(bin2.value().file_name, tmp.path() + "/dir2\\elf1");
+#else
+  EXPECT_EQ(bin2.value().file_name, tmp.path() + "/dir2/elf1");
+#endif
+}
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+
+#if defined(MEMORY_SANITIZER)
+// fts_read() causes some error under msan.
+#define NOMSAN_Symlinks DISABLED_Symlinks
+#else
+#define NOMSAN_Symlinks Symlinks
+#endif
+TEST(LocalBinaryIndexerTest, NOMSAN_Symlinks) {
+  base::TmpDirTree tmp;
+  tmp.AddDir("real");
+  tmp.AddFile("real/elf1", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
+  tmp.AddDir("real/dir1");
+  tmp.AddFile("real/dir1/elf2", CreateElfWithBuildId("BBBBBBBBBBBBBBBBBBBB"));
+  tmp.AddFile("real/dir1/elf3", CreateElfWithBuildId("CCCCCCCCCCCCCCCCCCCC"));
+  tmp.AddDir("sym");
+  symlink(tmp.AbsolutePath("real/elf1").c_str(),
+          tmp.AbsolutePath("sym/elf1").c_str());
+  tmp.TrackFile("sym/elf1");
+  symlink(tmp.AbsolutePath("real/dir1").c_str(),
+          tmp.AbsolutePath("sym/dir1").c_str());
+  tmp.TrackFile("sym/dir1");
+
+  LocalBinaryIndexer indexer({tmp.AbsolutePath("sym")});
+
+  base::Optional<FoundBinary> bin1 =
+      indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
+  ASSERT_TRUE(bin1.has_value());
+  EXPECT_EQ(bin1.value().file_name, tmp.AbsolutePath("sym/elf1"));
 
   base::Optional<FoundBinary> bin2 =
       indexer.FindBinary("", "BBBBBBBBBBBBBBBBBBBB");
   ASSERT_TRUE(bin2.has_value());
-  EXPECT_EQ(bin2.value().file_name, tmp.path() + "/dir2/elf1");
+  EXPECT_EQ(bin2.value().file_name, tmp.AbsolutePath("sym/dir1/elf2"));
+
+  base::Optional<FoundBinary> bin3 =
+      indexer.FindBinary("", "CCCCCCCCCCCCCCCCCCCC");
+  ASSERT_TRUE(bin3.has_value());
+  EXPECT_EQ(bin3.value().file_name, tmp.AbsolutePath("sym/dir1/elf3"));
 }
 
+#if defined(MEMORY_SANITIZER)
+// fts_read() causes some error under msan.
+#define NOMSAN_RecursiveSymlinks DISABLED_RecursiveSymlinks
+#else
+#define NOMSAN_RecursiveSymlinks RecursiveSymlinks
+#endif
+TEST(LocalBinaryIndexerTest, NOMSAN_RecursiveSymlinks) {
+  base::TmpDirTree tmp;
+  tmp.AddDir("main");
+  tmp.AddFile("main/elf1", CreateElfWithBuildId("AAAAAAAAAAAAAAAAAAAA"));
+  tmp.AddDir("main/dir1");
+  symlink(tmp.AbsolutePath("main").c_str(),
+          tmp.AbsolutePath("main/dir1/sym").c_str());
+  tmp.TrackFile("main/dir1/sym");
+
+  LocalBinaryIndexer indexer({tmp.AbsolutePath("main")});
+
+  base::Optional<FoundBinary> bin1 =
+      indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
+  ASSERT_TRUE(bin1.has_value());
+  EXPECT_EQ(bin1.value().file_name, tmp.AbsolutePath("main/elf1"));
+}
+
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||
+        // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) ||
+        // PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+
 TEST(LocalBinaryFinderTest, AbsolutePath) {
   base::TmpDirTree tmp;
   tmp.AddDir("root");
diff --git a/src/protozero/BUILD.gn b/src/protozero/BUILD.gn
index dcb85d3..d553aac 100644
--- a/src/protozero/BUILD.gn
+++ b/src/protozero/BUILD.gn
@@ -31,6 +31,7 @@
   ]
   sources = [
     "field.cc",
+    "gen_field_helpers.cc",
     "message.cc",
     "message_arena.cc",
     "message_handle.cc",
diff --git a/src/protozero/gen_field_helpers.cc b/src/protozero/gen_field_helpers.cc
new file mode 100644
index 0000000..85a41f7
--- /dev/null
+++ b/src/protozero/gen_field_helpers.cc
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/protozero/gen_field_helpers.h"
+
+namespace protozero {
+namespace internal {
+namespace gen_helpers {
+
+void DeserializeString(const protozero::Field& field, std::string* dst) {
+  field.get(dst);
+}
+
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+                                        uint64_t>(const protozero::Field& field,
+                                                  std::vector<uint64_t>* dst);
+
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+                                        int64_t>(const protozero::Field& field,
+                                                 std::vector<int64_t>* dst);
+
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+                                        uint32_t>(const protozero::Field& field,
+                                                  std::vector<uint32_t>* dst);
+
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+                                        int32_t>(const protozero::Field& field,
+                                                 std::vector<int32_t>* dst);
+
+void SerializeTinyVarInt(uint32_t field_id, bool value, Message* msg) {
+  msg->AppendTinyVarInt(field_id, value);
+}
+
+template void SerializeExtendedVarInt<uint64_t>(uint32_t field_id,
+                                                uint64_t value,
+                                                Message* msg);
+
+template void SerializeExtendedVarInt<uint32_t>(uint32_t field_id,
+                                                uint32_t value,
+                                                Message* msg);
+
+template void SerializeFixed<double>(uint32_t field_id,
+                                     double value,
+                                     Message* msg);
+
+template void SerializeFixed<float>(uint32_t field_id,
+                                    float value,
+                                    Message* msg);
+
+template void SerializeFixed<uint64_t>(uint32_t field_id,
+                                       uint64_t value,
+                                       Message* msg);
+
+template void SerializeFixed<int64_t>(uint32_t field_id,
+                                      int64_t value,
+                                      Message* msg);
+
+template void SerializeFixed<uint32_t>(uint32_t field_id,
+                                       uint32_t value,
+                                       Message* msg);
+
+template void SerializeFixed<int32_t>(uint32_t field_id,
+                                      int32_t value,
+                                      Message* msg);
+
+void SerializeString(uint32_t field_id,
+                     const std::string& value,
+                     Message* msg) {
+  msg->AppendString(field_id, value);
+}
+
+void SerializeUnknownFields(const std::string& unknown_fields, Message* msg) {
+  msg->AppendRawProtoBytes(unknown_fields.data(), unknown_fields.size());
+}
+
+MessageSerializer::MessageSerializer() = default;
+
+MessageSerializer::~MessageSerializer() = default;
+
+std::vector<uint8_t> MessageSerializer::SerializeAsArray() {
+  return msg_.SerializeAsArray();
+}
+
+std::string MessageSerializer::SerializeAsString() {
+  return msg_.SerializeAsString();
+}
+
+}  // namespace gen_helpers
+}  // namespace internal
+}  // namespace protozero
diff --git a/src/protozero/protoc_plugin/cppgen_plugin.cc b/src/protozero/protoc_plugin/cppgen_plugin.cc
index a963910..eadc23a 100644
--- a/src/protozero/protoc_plugin/cppgen_plugin.cc
+++ b/src/protozero/protoc_plugin/cppgen_plugin.cc
@@ -46,6 +46,7 @@
 using perfetto::base::StripSuffix;
 using perfetto::base::ToUpper;
 
+static constexpr auto TYPE_STRING = FieldDescriptor::TYPE_STRING;
 static constexpr auto TYPE_MESSAGE = FieldDescriptor::TYPE_MESSAGE;
 static constexpr auto TYPE_SINT32 = FieldDescriptor::TYPE_SINT32;
 static constexpr auto TYPE_SINT64 = FieldDescriptor::TYPE_SINT64;
@@ -147,6 +148,7 @@
   h_printer.Print("#include \"perfetto/protozero/copyable_ptr.h\"\n");
   h_printer.Print("#include \"perfetto/base/export.h\"\n\n");
 
+  cc_printer.Print("#include \"perfetto/protozero/gen_field_helpers.h\"\n");
   cc_printer.Print("#include \"perfetto/protozero/message.h\"\n");
   cc_printer.Print(
       "#include \"perfetto/protozero/packed_repeated_fields.h\"\n");
@@ -388,26 +390,26 @@
     const FieldDescriptor* field) const {
   switch (field->type()) {
     case FieldDescriptor::TYPE_BOOL:
-      return "AppendTinyVarInt";
+      return "::protozero::internal::gen_helpers::SerializeTinyVarInt";
     case FieldDescriptor::TYPE_INT32:
     case FieldDescriptor::TYPE_INT64:
     case FieldDescriptor::TYPE_UINT32:
     case FieldDescriptor::TYPE_UINT64:
     case FieldDescriptor::TYPE_ENUM:
-      return "AppendVarInt";
+      return "::protozero::internal::gen_helpers::SerializeVarInt";
     case FieldDescriptor::TYPE_SINT32:
     case FieldDescriptor::TYPE_SINT64:
-      return "AppendSignedVarInt";
+      return "::protozero::internal::gen_helpers::SerializeSignedVarInt";
     case FieldDescriptor::TYPE_FIXED32:
     case FieldDescriptor::TYPE_FIXED64:
     case FieldDescriptor::TYPE_SFIXED32:
     case FieldDescriptor::TYPE_SFIXED64:
     case FieldDescriptor::TYPE_FLOAT:
     case FieldDescriptor::TYPE_DOUBLE:
-      return "AppendFixed";
+      return "::protozero::internal::gen_helpers::SerializeFixed";
     case FieldDescriptor::TYPE_STRING:
     case FieldDescriptor::TYPE_BYTES:
-      return "AppendString";
+      return "::protozero::internal::gen_helpers::SerializeString";
     case FieldDescriptor::TYPE_GROUP:
     case FieldDescriptor::TYPE_MESSAGE:
       abort();
@@ -755,7 +757,10 @@
              "n", field->lowercase_name());
     p->Indent();
     if (field->options().lazy()) {
-      p->Print("$n$_ = field.as_std_string();\n", "n", field->lowercase_name());
+      p->Print(
+          "::protozero::internal::gen_helpers::DeserializeString(field, "
+          "&$n$_);\n",
+          "n", field->lowercase_name());
     } else {
       std::string statement;
       if (field->type() == TYPE_MESSAGE) {
@@ -764,6 +769,10 @@
         if (field->type() == TYPE_SINT32 || field->type() == TYPE_SINT64) {
           // sint32/64 fields are special and need to be zig-zag-decoded.
           statement = "field.get_signed(&$rval$);\n";
+        } else if (field->type() == TYPE_STRING) {
+          statement =
+              "::protozero::internal::gen_helpers::DeserializeString(field, "
+              "&$rval$);\n";
         } else {
           statement = "field.get(&$rval$);\n";
         }
@@ -774,10 +783,12 @@
           PERFETTO_FATAL("packed signed (zigzag) fields are not supported");
         }
         p->Print(
-            "for (::protozero::PackedRepeatedFieldIterator<$w$, $c$> "
-            "rep(field.data(), field.size(), &packed_error); rep; ++rep) {\n",
-            "w", GetPackedWireType(field), "c", GetCppType(field, false));
-        p->Print("  $n$_.emplace_back(*rep);\n", "n", field->lowercase_name());
+            "if "
+            "(!::protozero::internal::gen_helpers::DeserializePackedRepeated"
+            "<$w$, $c$>(field, &$n$_)) {\n",
+            "w", GetPackedWireType(field), "c", GetCppType(field, false), "n",
+            field->lowercase_name());
+        p->Print("  packed_error = true;");
         p->Print("}\n");
       } else if (field->is_repeated()) {
         p->Print("$n$_.emplace_back();\n", "n", field->lowercase_name());
@@ -807,7 +818,7 @@
   // Generate the SerializeAsString() method definition.
   p->Print("std::string $f$::SerializeAsString() const {\n", "f", full_name);
   p->Indent();
-  p->Print("::protozero::HeapBuffered<::protozero::Message> msg;\n");
+  p->Print("::protozero::internal::gen_helpers::MessageSerializer msg;\n");
   p->Print("Serialize(msg.get());\n");
   p->Print("return msg.SerializeAsString();\n");
   p->Outdent();
@@ -817,7 +828,7 @@
   p->Print("std::vector<uint8_t> $f$::SerializeAsArray() const {\n", "f",
            full_name);
   p->Indent();
-  p->Print("::protozero::HeapBuffered<::protozero::Message> msg;\n");
+  p->Print("::protozero::internal::gen_helpers::MessageSerializer msg;\n");
   p->Print("Serialize(msg.get());\n");
   p->Print("return msg.SerializeAsArray();\n");
   p->Outdent();
@@ -863,7 +874,7 @@
                  "msg->BeginNestedMessage<::protozero::Message>($id$));\n");
       } else {
         args["setter"] = GetProtozeroSetter(field);
-        p->Print(args, "msg->$setter$($id$, $rvalue$);\n");
+        p->Print(args, "$setter$($id$, $rvalue$, msg);\n");
       }
       p->Outdent();
       p->Print("}\n");
@@ -872,8 +883,8 @@
     p->Print("\n");
   }  // for (field)
   p->Print(
-      "msg->AppendRawProtoBytes(unknown_fields_.data(), "
-      "unknown_fields_.size());\n");
+      "protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_"
+      ", msg);\n");
   p->Outdent();
   p->Print("}\n\n");
 }
diff --git a/src/protozero/test/cppgen_conformance_unittest.cc b/src/protozero/test/cppgen_conformance_unittest.cc
index 933c74f..5ac43aa 100644
--- a/src/protozero/test/cppgen_conformance_unittest.cc
+++ b/src/protozero/test/cppgen_conformance_unittest.cc
@@ -62,7 +62,8 @@
   msg->set_big_enum(decltype(msg->big_enum())::BEGIN);
 
   msg->set_field_string("FizzBuzz");
-  msg->set_field_bytes(reinterpret_cast<const uint8_t*>("\x11\x00\xBE\xEF"), 4);
+  msg->set_field_bytes(reinterpret_cast<const uint8_t*>("\x11\x00\xBE\xEF"),
+                       4u);
   msg->add_repeated_int32(1);
   msg->add_repeated_int32(-1);
   msg->add_repeated_int32(100);
@@ -100,7 +101,7 @@
   pbgold::EveryField gold_msg;
   gold_msg.ParseFromString(serialized);
   CheckTestingFields(gold_msg);
-  EXPECT_EQ(serialized.size(), static_cast<size_t>(gold_msg.ByteSize()));
+  EXPECT_EQ(serialized.size(), static_cast<size_t>(gold_msg.ByteSizeLong()));
 }
 
 TEST(ProtoCppConformanceTest, GoldEncode_GenDecode) {
diff --git a/src/protozero/test/protozero_conformance_unittest.cc b/src/protozero/test/protozero_conformance_unittest.cc
index bdb15fa..7a73b69 100644
--- a/src/protozero/test/protozero_conformance_unittest.cc
+++ b/src/protozero/test/protozero_conformance_unittest.cc
@@ -94,7 +94,7 @@
   EXPECT_EQ(-1, gold_msg.repeated_int32(1));
   EXPECT_EQ(100, gold_msg.repeated_int32(2));
   EXPECT_EQ(2000000, gold_msg.repeated_int32(3));
-  EXPECT_EQ(serialized.size(), static_cast<size_t>(gold_msg.ByteSize()));
+  EXPECT_EQ(serialized.size(), static_cast<size_t>(gold_msg.ByteSizeLong()));
 }
 
 TEST(ProtoZeroConformanceTest, NestedMessages) {
diff --git a/src/shared_lib/BUILD.gn b/src/shared_lib/BUILD.gn
index 62bd06c..7879162 100644
--- a/src/shared_lib/BUILD.gn
+++ b/src/shared_lib/BUILD.gn
@@ -12,17 +12,21 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import("../../gn/perfetto_component.gni")
-
-perfetto_component("protozero") {
+shared_library("libperfetto_c") {
   deps = [
     "../../gn:default_deps",
-    "../../include/perfetto/base",
     "../../include/perfetto/protozero",
-    "../../include/perfetto/public:protozero",
+    "../tracing:client_api",
+    "../tracing:platform_impl",
   ]
   sources = [
+    "data_source.cc",
+    "heap_buffer.cc",
+    "producer.cc",
     "stream_writer.cc",
     "stream_writer.h",
+    "tracing_session.cc",
   ]
+  public_deps = [ "../../include/perfetto/public" ]
+  defines = [ "PERFETTO_SHLIB_SDK_IMPLEMENTATION" ]
 }
diff --git a/src/shared_lib/data_source.cc b/src/shared_lib/data_source.cc
new file mode 100644
index 0000000..dfab70a
--- /dev/null
+++ b/src/shared_lib/data_source.cc
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/public/abi/data_source_abi.h"
+
+#include <bitset>
+
+#include "perfetto/tracing/data_source.h"
+#include "perfetto/tracing/internal/basic_types.h"
+#include "protos/perfetto/common/data_source_descriptor.gen.h"
+#include "protos/perfetto/config/data_source_config.gen.h"
+#include "src/shared_lib/stream_writer.h"
+
+namespace {
+
+using ::perfetto::internal::DataSourceInstanceThreadLocalState;
+using ::perfetto::internal::DataSourceThreadLocalState;
+using ::perfetto::internal::DataSourceType;
+
+thread_local DataSourceThreadLocalState*
+    g_tls_cache[perfetto::internal::kMaxDataSources];
+
+}  // namespace
+
+// Implementation of a shared library data source type (there's one of these per
+// type, not per instance).
+//
+// Returned to the C side when invoking PerfettoDsCreateImpl(). The C side only
+// has an opaque pointer to this.
+struct PerfettoDsImpl {
+  // Instance lifecycle callbacks.
+  PerfettoDsOnSetupCb on_setup_cb = nullptr;
+  PerfettoDsOnStartCb on_start_cb = nullptr;
+  PerfettoDsOnStopCb on_stop_cb = nullptr;
+
+  // These are called to create/delete custom thread-local instance state.
+  PerfettoDsOnCreateCustomState on_create_tls_cb = nullptr;
+  PerfettoDsOnDeleteCustomState on_delete_tls_cb = nullptr;
+
+  // These are called to create/delete custom thread-local instance incremental
+  // state.
+  PerfettoDsOnCreateCustomState on_create_incr_cb = nullptr;
+  PerfettoDsOnDeleteCustomState on_delete_incr_cb = nullptr;
+
+  // Passed to all the callbacks as the `user_arg` param.
+  void* cb_user_arg;
+
+  DataSourceType cpp_type;
+  std::atomic<bool> enabled{false};
+  std::mutex mu;
+  // Guarded by mu
+  std::bitset<perfetto::internal::kMaxDataSourceInstances> enabled_instances;
+
+  bool IsRegistered() {
+    return cpp_type.static_state()->index !=
+           perfetto::internal::kMaxDataSources;
+  }
+};
+
+namespace {
+
+// Represents a global data source instance (there can be more than one of these
+// for a single data source type).
+class ShlibDataSource : public perfetto::DataSourceBase {
+ public:
+  explicit ShlibDataSource(PerfettoDsImpl* type) : type_(*type) {}
+
+  void OnSetup(const SetupArgs& args) override {
+    if (type_.on_setup_cb) {
+      std::vector<uint8_t> serialized_config = args.config->SerializeAsArray();
+      inst_ctx_ = type_.on_setup_cb(
+          args.internal_instance_index, serialized_config.data(),
+          serialized_config.size(), type_.cb_user_arg);
+    }
+    std::lock_guard<std::mutex> lock(type_.mu);
+    const bool was_enabled = type_.enabled_instances.any();
+    type_.enabled_instances.set(args.internal_instance_index);
+    if (!was_enabled && type_.enabled_instances.any()) {
+      type_.enabled.store(true, std::memory_order_release);
+    }
+  }
+
+  void OnStart(const StartArgs& args) override {
+    if (type_.on_start_cb) {
+      type_.on_start_cb(args.internal_instance_index, type_.cb_user_arg,
+                        inst_ctx_);
+    }
+  }
+
+  void OnStop(const StopArgs& args) override {
+    if (type_.on_stop_cb) {
+      type_.on_stop_cb(
+          args.internal_instance_index, type_.cb_user_arg, inst_ctx_,
+          const_cast<PerfettoDsOnStopArgs*>(
+              reinterpret_cast<const PerfettoDsOnStopArgs*>(&args)));
+    }
+
+    std::lock_guard<std::mutex> lock(type_.mu);
+    type_.enabled_instances.reset(args.internal_instance_index);
+    if (type_.enabled_instances.none()) {
+      type_.enabled.store(false, std::memory_order_release);
+    }
+  }
+
+  const PerfettoDsImpl& type() const { return type_; }
+
+  void* inst_ctx() const { return inst_ctx_; }
+
+ private:
+  PerfettoDsImpl& type_;
+  void* inst_ctx_ = nullptr;
+};
+
+struct DataSourceTraits {
+  static DataSourceThreadLocalState* GetDataSourceTLS(
+      perfetto::internal::DataSourceStaticState* static_state,
+      perfetto::internal::TracingTLS* root_tls) {
+    auto* ds_tls = &root_tls->data_sources_tls[static_state->index];
+    // The per-type TLS is either zero-initialized or must have been
+    // initialized for this specific data source type.
+    PERFETTO_DCHECK(!ds_tls->static_state ||
+                    ds_tls->static_state->index == static_state->index);
+    return ds_tls;
+  }
+};
+
+struct TracePointTraits {
+  using TracePointData = DataSourceType*;
+  static std::atomic<uint32_t>* GetActiveInstances(TracePointData s) {
+    return s->valid_instances();
+  }
+};
+
+DataSourceInstanceThreadLocalState::ObjectWithDeleter CreateShlibTls(
+    DataSourceInstanceThreadLocalState* tls_inst,
+    uint32_t inst_idx,
+    void* ctx) {
+  auto* ds_impl = reinterpret_cast<PerfettoDsImpl*>(ctx);
+
+  void* custom_state = ds_impl->on_create_tls_cb(
+      inst_idx, reinterpret_cast<PerfettoDsTracerImpl*>(tls_inst),
+      ds_impl->cb_user_arg);
+  return DataSourceInstanceThreadLocalState::ObjectWithDeleter(
+      custom_state, ds_impl->on_delete_tls_cb);
+}
+
+DataSourceInstanceThreadLocalState::ObjectWithDeleter
+CreateShlibIncrementalState(DataSourceInstanceThreadLocalState* tls_inst,
+                            uint32_t inst_idx,
+                            void* ctx) {
+  auto* ds_impl = reinterpret_cast<PerfettoDsImpl*>(ctx);
+
+  void* custom_state = ds_impl->on_create_incr_cb(
+      inst_idx, reinterpret_cast<PerfettoDsTracerImpl*>(tls_inst),
+      ds_impl->cb_user_arg);
+  return DataSourceInstanceThreadLocalState::ObjectWithDeleter(
+      custom_state, ds_impl->on_delete_incr_cb);
+}
+
+}  // namespace
+
+// Exposed through data_source_abi.h
+std::atomic<bool> perfetto_atomic_false{false};
+
+struct PerfettoDsImpl* PerfettoDsImplCreate() {
+  return new PerfettoDsImpl();
+}
+
+void PerfettoDsSetOnSetupCallback(struct PerfettoDsImpl* ds_impl,
+                                  PerfettoDsOnSetupCb cb) {
+  PERFETTO_CHECK(!ds_impl->IsRegistered());
+  ds_impl->on_setup_cb = cb;
+}
+
+void PerfettoDsSetOnStartCallback(struct PerfettoDsImpl* ds_impl,
+                                  PerfettoDsOnStartCb cb) {
+  PERFETTO_CHECK(!ds_impl->IsRegistered());
+  ds_impl->on_start_cb = cb;
+}
+
+void PerfettoDsSetOnStopCallback(struct PerfettoDsImpl* ds_impl,
+                                 PerfettoDsOnStopCb cb) {
+  PERFETTO_CHECK(!ds_impl->IsRegistered());
+  ds_impl->on_stop_cb = cb;
+}
+
+void PerfettoDsSetOnCreateTls(struct PerfettoDsImpl* ds_impl,
+                              PerfettoDsOnCreateCustomState cb) {
+  PERFETTO_CHECK(!ds_impl->IsRegistered());
+  ds_impl->on_create_tls_cb = cb;
+}
+
+void PerfettoDsSetOnDeleteTls(struct PerfettoDsImpl* ds_impl,
+                              PerfettoDsOnDeleteCustomState cb) {
+  PERFETTO_CHECK(!ds_impl->IsRegistered());
+  ds_impl->on_delete_tls_cb = cb;
+}
+
+void PerfettoDsSetOnCreateIncr(struct PerfettoDsImpl* ds_impl,
+                               PerfettoDsOnCreateCustomState cb) {
+  PERFETTO_CHECK(!ds_impl->IsRegistered());
+  ds_impl->on_create_incr_cb = cb;
+}
+
+void PerfettoDsSetOnDeleteIncr(struct PerfettoDsImpl* ds_impl,
+                               PerfettoDsOnDeleteCustomState cb) {
+  PERFETTO_CHECK(!ds_impl->IsRegistered());
+  ds_impl->on_delete_incr_cb = cb;
+}
+
+void PerfettoDsSetCbUserArg(struct PerfettoDsImpl* ds_impl, void* user_arg) {
+  PERFETTO_CHECK(!ds_impl->IsRegistered());
+  ds_impl->cb_user_arg = user_arg;
+}
+
+bool PerfettoDsImplRegister(struct PerfettoDsImpl* ds_impl,
+                            PERFETTO_ATOMIC(bool) * *enabled_ptr,
+                            const void* descriptor,
+                            size_t descriptor_size) {
+  perfetto::DataSourceDescriptor dsd;
+  dsd.ParseFromArray(descriptor, descriptor_size);
+
+  std::unique_ptr<PerfettoDsImpl> data_source_type(ds_impl);
+
+  auto factory = [ds_impl]() {
+    return std::unique_ptr<perfetto::DataSourceBase>(
+        new ShlibDataSource(ds_impl));
+  };
+
+  DataSourceType::CreateCustomTlsFn create_custom_tls_fn = nullptr;
+  DataSourceType::CreateIncrementalStateFn create_incremental_state_fn =
+      nullptr;
+  void* cb_ctx = nullptr;
+  if (data_source_type->on_create_incr_cb &&
+      data_source_type->on_delete_incr_cb) {
+    create_incremental_state_fn = CreateShlibIncrementalState;
+    cb_ctx = data_source_type.get();
+  }
+  if (data_source_type->on_create_tls_cb &&
+      data_source_type->on_delete_tls_cb) {
+    create_custom_tls_fn = CreateShlibTls;
+    cb_ctx = data_source_type.get();
+  }
+
+  perfetto::internal::DataSourceParams params;
+  params.supports_multiple_instances = true;
+  params.requires_callbacks_under_lock = false;
+  bool success = data_source_type->cpp_type.Register(
+      dsd, factory, params, perfetto::BufferExhaustedPolicy::kDrop,
+      create_custom_tls_fn, create_incremental_state_fn, cb_ctx);
+  if (!success) {
+    return false;
+  }
+  *enabled_ptr = &data_source_type->enabled;
+  perfetto::base::ignore_result(data_source_type.release());
+  return true;
+}
+
+void PerfettoDsImplUpdateDescriptor(struct PerfettoDsImpl* ds_impl,
+                                    const void* descriptor,
+                                    size_t descriptor_size) {
+  perfetto::DataSourceDescriptor dsd;
+  dsd.ParseFromArray(descriptor, descriptor_size);
+
+  ds_impl->cpp_type.UpdateDescriptor(dsd);
+}
+
+PerfettoDsAsyncStopper* PerfettoDsOnStopArgsPostpone(
+    PerfettoDsOnStopArgs* args) {
+  auto* cb = new std::function<void()>();
+  *cb = reinterpret_cast<const ShlibDataSource::StopArgs*>(args)
+            ->HandleStopAsynchronously();
+  return reinterpret_cast<PerfettoDsAsyncStopper*>(cb);
+}
+
+void PerfettoDsStopDone(PerfettoDsAsyncStopper* stopper) {
+  auto* cb = reinterpret_cast<std::function<void()>*>(stopper);
+  (*cb)();
+  delete cb;
+}
+
+void* PerfettoDsImplGetInstanceLocked(struct PerfettoDsImpl* ds_impl,
+                                      PerfettoDsInstanceIndex idx) {
+  auto* internal_state = ds_impl->cpp_type.static_state()->TryGet(idx);
+  if (!internal_state) {
+    return nullptr;
+  }
+  std::unique_lock<std::recursive_mutex> lock(internal_state->lock);
+  auto* data_source =
+      static_cast<ShlibDataSource*>(internal_state->data_source.get());
+  if (&data_source->type() != ds_impl) {
+    // The data source instance has been destroyed and recreated as a different
+    // type while we where tracing.
+    return nullptr;
+  }
+  void* inst_ctx = data_source->inst_ctx();
+  if (inst_ctx != nullptr) {
+    lock.release();
+  }
+  return inst_ctx;
+}
+
+void PerfettoDsImplReleaseInstanceLocked(struct PerfettoDsImpl* ds_impl,
+                                         PerfettoDsInstanceIndex idx) {
+  auto* internal_state = ds_impl->cpp_type.static_state()->TryGet(idx);
+  PERFETTO_CHECK(internal_state);
+  internal_state->lock.unlock();
+}
+
+void* PerfettoDsImplGetCustomTls(struct PerfettoDsImpl*,
+                                 struct PerfettoDsTracerImpl* tracer,
+                                 PerfettoDsInstanceIndex) {
+  auto* tls_inst =
+      reinterpret_cast<DataSourceInstanceThreadLocalState*>(tracer);
+
+  PERFETTO_DCHECK(tls_inst->data_source_custom_tls);
+  return tls_inst->data_source_custom_tls.get();
+}
+
+void* PerfettoDsImplGetIncrementalState(struct PerfettoDsImpl* ds_impl,
+                                        struct PerfettoDsTracerImpl* tracer,
+                                        PerfettoDsInstanceIndex idx) {
+  auto* tls_inst =
+      reinterpret_cast<DataSourceInstanceThreadLocalState*>(tracer);
+
+  return ds_impl->cpp_type.GetIncrementalState(tls_inst, idx);
+}
+
+struct PerfettoDsImplTracerIterator PerfettoDsImplTraceIterateBegin(
+    struct PerfettoDsImpl* ds_impl) {
+  DataSourceThreadLocalState** tls =
+      &g_tls_cache[ds_impl->cpp_type.static_state()->index];
+
+  struct PerfettoDsImplTracerIterator ret = {0, nullptr, nullptr};
+  uint32_t cached_instances =
+      ds_impl->cpp_type.valid_instances()->load(std::memory_order_relaxed);
+  if (!cached_instances) {
+    return ret;
+  }
+  bool res =
+      ds_impl->cpp_type.TracePrologue<DataSourceTraits, TracePointTraits>(
+          tls, &cached_instances, &ds_impl->cpp_type);
+  if (!res) {
+    return ret;
+  }
+  DataSourceType::InstancesIterator it =
+      ds_impl->cpp_type.BeginIteration<TracePointTraits>(cached_instances, *tls,
+                                                         &ds_impl->cpp_type);
+  ret.inst_id = it.i;
+  (*tls)->root_tls->cached_instances = it.cached_instances;
+  ret.tracer = reinterpret_cast<struct PerfettoDsTracerImpl*>(it.instance);
+  if (!ret.tracer) {
+    ds_impl->cpp_type.TraceEpilogue(*tls);
+  }
+
+  ret.tls = reinterpret_cast<struct PerfettoDsTlsImpl*>(*tls);
+  return ret;
+}
+
+void PerfettoDsImplTraceIterateNext(
+    struct PerfettoDsImpl* ds_impl,
+    struct PerfettoDsImplTracerIterator* iterator) {
+  auto* tls = reinterpret_cast<DataSourceThreadLocalState*>(iterator->tls);
+
+  DataSourceType::InstancesIterator it;
+  it.i = iterator->inst_id;
+  it.cached_instances = tls->root_tls->cached_instances;
+  it.instance =
+      reinterpret_cast<DataSourceInstanceThreadLocalState*>(iterator->tracer);
+
+  ds_impl->cpp_type.NextIteration<TracePointTraits>(&it, tls,
+                                                    &ds_impl->cpp_type);
+
+  iterator->inst_id = it.i;
+  tls->root_tls->cached_instances = it.cached_instances;
+  iterator->tracer =
+      reinterpret_cast<struct PerfettoDsTracerImpl*>(it.instance);
+
+  if (!iterator->tracer) {
+    ds_impl->cpp_type.TraceEpilogue(tls);
+  }
+}
+
+void PerfettoDsImplTraceIterateBreak(
+    struct PerfettoDsImpl* ds_impl,
+    struct PerfettoDsImplTracerIterator* iterator) {
+  auto* tls = reinterpret_cast<DataSourceThreadLocalState*>(iterator->tls);
+
+  ds_impl->cpp_type.TraceEpilogue(tls);
+}
+
+struct PerfettoStreamWriter PerfettoDsTracerImplPacketBegin(
+    struct PerfettoDsTracerImpl* tracer) {
+  auto* tls_inst =
+      reinterpret_cast<DataSourceInstanceThreadLocalState*>(tracer);
+
+  auto message_handle = tls_inst->trace_writer->NewTracePacket();
+  struct PerfettoStreamWriter ret;
+  protozero::ScatteredStreamWriter* sw = message_handle.TakeStreamWriter();
+  ret.impl = reinterpret_cast<PerfettoStreamWriterImpl*>(sw);
+  perfetto::UpdateStreamWriter(*sw, &ret);
+  return ret;
+}
+
+void PerfettoDsTracerImplPacketEnd(struct PerfettoDsTracerImpl* tracer,
+                                   struct PerfettoStreamWriter* w) {
+  auto* tls_inst =
+      reinterpret_cast<DataSourceInstanceThreadLocalState*>(tracer);
+  auto* sw = reinterpret_cast<protozero::ScatteredStreamWriter*>(w->impl);
+
+  sw->set_write_ptr(w->write_ptr);
+  tls_inst->trace_writer->FinishTracePacket();
+}
+
+void PerfettoDsTracerImplFlush(struct PerfettoDsTracerImpl* tracer,
+                               PerfettoDsTracerOnFlushCb cb,
+                               void* user_arg) {
+  auto* tls_inst =
+      reinterpret_cast<DataSourceInstanceThreadLocalState*>(tracer);
+
+  std::function<void()> fn;
+  if (cb != nullptr) {
+    fn = [user_arg, cb]() { cb(user_arg); };
+  }
+  tls_inst->trace_writer->Flush(fn);
+}
diff --git a/src/shared_lib/heap_buffer.cc b/src/shared_lib/heap_buffer.cc
new file mode 100644
index 0000000..358dd9f
--- /dev/null
+++ b/src/shared_lib/heap_buffer.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/public/abi/heap_buffer.h"
+
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "src/shared_lib/stream_writer.h"
+
+struct PerfettoHeapBuffer* PerfettoHeapBufferCreate(
+    struct PerfettoStreamWriter* w) {
+  auto* shb = new protozero::ScatteredHeapBuffer(4096, 4096);
+  auto* sw = new protozero::ScatteredStreamWriter(shb);
+  shb->set_writer(sw);
+
+  w->impl = reinterpret_cast<PerfettoStreamWriterImpl*>(sw);
+  perfetto::UpdateStreamWriter(*sw, w);
+  return reinterpret_cast<PerfettoHeapBuffer*>(shb);
+}
+
+void PerfettoHeapBufferCopyInto(struct PerfettoHeapBuffer* buf,
+                                struct PerfettoStreamWriter* w,
+                                void* dst,
+                                size_t size) {
+  auto* shb = reinterpret_cast<protozero::ScatteredHeapBuffer*>(buf);
+  auto* sw = reinterpret_cast<protozero::ScatteredStreamWriter*>(w->impl);
+  sw->set_write_ptr(w->write_ptr);
+
+  uint8_t* dst_ptr = reinterpret_cast<uint8_t*>(dst);
+  for (const protozero::ScatteredHeapBuffer::Slice& slice : shb->GetSlices()) {
+    if (size == 0) {
+      break;
+    }
+    protozero::ContiguousMemoryRange used_range = slice.GetUsedRange();
+    size_t to_copy = std::min(size, used_range.size());
+    memcpy(dst_ptr, used_range.begin, to_copy);
+    dst_ptr += to_copy;
+    size -= to_copy;
+  }
+}
+
+void PerfettoHeapBufferDestroy(struct PerfettoHeapBuffer* buf,
+                               struct PerfettoStreamWriter* w) {
+  auto* shb = reinterpret_cast<protozero::ScatteredHeapBuffer*>(buf);
+  auto* sw = reinterpret_cast<protozero::ScatteredStreamWriter*>(w->impl);
+  delete sw;
+  delete shb;
+}
diff --git a/src/shared_lib/producer.cc b/src/shared_lib/producer.cc
new file mode 100644
index 0000000..04e001e
--- /dev/null
+++ b/src/shared_lib/producer.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/public/abi/producer.h"
+
+#include <atomic>
+#include <bitset>
+
+#include "perfetto/tracing/backend_type.h"
+#include "perfetto/tracing/tracing.h"
+
+void PerfettoProducerInProcessInit() {
+  perfetto::TracingInitArgs args;
+  args.backends = perfetto::kInProcessBackend;
+  perfetto::Tracing::Initialize(args);
+}
+
+void PerfettoProducerSystemInit() {
+  perfetto::TracingInitArgs args;
+  args.backends = perfetto::kSystemBackend;
+  perfetto::Tracing::Initialize(args);
+}
+
+void PerfettoProducerInProcessAndSystemInit() {
+  perfetto::TracingInitArgs args;
+  args.backends = perfetto::kInProcessBackend | perfetto::kSystemBackend;
+  perfetto::Tracing::Initialize(args);
+}
diff --git a/src/shared_lib/stream_writer.h b/src/shared_lib/stream_writer.h
index 19a0f37..8a08075 100644
--- a/src/shared_lib/stream_writer.h
+++ b/src/shared_lib/stream_writer.h
@@ -29,7 +29,7 @@
   w->begin = sw.cur_range().begin;
   w->end = sw.cur_range().end;
   w->write_ptr = sw.write_ptr();
-  w->written_previously = sw.written_previously();
+  w->written_previously = static_cast<size_t>(sw.written_previously());
 }
 
 }  // namespace perfetto
diff --git a/src/shared_lib/tracing_session.cc b/src/shared_lib/tracing_session.cc
new file mode 100644
index 0000000..313a88d
--- /dev/null
+++ b/src/shared_lib/tracing_session.cc
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/public/abi/tracing_session_abi.h"
+
+#include <condition_variable>
+#include <mutex>
+
+#include "perfetto/tracing/backend_type.h"
+#include "perfetto/tracing/tracing.h"
+#include "protos/perfetto/config/trace_config.gen.h"
+
+struct PerfettoTracingSessionImpl* PerfettoTracingSessionCreate(
+    PerfettoBackendTypes backend) {
+  uint32_t backend_type = 0;
+  if (backend & PERFETTO_BACKEND_IN_PROCESS) {
+    backend_type |= perfetto::kInProcessBackend;
+  }
+  if (backend & PERFETTO_BACKEND_SYSTEM) {
+    backend_type |= perfetto::kSystemBackend;
+  }
+  std::unique_ptr<perfetto::TracingSession> tracing_session =
+      perfetto::Tracing::NewTrace(
+          static_cast<perfetto::BackendType>(backend_type));
+  return reinterpret_cast<struct PerfettoTracingSessionImpl*>(
+      tracing_session.release());
+}
+
+void PerfettoTracingSessionSetup(struct PerfettoTracingSessionImpl* session,
+                                 void* cfg_begin,
+                                 size_t cfg_len) {
+  auto* ts = reinterpret_cast<perfetto::TracingSession*>(session);
+  perfetto::TraceConfig cfg;
+  cfg.ParseFromArray(cfg_begin, cfg_len);
+  ts->Setup(cfg);
+}
+
+void PerfettoTracingSessionStartAsync(
+    struct PerfettoTracingSessionImpl* session) {
+  auto* ts = reinterpret_cast<perfetto::TracingSession*>(session);
+  ts->Start();
+}
+
+void PerfettoTracingSessionStartBlocking(
+    struct PerfettoTracingSessionImpl* session) {
+  auto* ts = reinterpret_cast<perfetto::TracingSession*>(session);
+  ts->StartBlocking();
+}
+
+void PerfettoTracingSessionStopAsync(
+    struct PerfettoTracingSessionImpl* session) {
+  auto* ts = reinterpret_cast<perfetto::TracingSession*>(session);
+  ts->Stop();
+}
+
+void PerfettoTracingSessionStopBlocking(
+    struct PerfettoTracingSessionImpl* session) {
+  auto* ts = reinterpret_cast<perfetto::TracingSession*>(session);
+  ts->StopBlocking();
+}
+
+void PerfettoTracingSessionReadTraceBlocking(
+    struct PerfettoTracingSessionImpl* session,
+    PerfettoTracingSessionReadCb callback,
+    void* user_arg) {
+  auto* ts = reinterpret_cast<perfetto::TracingSession*>(session);
+
+  std::mutex mutex;
+  std::condition_variable cv;
+
+  bool all_read = false;
+
+  ts->ReadTrace([&mutex, &all_read, &cv, session, callback, user_arg](
+                    perfetto::TracingSession::ReadTraceCallbackArgs args) {
+    callback(session, static_cast<const void*>(args.data), args.size,
+             args.has_more, user_arg);
+    std::unique_lock<std::mutex> lock(mutex);
+    all_read = !args.has_more;
+    if (all_read)
+      cv.notify_one();
+  });
+
+  {
+    std::unique_lock<std::mutex> lock(mutex);
+    cv.wait(lock, [&all_read] { return all_read; });
+  }
+}
+
+void PerfettoTracingSessionDestroy(struct PerfettoTracingSessionImpl* session) {
+  auto* ts = reinterpret_cast<perfetto::TracingSession*>(session);
+  delete ts;
+}
diff --git a/src/trace_processor/importers/common/parser_types.h b/src/trace_processor/importers/common/parser_types.h
index 9d5976c..e6575f1 100644
--- a/src/trace_processor/importers/common/parser_types.h
+++ b/src/trace_processor/importers/common/parser_types.h
@@ -26,20 +26,24 @@
 namespace perfetto {
 namespace trace_processor {
 
-struct InlineSchedSwitch {
+struct alignas(8) InlineSchedSwitch {
   int64_t prev_state;
   int32_t next_pid;
   int32_t next_prio;
   StringPool::Id next_comm;
 };
 
-struct InlineSchedWaking {
+struct alignas(8) InlineSchedWaking {
   int32_t pid;
   int32_t target_cpu;
   int32_t prio;
   StringPool::Id comm;
 };
 
+struct alignas(8) JsonEvent {
+  std::string value;
+};
+
 struct TracePacketData {
   TraceBlobView packet;
   RefPtr<PacketSequenceStateGeneration> sequence_state;
@@ -53,7 +57,15 @@
   explicit TrackEventData(TracePacketData tpd)
       : trace_packet_data(std::move(tpd)) {}
 
-  static constexpr size_t kMaxNumExtraCounters = 8;
+  static constexpr uint8_t kMaxNumExtraCounters = 8;
+
+  uint8_t CountExtraCounterValues() const {
+    for (uint8_t i = 0; i < TrackEventData::kMaxNumExtraCounters; ++i) {
+      if (std::equal_to<double>()(extra_counter_values[i], 0))
+        return i;
+    }
+    return TrackEventData::kMaxNumExtraCounters;
+  }
 
   TracePacketData trace_packet_data;
   base::Optional<int64_t> thread_timestamp;
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index 6e57384..4f9f6fc 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -501,7 +501,7 @@
 
   // Compute atrace + ftrace setup errors. We do two things here:
   // 1. We add up all the errors and put the counter in the stats table (which
-  //    can hold only numerals). This will raise an orange flag in the UI.
+  //    can hold only numerals).
   // 2. We concatenate together all the errors in a string and put that in the
   //    medatata table.
   // Both will be reported in the 'Info & stats' page in the UI.
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc b/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
index e36da64..a4dc251 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
@@ -270,6 +270,11 @@
               procs->GetOrCreateProcess(static_cast<uint32_t>(tinfo.pid));
           std::string name_str =
               context_->storage->GetString(name).ToStdString();
+          uint64_t counter_id;
+          if (!cursor.ReadUint64(&counter_id)) {
+            context_->storage->IncrementStats(stats::fuchsia_invalid_event);
+            return;
+          }
           // Note: In the Fuchsia trace format, counter values are stored in the
           // arguments for the record, with the data series defined by both the
           // record name and the argument name. In Perfetto, counters only have
@@ -278,6 +283,7 @@
             std::string counter_name_str = name_str + ":";
             counter_name_str +=
                 context_->storage->GetString(arg.name).ToStdString();
+            counter_name_str += ":" + std::to_string(counter_id);
             bool is_valid_value = false;
             double counter_value = -1;
             switch (arg.value.Type()) {
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc b/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc
index d7879be..47de0f8 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc
@@ -80,7 +80,7 @@
     case ArgType::kString:
       return Variadic::String(string_);
     case ArgType::kPointer:
-      return Variadic::Integer(static_cast<int64_t>(pointer_));
+      return Variadic::Pointer(pointer_);
     case ArgType::kKoid:
       return Variadic::Integer(static_cast<int64_t>(koid_));
     case ArgType::kUnknown:
diff --git a/src/trace_processor/importers/proto/android_probes_module.cc b/src/trace_processor/importers/proto/android_probes_module.cc
index 17f57ee..58423f7 100644
--- a/src/trace_processor/importers/proto/android_probes_module.cc
+++ b/src/trace_processor/importers/proto/android_probes_module.cc
@@ -28,6 +28,7 @@
 #include "protos/perfetto/common/android_energy_consumer_descriptor.pbzero.h"
 #include "protos/perfetto/config/trace_config.pbzero.h"
 #include "protos/perfetto/trace/power/android_energy_estimation_breakdown.pbzero.h"
+#include "protos/perfetto/trace/power/android_entity_state_residency.pbzero.h"
 #include "protos/perfetto/trace/power/power_rails.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
@@ -88,6 +89,7 @@
   RegisterForField(TracePacket::kPowerRailsFieldNumber, context);
   RegisterForField(TracePacket::kAndroidEnergyEstimationBreakdownFieldNumber,
                    context);
+  RegisterForField(TracePacket::kEntityStateResidencyFieldNumber, context);
   RegisterForField(TracePacket::kAndroidLogFieldNumber, context);
   RegisterForField(TracePacket::kPackagesListFieldNumber, context);
   RegisterForField(TracePacket::kAndroidGameInterventionListFieldNumber,
@@ -110,6 +112,11 @@
   // handled at the tokenization phase.
   if (field_id == TracePacket::kAndroidEnergyEstimationBreakdownFieldNumber) {
     return ParseEnergyDescriptor(decoder.android_energy_estimation_breakdown());
+  } else if (field_id == TracePacket::kEntityStateResidencyFieldNumber) {
+    ParseEntityStateDescriptor(decoder.entity_state_residency());
+    // Ignore so that we get a go at parsing any actual residency data that
+    // should also be in the packet.
+    return ModuleResult::Ignored();
   }
 
   if (field_id != TracePacket::kPowerRailsFieldNumber) {
@@ -203,6 +210,9 @@
       parser_.ParseEnergyBreakdown(
           ts, decoder.android_energy_estimation_breakdown());
       return;
+    case TracePacket::kEntityStateResidencyFieldNumber:
+      parser_.ParseEntityStateResidency(ts, decoder.entity_state_residency());
+      return;
     case TracePacket::kAndroidLogFieldNumber:
       parser_.ParseAndroidLogPacket(decoder.android_log());
       return;
@@ -257,5 +267,27 @@
   return ModuleResult::Handled();
 }
 
+void AndroidProbesModule::ParseEntityStateDescriptor(
+    protozero::ConstBytes blob) {
+  protos::pbzero::EntityStateResidency::Decoder event(blob);
+  if (!event.has_power_entity_state())
+    return;
+
+  for (auto it = event.power_entity_state(); it; ++it) {
+    protos::pbzero::EntityStateResidency::PowerEntityState::Decoder
+        entity_state(*it);
+
+    if (!entity_state.has_entity_index() || !entity_state.has_state_index()) {
+      context_->storage->IncrementStats(stats::energy_descriptor_invalid);
+      continue;
+    }
+
+    AndroidProbesTracker::GetOrCreate(context_)->SetEntityStateDescriptor(
+        entity_state.entity_index(), entity_state.state_index(),
+        context_->storage->InternString(entity_state.entity_name()),
+        context_->storage->InternString(entity_state.state_name()));
+  }
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/android_probes_module.h b/src/trace_processor/importers/proto/android_probes_module.h
index cd0b40a..66c3c27 100644
--- a/src/trace_processor/importers/proto/android_probes_module.h
+++ b/src/trace_processor/importers/proto/android_probes_module.h
@@ -45,6 +45,7 @@
       const protos::pbzero::TraceConfig::Decoder& decoder) override;
 
   ModuleResult ParseEnergyDescriptor(protozero::ConstBytes blob);
+  void ParseEntityStateDescriptor(protozero::ConstBytes blob);
 
  private:
   AndroidProbesParser parser_;
diff --git a/src/trace_processor/importers/proto/android_probes_parser.cc b/src/trace_processor/importers/proto/android_probes_parser.cc
index d4018e3..351af4a 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.cc
+++ b/src/trace_processor/importers/proto/android_probes_parser.cc
@@ -40,6 +40,7 @@
 #include "protos/perfetto/trace/android/network_trace.pbzero.h"
 #include "protos/perfetto/trace/android/packages_list.pbzero.h"
 #include "protos/perfetto/trace/power/android_energy_estimation_breakdown.pbzero.h"
+#include "protos/perfetto/trace/power/android_entity_state_residency.pbzero.h"
 #include "protos/perfetto/trace/power/battery_counters.pbzero.h"
 #include "protos/perfetto/trace/power/power_rails.pbzero.h"
 #include "protos/perfetto/trace/ps/process_stats.pbzero.h"
@@ -110,7 +111,18 @@
         context_->track_tracker->InternGlobalCounterTrack(batt_charge_id);
     context_->event_tracker->PushCounter(
         ts, static_cast<double>(evt.charge_counter_uah()), track);
+  } else if (evt.has_energy_counter_uwh() && evt.has_voltage_uv()) {
+    // Calculate charge counter from energy counter and voltage.
+    TrackId track =
+        context_->track_tracker->InternGlobalCounterTrack(batt_charge_id);
+    auto energy = evt.energy_counter_uwh();
+    auto voltage = evt.voltage_uv();
+    if (voltage > 0) {
+      context_->event_tracker->PushCounter(
+          ts, static_cast<double>(energy * 1000000 / voltage), track);
+    }
   }
+
   if (evt.has_capacity_percent()) {
     TrackId track =
         context_->track_tracker->InternGlobalCounterTrack(batt_capacity_id);
@@ -208,6 +220,36 @@
   }
 }
 
+void AndroidProbesParser::ParseEntityStateResidency(int64_t ts,
+                                                    ConstBytes blob) {
+  protos::pbzero::EntityStateResidency::Decoder event(blob.data, blob.size);
+
+  if (!event.has_residency()) {
+    context_->storage->IncrementStats(stats::entity_state_residency_invalid);
+    return;
+  }
+
+  auto* tracker = AndroidProbesTracker::GetOrCreate(context_);
+
+  for (auto it = event.residency(); it; ++it) {
+    protos::pbzero::EntityStateResidency::StateResidency::Decoder residency(
+        *it);
+
+    auto entity_state = tracker->GetEntityStateDescriptor(
+        residency.entity_index(), residency.state_index());
+    if (!entity_state) {
+      context_->storage->IncrementStats(
+          stats::entity_state_residency_lookup_failed);
+      return;
+    }
+
+    TrackId track = context_->track_tracker->InternGlobalCounterTrack(
+        entity_state->overall_name);
+    context_->event_tracker->PushCounter(
+        ts, double(residency.total_time_in_state_ms()), track);
+  }
+}
+
 void AndroidProbesParser::ParseAndroidLogPacket(ConstBytes blob) {
   protos::pbzero::AndroidLogPacket::Decoder packet(blob.data, blob.size);
   for (auto it = packet.events(); it; ++it)
diff --git a/src/trace_processor/importers/proto/android_probes_parser.h b/src/trace_processor/importers/proto/android_probes_parser.h
index 420ffb8..75f019d 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.h
+++ b/src/trace_processor/importers/proto/android_probes_parser.h
@@ -36,6 +36,7 @@
   void ParseBatteryCounters(int64_t ts, ConstBytes);
   void ParsePowerRails(int64_t ts, ConstBytes);
   void ParseEnergyBreakdown(int64_t ts, ConstBytes);
+  void ParseEntityStateResidency(int64_t ts, ConstBytes);
   void ParseAndroidLogPacket(ConstBytes);
   void ParseAndroidLogEvent(ConstBytes);
   void ParseAndroidLogStats(ConstBytes);
diff --git a/src/trace_processor/importers/proto/android_probes_tracker.cc b/src/trace_processor/importers/proto/android_probes_tracker.cc
index 4235cf1..1c287c9 100644
--- a/src/trace_processor/importers/proto/android_probes_tracker.cc
+++ b/src/trace_processor/importers/proto/android_probes_tracker.cc
@@ -19,7 +19,8 @@
 namespace perfetto {
 namespace trace_processor {
 
-AndroidProbesTracker::AndroidProbesTracker(TraceStorage*) {}
+AndroidProbesTracker::AndroidProbesTracker(TraceStorage* storage)
+    : storage_(storage) {}
 
 AndroidProbesTracker::~AndroidProbesTracker() = default;
 
diff --git a/src/trace_processor/importers/proto/android_probes_tracker.h b/src/trace_processor/importers/proto/android_probes_tracker.h
index 1180872..e5e2469 100644
--- a/src/trace_processor/importers/proto/android_probes_tracker.h
+++ b/src/trace_processor/importers/proto/android_probes_tracker.h
@@ -20,6 +20,7 @@
 #include <set>
 
 #include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_view.h"
 
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/types/trace_processor_context.h"
@@ -41,6 +42,12 @@
     int32_t ordinal;
   };
 
+  struct EntityStateDescriptor {
+    StringId entity_name;
+    StringId state_name;
+    StringId overall_name;
+  };
+
   static AndroidProbesTracker* GetOrCreate(TraceProcessorContext* context) {
     if (!context->android_probes_tracker) {
       context->android_probes_tracker.reset(
@@ -98,10 +105,50 @@
         EnergyConsumerSpecs{name, type, ordinal};
   }
 
+  base::Optional<EntityStateDescriptor> GetEntityStateDescriptor(
+      int32_t entity_id,
+      int32_t state_id) {
+    uint64_t id = EntityStateKey(entity_id, state_id);
+    auto it = entity_state_descriptors_.find(id);
+    // Didn't receive the descriptor
+    if (it == entity_state_descriptors_.end()) {
+      return base::nullopt;
+    }
+    return it->second;
+  }
+
+  void SetEntityStateDescriptor(int32_t entity_id,
+                                int32_t state_id,
+                                StringId entity_name,
+                                StringId state_name) {
+    uint64_t id = EntityStateKey(entity_id, state_id);
+    auto it_descriptor = entity_state_descriptors_.find(id);
+
+    // Ignore repeated descriptors.
+    if (it_descriptor != entity_state_descriptors_.end())
+      return;
+
+    std::string overall_str =
+        "Entity residency: " + storage_->GetString(entity_name).ToStdString() +
+        " is " + storage_->GetString(state_name).ToStdString();
+
+    StringId overall = storage_->InternString(base::StringView(overall_str));
+
+    entity_state_descriptors_[id] =
+        EntityStateDescriptor{entity_name, state_name, overall};
+  }
+
  private:
+  TraceStorage* storage_;
   std::set<std::string> seen_packages_;
   std::vector<TrackId> power_rail_tracks_;
   std::unordered_map<int32_t, EnergyConsumerSpecs> energy_consumer_descriptors_;
+  std::unordered_map<uint64_t, EntityStateDescriptor> entity_state_descriptors_;
+
+  uint64_t EntityStateKey(int32_t entity_id, int32_t state_id) {
+    return (static_cast<uint64_t>(entity_id) << 32) |
+           static_cast<uint32_t>(state_id);
+  }
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/frame_timeline_event_parser.cc b/src/trace_processor/importers/proto/frame_timeline_event_parser.cc
index d03dc73..6a7faf3 100644
--- a/src/trace_processor/importers/proto/frame_timeline_event_parser.cc
+++ b/src/trace_processor/importers/proto/frame_timeline_event_parser.cc
@@ -544,8 +544,10 @@
 
   int64_t cookie = event.cookie();
   auto it = cookie_track_set_id_map_.find(cookie);
-  if (it == cookie_track_set_id_map_.end())
+  if (it == cookie_track_set_id_map_.end()) {
+    context_->storage->IncrementStats(stats::frame_timeline_unpaired_end_event);
     return;
+  }
   auto track_set_id = it->second;
   auto track_id = context_->async_track_set_tracker->End(track_set_id, cookie);
   context_->slice_tracker->End(timestamp, track_id);
diff --git a/src/trace_processor/importers/systrace/systrace_line.h b/src/trace_processor/importers/systrace/systrace_line.h
index 80ce931..d125d0e 100644
--- a/src/trace_processor/importers/systrace/systrace_line.h
+++ b/src/trace_processor/importers/systrace/systrace_line.h
@@ -23,7 +23,7 @@
 namespace perfetto {
 namespace trace_processor {
 
-struct SystraceLine {
+struct alignas(8) SystraceLine {
   int64_t ts;
   uint32_t pid;
   uint32_t cpu;
diff --git a/src/trace_processor/metrics/sql/android/BUILD.gn b/src/trace_processor/metrics/sql/android/BUILD.gn
index 833faa7..aff43fd 100644
--- a/src/trace_processor/metrics/sql/android/BUILD.gn
+++ b/src/trace_processor/metrics/sql/android/BUILD.gn
@@ -57,13 +57,13 @@
     "android_trusty_workqueues.sql",
     "composer_execution.sql",
     "composition_layers.sql",
+    "counter_span_view_merged.sql",
     "cpu_info.sql",
     "display_metrics.sql",
     "frame_missed.sql",
     "g2d.sql",
     "g2d_duration.sql",
     "global_counter_span_view.sql",
-    "global_counter_span_view_merged.sql",
     "gpu_counter_span_view.sql",
     "jank/cujs.sql",
     "jank/cujs_boundaries.sql",
diff --git a/src/trace_processor/metrics/sql/android/android_batt.sql b/src/trace_processor/metrics/sql/android/android_batt.sql
index c9aad7e..7dde099 100644
--- a/src/trace_processor/metrics/sql/android/android_batt.sql
+++ b/src/trace_processor/metrics/sql/android/android_batt.sql
@@ -64,18 +64,26 @@
   AND (slice.name = 'syscore_resume(0)' OR slice.name = 'timekeeping_freeze(0)')
   AND dur != -1;
 
-SELECT RUN_METRIC('android/global_counter_span_view_merged.sql',
+SELECT RUN_METRIC('android/counter_span_view_merged.sql',
   'table_name', 'screen_state',
   'counter_name', 'ScreenState');
 
-SELECT RUN_METRIC('android/process_counter_span_view.sql',
+SELECT RUN_METRIC('android/counter_span_view_merged.sql',
   'table_name', 'doze_light_state',
   'counter_name', 'DozeLightState');
 
-SELECT RUN_METRIC('android/process_counter_span_view.sql',
+SELECT RUN_METRIC('android/counter_span_view_merged.sql',
   'table_name', 'doze_deep_state',
   'counter_name', 'DozeDeepState');
 
+SELECT RUN_METRIC('android/counter_span_view_merged.sql',
+  'table_name', 'battery_status',
+  'counter_name', 'BatteryStatus');
+
+SELECT RUN_METRIC('android/counter_span_view_merged.sql',
+  'table_name', 'plug_type',
+  'counter_name', 'PlugType');
+
 DROP TABLE IF EXISTS screen_state_span_with_suspend;
 CREATE VIRTUAL TABLE screen_state_span_with_suspend
 USING span_join(screen_state_span, suspend_slice_);
@@ -134,7 +142,36 @@
        END AS slice_name,
        'Doze deep state' AS track_name,
        'slice' AS track_type
-FROM doze_deep_state_span;
+FROM doze_deep_state_span
+UNION ALL
+SELECT ts,
+       dur,
+       CASE battery_status_val
+       -- 0 and 1 are both unknown
+       WHEN 2 THEN 'Charging'
+       WHEN 3 THEN 'Discharging'
+       -- special case when charger is present but battery isn't charging
+       WHEN 4 THEN 'Not charging'
+       WHEN 5 THEN 'Full'
+       ELSE 'unknown'
+       END AS slice_name,
+       'Charging state' AS track_name,
+       'slice' AS track_type
+FROM battery_status_span
+UNION ALL
+SELECT ts,
+       dur,
+       CASE plug_type_val
+       WHEN 0 THEN 'None'
+       WHEN 1 THEN 'AC'
+       WHEN 2 THEN 'USB'
+       WHEN 4 THEN 'Wireless'
+       WHEN 8 THEN 'Dock'
+       ELSE 'unknown'
+       END AS slice_name,
+       'Plug type' AS track_name,
+       'slice' AS track_type
+FROM plug_type_span;
 
 DROP VIEW IF EXISTS android_batt_output;
 CREATE VIEW android_batt_output AS
diff --git a/src/trace_processor/metrics/sql/android/android_startup.sql b/src/trace_processor/metrics/sql/android/android_startup.sql
index bd80e8a..1f75415 100644
--- a/src/trace_processor/metrics/sql/android/android_startup.sql
+++ b/src/trace_processor/metrics/sql/android/android_startup.sql
@@ -151,7 +151,7 @@
           MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'Running'), 0
         ),
         'runnable_dur_ns', IFNULL(
-          MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'R*'), 0
+          MAIN_THREAD_TIME_FOR_LAUNCH_IN_RUNNABLE_STATE(launches.startup_id), 0
         ),
         'uninterruptible_sleep_dur_ns', IFNULL(
           MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'D*'), 0
@@ -320,7 +320,7 @@
         UNION ALL
         SELECT 'Main Thread - Time spent in Runnable state'
           AS slow_cause
-        WHERE MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'R*') > 100e6
+        WHERE MAIN_THREAD_TIME_FOR_LAUNCH_IN_RUNNABLE_STATE(launches.startup_id) > 100e6
 
         UNION ALL
         SELECT 'Main Thread - Time spent in interruptible sleep state'
@@ -361,7 +361,7 @@
         SELECT 'Potential CPU contention with '
           || MOST_ACTIVE_PROCESS_FOR_LAUNCH(launches.startup_id)
           AS slow_cause
-        WHERE MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'R*') > 100e6
+        WHERE MAIN_THREAD_TIME_FOR_LAUNCH_IN_RUNNABLE_STATE(launches.startup_id) > 100e6
           AND MOST_ACTIVE_PROCESS_FOR_LAUNCH(launches.startup_id) IS NOT NULL
 
         UNION ALL
diff --git a/src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql b/src/trace_processor/metrics/sql/android/counter_span_view_merged.sql
similarity index 69%
rename from src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql
rename to src/trace_processor/metrics/sql/android/counter_span_view_merged.sql
index c3da378..01b7079 100644
--- a/src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql
+++ b/src/trace_processor/metrics/sql/android/counter_span_view_merged.sql
@@ -14,6 +14,15 @@
 -- limitations under the License.
 --
 
+-- Creates a span view for counters that may be global or associated with a 
+-- process, assuming that in the latter case we don't actually care about the
+-- process (probably because it's always system_server). We may want to erase
+-- this distinction for example when merging system properties and atrace
+-- counters.
+--
+-- It also does another type of merging: it merges together temporally adjacent
+-- identical values.
+
 DROP VIEW IF EXISTS {{table_name}}_span;
 CREATE VIEW {{table_name}}_span AS
 SELECT
@@ -25,7 +34,6 @@
     SELECT ts, value, LAG(value) OVER (ORDER BY ts) AS lag_value
     FROM counter c JOIN counter_track t
       ON t.id = c.track_id
-    WHERE t.type = 'counter_track'
-      AND name = '{{counter_name}}'
+    WHERE name = '{{counter_name}}'
 )
 WHERE value != lag_value OR lag_value IS NULL;
diff --git a/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql b/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql
index b85d40b..b3939bb 100644
--- a/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql
+++ b/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql
@@ -66,6 +66,17 @@
   '
 );
 
+-- Given a launch id, returns the aggregate sum of time spent in runnable state
+-- by the main thread of the process being started up.
+SELECT CREATE_FUNCTION(
+  'MAIN_THREAD_TIME_FOR_LAUNCH_IN_RUNNABLE_STATE(startup_id INT)',
+  'INT',
+  '
+    SELECT IFNULL(MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE($startup_id, "R"), 0)
+      + IFNULL(MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE($startup_id, "R+"), 0);
+  '
+);
+
 -- Given a launch id, thread state  and io_wait value, returns the aggregate sum
 -- of time spent in that state by the main thread of the process being started up.
 SELECT CREATE_FUNCTION(
diff --git a/src/trace_processor/sorter/BUILD.gn b/src/trace_processor/sorter/BUILD.gn
index d1d3069..5d27204 100644
--- a/src/trace_processor/sorter/BUILD.gn
+++ b/src/trace_processor/sorter/BUILD.gn
@@ -22,8 +22,8 @@
   sources = [
     "trace_sorter.cc",
     "trace_sorter.h",
-    "trace_sorter_internal.h",
-    "trace_sorter_queue.h",
+    "trace_token_buffer.cc",
+    "trace_token_buffer.h",
   ]
   deps = [
     "../../../gn:default_deps",
@@ -35,14 +35,15 @@
     "../importers/systrace:systrace_line",
     "../storage",
     "../types",
+    "../util:bump_allocator",
   ]
 }
 
 perfetto_unittest_source_set("unittests") {
   testonly = true
   sources = [
-    "trace_sorter_queue_unittest.cc",
     "trace_sorter_unittest.cc",
+    "trace_token_buffer_unittest.cc",
   ]
   deps = [
     ":sorter",
diff --git a/src/trace_processor/sorter/trace_sorter.cc b/src/trace_processor/sorter/trace_sorter.cc
index 6ecbb04..f72c892 100644
--- a/src/trace_processor/sorter/trace_sorter.cc
+++ b/src/trace_processor/sorter/trace_sorter.cc
@@ -18,11 +18,12 @@
 #include <memory>
 #include <utility>
 
+#include "perfetto/base/compiler.h"
 #include "src/trace_processor/importers/common/parser_types.h"
 #include "src/trace_processor/importers/fuchsia/fuchsia_record.h"
 #include "src/trace_processor/sorter/trace_sorter.h"
-#include "src/trace_processor/sorter/trace_sorter_queue.h"
 #include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/util/bump_allocator.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -45,9 +46,7 @@
   // that now.
   for (auto& queue : queues_) {
     for (const auto& event : queue.events_) {
-      // Calling this function without using the packet the same
-      // as just calling the destructor for the element.
-      EvictVariadic(event);
+      ExtractAndDiscardTokenizedObject(event);
     }
   }
 }
@@ -66,7 +65,7 @@
   auto sort_end = events_.begin() + static_cast<ssize_t>(sort_start_idx_);
   PERFETTO_DCHECK(std::is_sorted(events_.begin(), sort_end));
   auto sort_begin = std::lower_bound(events_.begin(), sort_end, sort_min_ts_,
-                                     &TimestampedDescriptor::Compare);
+                                     &TimestampedEvent::Compare);
   std::sort(sort_begin, events_.end());
   sort_start_idx_ = 0;
   sort_min_ts_ = 0;
@@ -95,7 +94,8 @@
 // to avoid re-scanning all the queues all the times) but doesn't seem worth it.
 // With Android traces (that have 8 CPUs) this function accounts for ~1-3% cpu
 // time in a profiler.
-void TraceSorter::SortAndExtractEventsUntilPacket(uint64_t limit_offset) {
+void TraceSorter::SortAndExtractEventsUntilAllocId(
+    BumpAllocator::AllocId limit_alloc_id) {
   constexpr int64_t kTsMax = std::numeric_limits<int64_t>::max();
   for (;;) {
     size_t min_queue_idx = 0;  // The index of the queue with the min(ts).
@@ -136,7 +136,7 @@
     // limit, whichever comes first.
     size_t num_extracted = 0;
     for (auto& event : events) {
-      if (event.descriptor.offset() >= limit_offset) {
+      if (event.alloc_id() >= limit_alloc_id) {
         break;
       }
 
@@ -148,7 +148,7 @@
       }
 
       ++num_extracted;
-      MaybePushAndEvictEvent(min_queue_idx, event);
+      MaybeExtractEvent(min_queue_idx, event);
     }  // for (event: events)
 
     // The earliest event cannot be extracted without going past the limit.
@@ -158,10 +158,11 @@
     // Now remove the entries from the event buffer and update the queue-local
     // and global time bounds.
     events.erase_front(num_extracted);
+    events.shrink_to_fit();
 
-    // After evicting elements we can empty memory in the front of the
-    // queue.
-    variadic_queue_.FreeMemory();
+    // Since we likely just removed a bunch of items try to reduce the memory
+    // usage of the token buffer.
+    token_buffer_.FreeMemory();
 
     // Update the queue timestamps to reflect the bounds after extraction.
     if (events.empty()) {
@@ -173,116 +174,116 @@
   }  // for(;;)
 }
 
-void TraceSorter::EvictVariadic(const TimestampedDescriptor& ts_desc) {
-  switch (ts_desc.descriptor.type()) {
-    case EventType::kTracePacket:
-      EvictTypedVariadic<TracePacketData>(ts_desc);
+void TraceSorter::ParseTracePacket(const TimestampedEvent& event) {
+  TraceTokenBuffer::Id id = GetTokenBufferId(event);
+  switch (static_cast<TimestampedEvent::Type>(event.event_type)) {
+    case TimestampedEvent::Type::kTracePacket:
+      parser_->ParseTracePacket(event.ts,
+                                token_buffer_.Extract<TracePacketData>(id));
       return;
-    case EventType::kTrackEvent:
-      EvictTypedVariadic<TrackEventData>(ts_desc);
+    case TimestampedEvent::Type::kTrackEvent:
+      parser_->ParseTrackEvent(event.ts,
+                               token_buffer_.Extract<TrackEventData>(id));
       return;
-    case EventType::kFuchsiaRecord:
-      EvictTypedVariadic<FuchsiaRecord>(ts_desc);
+    case TimestampedEvent::Type::kFuchsiaRecord:
+      parser_->ParseFuchsiaRecord(event.ts,
+                                  token_buffer_.Extract<FuchsiaRecord>(id));
       return;
-    case EventType::kJsonValue:
-      EvictTypedVariadic<std::string>(ts_desc);
+    case TimestampedEvent::Type::kJsonValue:
+      parser_->ParseJsonPacket(
+          event.ts, std::move(token_buffer_.Extract<JsonEvent>(id).value));
       return;
-    case EventType::kSystraceLine:
-      EvictTypedVariadic<SystraceLine>(ts_desc);
+    case TimestampedEvent::Type::kSystraceLine:
+      parser_->ParseSystraceLine(event.ts,
+                                 token_buffer_.Extract<SystraceLine>(id));
       return;
-    case EventType::kInlineSchedSwitch:
-      EvictTypedVariadic<InlineSchedSwitch>(ts_desc);
-      return;
-    case EventType::kInlineSchedWaking:
-      EvictTypedVariadic<InlineSchedWaking>(ts_desc);
-      return;
-    case EventType::kFtraceEvent:
-      EvictTypedVariadic<TracePacketData>(ts_desc);
-      return;
-    case EventType::kInvalid:
-      PERFETTO_FATAL("Invalid event type");
-  }
-  PERFETTO_FATAL("For GCC");
-}
-
-void TraceSorter::ParseTracePacket(const TimestampedDescriptor& ts_desc) {
-  switch (ts_desc.descriptor.type()) {
-    case EventType::kTracePacket:
-      parser_->ParseTracePacket(ts_desc.ts,
-                                EvictTypedVariadic<TracePacketData>(ts_desc));
-      return;
-    case EventType::kTrackEvent:
-      parser_->ParseTrackEvent(ts_desc.ts,
-                               EvictTypedVariadic<TrackEventData>(ts_desc));
-      return;
-    case EventType::kFuchsiaRecord:
-      parser_->ParseFuchsiaRecord(ts_desc.ts,
-                                  EvictTypedVariadic<FuchsiaRecord>(ts_desc));
-      return;
-    case EventType::kJsonValue:
-      parser_->ParseJsonPacket(ts_desc.ts,
-                               EvictTypedVariadic<std::string>(ts_desc));
-      return;
-    case EventType::kSystraceLine:
-      parser_->ParseSystraceLine(ts_desc.ts,
-                                 EvictTypedVariadic<SystraceLine>(ts_desc));
-      return;
-    case EventType::kInlineSchedSwitch:
-    case EventType::kInlineSchedWaking:
-    case EventType::kFtraceEvent:
-    case EventType::kInvalid:
+    case TimestampedEvent::Type::kInlineSchedSwitch:
+    case TimestampedEvent::Type::kInlineSchedWaking:
+    case TimestampedEvent::Type::kFtraceEvent:
       PERFETTO_FATAL("Invalid event type");
   }
   PERFETTO_FATAL("For GCC");
 }
 
 void TraceSorter::ParseFtracePacket(uint32_t cpu,
-                                    const TimestampedDescriptor& ts_desc) {
-  switch (ts_desc.descriptor.type()) {
-    case EventType::kInlineSchedSwitch:
+                                    const TimestampedEvent& event) {
+  TraceTokenBuffer::Id id = GetTokenBufferId(event);
+  switch (static_cast<TimestampedEvent::Type>(event.event_type)) {
+    case TimestampedEvent::Type::kInlineSchedSwitch:
       parser_->ParseInlineSchedSwitch(
-          cpu, ts_desc.ts, EvictTypedVariadic<InlineSchedSwitch>(ts_desc));
+          cpu, event.ts, token_buffer_.Extract<InlineSchedSwitch>(id));
       return;
-    case EventType::kInlineSchedWaking:
+    case TimestampedEvent::Type::kInlineSchedWaking:
       parser_->ParseInlineSchedWaking(
-          cpu, ts_desc.ts, EvictTypedVariadic<InlineSchedWaking>(ts_desc));
+          cpu, event.ts, token_buffer_.Extract<InlineSchedWaking>(id));
       return;
-    case EventType::kFtraceEvent:
-      parser_->ParseFtraceEvent(cpu, ts_desc.ts,
-                                EvictTypedVariadic<TracePacketData>(ts_desc));
+    case TimestampedEvent::Type::kFtraceEvent:
+      parser_->ParseFtraceEvent(cpu, event.ts,
+                                token_buffer_.Extract<TracePacketData>(id));
       return;
-    case EventType::kTrackEvent:
-    case EventType::kSystraceLine:
-    case EventType::kTracePacket:
-    case EventType::kJsonValue:
-    case EventType::kFuchsiaRecord:
-    case EventType::kInvalid:
+    case TimestampedEvent::Type::kTrackEvent:
+    case TimestampedEvent::Type::kSystraceLine:
+    case TimestampedEvent::Type::kTracePacket:
+    case TimestampedEvent::Type::kJsonValue:
+    case TimestampedEvent::Type::kFuchsiaRecord:
       PERFETTO_FATAL("Invalid event type");
   }
   PERFETTO_FATAL("For GCC");
 }
 
-void TraceSorter::MaybePushAndEvictEvent(size_t queue_idx,
-                                         const TimestampedDescriptor& ts_desc) {
-  int64_t timestamp = ts_desc.ts;
+void TraceSorter::ExtractAndDiscardTokenizedObject(
+    const TimestampedEvent& event) {
+  TraceTokenBuffer::Id id = GetTokenBufferId(event);
+  switch (static_cast<TimestampedEvent::Type>(event.event_type)) {
+    case TimestampedEvent::Type::kTracePacket:
+      base::ignore_result(token_buffer_.Extract<TracePacketData>(id));
+      return;
+    case TimestampedEvent::Type::kTrackEvent:
+      base::ignore_result(token_buffer_.Extract<TrackEventData>(id));
+      return;
+    case TimestampedEvent::Type::kFuchsiaRecord:
+      base::ignore_result(token_buffer_.Extract<FuchsiaRecord>(id));
+      return;
+    case TimestampedEvent::Type::kJsonValue:
+      base::ignore_result(token_buffer_.Extract<JsonEvent>(id));
+      return;
+    case TimestampedEvent::Type::kSystraceLine:
+      base::ignore_result(token_buffer_.Extract<SystraceLine>(id));
+      return;
+    case TimestampedEvent::Type::kInlineSchedSwitch:
+      base::ignore_result(token_buffer_.Extract<InlineSchedSwitch>(id));
+      return;
+    case TimestampedEvent::Type::kInlineSchedWaking:
+      base::ignore_result(token_buffer_.Extract<InlineSchedWaking>(id));
+      return;
+    case TimestampedEvent::Type::kFtraceEvent:
+      base::ignore_result(token_buffer_.Extract<TracePacketData>(id));
+      return;
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+void TraceSorter::MaybeExtractEvent(size_t queue_idx,
+                                    const TimestampedEvent& event) {
+  int64_t timestamp = event.ts;
   if (timestamp < latest_pushed_event_ts_)
     context_->storage->IncrementStats(stats::sorter_push_event_out_of_order);
 
   latest_pushed_event_ts_ = std::max(latest_pushed_event_ts_, timestamp);
 
   if (PERFETTO_UNLIKELY(bypass_next_stage_for_testing_)) {
-    // In standard run the object would be evicted by Parsing{F}tracePacket.
-    // Without it we need to evict it manually.
-    EvictVariadic(ts_desc);
+    // Parse* would extract this event and push it to the next stage. Since we
+    // are skipping that, just extract and discard it.
+    ExtractAndDiscardTokenizedObject(event);
     return;
   }
 
   if (queue_idx == 0) {
-    ParseTracePacket(ts_desc);
+    ParseTracePacket(event);
   } else {
     // Ftrace queues start at offset 1. So queues_[1] = cpu[0] and so on.
     uint32_t cpu = static_cast<uint32_t>(queue_idx - 1);
-    ParseFtracePacket(cpu, ts_desc);
+    ParseFtracePacket(cpu, event);
   }
 }
 
diff --git a/src/trace_processor/sorter/trace_sorter.h b/src/trace_processor/sorter/trace_sorter.h
index 215194d..ac0c431 100644
--- a/src/trace_processor/sorter/trace_sorter.h
+++ b/src/trace_processor/sorter/trace_sorter.h
@@ -30,29 +30,13 @@
 #include "src/trace_processor/importers/common/trace_parser.h"
 #include "src/trace_processor/importers/fuchsia/fuchsia_record.h"
 #include "src/trace_processor/importers/systrace/systrace_line.h"
-#include "src/trace_processor/sorter/trace_sorter_queue.h"
+#include "src/trace_processor/sorter/trace_token_buffer.h"
 #include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/bump_allocator.h"
 
 namespace perfetto {
 namespace trace_processor {
 
-enum class EventType : uint8_t {
-  kFtraceEvent,
-  kTracePacket,
-  kInlineSchedSwitch,
-  kInlineSchedWaking,
-  kJsonValue,
-  kFuchsiaRecord,
-  kTrackEvent,
-  kSystraceLine,
-  kInvalid,
-  kSize = kInvalid,
-};
-
-namespace trace_sorter_internal {
-class VariadicQueue;
-}  // namespace trace_sorter_internal
-
 // This class takes care of sorting events parsed from the trace stream in
 // arbitrary order and pushing them to the next pipeline stages (parsing) in
 // order. In order to support streaming use-cases, sorting happens within a
@@ -100,9 +84,6 @@
 // within the first partition where sorting should start, and sort all events
 // from there to the end.
 class TraceSorter {
- private:
-  using VariadicQueue = trace_sorter_internal::VariadicQueue;
-
  public:
   enum class SortingMode {
     kDefault,
@@ -116,46 +97,47 @@
 
   inline void PushTracePacket(int64_t timestamp,
                               RefPtr<PacketSequenceStateGeneration> state,
-                              TraceBlobView event) {
-    uint32_t offset = variadic_queue_.Append(
-        TracePacketData{std::move(event), std::move(state)});
-    AppendNonFtraceEvent(timestamp, offset, EventType::kTracePacket);
+                              TraceBlobView tbv) {
+    TraceTokenBuffer::Id id =
+        token_buffer_.Append(TracePacketData{std::move(tbv), std::move(state)});
+    AppendNonFtraceEvent(timestamp, TimestampedEvent::Type::kTracePacket, id);
   }
 
   inline void PushJsonValue(int64_t timestamp, std::string json_value) {
-    uint32_t offset = variadic_queue_.Append(std::move(json_value));
-    AppendNonFtraceEvent(timestamp, offset, EventType::kJsonValue);
+    TraceTokenBuffer::Id id =
+        token_buffer_.Append(JsonEvent{std::move(json_value)});
+    AppendNonFtraceEvent(timestamp, TimestampedEvent::Type::kJsonValue, id);
   }
 
   inline void PushFuchsiaRecord(int64_t timestamp,
                                 FuchsiaRecord fuchsia_record) {
-    uint32_t offset = variadic_queue_.Append(std::move(fuchsia_record));
-    AppendNonFtraceEvent(timestamp, offset, EventType::kFuchsiaRecord);
+    TraceTokenBuffer::Id id = token_buffer_.Append(std::move(fuchsia_record));
+    AppendNonFtraceEvent(timestamp, TimestampedEvent::Type::kFuchsiaRecord, id);
   }
 
   inline void PushSystraceLine(SystraceLine systrace_line) {
-    auto ts = systrace_line.ts;
-    auto offset = variadic_queue_.Append(std::move(systrace_line));
-    AppendNonFtraceEvent(ts, offset, EventType::kSystraceLine);
+    TraceTokenBuffer::Id id = token_buffer_.Append(std::move(systrace_line));
+    AppendNonFtraceEvent(systrace_line.ts,
+                         TimestampedEvent::Type::kSystraceLine, id);
   }
 
   inline void PushTrackEventPacket(int64_t timestamp,
                                    TrackEventData track_event) {
-    uint32_t offset = variadic_queue_.Append(std::move(track_event));
-    AppendNonFtraceEvent(timestamp, offset, EventType::kTrackEvent);
+    TraceTokenBuffer::Id id = token_buffer_.Append(std::move(track_event));
+    AppendNonFtraceEvent(timestamp, TimestampedEvent::Type::kTrackEvent, id);
   }
 
   inline void PushFtraceEvent(uint32_t cpu,
                               int64_t timestamp,
-                              TraceBlobView event,
+                              TraceBlobView tbv,
                               RefPtr<PacketSequenceStateGeneration> state) {
+    TraceTokenBuffer::Id id =
+        token_buffer_.Append(TracePacketData{std::move(tbv), std::move(state)});
     auto* queue = GetQueue(cpu + 1);
-    uint32_t offset = variadic_queue_.Append(
-        TracePacketData{std::move(event), std::move(state)});
-    queue->Append(TimestampedDescriptor{
-        timestamp, Descriptor(offset, EventType::kFtraceEvent)});
+    queue->Append(timestamp, TimestampedEvent::Type::kFtraceEvent, id);
     UpdateAppendMaxTs(queue);
   }
+
   inline void PushInlineFtraceEvent(uint32_t cpu,
                                     int64_t timestamp,
                                     InlineSchedSwitch inline_sched_switch) {
@@ -166,29 +148,32 @@
     // sorted however. Consider adding extra queues, or pushing them in a
     // merge-sort fashion
     // // instead.
+    TraceTokenBuffer::Id id =
+        token_buffer_.Append(std::move(inline_sched_switch));
     auto* queue = GetQueue(cpu + 1);
-    uint32_t offset = variadic_queue_.Append(inline_sched_switch);
-    queue->Append(TimestampedDescriptor{
-        timestamp, Descriptor(offset, EventType::kInlineSchedSwitch)});
+    queue->Append(timestamp, TimestampedEvent::Type::kInlineSchedSwitch, id);
     UpdateAppendMaxTs(queue);
   }
+
   inline void PushInlineFtraceEvent(uint32_t cpu,
                                     int64_t timestamp,
                                     InlineSchedWaking inline_sched_waking) {
+    TraceTokenBuffer::Id id =
+        token_buffer_.Append(std::move(inline_sched_waking));
     auto* queue = GetQueue(cpu + 1);
-
-    uint32_t offset = variadic_queue_.Append(inline_sched_waking);
-    queue->Append(TimestampedDescriptor{
-        timestamp, Descriptor(offset, EventType::kInlineSchedWaking)});
+    queue->Append(timestamp, TimestampedEvent::Type::kInlineSchedWaking, id);
     UpdateAppendMaxTs(queue);
   }
 
   void ExtractEventsForced() {
-    uint32_t cur_mem_block_offset = variadic_queue_.NextOffset();
-    SortAndExtractEventsUntilPacket(cur_mem_block_offset);
+    BumpAllocator::AllocId end_id = token_buffer_.PastTheEndAllocId();
+    SortAndExtractEventsUntilAllocId(end_id);
+    for (const auto& queue : queues_) {
+      PERFETTO_DCHECK(queue.events_.empty());
+    }
     queues_.clear();
 
-    offset_for_extraction_ = cur_mem_block_offset;
+    alloc_id_for_extraction_ = end_id;
     flushes_since_extraction_ = 0;
   }
 
@@ -200,77 +185,83 @@
       return;
     }
 
-    SortAndExtractEventsUntilPacket(offset_for_extraction_);
-    offset_for_extraction_ = variadic_queue_.NextOffset();
+    SortAndExtractEventsUntilAllocId(alloc_id_for_extraction_);
+    alloc_id_for_extraction_ = token_buffer_.PastTheEndAllocId();
     flushes_since_extraction_ = 0;
   }
 
   int64_t max_timestamp() const { return append_max_ts_; }
 
  private:
-  // Stores offset and type of metadata.
-  struct Descriptor {
-   public:
-    static constexpr uint8_t kTypeBits = 4;
-    static constexpr uint64_t kTypeMask = (1 << kTypeBits) - 1;
-    static constexpr uint64_t kOffsetShift = kTypeBits;
-    static constexpr uint64_t kMaxType = kTypeMask;
+  struct TimestampedEvent {
+    enum class Type : uint8_t {
+      kFtraceEvent,
+      kTracePacket,
+      kInlineSchedSwitch,
+      kInlineSchedWaking,
+      kJsonValue,
+      kFuchsiaRecord,
+      kTrackEvent,
+      kSystraceLine,
+      kMax = kSystraceLine,
+    };
 
-    static_assert(static_cast<uint8_t>(EventType::kSize) <= kTypeMask,
-                  "Too many bits for type");
+    // Number of bits required to store the max element in |Type|.
+    static constexpr uint32_t kMaxTypeBits = 4;
+    static_assert(static_cast<uint8_t>(Type::kMax) <= (1 << kMaxTypeBits),
+                  "Max type does not fit inside storage");
 
-    Descriptor(uint32_t offset, EventType type)
-        : packed_value_((static_cast<uint64_t>(offset) << kOffsetShift) |
-                        static_cast<uint64_t>(type)) {}
-
-    uint32_t offset() const {
-      return static_cast<uint32_t>(packed_value_ >> kOffsetShift);
-    }
-
-    EventType type() const {
-      return static_cast<EventType>(packed_value_ & kTypeMask);
-    }
-
-   private:
-    uint64_t packed_value_ = 0;
-  };
-
-  struct TimestampedDescriptor {
+    // The timestamp of this event.
     int64_t ts;
-    Descriptor descriptor;
+
+    // The fields inside BumpAllocator::AllocId of this tokenized object
+    // corresponding to this event.
+    uint64_t chunk_index : BumpAllocator::kChunkIndexAllocIdBits;
+    uint64_t chunk_offset : BumpAllocator::kChunkOffsetAllocIdBits;
+
+    // The type of this event. GCC7 does not like bit-field enums (see
+    // https://stackoverflow.com/questions/36005063/gcc-suppress-warning-too-small-to-hold-all-values-of)
+    // so use an uint32_t instead and cast to the enum type.
+    uint32_t event_type : kMaxTypeBits;
+
+    BumpAllocator::AllocId alloc_id() const {
+      return BumpAllocator::AllocId{chunk_index, chunk_offset};
+    }
 
     // For std::lower_bound().
-    static inline bool Compare(const TimestampedDescriptor& x, int64_t ts) {
+    static inline bool Compare(const TimestampedEvent& x, int64_t ts) {
       return x.ts < ts;
     }
 
     // For std::sort().
-    inline bool operator<(const TimestampedDescriptor& desc) const {
-      return ts < desc.ts ||
-             (ts == desc.ts && descriptor.offset() < desc.descriptor.offset());
-    }
-
-    // For std::sort(). Without this the compiler will fall back on invoking
-    // move operators on temporary objects.
-    friend void swap(TimestampedDescriptor& a, TimestampedDescriptor& b) {
-      // TimestampedDescriptor is 16 bytes + trivially swappable so it can be
-      // done without doing any moving.
-      using AS =
-          typename std::aligned_storage<sizeof(TimestampedDescriptor),
-                                        alignof(TimestampedDescriptor)>::type;
-      using std::swap;
-      swap(reinterpret_cast<AS&>(a), reinterpret_cast<AS&>(b));
+    inline bool operator<(const TimestampedEvent& evt) const {
+      return std::tie(ts, chunk_index, chunk_offset) <
+             std::tie(evt.ts, evt.chunk_index, evt.chunk_offset);
     }
   };
-
-  static_assert(sizeof(TimestampedDescriptor) == 16,
-                "TimestampeDescriptor cannot grow beyond 16 bytes");
+  static_assert(sizeof(TimestampedEvent) == 16,
+                "TimestampedEvent must be equal to 16 bytes");
+  static_assert(std::is_trivially_copyable<TimestampedEvent>::value,
+                "TimestampedEvent must be trivially copyable");
+  static_assert(std::is_trivially_move_assignable<TimestampedEvent>::value,
+                "TimestampedEvent must be trivially move assignable");
+  static_assert(std::is_trivially_move_constructible<TimestampedEvent>::value,
+                "TimestampedEvent must be trivially move constructible");
+  static_assert(std::is_nothrow_swappable<TimestampedEvent>::value,
+                "TimestampedEvent must be trivially swappable");
 
   struct Queue {
-    inline void Append(TimestampedDescriptor ts_desc) {
-      auto ts = ts_desc.ts;
-      events_.emplace_back(std::move(ts_desc));
-      min_ts_ = std::min(min_ts_, ts);
+    void Append(int64_t ts,
+                TimestampedEvent::Type type,
+                TraceTokenBuffer::Id id) {
+      {
+        TimestampedEvent event;
+        event.ts = ts;
+        event.chunk_index = id.alloc_id.chunk_index;
+        event.chunk_offset = id.alloc_id.chunk_offset;
+        event.event_type = static_cast<uint8_t>(type);
+        events_.emplace_back(std::move(event));
+      }
 
       // Events are often seen in order.
       if (PERFETTO_LIKELY(ts >= max_ts_)) {
@@ -290,20 +281,21 @@
         }
       }
 
+      min_ts_ = std::min(min_ts_, ts);
       PERFETTO_DCHECK(min_ts_ <= max_ts_);
     }
 
     bool needs_sorting() const { return sort_start_idx_ != 0; }
     void Sort();
 
-    base::CircularQueue<TimestampedDescriptor> events_;
+    base::CircularQueue<TimestampedEvent> events_;
     int64_t min_ts_ = std::numeric_limits<int64_t>::max();
     int64_t max_ts_ = 0;
     size_t sort_start_idx_ = 0;
     int64_t sort_min_ts_ = std::numeric_limits<int64_t>::max();
   };
 
-  void SortAndExtractEventsUntilPacket(uint64_t limit_packet_idx);
+  void SortAndExtractEventsUntilAllocId(BumpAllocator::AllocId alloc_id);
 
   inline Queue* GetQueue(size_t index) {
     if (PERFETTO_UNLIKELY(index >= queues_.size()))
@@ -312,10 +304,10 @@
   }
 
   inline void AppendNonFtraceEvent(int64_t ts,
-                                   uint32_t offset,
-                                   EventType type) {
+                                   TimestampedEvent::Type event_type,
+                                   TraceTokenBuffer::Id id) {
     Queue* queue = GetQueue(0);
-    queue->Append(TimestampedDescriptor{ts, Descriptor{offset, type}});
+    queue->Append(ts, event_type, id);
     UpdateAppendMaxTs(queue);
   }
 
@@ -323,38 +315,36 @@
     append_max_ts_ = std::max(append_max_ts_, queue->max_ts_);
   }
 
-  void ParseTracePacket(const TimestampedDescriptor& ts_desc);
-  void ParseFtracePacket(uint32_t cpu, const TimestampedDescriptor& ts_desc);
+  void ParseTracePacket(const TimestampedEvent&);
+  void ParseFtracePacket(uint32_t cpu, const TimestampedEvent&);
 
-  template <typename T>
-  T EvictTypedVariadic(const TimestampedDescriptor& ts_desc) {
-    return variadic_queue_.Evict<T>(ts_desc.descriptor.offset());
+  void MaybeExtractEvent(size_t queue_idx, const TimestampedEvent&);
+  void ExtractAndDiscardTokenizedObject(const TimestampedEvent& event);
+
+  TraceTokenBuffer::Id GetTokenBufferId(const TimestampedEvent& event) {
+    return TraceTokenBuffer::Id{event.alloc_id()};
   }
 
-  void EvictVariadic(const TimestampedDescriptor& ts_desc);
-
-  void MaybePushAndEvictEvent(size_t queue_idx,
-                              const TimestampedDescriptor& ts_desc)
-      PERFETTO_ALWAYS_INLINE;
-
-  TraceProcessorContext* context_;
+  TraceProcessorContext* context_ = nullptr;
   std::unique_ptr<TraceParser> parser_;
 
   // Whether we should ignore incremental extraction and just wait for
   // forced extractionn at the end of the trace.
   SortingMode sorting_mode_ = SortingMode::kDefault;
 
-  // The packet offset until which events should be extracted. Set based
-  // on the packet offset in |OnReadBuffer|.
-  uint32_t offset_for_extraction_ = 0;
+  // Buffer for storing tokenized objects while the corresponding events are
+  // being sorted.
+  TraceTokenBuffer token_buffer_;
+
+  // The AllocId until which events should be extracted. Set based
+  // on the AllocId in |OnReadBuffer|.
+  BumpAllocator::AllocId alloc_id_for_extraction_ =
+      token_buffer_.PastTheEndAllocId();
 
   // The number of flushes which have happened since the last incremental
   // extraction.
   uint32_t flushes_since_extraction_ = 0;
 
-  // Stores the metadata for each event type in a memory efficient manner.
-  VariadicQueue variadic_queue_;
-
   // queues_[0] is the general (non-ftrace) queue.
   // queues_[1] is the ftrace queue for CPU(0).
   // queues_[x] is the ftrace queue for CPU(x - 1).
diff --git a/src/trace_processor/sorter/trace_sorter_internal.h b/src/trace_processor/sorter/trace_sorter_internal.h
deleted file mode 100644
index 4caebb0..0000000
--- a/src/trace_processor/sorter/trace_sorter_internal.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SRC_TRACE_PROCESSOR_SORTER_TRACE_SORTER_INTERNAL_H_
-#define SRC_TRACE_PROCESSOR_SORTER_TRACE_SORTER_INTERNAL_H_
-
-#include <deque>
-
-#include "perfetto/base/logging.h"
-#include "src/trace_processor/importers/common/parser_types.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace trace_sorter_internal {
-
-// Moves object to the specified pointer and returns the pointer to the space
-// behind it.
-template <typename T>
-char* AppendUnchecked(char* ptr, T value) {
-  PERFETTO_DCHECK(reinterpret_cast<uintptr_t>(ptr) % alignof(T) == 0);
-  new (ptr) T(std::move(value));
-  return ptr + base::AlignUp<8>(sizeof(T));
-}
-
-// Evicts object the the specified pointer, which now points to the space behind
-// it.
-template <typename T>
-T EvictUnchecked(char** ptr) {
-  PERFETTO_DCHECK(reinterpret_cast<uintptr_t>(*ptr) % alignof(T) == 0);
-  T* type_ptr = reinterpret_cast<T*>(*ptr);
-  T out(std::move(*type_ptr));
-  type_ptr->~T();
-  *ptr += base::AlignUp<8>(sizeof(T));
-  return out;
-}
-
-// Stores details of TrackEventData: presence of attributes and the
-// lenght of the array.
-struct TrackEventDataDescriptor {
- public:
-  static constexpr uint64_t kBitsForCounterValues = 4;
-  static constexpr uint64_t kThreadTimestampMask =
-      1 << (kBitsForCounterValues + 1);
-  static constexpr uint64_t kThreadInstructionCountMask =
-      1 << kBitsForCounterValues;
-
-  TrackEventDataDescriptor(bool has_thread_timestamp,
-                           bool has_thread_instruction_count,
-                           uint64_t number_of_counter_values)
-      : packed_value_(GetPacketValue(has_thread_timestamp,
-                                     has_thread_instruction_count,
-                                     number_of_counter_values)) {
-    PERFETTO_DCHECK(number_of_counter_values <=
-                    TrackEventData::kMaxNumExtraCounters);
-  }
-
-  explicit TrackEventDataDescriptor(const TrackEventData& ted)
-      : TrackEventDataDescriptor(ted.thread_timestamp.has_value(),
-                                 ted.thread_instruction_count.has_value(),
-                                 CountNumberOfCounterValues(ted)) {
-    static_assert(
-        TrackEventData::kMaxNumExtraCounters < (1 << kBitsForCounterValues),
-        "kMaxNumExtraCounters can't be compressed properly");
-  }
-
-  static uint64_t CountNumberOfCounterValues(const TrackEventData& ted) {
-    for (uint32_t i = 0; i < TrackEventData::kMaxNumExtraCounters; ++i) {
-      if (std::equal_to<double>()(ted.extra_counter_values[i], 0)) {
-        return i;
-      }
-    }
-    return TrackEventData::kMaxNumExtraCounters;
-  }
-
-  static uint64_t GetPacketValue(bool has_thread_timestamp,
-                                 bool has_thread_instruction_count,
-                                 uint64_t number_of_counter_values) {
-    return (static_cast<uint64_t>(has_thread_timestamp)
-            << (kBitsForCounterValues + 1)) |
-           (static_cast<uint64_t>(has_thread_instruction_count)
-            << kBitsForCounterValues) |
-           number_of_counter_values;
-  }
-
-  bool HasThreadTimestamp() const {
-    return static_cast<bool>(packed_value_ & kThreadTimestampMask);
-  }
-
-  bool HasThreadInstructionCount() const {
-    return static_cast<bool>(packed_value_ & kThreadInstructionCountMask);
-  }
-
-  uint64_t NumberOfCounterValues() const {
-    return static_cast<uint64_t>(
-        packed_value_ & static_cast<uint64_t>(~(3 << kBitsForCounterValues)));
-  }
-
-  uint64_t AppendedSize() const {
-    return sizeof(TracePacketData) +
-           8l * (/*counter_value*/ 1 + HasThreadTimestamp() +
-                 HasThreadInstructionCount() + NumberOfCounterValues());
-  }
-
- private:
-  // uint8_t would be enough to hold all of the required data, but we need 8
-  // bytes type for alignment.
-  uint64_t packed_value_ = 0;
-};
-
-// Adds and removes object of the type from queue memory. Can be overriden
-// for more specific functionality related to a type. All child classes
-// should implement the same interface.
-template <typename T>
-class TypedMemoryAccessor {
- public:
-  static char* Append(char* ptr, T value) {
-    return AppendUnchecked(ptr, std::move(value));
-  }
-  static T Evict(char* ptr) { return EvictUnchecked<T>(&ptr); }
-  static uint64_t AppendSize(const T&) {
-    return static_cast<uint64_t>(sizeof(T));
-  }
-};
-
-// Responsibe for accessing memory in the queue related to TrackEventData.
-// Appends the struct more efficiently by compressing and decompressing some
-// of TrackEventData attributes.
-template <>
-class TypedMemoryAccessor<TrackEventData> {
- public:
-  static char* Append(char* ptr, TrackEventData ted) {
-    auto ted_desc = TrackEventDataDescriptor(ted);
-    ptr = AppendUnchecked(ptr, ted_desc);
-    ptr = AppendUnchecked(ptr, std::move(ted.trace_packet_data));
-    ptr = AppendUnchecked(ptr, ted.counter_value);
-    if (ted_desc.HasThreadTimestamp()) {
-      ptr = AppendUnchecked(ptr, ted.thread_timestamp.value());
-    }
-    if (ted_desc.HasThreadInstructionCount()) {
-      ptr = AppendUnchecked(ptr, ted.thread_instruction_count.value());
-    }
-    for (uint32_t i = 0; i < ted_desc.NumberOfCounterValues(); i++) {
-      ptr = AppendUnchecked(ptr, ted.extra_counter_values[i]);
-    }
-    return ptr;
-  }
-
-  static TrackEventData Evict(char* ptr) {
-    auto ted_desc = EvictUnchecked<TrackEventDataDescriptor>(&ptr);
-    TrackEventData ted(EvictUnchecked<TracePacketData>(&ptr));
-    ted.counter_value = EvictUnchecked<double>(&ptr);
-    if (ted_desc.HasThreadTimestamp()) {
-      ted.thread_timestamp = EvictUnchecked<int64_t>(&ptr);
-    }
-    if (ted_desc.HasThreadInstructionCount()) {
-      ted.thread_instruction_count = EvictUnchecked<int64_t>(&ptr);
-    }
-    for (uint32_t i = 0; i < ted_desc.NumberOfCounterValues(); i++) {
-      ted.extra_counter_values[i] = EvictUnchecked<double>(&ptr);
-    }
-    return ted;
-  }
-
-  static uint64_t AppendSize(const TrackEventData& value) {
-    return static_cast<uint64_t>(sizeof(TrackEventDataDescriptor)) +
-           TrackEventDataDescriptor(value).AppendedSize();
-  }
-};
-
-}  // namespace trace_sorter_internal
-}  // namespace trace_processor
-}  // namespace perfetto
-#endif  // SRC_TRACE_PROCESSOR_SORTER_TRACE_SORTER_INTERNAL_H_
diff --git a/src/trace_processor/sorter/trace_sorter_queue.h b/src/trace_processor/sorter/trace_sorter_queue.h
deleted file mode 100644
index aabd302..0000000
--- a/src/trace_processor/sorter/trace_sorter_queue.h
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SRC_TRACE_PROCESSOR_SORTER_TRACE_SORTER_QUEUE_H_
-#define SRC_TRACE_PROCESSOR_SORTER_TRACE_SORTER_QUEUE_H_
-
-#include <cstddef>
-#include <deque>
-
-#include "perfetto/base/logging.h"
-#include "perfetto/ext/base/small_vector.h"
-#include "perfetto/ext/base/utils.h"
-#include "src/trace_processor/sorter/trace_sorter_internal.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace trace_sorter_internal {
-
-// 1MB is good tradeoff between having big enough memory blocks so that we don't
-// need to often append and remove blocks for big traces, but small enough to
-// not overuse memory for small traces.
-static constexpr uint32_t kDefaultSize = 1 * 1024 * 1024;  // 1MB
-
-// Used for storing the data for all different packet data types.
-class VariadicQueue {
- public:
-  VariadicQueue() : VariadicQueue(kDefaultSize) {}
-  ~VariadicQueue() {
-    // These checks verify that we evicted all elements from this queue. This is
-    // important as we need to call the destructor to make sure we're not
-    // leaking memory.
-    FreeMemory();
-    PERFETTO_CHECK(mem_blocks_.size() == 1);
-    PERFETTO_CHECK(mem_blocks_.back().empty());
-  }
-
-  VariadicQueue(const VariadicQueue&) = delete;
-  VariadicQueue& operator=(const VariadicQueue&) = delete;
-
-  VariadicQueue(VariadicQueue&&) = default;
-  VariadicQueue& operator=(VariadicQueue&&) noexcept = default;
-
-  // Moves packet data type to the end of the queue storage.
-  template <typename T>
-  uint32_t Append(T value) {
-    PERFETTO_DCHECK(!mem_blocks_.empty());
-
-    uint64_t size = Block::AppendSize<T>(value);
-    if (PERFETTO_UNLIKELY(!mem_blocks_.back().HasSpace(size))) {
-      mem_blocks_.emplace_back(Block(block_size_));
-    }
-    auto& back_block = mem_blocks_.back();
-    PERFETTO_DCHECK(back_block.HasSpace(size));
-    return GlobalMemOffsetFromLastBlockOffset(
-        back_block.Append(std::move(value)));
-  }
-
-  // Moves object out of queue storage.
-  template <typename T>
-  T Evict(uint32_t global_offset) {
-    uint32_t block = (global_offset / block_size_) - deleted_blocks_;
-    uint32_t block_offset = global_offset % block_size_;
-    return mem_blocks_[block].Evict<T>(block_offset);
-  }
-
-  // Clears the empty front of queue storage.
-  void FreeMemory() {
-    while (mem_blocks_.size() > 1 && mem_blocks_.front().empty()) {
-      mem_blocks_.pop_front();
-      deleted_blocks_++;
-    }
-  }
-
-  // Returns the offset value in which new element can be stored.
-  uint32_t NextOffset() const {
-    PERFETTO_DCHECK(!mem_blocks_.empty());
-    return GlobalMemOffsetFromLastBlockOffset(mem_blocks_.back().offset());
-  }
-
-  static VariadicQueue VariadicQueueForTesting(uint32_t size) {
-    return VariadicQueue(size);
-  }
-
- private:
-  // Implementation note: this class stores an extra 8 bytes in debug builds to
-  // store the size of the type stored inside.
-  class Block {
-   public:
-    explicit Block(uint32_t block_size)
-        : size_(block_size),
-          storage_(
-              base::AlignedAllocTyped<uint64_t>(size_ / sizeof(uint64_t))) {}
-
-    bool HasSpace(uint64_t size) const { return size <= size_ - offset_; }
-
-    template <typename T>
-    uint32_t Append(T value) {
-      static_assert(alignof(T) <= 8,
-                    "Class must have at most 8 byte alignment");
-      PERFETTO_DCHECK(offset_ % 8 == 0);
-      PERFETTO_DCHECK(HasSpace(AppendSize(value)));
-
-      char* storage_begin_ptr = reinterpret_cast<char*>(storage_.get());
-      char* ptr = storage_begin_ptr + offset_;
-
-#if PERFETTO_DCHECK_IS_ON()
-      ptr = AppendUnchecked(ptr, TypedMemoryAccessor<T>::AppendSize(value));
-#endif
-      ptr = TypedMemoryAccessor<T>::Append(ptr, std::move(value));
-      num_elements_++;
-
-      auto cur_offset = offset_;
-      offset_ = static_cast<uint32_t>(base::AlignUp<8>(static_cast<uint32_t>(
-          ptr - reinterpret_cast<char*>(storage_.get()))));
-      return cur_offset;
-    }
-
-    template <typename T>
-    T Evict(uint32_t offset) {
-      PERFETTO_DCHECK(offset < size_);
-      PERFETTO_DCHECK(offset % 8 == 0);
-
-      char* ptr = reinterpret_cast<char*>(storage_.get()) + offset;
-      uint64_t size = 0;
-#if PERFETTO_DCHECK_IS_ON()
-      size = EvictUnchecked<uint64_t>(&ptr);
-#endif
-      T value = TypedMemoryAccessor<T>::Evict(ptr);
-      PERFETTO_DCHECK(size == TypedMemoryAccessor<T>::AppendSize(value));
-      num_elements_evicted_++;
-      return value;
-    }
-
-    template <typename T>
-    static uint64_t AppendSize(const T& value) {
-#if PERFETTO_DCHECK_IS_ON()
-      // On debug runs for each append of T we also append the sizeof(T) to the
-      // queue for sanity check, which we later evict and compare with object
-      // size. This value needs to be added to general size of an object.
-      return sizeof(uint64_t) + TypedMemoryAccessor<T>::AppendSize(value);
-#else
-      return TypedMemoryAccessor<T>::AppendSize(value);
-#endif
-    }
-
-    uint32_t offset() const { return offset_; }
-    bool empty() const { return num_elements_ == num_elements_evicted_; }
-
-   private:
-    uint32_t size_;
-    uint32_t offset_ = 0;
-
-    uint32_t num_elements_ = 0;
-    uint32_t num_elements_evicted_ = 0;
-
-    base::AlignedUniquePtr<uint64_t> storage_;
-  };
-
-  explicit VariadicQueue(uint32_t block_size) : block_size_(block_size) {
-    mem_blocks_.emplace_back(Block(block_size_));
-  }
-
-  uint32_t GlobalMemOffsetFromLastBlockOffset(uint32_t block_offset) const {
-    return (deleted_blocks_ + static_cast<uint32_t>(mem_blocks_.size()) - 1) *
-               block_size_ +
-           block_offset;
-  }
-
-  std::deque<Block> mem_blocks_;
-
-  uint32_t block_size_ = kDefaultSize;
-  uint32_t deleted_blocks_ = 0;
-};
-
-}  // namespace trace_sorter_internal
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_SORTER_TRACE_SORTER_QUEUE_H_
diff --git a/src/trace_processor/sorter/trace_sorter_queue_unittest.cc b/src/trace_processor/sorter/trace_sorter_queue_unittest.cc
deleted file mode 100644
index a6cd7a4..0000000
--- a/src/trace_processor/sorter/trace_sorter_queue_unittest.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "src/trace_processor/sorter/trace_sorter_queue.h"
-
-#include "src/trace_processor/types/variadic.h"
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace trace_sorter_internal {
-
-#if PERFETTO_DCHECK_IS_ON()
-constexpr uint32_t RESERVED_SIZE_BYTES = 8ul;
-#else
-constexpr uint32_t RESERVED_SIZE_BYTES = 0ul;
-#endif
-
-using ::testing::_;
-
-TEST(VariadicQueueUnittest, AddAndEvict) {
-  VariadicQueue queue =
-      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
-  auto offset = queue.Append<int64_t>(10);
-  int64_t evicted_val = queue.Evict<int64_t>(offset);
-  ASSERT_EQ(evicted_val, 10l);
-}
-
-TEST(VariadicQueueUnittest, AddAndEvictFirstElement) {
-  VariadicQueue queue =
-      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
-  auto offset1 = queue.Append<int64_t>(10);
-  auto offset2 = queue.Append<int64_t>(20);
-  ASSERT_EQ(queue.Evict<int64_t>(offset1), 10);
-  ASSERT_EQ(queue.Evict<int64_t>(offset2), 20);
-}
-
-TEST(VariadicQueueUnittest, AppendAfterEviction) {
-  VariadicQueue queue =
-      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
-  auto offset = queue.Append<int64_t>(10);
-  ASSERT_EQ(queue.Evict<int64_t>(offset), 10);
-  offset = queue.Append<int64_t>(20);
-  ASSERT_EQ(queue.Evict<int64_t>(offset), 20);
-}
-
-TEST(VariadicQueueUnittest, FreeAllMemory) {
-  VariadicQueue queue =
-      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
-  auto offset1 = queue.Append<int64_t>(10);
-  auto offset2 = queue.Append<int64_t>(20);
-  ASSERT_EQ(queue.Evict<int64_t>(offset1), 10);
-  ASSERT_EQ(queue.Evict<int64_t>(offset2), 20);
-  queue.FreeMemory();
-}
-
-TEST(VariadicQueueUnittest, FreeMemoryPartially) {
-  VariadicQueue queue =
-      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
-  uint32_t offset1 = queue.Append<int64_t>(10);
-  uint32_t offset2 = queue.Append<int64_t>(20);
-  ASSERT_EQ(queue.Evict<int64_t>(offset1), 10);
-  queue.FreeMemory();
-  ASSERT_EQ(queue.Evict<int64_t>(offset2), 20);
-  queue.FreeMemory();
-}
-
-TEST(VariadicQueueUnittest, AppendDifferentSizes) {
-  VariadicQueue queue =
-      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
-  uint32_t offset_long_long = queue.Append<int64_t>(10);
-  uint32_t offset_int = queue.Append<int32_t>(20);
-  uint32_t offset_short = queue.Append<int16_t>(30);
-  uint32_t offset_char = queue.Append<char>('s');
-  ASSERT_EQ(queue.Evict<int64_t>(offset_long_long), 10l);
-  ASSERT_EQ(queue.Evict<int32_t>(offset_int), 20);
-  ASSERT_EQ(queue.Evict<int16_t>(offset_short), static_cast<int16_t>(30));
-  ASSERT_EQ(queue.Evict<char>(offset_char), 's');
-}
-
-}  // namespace trace_sorter_internal
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/sorter/trace_sorter_unittest.cc b/src/trace_processor/sorter/trace_sorter_unittest.cc
index 53a1ebe..e2fc286 100644
--- a/src/trace_processor/sorter/trace_sorter_unittest.cc
+++ b/src/trace_processor/sorter/trace_sorter_unittest.cc
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/importers/proto/proto_trace_parser.h"
 
 #include <map>
@@ -297,13 +298,18 @@
         EXPECT_TRUE(cpu_found);
       }));
 
-  for (int i = 0; i < 1000; i++) {
+  // Allocate a 1000 byte trace blob and push one byte chunks to be sorted with
+  // random timestamps. This will stress test the sorter with worst case
+  // scenarios and will (and has many times) expose any subtle bugs hiding in
+  // the sorter logic.
+  TraceBlobView tbv(TraceBlob::Allocate(1000));
+  for (uint16_t i = 0; i < 1000; i++) {
     int64_t ts = abs(static_cast<int64_t>(rnd_engine()));
-    int num_cpus = rnd_engine() % 3;
-    for (int j = 0; j < num_cpus; j++) {
+    uint8_t num_cpus = rnd_engine() % 3;
+    for (uint8_t j = 0; j < num_cpus; j++) {
       uint32_t cpu = static_cast<uint32_t>(rnd_engine() % 32);
       expectations[ts].push_back(cpu);
-      context_.sorter->PushFtraceEvent(cpu, ts, TraceBlobView(),
+      context_.sorter->PushFtraceEvent(cpu, ts, tbv.slice_off(i, 1),
                                        state.current_generation());
     }
   }
diff --git a/src/trace_processor/sorter/trace_token_buffer.cc b/src/trace_processor/sorter/trace_token_buffer.cc
new file mode 100644
index 0000000..c8d2899
--- /dev/null
+++ b/src/trace_processor/sorter/trace_token_buffer.cc
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/sorter/trace_token_buffer.h"
+
+#include <stdint.h>
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <functional>
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+#include "perfetto/base/compiler.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/trace_processor/trace_blob.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/util/bump_allocator.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+struct alignas(8) TrackEventDataDescriptor {
+  static constexpr uint8_t kMaxOffsetFromInternedBlobBits = 25;
+  static constexpr uint32_t kMaxOffsetFromInternedBlob =
+      (1Ul << kMaxOffsetFromInternedBlobBits) - 1;
+
+  static constexpr uint8_t kMaxExtraCountersBits = 4;
+  static constexpr uint8_t kMaxExtraCounters = (1 << kMaxExtraCountersBits) - 1;
+  static_assert(TrackEventData::kMaxNumExtraCounters <= kMaxExtraCounters,
+                "Counter bits must be able to fit TrackEventData counters");
+
+  uint16_t intern_blob_index;
+  uint16_t intern_seq_index;
+  uint32_t intern_blob_offset : kMaxOffsetFromInternedBlobBits;
+  uint32_t has_thread_timestamp : 1;
+  uint32_t has_thread_instruction_count : 1;
+  uint32_t has_counter_value : 1;
+  uint32_t extra_counter_count : kMaxExtraCountersBits;
+};
+static_assert(sizeof(TrackEventDataDescriptor) == 8,
+              "CompressedTracePacketData must be small");
+static_assert(alignof(TrackEventDataDescriptor) == 8,
+              "CompressedTracePacketData must be 8-aligned");
+
+template <typename T>
+T ExtractFromPtr(uint8_t** ptr) {
+  T* typed_ptr = reinterpret_cast<T*>(*ptr);
+  T value(std::move(*typed_ptr));
+  typed_ptr->~T();
+  *ptr += sizeof(T);
+  return value;
+}
+
+template <typename T>
+uint8_t* AppendToPtr(uint8_t* ptr, T value) {
+  new (ptr) T(std::move(value));
+  return ptr + sizeof(T);
+}
+
+uint32_t GetAllocSize(const TrackEventDataDescriptor& desc) {
+  uint32_t alloc_size = sizeof(TrackEventDataDescriptor);
+  alloc_size += sizeof(uint64_t);
+  alloc_size += desc.has_thread_instruction_count * sizeof(int64_t);
+  alloc_size += desc.has_thread_timestamp * sizeof(int64_t);
+  alloc_size += desc.has_counter_value * sizeof(double);
+  alloc_size += desc.extra_counter_count * sizeof(double);
+  return alloc_size;
+}
+
+}  // namespace
+
+TraceTokenBuffer::Id TraceTokenBuffer::Append(TrackEventData ted) {
+  // TrackEventData (and TracePacketData) are two big contributors to the size
+  // of the peak memory usage by sorted. The main reasons for this are a) object
+  // padding and b) using more bits than necessary to store their contents.
+  //
+  // The purpose of this function is to "compress" the contents of
+  // TrackEventData by utilising techniques like bitpacking, interning and
+  // variable length encoding to ensure only the amount of data which really
+  // needs to be stored is done so.
+
+  // Compress all the booleans indicating the presence of a value into 4 bits
+  // instead of 4 bytes as they would take inside base::Optional.
+  TrackEventDataDescriptor desc;
+  desc.has_thread_instruction_count = ted.thread_instruction_count.has_value();
+  desc.has_thread_timestamp = ted.thread_timestamp.has_value();
+  desc.has_counter_value = std::not_equal_to<double>()(ted.counter_value, 0);
+  desc.extra_counter_count = ted.CountExtraCounterValues();
+
+  // Allocate enough memory using the BumpAllocator to store the data in |ted|.
+  // Also figure out the interned index.
+  BumpAllocator::AllocId alloc_id =
+      AllocAndResizeInternedVectors(GetAllocSize(desc));
+  InternedIndex interned_index = GetInternedIndex(alloc_id);
+
+  // Compute the interning information for the TrackBlob and the SequenceState.
+  const TracePacketData& tpd = ted.trace_packet_data;
+  desc.intern_blob_offset = InternTraceBlob(interned_index, tpd.packet);
+  desc.intern_blob_index =
+      static_cast<uint16_t>(interned_blobs_.at(interned_index).size() - 1);
+  desc.intern_seq_index =
+      InternSeqState(interned_index, std::move(tpd.sequence_state));
+
+  // Store the descriptor
+  uint8_t* ptr = static_cast<uint8_t*>(allocator_.GetPointer(alloc_id));
+  ptr = AppendToPtr(ptr, desc);
+
+  // Store the packet sizes.
+  uint64_t packet_size = static_cast<uint64_t>(tpd.packet.size());
+  ptr = AppendToPtr(ptr, packet_size);
+
+  // Add the "optional" fields of TrackEventData based on whether or not they
+  // are non-null.
+  if (desc.has_thread_instruction_count) {
+    ptr = AppendToPtr(ptr, ted.thread_instruction_count.value());
+  }
+  if (desc.has_thread_timestamp) {
+    ptr = AppendToPtr(ptr, ted.thread_timestamp.value());
+  }
+  if (desc.has_counter_value) {
+    ptr = AppendToPtr(ptr, ted.counter_value);
+  }
+  for (uint32_t i = 0; i < desc.extra_counter_count; ++i) {
+    ptr = AppendToPtr(ptr, ted.extra_counter_values[i]);
+  }
+  return Id{alloc_id};
+}
+
+template <>
+TrackEventData TraceTokenBuffer::Extract<TrackEventData>(Id id) {
+  uint8_t* ptr = static_cast<uint8_t*>(allocator_.GetPointer(id.alloc_id));
+  TrackEventDataDescriptor desc =
+      ExtractFromPtr<TrackEventDataDescriptor>(&ptr);
+  uint64_t packet_size = ExtractFromPtr<uint64_t>(&ptr);
+
+  InternedIndex interned_index = GetInternedIndex(id.alloc_id);
+  BlobWithOffset& bwo =
+      interned_blobs_.at(interned_index)[desc.intern_blob_index];
+  TraceBlobView tbv(RefPtr<TraceBlob>::FromReleasedUnsafe(bwo.blob),
+                    bwo.offset_in_blob + desc.intern_blob_offset,
+                    static_cast<uint32_t>(packet_size));
+  auto seq = RefPtr<PacketSequenceStateGeneration>::FromReleasedUnsafe(
+      interned_seqs_.at(interned_index)[desc.intern_seq_index]);
+
+  TrackEventData ted{std::move(tbv), std::move(seq)};
+  if (desc.has_thread_instruction_count) {
+    ted.thread_instruction_count = ExtractFromPtr<int64_t>(&ptr);
+  }
+  if (desc.has_thread_timestamp) {
+    ted.thread_timestamp = ExtractFromPtr<int64_t>(&ptr);
+  }
+  if (desc.has_counter_value) {
+    ted.counter_value = ExtractFromPtr<double>(&ptr);
+  }
+  for (uint32_t i = 0; i < desc.extra_counter_count; ++i) {
+    ted.extra_counter_values[i] = ExtractFromPtr<double>(&ptr);
+  }
+  allocator_.Free(id.alloc_id);
+  return ted;
+}
+
+uint32_t TraceTokenBuffer::InternTraceBlob(InternedIndex interned_index,
+                                           const TraceBlobView& tbv) {
+  BlobWithOffsets& blobs = interned_blobs_.at(interned_index);
+  if (blobs.empty()) {
+    return AddTraceBlob(interned_index, tbv);
+  }
+
+  BlobWithOffset& last_blob = blobs.back();
+  if (last_blob.blob != tbv.blob().get()) {
+    return AddTraceBlob(interned_index, tbv);
+  }
+  PERFETTO_CHECK(last_blob.offset_in_blob <= tbv.offset());
+
+  // To allow our offsets in the store to be 16 bits, we intern not only the
+  // TraceBlob pointer but also the offset. By having this double indirection,
+  // we can store offset always as uint16 at the cost of storing blobs here more
+  // often: this more than pays for itself as in the majority of cases the
+  // offsets are small anyway.
+  size_t rel_offset = tbv.offset() - last_blob.offset_in_blob;
+  if (rel_offset > TrackEventDataDescriptor::kMaxOffsetFromInternedBlob) {
+    return AddTraceBlob(interned_index, tbv);
+  }
+
+  // Intentionally "leak" this pointer. This essentially keeps the refcount
+  // of this TraceBlob one higher than the number of RefPtrs pointing to it.
+  // This allows avoid storing the same RefPtr n times.
+  //
+  // Calls to this function are paired to Extract<TrackEventData> which picks
+  // up this "leaked" pointer.
+  TraceBlob* leaked = tbv.blob().ReleaseUnsafe();
+  base::ignore_result(leaked);
+  return static_cast<uint32_t>(rel_offset);
+}
+
+uint16_t TraceTokenBuffer::InternSeqState(
+    InternedIndex interned_index,
+    RefPtr<PacketSequenceStateGeneration> ptr) {
+  // Look back at most 32 elements. This should be far enough in most cases
+  // unless either: a) we are essentially round-robining between >32 sequences
+  // b) we are churning through generations. Either case seems pathalogical.
+  SequenceStates& states = interned_seqs_.at(interned_index);
+  size_t lookback = std::min<size_t>(32u, states.size());
+  for (uint32_t i = 0; i < lookback; ++i) {
+    uint16_t idx = static_cast<uint16_t>(states.size() - 1 - i);
+    if (states[idx] == ptr.get()) {
+      // Intentionally "leak" this pointer. See |InternTraceBlob| for an
+      // explanation.
+      PacketSequenceStateGeneration* leaked = ptr.ReleaseUnsafe();
+      base::ignore_result(leaked);
+      return idx;
+    }
+  }
+  states.emplace_back(ptr.ReleaseUnsafe());
+  PERFETTO_CHECK(states.size() <= std::numeric_limits<uint16_t>::max());
+  return static_cast<uint16_t>(states.size() - 1);
+}
+
+uint32_t TraceTokenBuffer::AddTraceBlob(InternedIndex interned_index,
+                                        const TraceBlobView& tbv) {
+  BlobWithOffsets& blobs = interned_blobs_.at(interned_index);
+  blobs.emplace_back(BlobWithOffset{tbv.blob().ReleaseUnsafe(), tbv.offset()});
+  PERFETTO_CHECK(blobs.size() <= std::numeric_limits<uint16_t>::max());
+  return 0u;
+}
+
+void TraceTokenBuffer::FreeMemory() {
+  uint64_t erased = allocator_.EraseFrontFreeChunks();
+  PERFETTO_CHECK(erased <= std::numeric_limits<size_t>::max());
+  interned_blobs_.erase_front(static_cast<size_t>(erased));
+  interned_seqs_.erase_front(static_cast<size_t>(erased));
+  PERFETTO_CHECK(interned_blobs_.size() == interned_seqs_.size());
+}
+
+BumpAllocator::AllocId TraceTokenBuffer::AllocAndResizeInternedVectors(
+    uint32_t size) {
+  uint64_t erased = allocator_.erased_front_chunks_count();
+  BumpAllocator::AllocId alloc_id = allocator_.Alloc(size);
+  uint64_t allocator_chunks_size = alloc_id.chunk_index - erased + 1;
+
+  // The allocator should never "remove" chunks from being tracked.
+  PERFETTO_DCHECK(allocator_chunks_size >= interned_blobs_.size());
+
+  // We should add at most one chunk in the allocator.
+  uint64_t chunks_added = allocator_chunks_size - interned_blobs_.size();
+  PERFETTO_DCHECK(chunks_added <= 1);
+  PERFETTO_DCHECK(interned_blobs_.size() == interned_seqs_.size());
+  for (uint64_t i = 0; i < chunks_added; ++i) {
+    interned_blobs_.emplace_back();
+    interned_seqs_.emplace_back();
+  }
+  return alloc_id;
+}
+
+TraceTokenBuffer::InternedIndex TraceTokenBuffer::GetInternedIndex(
+    BumpAllocator::AllocId alloc_id) {
+  uint64_t interned_index =
+      alloc_id.chunk_index - allocator_.erased_front_chunks_count();
+  PERFETTO_DCHECK(interned_index <= std::numeric_limits<size_t>::max());
+  PERFETTO_DCHECK(interned_index < interned_blobs_.size());
+  PERFETTO_DCHECK(interned_index < interned_seqs_.size());
+  PERFETTO_DCHECK(interned_blobs_.size() == interned_seqs_.size());
+  return static_cast<size_t>(interned_index);
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/sorter/trace_token_buffer.h b/src/trace_processor/sorter/trace_token_buffer.h
new file mode 100644
index 0000000..a222bbc
--- /dev/null
+++ b/src/trace_processor/sorter/trace_token_buffer.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_SORTER_TRACE_TOKEN_BUFFER_H_
+#define SRC_TRACE_PROCESSOR_SORTER_TRACE_TOKEN_BUFFER_H_
+
+#include <cstdint>
+#include <limits>
+#include <utility>
+#include <vector>
+
+#include "perfetto/base/compiler.h"
+#include "perfetto/ext/base/circular_queue.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/utils.h"
+#include "perfetto/trace_processor/trace_blob.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/util/bump_allocator.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Helper class which stores tokenized objects while the corresponding events
+// are being sorted by TraceSorter.
+//
+// This class intrusively compresses the tokenized objects as much as possible
+// to reduce their memory footprint. This is important to reduce the peak memory
+// usage of TraceProcessor which is always hit at some point during sorting.
+// The tokenized objects make up the vast majority of this peak so we trade the
+// complexity in this class for big reductions in the peak use.
+//
+// go/perfetto-tp-memory-use gives an overview of trace processor memory usage.
+class TraceTokenBuffer {
+ public:
+  // Identifier returned when appending items to this buffer. This id can
+  // later by passed to |Extract| to retrieve the event.
+  struct Id {
+    // The allocation id of the object in the buffer.
+    BumpAllocator::AllocId alloc_id;
+  };
+
+  // Appends an object of type |T| to the token buffer. Returns an id for
+  // looking up the object later using |Extract|.
+  template <typename T>
+  PERFETTO_WARN_UNUSED_RESULT Id Append(T object) {
+    static_assert(sizeof(T) % 8 == 0, "Size must be a multiple of 8");
+    static_assert(alignof(T) == 8, "Alignment must be 8");
+    BumpAllocator::AllocId id = AllocAndResizeInternedVectors(sizeof(T));
+    new (allocator_.GetPointer(id)) T(std::move(object));
+    return Id{id};
+  }
+  PERFETTO_WARN_UNUSED_RESULT Id Append(TrackEventData);
+  PERFETTO_WARN_UNUSED_RESULT Id Append(TracePacketData data) {
+    // While in theory we could add a special case for TracePacketData, the
+    // judgement call we make is that the code complexity does not justify the
+    // micro-performance gain you might hope to see by avoiding the few if
+    // conditions in the |TracePacketData| path.
+    return Append(TrackEventData(std::move(data)));
+  }
+
+  // Extracts an object of type |T| from the token buffer using an id previously
+  // returned by |Append|. This type *must* match the type added using Append.
+  // Mismatching types will caused undefined behaviour.
+  template <typename T>
+  PERFETTO_WARN_UNUSED_RESULT T Extract(Id id) {
+    T* typed_ptr = static_cast<T*>(allocator_.GetPointer(id.alloc_id));
+    T object(std::move(*typed_ptr));
+    typed_ptr->~T();
+    allocator_.Free(id.alloc_id);
+    return object;
+  }
+
+  // Returns the "past-the-end" id from the underlying allocator.
+  // The main use of this function is to provide an id which is greater than
+  // all ids previously returned by |Append|.
+  //
+  // This is similar to the |end()| function in standard library vector classes.
+  BumpAllocator::AllocId PastTheEndAllocId() {
+    return allocator_.PastTheEndId();
+  }
+
+  // Attempts to free any memory retained by this buffer and the underlying
+  // allocator. The amount of memory free is implementation defined.
+  void FreeMemory();
+
+ private:
+  struct BlobWithOffset {
+    TraceBlob* blob;
+    size_t offset_in_blob;
+  };
+  using InternedIndex = size_t;
+  using BlobWithOffsets = std::vector<BlobWithOffset>;
+  using SequenceStates = std::vector<PacketSequenceStateGeneration*>;
+
+  // Functions to intern TraceBlob and PacketSequenceStateGeneration: as these
+  // are often shared between packets, we can significantly reduce memory use
+  // by only storing them once.
+  uint32_t InternTraceBlob(InternedIndex, const TraceBlobView&);
+  uint16_t InternSeqState(InternedIndex, RefPtr<PacketSequenceStateGeneration>);
+  uint32_t AddTraceBlob(InternedIndex, const TraceBlobView&);
+
+  BumpAllocator::AllocId AllocAndResizeInternedVectors(uint32_t size);
+  InternedIndex GetInternedIndex(BumpAllocator::AllocId);
+
+  BumpAllocator allocator_;
+  base::CircularQueue<BlobWithOffsets> interned_blobs_;
+  base::CircularQueue<SequenceStates> interned_seqs_;
+};
+
+// GCC7 does not like us declaring these inside the class so define these
+// out-of-line.
+template <>
+PERFETTO_WARN_UNUSED_RESULT TrackEventData
+    TraceTokenBuffer::Extract<TrackEventData>(Id);
+template <>
+PERFETTO_WARN_UNUSED_RESULT inline TracePacketData
+TraceTokenBuffer::Extract<TracePacketData>(Id id) {
+  // See the comment in Append(TracePacketData) for why we do this.
+  return Extract<TrackEventData>(id).trace_packet_data;
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_SORTER_TRACE_TOKEN_BUFFER_H_
diff --git a/src/trace_processor/sorter/trace_token_buffer_unittest.cc b/src/trace_processor/sorter/trace_token_buffer_unittest.cc
new file mode 100644
index 0000000..7d6292f
--- /dev/null
+++ b/src/trace_processor/sorter/trace_token_buffer_unittest.cc
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/sorter/trace_token_buffer.h"
+
+#include "perfetto/base/compiler.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/trace_processor/trace_blob.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+class TraceTokenBufferUnittest : public testing::Test {
+ protected:
+  TraceTokenBuffer store;
+  TraceProcessorContext context;
+  PacketSequenceState state{&context};
+};
+
+TEST_F(TraceTokenBufferUnittest, TracePacketDataInOut) {
+  TraceBlobView tbv(TraceBlob::Allocate(1024));
+  TracePacketData tpd{tbv.copy(), state.current_generation()};
+
+  TraceTokenBuffer::Id id = store.Append(std::move(tpd));
+  TracePacketData extracted = store.Extract<TracePacketData>(id);
+  ASSERT_EQ(extracted.packet, tbv);
+  ASSERT_EQ(extracted.sequence_state, state.current_generation());
+}
+
+TEST_F(TraceTokenBufferUnittest, PacketAppendMultipleBlobs) {
+  TraceBlobView tbv_1(TraceBlob::Allocate(1024));
+  TraceBlobView tbv_2(TraceBlob::Allocate(2048));
+  TraceBlobView tbv_3(TraceBlob::Allocate(4096));
+
+  TraceTokenBuffer::Id id_1 =
+      store.Append(TracePacketData{tbv_1.copy(), state.current_generation()});
+  TraceTokenBuffer::Id id_2 =
+      store.Append(TracePacketData{tbv_2.copy(), state.current_generation()});
+  ASSERT_EQ(store.Extract<TracePacketData>(id_1).packet, tbv_1);
+  ASSERT_EQ(store.Extract<TracePacketData>(id_2).packet, tbv_2);
+
+  TraceTokenBuffer::Id id_3 =
+      store.Append(TracePacketData{tbv_3.copy(), state.current_generation()});
+  ASSERT_EQ(store.Extract<TracePacketData>(id_3).packet, tbv_3);
+}
+
+TEST_F(TraceTokenBufferUnittest, BlobSharing) {
+  TraceBlobView root(TraceBlob::Allocate(2048));
+  TraceBlobView tbv_1 = root.slice_off(0, 1024);
+  TraceBlobView tbv_2 = root.slice_off(1024, 512);
+  TraceBlobView tbv_3 = root.slice_off(1536, 512);
+
+  TraceTokenBuffer::Id id_1 =
+      store.Append(TracePacketData{tbv_1.copy(), state.current_generation()});
+  TraceTokenBuffer::Id id_2 =
+      store.Append(TracePacketData{tbv_2.copy(), state.current_generation()});
+  ASSERT_EQ(store.Extract<TracePacketData>(id_1).packet, tbv_1);
+  ASSERT_EQ(store.Extract<TracePacketData>(id_2).packet, tbv_2);
+
+  TraceTokenBuffer::Id id_3 =
+      store.Append(TracePacketData{tbv_3.copy(), state.current_generation()});
+  ASSERT_EQ(store.Extract<TracePacketData>(id_3).packet, tbv_3);
+}
+
+TEST_F(TraceTokenBufferUnittest, SequenceStateSharing) {
+  TraceBlobView root(TraceBlob::Allocate(2048));
+  TraceBlobView tbv_1 = root.slice_off(0, 1024);
+  TraceBlobView tbv_2 = root.slice_off(1024, 512);
+
+  TraceTokenBuffer::Id id_1 =
+      store.Append(TracePacketData{tbv_1.copy(), state.current_generation()});
+  TraceTokenBuffer::Id id_2 =
+      store.Append(TracePacketData{tbv_2.copy(), state.current_generation()});
+  ASSERT_EQ(store.Extract<TracePacketData>(id_1).sequence_state,
+            state.current_generation());
+  ASSERT_EQ(store.Extract<TracePacketData>(id_2).sequence_state,
+            state.current_generation());
+}
+
+TEST_F(TraceTokenBufferUnittest, ManySequenceState) {
+  TraceBlobView root(TraceBlob::Allocate(1024));
+
+  std::array<TraceTokenBuffer::Id, 1024> ids;
+  std::array<PacketSequenceStateGeneration*, 1024> refs;
+  for (uint32_t i = 0; i < 1024; ++i) {
+    refs[i] = state.current_generation().get();
+    ids[i] = store.Append(
+        TracePacketData{root.slice_off(i, 1), state.current_generation()});
+    state.OnIncrementalStateCleared();
+  }
+
+  for (uint32_t i = 0; i < 1024; ++i) {
+    ASSERT_EQ(refs[i],
+              store.Extract<TracePacketData>(ids[i]).sequence_state.get());
+  }
+}
+
+TEST_F(TraceTokenBufferUnittest, PacketLargeOffset) {
+  TraceBlobView tbv(TraceBlob::Allocate(256ul * 1024));
+
+  TraceBlobView slice_1 = tbv.slice_off(0, 1024ul);
+  TraceTokenBuffer::Id id_1 =
+      store.Append(TracePacketData{slice_1.copy(), state.current_generation()});
+  TracePacketData out_1 = store.Extract<TracePacketData>(id_1);
+  ASSERT_EQ(out_1.packet, slice_1);
+  ASSERT_EQ(out_1.sequence_state, state.current_generation());
+
+  TraceBlobView slice_2 = tbv.slice_off(128ul * 1024, 1024ul);
+  TraceTokenBuffer::Id id_2 =
+      store.Append(TracePacketData{slice_2.copy(), state.current_generation()});
+  TracePacketData out_2 = store.Extract<TracePacketData>(id_2);
+  ASSERT_EQ(out_2.packet, slice_2);
+  ASSERT_EQ(out_2.sequence_state, state.current_generation());
+}
+
+TEST_F(TraceTokenBufferUnittest, TrackEventDataInOut) {
+  TraceBlobView tbv(TraceBlob::Allocate(1234));
+  TrackEventData ted(tbv.copy(), state.current_generation());
+  ted.thread_instruction_count = 123;
+  ted.extra_counter_values = {10, 2, 0, 0, 0, 0, 0, 0};
+  auto counter_array = ted.extra_counter_values;
+
+  TraceTokenBuffer::Id id = store.Append(std::move(ted));
+  TrackEventData extracted = store.Extract<TrackEventData>(id);
+  ASSERT_EQ(extracted.trace_packet_data.packet, tbv);
+  ASSERT_EQ(extracted.trace_packet_data.sequence_state,
+            state.current_generation());
+  ASSERT_EQ(extracted.thread_instruction_count, 123);
+  ASSERT_EQ(extracted.thread_timestamp, base::nullopt);
+  ASSERT_DOUBLE_EQ(extracted.counter_value, 0.0);
+  ASSERT_EQ(extracted.extra_counter_values, counter_array);
+}
+
+TEST_F(TraceTokenBufferUnittest, ExtractOrAppendAfterFreeMemory) {
+  auto unused_res = store.Extract<TraceBlobView>(
+      store.Append(TraceBlobView(TraceBlob::Allocate(1234))));
+  base::ignore_result(unused_res);
+
+  store.FreeMemory();
+
+  TraceTokenBuffer::Id id =
+      store.Append(TraceBlobView(TraceBlob::Allocate(4567)));
+  TraceBlobView tbv = store.Extract<TraceBlobView>(id);
+  ASSERT_EQ(tbv.size(), 4567u);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/stdlib/android/binder.sql b/src/trace_processor/stdlib/android/binder.sql
index 7fb9611..f673d46 100644
--- a/src/trace_processor/stdlib/android/binder.sql
+++ b/src/trace_processor/stdlib/android/binder.sql
@@ -58,7 +58,21 @@
 -- @column server_dur dur of the server txn
 CREATE VIEW android_sync_binder_metrics_by_txn AS
 WITH
-  binder_txn AS (
+  -- Adding MATERIALIZED here matters in cases where there are few/no binder
+  -- transactions in the trace. Our cost estimation is not good enough to allow
+  -- the query planner to see through to this fact. Instead, our cost estimation
+  -- causes repeated queries on this table which is slow because it's an O(n)
+  -- query.
+  --
+  -- We should fix this by doing some (ideally all) of the following:
+  --  1) Add support for columnar tables in SQL which will allow for
+  --     "subsetting" the slice table to only contain binder transactions.
+  --  2) Make this query faster by adding improving string filtering.
+  --  3) Add caching so that even if these queries happen many times, they are
+  --     fast.
+  --  4) Improve cost estimation algorithm to allow the joins to happen the
+  --     right way around.
+  binder_txn AS MATERIALIZED (
     SELECT
       slice.id AS binder_txn_id,
       process.name AS process_name,
@@ -69,15 +83,11 @@
       slice.dur,
       thread.is_main_thread
     FROM slice
-    INNER JOIN thread_track
-      ON slice.track_id = thread_track.id
-    INNER JOIN thread
-      USING (utid)
-    INNER JOIN process
-      USING (upid)
-    LEFT JOIN slice non_existent ON non_existent.parent_id = slice.id
+    JOIN thread_track ON slice.track_id = thread_track.id
+    JOIN thread USING (utid)
+    JOIN process USING (upid)
     WHERE slice.name = 'binder transaction'
-    AND non_existent.depth IS NULL
+      AND NOT EXISTS(SELECT 1 FROM slice child WHERE child.parent_id = slice.id)
   ),
   binder_reply AS (
     SELECT
@@ -85,20 +95,20 @@
       binder_reply.ts AS server_ts,
       binder_reply.dur AS server_dur,
       binder_reply.id AS binder_reply_id,
-      IIF(aidl.name LIKE 'AIDL::%' AND aidl.depth = binder_reply.depth + 1, aidl.name, NULL) AS aidl_name,
       reply_thread.name AS server_thread,
       reply_process.name AS server_process,
       reply_thread.utid AS server_utid,
-      reply_process.upid AS server_upid
+      reply_process.upid AS server_upid,
+      aidl.name AS aidl_name
     FROM binder_txn
-    INNER JOIN flow binder_flow
-      ON binder_txn.binder_txn_id = binder_flow.slice_out
-    INNER JOIN slice binder_reply
-      ON binder_flow.slice_in = binder_reply.id
-    INNER JOIN thread_track reply_thread_track ON binder_reply.track_id = reply_thread_track.id
-    INNER JOIN thread reply_thread ON reply_thread.utid = reply_thread_track.utid
-    INNER JOIN process reply_process ON reply_process.upid = reply_thread.upid
-    LEFT JOIN slice aidl ON aidl.parent_id = binder_reply.id
+    JOIN flow binder_flow ON binder_txn.binder_txn_id = binder_flow.slice_out
+    JOIN slice binder_reply ON binder_flow.slice_in = binder_reply.id
+    JOIN thread_track reply_thread_track
+      ON binder_reply.track_id = reply_thread_track.id
+    JOIN thread reply_thread ON reply_thread.utid = reply_thread_track.utid
+    JOIN process reply_process ON reply_process.upid = reply_thread.upid
+    LEFT JOIN slice aidl
+      ON aidl.parent_id = binder_reply.id AND aidl.name LIKE 'AIDL::%'
   )
 SELECT
   MIN(aidl_name) AS aidl_name,
@@ -118,7 +128,7 @@
   server_ts,
   server_dur
 FROM binder_reply
-WHERE client_dur >= 0 AND server_dur >= 0 AND client_dur >= server_dur
+WHERE client_dur != -1 AND server_dur != -1 AND client_dur >= server_dur
 GROUP BY
   process_name,
   thread_name,
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index 430b1ab..43f3998 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -26,220 +26,224 @@
 // Compile time list of parsing and processing stats.
 // clang-format off
 #define PERFETTO_TP_STATS(F)                                                   \
-  F(android_br_parse_errors,            kSingle,  kError,    kTrace,    ""),   \
-  F(android_log_num_failed,             kSingle,  kError,    kTrace,    ""),   \
-  F(android_log_format_invalid,         kSingle,  kError,    kTrace,    ""),   \
-  F(android_log_num_skipped,            kSingle,  kInfo,     kTrace,    ""),   \
-  F(android_log_num_total,              kSingle,  kInfo,     kTrace,    ""),   \
-  F(counter_events_out_of_order,        kSingle,  kError,    kAnalysis, ""),   \
-  F(deobfuscate_location_parse_error,   kSingle,  kError,    kTrace,    ""),   \
-  F(energy_breakdown_missing_values,    kSingle,  kError,    kAnalysis, ""),   \
-  F(energy_descriptor_invalid,          kSingle,  kError,    kAnalysis, ""),   \
-  F(energy_uid_breakdown_missing_values,kSingle,  kError,    kAnalysis, ""),   \
-  F(frame_timeline_event_parser_errors, kSingle,  kInfo,     kAnalysis, ""),   \
-  F(ftrace_bundle_tokenizer_errors,     kSingle,  kError,    kAnalysis, ""),   \
-  F(ftrace_cpu_bytes_read_begin,        kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_bytes_read_end,          kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_bytes_read_delta,        kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_commit_overrun_begin,    kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_commit_overrun_end,      kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_commit_overrun_delta,    kIndexed, kError,    kTrace,    ""),   \
-  F(ftrace_cpu_dropped_events_begin,    kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_dropped_events_end,      kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_dropped_events_delta,    kIndexed, kError,    kTrace,    ""),   \
-  F(ftrace_cpu_entries_begin,           kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_entries_end,             kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_entries_delta,           kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_now_ts_begin,            kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_now_ts_end,              kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_oldest_event_ts_begin,   kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_oldest_event_ts_end,     kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_overrun_begin,           kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_overrun_end,             kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_overrun_delta,           kIndexed, kDataLoss, kTrace,           \
+  F(android_br_parse_errors,              kSingle,  kError,    kTrace,    ""), \
+  F(android_log_num_failed,               kSingle,  kError,    kTrace,    ""), \
+  F(android_log_format_invalid,           kSingle,  kError,    kTrace,    ""), \
+  F(android_log_num_skipped,              kSingle,  kInfo,     kTrace,    ""), \
+  F(android_log_num_total,                kSingle,  kInfo,     kTrace,    ""), \
+  F(counter_events_out_of_order,          kSingle,  kError,    kAnalysis, ""), \
+  F(deobfuscate_location_parse_error,     kSingle,  kError,    kTrace,    ""), \
+  F(energy_breakdown_missing_values,      kSingle,  kError,    kAnalysis, ""), \
+  F(energy_descriptor_invalid,            kSingle,  kError,    kAnalysis, ""), \
+  F(entity_state_descriptor_invalid,      kSingle,  kError,    kAnalysis, ""), \
+  F(entity_state_residency_invalid,       kSingle,  kError,    kAnalysis, ""), \
+  F(entity_state_residency_lookup_failed, kSingle,  kError,    kAnalysis, ""), \
+  F(energy_uid_breakdown_missing_values,  kSingle,  kError,    kAnalysis, ""), \
+  F(frame_timeline_event_parser_errors,   kSingle,  kInfo,     kAnalysis, ""), \
+  F(frame_timeline_unpaired_end_event,    kSingle,  kInfo,     kAnalysis, ""), \
+  F(ftrace_bundle_tokenizer_errors,       kSingle,  kError,    kAnalysis, ""), \
+  F(ftrace_cpu_bytes_read_begin,          kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_bytes_read_end,            kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_bytes_read_delta,          kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_commit_overrun_begin,      kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_commit_overrun_end,        kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_commit_overrun_delta,      kIndexed, kError,    kTrace,    ""), \
+  F(ftrace_cpu_dropped_events_begin,      kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_dropped_events_end,        kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_dropped_events_delta,      kIndexed, kError,    kTrace,    ""), \
+  F(ftrace_cpu_entries_begin,             kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_entries_end,               kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_entries_delta,             kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_now_ts_begin,              kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_now_ts_end,                kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_oldest_event_ts_begin,     kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_oldest_event_ts_end,       kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_overrun_begin,             kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_overrun_end,               kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_overrun_delta,             kIndexed, kDataLoss, kTrace,         \
       "The kernel ftrace buffer cannot keep up with the rate of events "       \
       "produced. Indexed by CPU. This is likely a misconfiguration."),         \
-  F(ftrace_cpu_read_events_begin,       kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_read_events_end,         kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_cpu_read_events_delta,       kIndexed, kInfo,     kTrace,    ""),   \
-  F(ftrace_setup_errors,                kSingle,  kError,    kTrace,           \
+  F(ftrace_cpu_read_events_begin,         kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_read_events_end,           kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_cpu_read_events_delta,         kIndexed, kInfo,     kTrace,    ""), \
+  F(ftrace_setup_errors,                  kSingle,  kInfo,     kTrace,         \
   "One or more atrace/ftrace categories were not found or failed to enable. "  \
   "See ftrace_setup_errors in the metadata table for more details."),          \
-  F(fuchsia_non_numeric_counters,       kSingle,  kError,    kAnalysis, ""),   \
-  F(fuchsia_timestamp_overflow,         kSingle,  kError,    kAnalysis, ""),   \
-  F(fuchsia_invalid_event,              kSingle,  kError,    kAnalysis, ""),   \
-  F(gpu_counters_invalid_spec,          kSingle,  kError,    kAnalysis, ""),   \
-  F(gpu_counters_missing_spec,          kSingle,  kError,    kAnalysis, ""),   \
-  F(gpu_render_stage_parser_errors,     kSingle,  kError,    kAnalysis, ""),   \
-  F(graphics_frame_event_parser_errors, kSingle,  kInfo,     kAnalysis, ""),   \
-  F(guess_trace_type_duration_ns,       kSingle,  kInfo,     kAnalysis, ""),   \
-  F(interned_data_tokenizer_errors,     kSingle,  kInfo,     kAnalysis, ""),   \
-  F(invalid_clock_snapshots,            kSingle,  kError,    kAnalysis, ""),   \
-  F(invalid_cpu_times,                  kSingle,  kError,    kAnalysis, ""),   \
-  F(meminfo_unknown_keys,               kSingle,  kError,    kAnalysis, ""),   \
-  F(mismatched_sched_switch_tids,       kSingle,  kError,    kAnalysis, ""),   \
-  F(mm_unknown_type,                    kSingle,  kError,    kAnalysis, ""),   \
-  F(parse_trace_duration_ns,            kSingle,  kInfo,     kAnalysis, ""),   \
-  F(power_rail_unknown_index,           kSingle,  kError,    kTrace,    ""),   \
-  F(proc_stat_unknown_counters,         kSingle,  kError,    kAnalysis, ""),   \
-  F(rss_stat_unknown_keys,              kSingle,  kError,    kAnalysis, ""),   \
-  F(rss_stat_negative_size,             kSingle,  kInfo,     kAnalysis, ""),   \
-  F(rss_stat_unknown_thread_for_mm_id,  kSingle,  kInfo,     kAnalysis, ""),   \
-  F(sched_switch_out_of_order,          kSingle,  kError,    kAnalysis, ""),   \
-  F(slice_out_of_order,                 kSingle,  kError,    kAnalysis, ""),   \
-  F(flow_duplicate_id,                  kSingle,  kError,    kTrace,    ""),   \
-  F(flow_no_enclosing_slice,            kSingle,  kError,    kTrace,    ""),   \
-  F(flow_step_without_start,            kSingle,  kInfo,     kTrace,    ""),   \
-  F(flow_end_without_start,             kSingle,  kInfo,     kTrace,    ""),   \
-  F(flow_invalid_id,                    kSingle,  kError,    kTrace,    ""),   \
-  F(flow_without_direction,             kSingle,  kError,    kTrace,    ""),   \
-  F(stackprofile_invalid_string_id,     kSingle,  kError,    kTrace,    ""),   \
-  F(stackprofile_invalid_mapping_id,    kSingle,  kError,    kTrace,    ""),   \
-  F(stackprofile_invalid_frame_id,      kSingle,  kError,    kTrace,    ""),   \
-  F(stackprofile_invalid_callstack_id,  kSingle,  kError,    kTrace,    ""),   \
-  F(stackprofile_parser_error,          kSingle,  kError,    kTrace,    ""),   \
-  F(systrace_parse_failure,             kSingle,  kError,    kAnalysis, ""),   \
-  F(task_state_invalid,                 kSingle,  kError,    kAnalysis, ""),   \
-  F(traced_buf_abi_violations,          kIndexed, kDataLoss, kTrace,    ""),   \
-  F(traced_buf_buffer_size,             kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_buf_bytes_overwritten,       kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_buf_bytes_read,              kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_buf_bytes_written,           kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_buf_chunks_discarded,        kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_buf_chunks_overwritten,      kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_buf_chunks_read,             kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_buf_chunks_rewritten,        kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_buf_chunks_written,          kIndexed, kInfo,     kTrace,    ""),   \
+  F(fuchsia_non_numeric_counters,         kSingle,  kError,    kAnalysis, ""), \
+  F(fuchsia_timestamp_overflow,           kSingle,  kError,    kAnalysis, ""), \
+  F(fuchsia_invalid_event,                kSingle,  kError,    kAnalysis, ""), \
+  F(gpu_counters_invalid_spec,            kSingle,  kError,    kAnalysis, ""), \
+  F(gpu_counters_missing_spec,            kSingle,  kError,    kAnalysis, ""), \
+  F(gpu_render_stage_parser_errors,       kSingle,  kError,    kAnalysis, ""), \
+  F(graphics_frame_event_parser_errors,   kSingle,  kInfo,     kAnalysis, ""), \
+  F(guess_trace_type_duration_ns,         kSingle,  kInfo,     kAnalysis, ""), \
+  F(interned_data_tokenizer_errors,       kSingle,  kInfo,     kAnalysis, ""), \
+  F(invalid_clock_snapshots,              kSingle,  kError,    kAnalysis, ""), \
+  F(invalid_cpu_times,                    kSingle,  kError,    kAnalysis, ""), \
+  F(meminfo_unknown_keys,                 kSingle,  kError,    kAnalysis, ""), \
+  F(mismatched_sched_switch_tids,         kSingle,  kError,    kAnalysis, ""), \
+  F(mm_unknown_type,                      kSingle,  kError,    kAnalysis, ""), \
+  F(parse_trace_duration_ns,              kSingle,  kInfo,     kAnalysis, ""), \
+  F(power_rail_unknown_index,             kSingle,  kError,    kTrace,    ""), \
+  F(proc_stat_unknown_counters,           kSingle,  kError,    kAnalysis, ""), \
+  F(rss_stat_unknown_keys,                kSingle,  kError,    kAnalysis, ""), \
+  F(rss_stat_negative_size,               kSingle,  kInfo,     kAnalysis, ""), \
+  F(rss_stat_unknown_thread_for_mm_id,    kSingle,  kInfo,     kAnalysis, ""), \
+  F(sched_switch_out_of_order,            kSingle,  kError,    kAnalysis, ""), \
+  F(slice_out_of_order,                   kSingle,  kError,    kAnalysis, ""), \
+  F(flow_duplicate_id,                    kSingle,  kError,    kTrace,    ""), \
+  F(flow_no_enclosing_slice,              kSingle,  kError,    kTrace,    ""), \
+  F(flow_step_without_start,              kSingle,  kInfo,     kTrace,    ""), \
+  F(flow_end_without_start,               kSingle,  kInfo,     kTrace,    ""), \
+  F(flow_invalid_id,                      kSingle,  kError,    kTrace,    ""), \
+  F(flow_without_direction,               kSingle,  kError,    kTrace,    ""), \
+  F(stackprofile_invalid_string_id,       kSingle,  kError,    kTrace,    ""), \
+  F(stackprofile_invalid_mapping_id,      kSingle,  kError,    kTrace,    ""), \
+  F(stackprofile_invalid_frame_id,        kSingle,  kError,    kTrace,    ""), \
+  F(stackprofile_invalid_callstack_id,    kSingle,  kError,    kTrace,    ""), \
+  F(stackprofile_parser_error,            kSingle,  kError,    kTrace,    ""), \
+  F(systrace_parse_failure,               kSingle,  kError,    kAnalysis, ""), \
+  F(task_state_invalid,                   kSingle,  kError,    kAnalysis, ""), \
+  F(traced_buf_abi_violations,            kIndexed, kDataLoss, kTrace,    ""), \
+  F(traced_buf_buffer_size,               kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_buf_bytes_overwritten,         kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_buf_bytes_read,                kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_buf_bytes_written,             kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_buf_chunks_discarded,          kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_buf_chunks_overwritten,        kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_buf_chunks_read,               kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_buf_chunks_rewritten,          kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_buf_chunks_written,            kIndexed, kInfo,     kTrace,    ""), \
   F(traced_buf_chunks_committed_out_of_order,                                  \
-                                        kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_buf_padding_bytes_cleared,   kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_buf_padding_bytes_written,   kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_buf_patches_failed,          kIndexed, kDataLoss, kTrace,    ""),   \
-  F(traced_buf_patches_succeeded,       kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_buf_readaheads_failed,       kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_buf_readaheads_succeeded,    kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_buf_trace_writer_packet_loss,kIndexed, kDataLoss, kTrace,    ""),   \
-  F(traced_buf_write_wrap_count,        kIndexed, kInfo,     kTrace,    ""),   \
-  F(traced_chunks_discarded,            kSingle,  kInfo,     kTrace,    ""),   \
-  F(traced_data_sources_registered,     kSingle,  kInfo,     kTrace,    ""),   \
-  F(traced_data_sources_seen,           kSingle,  kInfo,     kTrace,    ""),   \
-  F(traced_final_flush_failed,          kSingle,  kDataLoss, kTrace,    ""),   \
-  F(traced_final_flush_succeeded,       kSingle,  kInfo,     kTrace,    ""),   \
-  F(traced_flushes_failed,              kSingle,  kDataLoss, kTrace,    ""),   \
-  F(traced_flushes_requested,           kSingle,  kInfo,     kTrace,    ""),   \
-  F(traced_flushes_succeeded,           kSingle,  kInfo,     kTrace,    ""),   \
-  F(traced_patches_discarded,           kSingle,  kInfo,     kTrace,    ""),   \
-  F(traced_producers_connected,         kSingle,  kInfo,     kTrace,    ""),   \
-  F(traced_producers_seen,              kSingle,  kInfo,     kTrace,    ""),   \
-  F(traced_total_buffers,               kSingle,  kInfo,     kTrace,    ""),   \
-  F(traced_tracing_sessions,            kSingle,  kInfo,     kTrace,    ""),   \
-  F(track_event_parser_errors,          kSingle,  kInfo,     kAnalysis, ""),   \
+                                          kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_buf_padding_bytes_cleared,     kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_buf_padding_bytes_written,     kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_buf_patches_failed,            kIndexed, kDataLoss, kTrace,    ""), \
+  F(traced_buf_patches_succeeded,         kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_buf_readaheads_failed,         kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_buf_readaheads_succeeded,      kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_buf_trace_writer_packet_loss,  kIndexed, kDataLoss, kTrace,    ""), \
+  F(traced_buf_write_wrap_count,          kIndexed, kInfo,     kTrace,    ""), \
+  F(traced_chunks_discarded,              kSingle,  kInfo,     kTrace,    ""), \
+  F(traced_data_sources_registered,       kSingle,  kInfo,     kTrace,    ""), \
+  F(traced_data_sources_seen,             kSingle,  kInfo,     kTrace,    ""), \
+  F(traced_final_flush_failed,            kSingle,  kDataLoss, kTrace,    ""), \
+  F(traced_final_flush_succeeded,         kSingle,  kInfo,     kTrace,    ""), \
+  F(traced_flushes_failed,                kSingle,  kDataLoss, kTrace,    ""), \
+  F(traced_flushes_requested,             kSingle,  kInfo,     kTrace,    ""), \
+  F(traced_flushes_succeeded,             kSingle,  kInfo,     kTrace,    ""), \
+  F(traced_patches_discarded,             kSingle,  kInfo,     kTrace,    ""), \
+  F(traced_producers_connected,           kSingle,  kInfo,     kTrace,    ""), \
+  F(traced_producers_seen,                kSingle,  kInfo,     kTrace,    ""), \
+  F(traced_total_buffers,                 kSingle,  kInfo,     kTrace,    ""), \
+  F(traced_tracing_sessions,              kSingle,  kInfo,     kTrace,    ""), \
+  F(track_event_parser_errors,            kSingle,  kInfo,     kAnalysis, ""), \
   F(track_event_dropped_packets_outside_of_range_of_interest,                  \
-                                        kSingle,  kInfo,     kAnalysis,        \
+                                          kSingle,  kInfo,     kAnalysis,      \
       "The number of TrackEvent packets dropped by trace processor due to "    \
       "being outside of the range of interest. This happens if a trace has a " \
       "TrackEventRangeOfInterest packet, and track event dropping is "         \
       "enabled."),                                                             \
-  F(track_event_tokenizer_errors,       kSingle,  kInfo,     kAnalysis, ""),   \
-  F(track_event_thread_invalid_end,     kSingle,  kError,    kTrace,           \
+  F(track_event_tokenizer_errors,         kSingle,  kInfo,     kAnalysis, ""), \
+  F(track_event_thread_invalid_end,       kSingle,  kError,    kTrace,         \
       "The end event for a thread track does not match a track event "         \
       "begin event. This can happen on mixed atrace/track_event traces "       \
       "and is usually caused by data loss or bugs when the events are "        \
       "emitted. The outcome of this is that slices can appear to be closed "   \
       "before they were closed in reality"),                                   \
-  F(tokenizer_skipped_packets,          kSingle,  kInfo,     kAnalysis, ""),   \
-  F(vmstat_unknown_keys,                kSingle,  kError,    kAnalysis, ""),   \
+  F(tokenizer_skipped_packets,            kSingle,  kInfo,     kAnalysis, ""), \
+  F(vmstat_unknown_keys,                  kSingle,  kError,    kAnalysis, ""), \
   F(vulkan_allocations_invalid_string_id,                                      \
-                                        kSingle,  kError,    kTrace,    ""),   \
-  F(clock_sync_failure,                 kSingle,  kError,    kAnalysis, ""),   \
-  F(clock_sync_cache_miss,              kSingle,  kInfo,     kAnalysis, ""),   \
-  F(process_tracker_errors,             kSingle,  kError,    kAnalysis, ""),   \
-  F(json_tokenizer_failure,             kSingle,  kError,    kTrace,    ""),   \
-  F(json_parser_failure,                kSingle,  kError,    kTrace,    ""),   \
-  F(json_display_time_unit,             kSingle,  kInfo,     kTrace,           \
+                                          kSingle,  kError,    kTrace,    ""), \
+  F(clock_sync_failure,                   kSingle,  kError,    kAnalysis, ""), \
+  F(clock_sync_cache_miss,                kSingle,  kInfo,     kAnalysis, ""), \
+  F(process_tracker_errors,               kSingle,  kError,    kAnalysis, ""), \
+  F(json_tokenizer_failure,               kSingle,  kError,    kTrace,    ""), \
+  F(json_parser_failure,                  kSingle,  kError,    kTrace,    ""), \
+  F(json_display_time_unit,               kSingle,  kInfo,     kTrace,         \
       "The displayTimeUnit key was set in the JSON trace. In some prior "      \
       "versions of trace processor this key could effect how the trace "       \
       "processor parsed timestamps and durations. In this version the key is " \
       "ignored which more closely matches the bavahiour of catapult."),        \
-  F(heap_graph_invalid_string_id,       kIndexed, kError,    kTrace,    ""),   \
-  F(heap_graph_non_finalized_graph,     kSingle,  kError,    kTrace,    ""),   \
-  F(heap_graph_malformed_packet,        kIndexed, kError,    kTrace,    ""),   \
-  F(heap_graph_missing_packet,          kIndexed, kError,    kTrace,    ""),   \
-  F(heapprofd_buffer_corrupted,         kIndexed, kError,    kTrace,           \
+  F(heap_graph_invalid_string_id,         kIndexed, kError,    kTrace,    ""), \
+  F(heap_graph_non_finalized_graph,       kSingle,  kError,    kTrace,    ""), \
+  F(heap_graph_malformed_packet,          kIndexed, kError,    kTrace,    ""), \
+  F(heap_graph_missing_packet,            kIndexed, kError,    kTrace,    ""), \
+  F(heapprofd_buffer_corrupted,           kIndexed, kError,    kTrace,         \
       "Shared memory buffer corrupted. This is a bug or memory corruption "    \
       "in the target. Indexed by target upid."),                               \
-  F(heapprofd_hit_guardrail,            kIndexed, kError,    kTrace,           \
+  F(heapprofd_hit_guardrail,              kIndexed, kError,    kTrace,         \
       "HeapprofdConfig specified a CPU or Memory Guardrail that was hit. "     \
       "Indexed by target upid."),                                              \
-  F(heapprofd_buffer_overran,           kIndexed, kDataLoss, kTrace,           \
+  F(heapprofd_buffer_overran,             kIndexed, kDataLoss, kTrace,         \
       "The shared memory buffer between the target and heapprofd overran. "    \
       "The profile was truncated early. Indexed by target upid."),             \
-  F(heapprofd_client_error,             kIndexed, kError,    kTrace,           \
+  F(heapprofd_client_error,               kIndexed, kError,    kTrace,         \
       "The heapprofd client ran into a problem and disconnected. "             \
       "See profile_packet.proto  for error codes."),                           \
-  F(heapprofd_client_disconnected,      kIndexed, kInfo,     kTrace,    ""),   \
-  F(heapprofd_malformed_packet,         kIndexed, kError,    kTrace,    ""),   \
-  F(heapprofd_missing_packet,           kSingle,  kError,    kTrace,    ""),   \
-  F(heapprofd_rejected_concurrent,      kIndexed, kError,    kTrace,           \
+  F(heapprofd_client_disconnected,        kIndexed, kInfo,     kTrace,    ""), \
+  F(heapprofd_malformed_packet,           kIndexed, kError,    kTrace,    ""), \
+  F(heapprofd_missing_packet,             kSingle,  kError,    kTrace,    ""), \
+  F(heapprofd_rejected_concurrent,        kIndexed, kError,    kTrace,         \
       "The target was already profiled by another tracing session, so the "    \
       "profile was not taken. Indexed by target upid."),                       \
-  F(heapprofd_non_finalized_profile,    kSingle,  kError,    kTrace,    ""),   \
+  F(heapprofd_non_finalized_profile,      kSingle,  kError,    kTrace,    ""), \
   F(heapprofd_sampling_interval_adjusted,                                      \
-      kIndexed, kInfo,    kTrace,                                              \
+    kIndexed, kInfo,    kTrace,                                                \
       "By how many byes the interval for PID was increased "                   \
       "by adaptive sampling."),                                                \
-  F(heapprofd_unwind_time_us,           kIndexed, kInfo,     kTrace,           \
+  F(heapprofd_unwind_time_us,             kIndexed, kInfo,     kTrace,         \
       "Time spent unwinding callstacks."),                                     \
-  F(heapprofd_unwind_samples,           kIndexed, kInfo,     kTrace,           \
+  F(heapprofd_unwind_samples,             kIndexed, kInfo,     kTrace,         \
       "Number of samples unwound."),                                           \
-  F(heapprofd_client_spinlock_blocked,  kIndexed, kInfo,     kTrace,           \
+  F(heapprofd_client_spinlock_blocked,    kIndexed, kInfo,     kTrace,         \
        "Time (us) the heapprofd client was blocked on the spinlock."),         \
-  F(heapprofd_last_profile_timestamp,   kIndexed, kInfo,     kTrace,           \
+  F(heapprofd_last_profile_timestamp,     kIndexed, kInfo,     kTrace,         \
        "The timestamp (in trace time) for the last dump for a process"),       \
-  F(symbolization_tmp_build_id_not_found,   kSingle,  kError,    kAnalysis,    \
+  F(symbolization_tmp_build_id_not_found,     kSingle,  kError,    kAnalysis,  \
        "Number of file mappings in /data/local/tmp without a build id. "       \
        "Symbolization doesn't work for executables in /data/local/tmp "        \
        "because of SELinux. Please use /data/local/tests"),                    \
-  F(metatrace_overruns,                 kSingle,  kError,    kTrace,    ""),   \
-  F(packages_list_has_parse_errors,     kSingle,  kError,    kTrace,    ""),   \
-  F(packages_list_has_read_errors,      kSingle,  kError,    kTrace,    ""),   \
-  F(game_intervention_has_parse_errors, kSingle,  kError,    kTrace,           \
+  F(metatrace_overruns,                   kSingle,  kError,    kTrace,    ""), \
+  F(packages_list_has_parse_errors,       kSingle,  kError,    kTrace,    ""), \
+  F(packages_list_has_read_errors,        kSingle,  kError,    kTrace,    ""), \
+  F(game_intervention_has_parse_errors,   kSingle,  kError,    kTrace,         \
        "One or more parsing errors occurred. This could result from "          \
        "unknown game more or intervention added to the file to be parsed."),   \
-  F(game_intervention_has_read_errors,  kSingle,  kError,    kTrace,           \
+  F(game_intervention_has_read_errors,    kSingle,  kError,    kTrace,         \
        "The file to be parsed can't be opened. This can happend when "         \
        "the file name is not found or no permission to access the file"),      \
-  F(compact_sched_has_parse_errors,     kSingle,  kError,    kTrace,    ""),   \
-  F(misplaced_end_event,                kSingle,  kDataLoss, kAnalysis, ""),   \
-  F(truncated_sys_write_duration,       kSingle,  kDataLoss,  kAnalysis,       \
+  F(compact_sched_has_parse_errors,       kSingle,  kError,    kTrace,    ""), \
+  F(misplaced_end_event,                  kSingle,  kDataLoss, kAnalysis, ""), \
+  F(truncated_sys_write_duration,         kSingle,  kDataLoss,  kAnalysis,     \
       "Count of sys_write slices that have a truncated duration to resolve "   \
       "nesting incompatibilities with atrace slices. Real durations "          \
       "can be recovered via the |raw| table."),                                \
-  F(sched_waking_out_of_order,          kSingle,  kError,    kAnalysis, ""),   \
-  F(compact_sched_switch_skipped,       kSingle,  kInfo,     kAnalysis, ""),   \
-  F(compact_sched_waking_skipped,       kSingle,  kInfo,     kAnalysis, ""),   \
-  F(empty_chrome_metadata,              kSingle,  kError,    kTrace,    ""),   \
-  F(ninja_parse_errors,                 kSingle,  kError,    kTrace,    ""),   \
-  F(perf_cpu_lost_records,              kIndexed, kDataLoss, kTrace,    ""),   \
-  F(perf_process_shard_count,           kIndexed, kInfo,     kTrace,    ""),   \
-  F(perf_chosen_process_shard,          kIndexed, kInfo,     kTrace,    ""),   \
-  F(perf_guardrail_stop_ts,             kIndexed, kDataLoss, kTrace,    ""),   \
-  F(perf_samples_skipped,               kSingle,  kInfo,     kTrace,    ""),   \
-  F(perf_samples_skipped_dataloss,      kSingle,  kDataLoss, kTrace,    ""),   \
-  F(memory_snapshot_parser_failure,     kSingle,  kError,    kAnalysis, ""),   \
-  F(thread_time_in_state_out_of_order,  kSingle,  kError,    kAnalysis, ""),   \
+  F(sched_waking_out_of_order,            kSingle,  kError,    kAnalysis, ""), \
+  F(compact_sched_switch_skipped,         kSingle,  kInfo,     kAnalysis, ""), \
+  F(compact_sched_waking_skipped,         kSingle,  kInfo,     kAnalysis, ""), \
+  F(empty_chrome_metadata,                kSingle,  kError,    kTrace,    ""), \
+  F(ninja_parse_errors,                   kSingle,  kError,    kTrace,    ""), \
+  F(perf_cpu_lost_records,                kIndexed, kDataLoss, kTrace,    ""), \
+  F(perf_process_shard_count,             kIndexed, kInfo,     kTrace,    ""), \
+  F(perf_chosen_process_shard,            kIndexed, kInfo,     kTrace,    ""), \
+  F(perf_guardrail_stop_ts,               kIndexed, kDataLoss, kTrace,    ""), \
+  F(perf_samples_skipped,                 kSingle,  kInfo,     kTrace,    ""), \
+  F(perf_samples_skipped_dataloss,        kSingle,  kDataLoss, kTrace,    ""), \
+  F(memory_snapshot_parser_failure,       kSingle,  kError,    kAnalysis, ""), \
+  F(thread_time_in_state_out_of_order,    kSingle,  kError,    kAnalysis, ""), \
   F(thread_time_in_state_unknown_cpu_freq,                                     \
-                                        kSingle,  kError,    kAnalysis, ""),   \
-  F(ftrace_packet_before_tracing_start, kSingle,  kInfo,     kAnalysis,        \
+                                          kSingle,  kError,    kAnalysis, ""), \
+  F(ftrace_packet_before_tracing_start,   kSingle,  kInfo,     kAnalysis,      \
       "An ftrace packet was seen before the tracing start timestamp from "     \
       "the tracing service. This happens if the ftrace buffers were not "      \
       "cleared properly. These packets are silently dropped by trace "         \
       "processor."),                                                           \
-  F(sorter_push_event_out_of_order,     kSingle, kError,     kTrace,           \
+  F(sorter_push_event_out_of_order,       kSingle, kError,     kTrace,         \
       "Trace events are out of order event after sorting. This can happen "    \
       "due to many factors including clock sync drift, producers emitting "    \
       "events out of order or a bug in trace processor's logic of sorting."),  \
-  F(unknown_extension_fields,           kSingle,  kError,    kTrace,           \
+  F(unknown_extension_fields,             kSingle,  kError,    kTrace,         \
       "TraceEvent had unknown extension fields, which might result in "        \
       "missing some arguments. You may need a newer version of trace "         \
       "processor to parse them.")
diff --git a/src/trace_processor/util/BUILD.gn b/src/trace_processor/util/BUILD.gn
index 98ff445..6613533 100644
--- a/src/trace_processor/util/BUILD.gn
+++ b/src/trace_processor/util/BUILD.gn
@@ -124,6 +124,7 @@
     "../../../gn:default_deps",
     "../../../protos/perfetto/common:zero",
     "../../../protos/perfetto/trace/interned_data:zero",
+    "../../../protos/perfetto/trace/profiling:zero",
     "../../../protos/perfetto/trace/track_event:zero",
     "../../../protos/perfetto/trace_processor:zero",
     "../../protozero",
@@ -243,10 +244,15 @@
     "../../../gn:gtest_and_gmock",
     "../../../protos/perfetto/common:zero",
     "../../../protos/perfetto/trace:non_minimal_zero",
+    "../../../protos/perfetto/trace/interned_data:zero",
+    "../../../protos/perfetto/trace/profiling:zero",
     "../../../protos/perfetto/trace/track_event:zero",
     "../../protozero",
     "../../protozero:testing_messages_zero",
     "../importers/proto:gen_cc_track_event_descriptor",
+    "../importers/proto:minimal",
+    "../storage",
+    "../types",
   ]
   if (enable_perfetto_zlib) {
     sources += [ "gzip_utils_unittest.cc" ]
diff --git a/src/trace_processor/util/bump_allocator.cc b/src/trace_processor/util/bump_allocator.cc
index 1c00a8c..1469525 100644
--- a/src/trace_processor/util/bump_allocator.cc
+++ b/src/trace_processor/util/bump_allocator.cc
@@ -15,6 +15,7 @@
  */
 
 #include "src/trace_processor/util/bump_allocator.h"
+#include <limits>
 
 #include "perfetto/base/compiler.h"
 #include "perfetto/base/logging.h"
@@ -75,18 +76,22 @@
 }
 
 void BumpAllocator::Free(AllocId id) {
-  Chunk& chunk = chunks_.at(ChunkIndexToQueueIndex(id.chunk_index));
+  uint64_t queue_index = ChunkIndexToQueueIndex(id.chunk_index);
+  PERFETTO_DCHECK(queue_index <= std::numeric_limits<size_t>::max());
+  Chunk& chunk = chunks_.at(static_cast<size_t>(queue_index));
   PERFETTO_DCHECK(chunk.unfreed_allocations > 0);
   chunk.unfreed_allocations--;
 }
 
 void* BumpAllocator::GetPointer(AllocId id) {
-  uint32_t queue_index = ChunkIndexToQueueIndex(id.chunk_index);
-  return chunks_.at(queue_index).allocation.get() + id.chunk_offset;
+  uint64_t queue_index = ChunkIndexToQueueIndex(id.chunk_index);
+  PERFETTO_CHECK(queue_index <= std::numeric_limits<size_t>::max());
+  return chunks_.at(static_cast<size_t>(queue_index)).allocation.get() +
+         id.chunk_offset;
 }
 
-uint32_t BumpAllocator::EraseFrontFreeChunks() {
-  uint32_t to_erase_chunks = 0;
+uint64_t BumpAllocator::EraseFrontFreeChunks() {
+  size_t to_erase_chunks = 0;
   for (; to_erase_chunks < chunks_.size(); ++to_erase_chunks) {
     // Break on the first chunk which still has unfreed allocations.
     if (chunks_.at(to_erase_chunks).unfreed_allocations > 0) {
@@ -98,11 +103,14 @@
   return to_erase_chunks;
 }
 
-uint32_t BumpAllocator::PastEndSerializedId() {
+BumpAllocator::AllocId BumpAllocator::PastTheEndId() {
   if (chunks_.empty()) {
-    return AllocId{erased_front_chunks_count_, 0}.Serialize();
+    return AllocId{erased_front_chunks_count_, 0};
   }
-  return AllocId{LastChunkIndex(), chunks_.back().bump_offset}.Serialize();
+  if (chunks_.back().bump_offset == kChunkSize) {
+    return AllocId{LastChunkIndex() + 1, 0};
+  }
+  return AllocId{LastChunkIndex(), chunks_.back().bump_offset};
 }
 
 base::Optional<BumpAllocator::AllocId> BumpAllocator::TryAllocInLastChunk(
diff --git a/src/trace_processor/util/bump_allocator.h b/src/trace_processor/util/bump_allocator.h
index cd5e593..b7b3499 100644
--- a/src/trace_processor/util/bump_allocator.h
+++ b/src/trace_processor/util/bump_allocator.h
@@ -22,6 +22,7 @@
 #include <cstring>
 #include <limits>
 #include <memory>
+#include <tuple>
 #include "perfetto/ext/base/circular_queue.h"
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/utils.h"
@@ -50,59 +51,55 @@
 // [1] https://rust-hosted-langs.github.io/book/chapter-simple-bump.html
 class BumpAllocator {
  public:
-  // The limit on the total amount of memory which can be allocated. Required
-  // as we can only address 4GB of memory with AllocId.
-  static constexpr uint64_t kAllocLimit = 4ull * 1024 * 1024 * 1024;  // 4GB
+  // The limit on the total number of bits which can be used to represent
+  // the chunk id.
+  static constexpr uint64_t kMaxIdBits = 60;
+
+  // The limit on the total amount of memory which can be allocated.
+  static constexpr uint64_t kAllocLimit = 1ull << kMaxIdBits;
 
   // The size of the "large chunk" requested from the system allocator.
   // The size of this value trades-off between unused memory use vs CPU cost
   // of going to the system allocator. 64KB feels a good trade-off there.
-  static constexpr uint32_t kChunkSize = 64u * 1024;  // 64KB
+  static constexpr uint64_t kChunkSize = 64ull * 1024;  // 64KB
 
   // The maximum number of chunks which this allocator can have.
-  static constexpr uint32_t kMaxChunkCount = kAllocLimit / kChunkSize;
+  static constexpr uint64_t kMaxChunkCount = kAllocLimit / kChunkSize;
 
   // The number of bits used to represent the offset the chunk in AllocId.
   //
   // This is simply log2(kChunkSize): we have a separate constant as log2 is
   // not a constexpr function: the static assets below verify this stays in
   // sync.
-  static constexpr uint32_t kChunkOffsetAllocIdBits = 16u;
+  static constexpr uint64_t kChunkOffsetAllocIdBits = 16u;
 
   // The number of bits used to represent the chunk index in AllocId.
-  static constexpr uint32_t kChunkIndexAllocIdBits =
-      32u - kChunkOffsetAllocIdBits;
+  static constexpr uint64_t kChunkIndexAllocIdBits =
+      kMaxIdBits - kChunkOffsetAllocIdBits;
 
   // Represents an allocation returned from the allocator. We return this
   // instead of just returning a pointer to allow looking up a chunk an
   // allocation belongs to without needing having to scan chunks.
   struct AllocId {
-    uint32_t chunk_index : kChunkIndexAllocIdBits;
-    uint32_t chunk_offset : kChunkOffsetAllocIdBits;
+    uint64_t chunk_index : kChunkIndexAllocIdBits;
+    uint64_t chunk_offset : kChunkOffsetAllocIdBits;
 
-    uint32_t Serialize() const {
-      return static_cast<uint32_t>(chunk_index) << kChunkOffsetAllocIdBits |
-             chunk_offset;
+    // Comparision operators mainly for sorting.
+    bool operator<(const AllocId& other) const {
+      return std::tie(chunk_index, chunk_offset) <
+             std::tie(other.chunk_index, other.chunk_offset);
     }
-
-    static AllocId FromSerialized(uint32_t serialized) {
-      AllocId id;
-      id.chunk_index = serialized >> kChunkOffsetAllocIdBits;
-      id.chunk_offset = serialized;
-      return id;
-    }
+    bool operator>=(const AllocId& other) const { return !(*this < other); }
+    bool operator>(const AllocId& other) const { return other < *this; }
   };
-  static_assert(sizeof(AllocId) == sizeof(uint32_t),
-                "AllocId should be 32-bit in size to allow serialization");
+  static_assert(sizeof(AllocId) == sizeof(uint64_t),
+                "AllocId should be 64-bit in size to allow serialization");
   static_assert(
-      kMaxChunkCount == (1 << kChunkIndexAllocIdBits),
+      kMaxChunkCount == (1ull << kChunkIndexAllocIdBits),
       "Max chunk count must match the number of bits used for chunk indices");
   static_assert(
       kChunkSize == (1 << kChunkOffsetAllocIdBits),
       "Chunk size must match the number of bits used for offset within chunk");
-  static_assert(kAllocLimit == 1ull << sizeof(AllocId) * 8,
-                "Total limit on allocations must be equal to the number of "
-                "bits used for AllocId");
 
   BumpAllocator();
 
@@ -138,17 +135,17 @@
   // in the chunks have been freed. This releases the memory back to the system.
   //
   // Returns the number of chunks freed.
-  uint32_t EraseFrontFreeChunks();
+  uint64_t EraseFrontFreeChunks();
 
   // Returns a "past the end" serialized AllocId i.e. a serialized value
   // greater than all previously returned AllocIds.
-  uint32_t PastEndSerializedId();
+  AllocId PastTheEndId();
 
   // Returns the number of erased chunks from the start of this allocator.
   //
   // This value may change any time |EraseFrontFreeChunks| is called but is
   // constant otherwise.
-  uint32_t erased_front_chunks_count() const {
+  uint64_t erased_front_chunks_count() const {
     return erased_front_chunks_count_;
   }
 
@@ -171,19 +168,19 @@
   // an AllocId if this was successful or base::nullopt otherwise.
   base::Optional<AllocId> TryAllocInLastChunk(uint32_t size);
 
-  uint32_t ChunkIndexToQueueIndex(uint32_t chunk_index) const {
+  uint64_t ChunkIndexToQueueIndex(uint64_t chunk_index) const {
     return chunk_index - erased_front_chunks_count_;
   }
-  uint32_t QueueIndexToChunkIndex(uint32_t index_in_chunks_vec) const {
+  uint64_t QueueIndexToChunkIndex(uint64_t index_in_chunks_vec) const {
     return erased_front_chunks_count_ + index_in_chunks_vec;
   }
-  uint32_t LastChunkIndex() const {
+  uint64_t LastChunkIndex() const {
     PERFETTO_DCHECK(!chunks_.empty());
-    return QueueIndexToChunkIndex(static_cast<uint32_t>(chunks_.size() - 1));
+    return QueueIndexToChunkIndex(static_cast<uint64_t>(chunks_.size() - 1));
   }
 
   base::CircularQueue<Chunk> chunks_;
-  uint32_t erased_front_chunks_count_ = 0;
+  uint64_t erased_front_chunks_count_ = 0;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/util/bump_allocator_unittest.cc b/src/trace_processor/util/bump_allocator_unittest.cc
index 4608be6..38490fd 100644
--- a/src/trace_processor/util/bump_allocator_unittest.cc
+++ b/src/trace_processor/util/bump_allocator_unittest.cc
@@ -70,26 +70,13 @@
   allocator_.EraseFrontFreeChunks();
 }
 
-TEST_F(BumpAllocatorUnittest, Serialize) {
-  BumpAllocator::AllocId id = allocator_.Alloc(8);
-  ASSERT_EQ(id.Serialize(), 0u);
-  ASSERT_EQ(allocator_.PastEndSerializedId(), 8u);
+TEST_F(BumpAllocatorUnittest, PastEndOnChunkBoundary) {
+  BumpAllocator::AllocId id = allocator_.Alloc(BumpAllocator::kChunkSize);
+  BumpAllocator::AllocId past_end = allocator_.PastTheEndId();
+  ASSERT_GT(past_end, id);
+  ASSERT_EQ(past_end.chunk_index, 1u);
+  ASSERT_EQ(past_end.chunk_offset, 0u);
   allocator_.Free(id);
-
-  id = allocator_.Alloc(8);
-  ASSERT_EQ(id.Serialize(), 8u);
-  allocator_.Free(id);
-
-  id = allocator_.Alloc(BumpAllocator::kChunkSize);
-  ASSERT_EQ(id.Serialize(), BumpAllocator::kChunkSize);
-  allocator_.Free(id);
-}
-
-TEST_F(BumpAllocatorUnittest, HighNumberSerialize) {
-  BumpAllocator::AllocId id = BumpAllocator::AllocId::FromSerialized(1138352);
-  ASSERT_EQ(id.chunk_index, 1138352 / BumpAllocator::kChunkSize);
-  ASSERT_EQ(id.chunk_offset, 1138352 % BumpAllocator::kChunkSize);
-  ASSERT_EQ(id.Serialize(), 1138352u);
 }
 
 TEST_F(BumpAllocatorUnittest, EraseFrontAccounting) {
@@ -105,8 +92,7 @@
   AllocateWriteReadAndFree(8);
   allocator_.EraseFrontFreeChunks();
 
-  auto past_id =
-      BumpAllocator::AllocId::FromSerialized(allocator_.PastEndSerializedId());
+  auto past_id = allocator_.PastTheEndId();
   ASSERT_EQ(past_id.chunk_index, 1u);
   ASSERT_EQ(past_id.chunk_offset, 0u);
 
diff --git a/src/trace_processor/util/debug_annotation_parser.cc b/src/trace_processor/util/debug_annotation_parser.cc
index 7b49d2d..0c2792e 100644
--- a/src/trace_processor/util/debug_annotation_parser.cc
+++ b/src/trace_processor/util/debug_annotation_parser.cc
@@ -15,10 +15,13 @@
  */
 
 #include "src/trace_processor/util/debug_annotation_parser.h"
+
 #include "perfetto/base/build_config.h"
-#include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
 #include "src/trace_processor/util/interned_message_view.h"
 
+#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
+#include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
+
 namespace perfetto {
 namespace trace_processor {
 namespace util {
@@ -81,6 +84,15 @@
     delegate.AddDouble(context_name, annotation.double_value());
   } else if (annotation.has_string_value()) {
     delegate.AddString(context_name, annotation.string_value());
+  } else if (annotation.has_string_value_iid()) {
+    auto* decoder = delegate.GetInternedMessage(
+        protos::pbzero::InternedData::kDebugAnnotationStringValues,
+        annotation.string_value_iid());
+    if (!decoder) {
+      return {base::ErrStatus("Debug annotation with invalid string_value_iid"),
+              false};
+    }
+    delegate.AddString(context_name, decoder->str().ToStdString());
   } else if (annotation.has_pointer_value()) {
     delegate.AddPointer(context_name, reinterpret_cast<const void*>(
                                           annotation.pointer_value()));
diff --git a/src/trace_processor/util/debug_annotation_parser_unittest.cc b/src/trace_processor/util/debug_annotation_parser_unittest.cc
index 7244d02..d9a4168 100644
--- a/src/trace_processor/util/debug_annotation_parser_unittest.cc
+++ b/src/trace_processor/util/debug_annotation_parser_unittest.cc
@@ -18,13 +18,19 @@
 
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
+#include "perfetto/trace_processor/trace_blob.h"
 #include "perfetto/trace_processor/trace_blob_view.h"
 #include "protos/perfetto/common/descriptor.pbzero.h"
+#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
 #include "protos/perfetto/trace/test_event.pbzero.h"
 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
 #include "protos/perfetto/trace/track_event/source_location.pbzero.h"
 #include "src/protozero/test/example_proto/test_messages.pbzero.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/test_messages.descriptor.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 #include "src/trace_processor/util/interned_message_view.h"
 #include "src/trace_processor/util/proto_to_args_parser.h"
 #include "test/gtest_and_gmock.h"
@@ -48,10 +54,14 @@
 class DebugAnnotationParserTest : public ::testing::Test,
                                   public ProtoToArgsParser::Delegate {
  protected:
-  DebugAnnotationParserTest() {}
+  DebugAnnotationParserTest() : sequence_state_(&context_) {
+    context_.storage.reset(new TraceStorage());
+  }
 
   const std::vector<std::string>& args() const { return args_; }
 
+  PacketSequenceState* mutable_seq_state() { return &sequence_state_; }
+
  private:
   using Key = ProtoToArgsParser::Key;
 
@@ -120,14 +130,25 @@
     return ++array_indices_[array_key];
   }
 
-  InternedMessageView* GetInternedMessageView(uint32_t, uint64_t) override {
-    return nullptr;
+  InternedMessageView* GetInternedMessageView(uint32_t field_id,
+                                              uint64_t iid) override {
+    if (field_id !=
+        protos::pbzero::InternedData::kDebugAnnotationStringValuesFieldNumber) {
+      return nullptr;
+    }
+    return sequence_state_.current_generation()->GetInternedMessageView(
+        field_id, iid);
   }
 
-  PacketSequenceStateGeneration* seq_state() final { return nullptr; }
+  PacketSequenceStateGeneration* seq_state() final {
+    return sequence_state_.current_generation().get();
+  }
 
   std::vector<std::string> args_;
   std::map<std::string, size_t> array_indices_;
+
+  TraceProcessorContext context_;
+  PacketSequenceState sequence_state_;
 };
 
 // This test checks that in when an array is nested inside a dict which is
@@ -273,6 +294,33 @@
                           "root.field_string root.field_string value"));
 }
 
+TEST_F(DebugAnnotationParserTest, InternedString) {
+  protozero::HeapBuffered<protos::pbzero::DebugAnnotation> msg;
+  msg->set_name("root");
+
+  protozero::HeapBuffered<protos::pbzero::InternedString> string;
+  string->set_iid(1);
+  string->set_str("foo");
+  std::vector<uint8_t> data_serialized = string.SerializeAsArray();
+
+  mutable_seq_state()->InternMessage(
+      protos::pbzero::InternedData::kDebugAnnotationStringValuesFieldNumber,
+      TraceBlobView(
+          TraceBlob::CopyFrom(data_serialized.data(), data_serialized.size())));
+
+  msg->set_string_value_iid(1);
+
+  DescriptorPool pool;
+  ProtoToArgsParser args_parser(pool);
+  DebugAnnotationParser parser(args_parser);
+
+  auto status = ParseDebugAnnotation(parser, msg, *this);
+  EXPECT_TRUE(status.ok()) << "DebugAnnotationParser::Parse failed with error: "
+                           << status.message();
+
+  EXPECT_THAT(args(), testing::ElementsAre("root root foo"));
+}
+
 }  // namespace
 }  // namespace util
 }  // namespace trace_processor
diff --git a/src/traced/probes/ftrace/cpu_reader.cc b/src/traced/probes/ftrace/cpu_reader.cc
index 9403af4..b4c1758 100644
--- a/src/traced/probes/ftrace/cpu_reader.cc
+++ b/src/traced/probes/ftrace/cpu_reader.cc
@@ -186,9 +186,10 @@
                              metatrace::FTRACE_CPU_READ_CYCLE);
 
   // Work in batches to keep cache locality, and limit memory usage.
-  size_t batch_pages = std::min(parsing_buf_size_pages, max_pages);
   size_t total_pages_read = 0;
   for (bool is_first_batch = true;; is_first_batch = false) {
+    size_t batch_pages =
+        std::min(parsing_buf_size_pages, max_pages - total_pages_read);
     size_t pages_read = ReadAndProcessBatch(
         parsing_buf, batch_pages, is_first_batch, started_data_sources);
 
diff --git a/src/traced/probes/power/android_power_data_source.cc b/src/traced/probes/power/android_power_data_source.cc
index 9d2d9a5..3f2866e 100644
--- a/src/traced/probes/power/android_power_data_source.cc
+++ b/src/traced/probes/power/android_power_data_source.cc
@@ -33,6 +33,7 @@
 #include "protos/perfetto/common/android_energy_consumer_descriptor.pbzero.h"
 #include "protos/perfetto/config/power/android_power_config.pbzero.h"
 #include "protos/perfetto/trace/power/android_energy_estimation_breakdown.pbzero.h"
+#include "protos/perfetto/trace/power/android_entity_state_residency.pbzero.h"
 #include "protos/perfetto/trace/power/battery_counters.pbzero.h"
 #include "protos/perfetto/trace/power/power_rails.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
@@ -44,7 +45,7 @@
 constexpr uint32_t kDefaultPollIntervalMs = 1000;
 constexpr size_t kMaxNumRails = 32;
 constexpr size_t kMaxNumEnergyConsumer = 32;
-constexpr size_t kMaxNumPowerEntities = 256;
+constexpr size_t kMaxNumPowerEntities = 1024;
 }  // namespace
 
 // static
@@ -64,6 +65,10 @@
   PERFETTO_LAZY_LOAD(android_internal::GetEnergyConsumerInfo,
                      get_energy_consumer_info_);
   PERFETTO_LAZY_LOAD(android_internal::GetEnergyConsumed, get_energy_consumed_);
+  PERFETTO_LAZY_LOAD(android_internal::GetPowerEntityStates,
+                     get_power_entity_states_);
+  PERFETTO_LAZY_LOAD(android_internal::GetPowerEntityStateResidency,
+                     get_power_entity_state_residency_);
 
   base::Optional<int64_t> GetCounter(android_internal::BatteryCounter counter) {
     if (!get_battery_counter_)
@@ -132,6 +137,37 @@
     energy_breakdown.resize(num_power_entities);
     return energy_breakdown;
   }
+
+  std::vector<android_internal::PowerEntityState> GetPowerEntityStates() {
+    if (!get_power_entity_states_)
+      return std::vector<android_internal::PowerEntityState>();
+
+    std::vector<android_internal::PowerEntityState> entity(
+        kMaxNumPowerEntities);
+    size_t num_power_entities = entity.size();
+    if (!get_power_entity_states_(&entity[0], &num_power_entities)) {
+      PERFETTO_ELOG("Failed to retrieve power entities.");
+      num_power_entities = 0;
+    }
+    entity.resize(num_power_entities);
+    return entity;
+  }
+
+  std::vector<android_internal::PowerEntityStateResidency>
+  GetPowerEntityStateResidency() {
+    if (!get_power_entity_state_residency_)
+      return std::vector<android_internal::PowerEntityStateResidency>();
+
+    std::vector<android_internal::PowerEntityStateResidency> entity(
+        kMaxNumPowerEntities);
+    size_t num_power_entities = entity.size();
+    if (!get_power_entity_state_residency_(&entity[0], &num_power_entities)) {
+      PERFETTO_ELOG("Failed to retrieve power entities.");
+      num_power_entities = 0;
+    }
+    entity.resize(num_power_entities);
+    return entity;
+  }
 };
 
 AndroidPowerDataSource::AndroidPowerDataSource(
@@ -149,6 +185,8 @@
   rails_collection_enabled_ = pcfg.collect_power_rails();
   energy_breakdown_collection_enabled_ =
       pcfg.collect_energy_estimation_breakdown();
+  entity_state_residency_collection_enabled_ =
+      pcfg.collect_entity_state_residency();
 
   if (poll_interval_ms_ == 0)
     poll_interval_ms_ = kDefaultPollIntervalMs;
@@ -211,6 +249,7 @@
   WriteBatteryCounters();
   WritePowerRailsData();
   WriteEnergyEstimationBreakdown();
+  WriteEntityStateResidency();
 
   should_emit_descriptors_ = false;
 }
@@ -341,6 +380,43 @@
   }
 }
 
+void AndroidPowerDataSource::WriteEntityStateResidency() {
+  if (!entity_state_residency_collection_enabled_)
+    return;
+
+  auto packet = writer_->NewTracePacket();
+  packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
+  packet->set_sequence_flags(
+      protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
+
+  auto* outer_proto = packet->set_entity_state_residency();
+  if (should_emit_descriptors_) {
+    auto entity_states = lib_->GetPowerEntityStates();
+    if (entity_states.empty()) {
+      // No entities to collect data for. Don't try again.
+      entity_state_residency_collection_enabled_ = false;
+      return;
+    }
+
+    for (const auto& entity_state : entity_states) {
+      auto* entity_state_proto = outer_proto->add_power_entity_state();
+      entity_state_proto->set_entity_index(entity_state.entity_id);
+      entity_state_proto->set_state_index(entity_state.state_id);
+      entity_state_proto->set_entity_name(entity_state.entity_name);
+      entity_state_proto->set_state_name(entity_state.state_name);
+    }
+  }
+
+  for (const auto& residency_data : lib_->GetPowerEntityStateResidency()) {
+    auto* data = outer_proto->add_residency();
+    data->set_entity_index(residency_data.entity_id);
+    data->set_state_index(residency_data.state_id);
+    data->set_total_time_in_state_ms(residency_data.total_time_in_state_ms);
+    data->set_total_state_entry_count(residency_data.total_state_entry_count);
+    data->set_last_entry_timestamp_ms(residency_data.last_entry_timestamp_ms);
+  }
+}
+
 void AndroidPowerDataSource::Flush(FlushRequestID,
                                    std::function<void()> callback) {
   writer_->Flush(callback);
diff --git a/src/traced/probes/power/android_power_data_source.h b/src/traced/probes/power/android_power_data_source.h
index 56b02f6..926fc21 100644
--- a/src/traced/probes/power/android_power_data_source.h
+++ b/src/traced/probes/power/android_power_data_source.h
@@ -57,6 +57,7 @@
   void WriteBatteryCounters();
   void WritePowerRailsData();
   void WriteEnergyEstimationBreakdown();
+  void WriteEntityStateResidency();
 
   // Battery counters.
   std::bitset<8> counters_enabled_;
@@ -67,6 +68,9 @@
   // Energy estimation.
   bool energy_breakdown_collection_enabled_ = false;
 
+  // Entity state residency
+  bool entity_state_residency_collection_enabled_ = false;
+
   uint32_t poll_interval_ms_ = 0;
   bool should_emit_descriptors_ = true;
 
diff --git a/src/traced/probes/power/linux_power_sysfs_data_source.cc b/src/traced/probes/power/linux_power_sysfs_data_source.cc
index b4bdec6..9d4365b 100644
--- a/src/traced/probes/power/linux_power_sysfs_data_source.cc
+++ b/src/traced/probes/power/linux_power_sysfs_data_source.cc
@@ -85,6 +85,21 @@
 }
 
 base::Optional<int64_t>
+LinuxPowerSysfsDataSource::BatteryInfo::GetEnergyCounterUah(
+    size_t battery_idx) {
+  PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
+  return ReadFileAsInt64(power_supply_dir_path_ + "/" +
+                         sysfs_battery_subdirs_[battery_idx] + "/energy_now");
+}
+
+base::Optional<int64_t> LinuxPowerSysfsDataSource::BatteryInfo::GetVoltageUv(
+    size_t battery_idx) {
+  PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
+  return ReadFileAsInt64(power_supply_dir_path_ + "/" +
+                         sysfs_battery_subdirs_[battery_idx] + "/voltage_now");
+}
+
+base::Optional<int64_t>
 LinuxPowerSysfsDataSource::BatteryInfo::GetCapacityPercent(size_t battery_idx) {
   PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
   return ReadFileAsInt64(power_supply_dir_path_ + "/" +
@@ -174,6 +189,12 @@
     value = battery_info_->GetAverageCurrentUa(battery_idx);
     if (value)
       counters_proto->set_current_ua(*value);
+    value = battery_info_->GetEnergyCounterUah(battery_idx);
+    if (value)
+      counters_proto->set_energy_counter_uwh(*value);
+    value = battery_info_->GetVoltageUv(battery_idx);
+    if (value)
+      counters_proto->set_voltage_uv(*value);
     // On systems with multiple batteries, disambiguate with battery names.
     if (battery_info_->num_batteries() > 1)
       counters_proto->set_name(battery_info_->GetBatteryName(battery_idx));
diff --git a/src/traced/probes/power/linux_power_sysfs_data_source.h b/src/traced/probes/power/linux_power_sysfs_data_source.h
index d757b38..058ccd6 100644
--- a/src/traced/probes/power/linux_power_sysfs_data_source.h
+++ b/src/traced/probes/power/linux_power_sysfs_data_source.h
@@ -41,6 +41,12 @@
     // The current coloumb counter value in µAh.
     base::Optional<int64_t> GetChargeCounterUah(size_t battery_idx);
 
+    // The current energy counter in µWh.
+    base::Optional<int64_t> GetEnergyCounterUah(size_t battery_idx);
+
+    // The voltage in µV.
+    base::Optional<int64_t> GetVoltageUv(size_t battery_idx);
+
     // The battery capacity in percent.
     base::Optional<int64_t> GetCapacityPercent(size_t battery_idx);
 
diff --git a/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc b/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc
index ff98604..2c1c8aa 100644
--- a/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc
+++ b/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc
@@ -104,5 +104,41 @@
   EXPECT_EQ(*battery_info_->GetChargeCounterUah(main_battery_idx), 3074000);
 }
 
+TEST(LinuxPowerSysfsDataSourceTest, EnergyNow) {
+  base::TmpDirTree tmpdir;
+  std::unique_ptr<LinuxPowerSysfsDataSource::BatteryInfo> battery_info_;
+
+  tmpdir.AddDir("BAT0");
+  tmpdir.AddFile("BAT0/type", "Battery\n");
+  tmpdir.AddFile("BAT0/present", "1\n");
+  tmpdir.AddFile("BAT0/capacity", "95\n");          // 95 percent.
+  tmpdir.AddFile("BAT0/energy_now", "56680000\n");  // 56680000 µWh.
+
+  battery_info_.reset(
+      new LinuxPowerSysfsDataSource::BatteryInfo(tmpdir.path().c_str()));
+
+  EXPECT_EQ(battery_info_->num_batteries(), 1u);
+  EXPECT_EQ(*battery_info_->GetCapacityPercent(0), 95);
+  EXPECT_EQ(*battery_info_->GetEnergyCounterUah(0), 56680000);
+}
+
+TEST(LinuxPowerSysfsDataSourceTest, EnergyVoltageNow) {
+  base::TmpDirTree tmpdir;
+  std::unique_ptr<LinuxPowerSysfsDataSource::BatteryInfo> battery_info_;
+
+  tmpdir.AddDir("BAT0");
+  tmpdir.AddFile("BAT0/type", "Battery\n");
+  tmpdir.AddFile("BAT0/present", "1\n");
+  tmpdir.AddFile("BAT0/capacity", "95\n");           // 95 percent.
+  tmpdir.AddFile("BAT0/voltage_now", "17356000\n");  // Now at 17.356 µV.
+
+  battery_info_.reset(
+      new LinuxPowerSysfsDataSource::BatteryInfo(tmpdir.path().c_str()));
+
+  EXPECT_EQ(battery_info_->num_batteries(), 1u);
+  EXPECT_EQ(*battery_info_->GetCapacityPercent(0), 95);
+  EXPECT_EQ(*battery_info_->GetVoltageUv(0), 17356000);
+}
+
 }  // namespace
 }  // namespace perfetto
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index 0d2573a..cc1e744 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -433,6 +433,10 @@
     // We need to ensure this timeout is worse than the worst case
     // time from us starting to traced managing to disable us.
     // See b/236814186#comment8 for context
+    // Note: when using prefer_suspend_clock_for_duration the actual duration
+    // might be < timeout measured in in wall time. But this is fine
+    // because the resulting timeout will be conservative (it will be accurate
+    // if the device never suspends, and will be more lax if it does).
     uint32_t timeout =
         2 * (kDefaultFlushTimeoutMs + config.trace_duration_ms() +
              config.stop_timeout_ms());
diff --git a/src/traced/service/builtin_producer.cc b/src/traced/service/builtin_producer.cc
index a2007be..0664b5d 100644
--- a/src/traced/service/builtin_producer.cc
+++ b/src/traced/service/builtin_producer.cc
@@ -46,9 +46,11 @@
 
 constexpr char kHeapprofdDataSourceName[] = "android.heapprofd";
 constexpr char kJavaHprofDataSourceName[] = "android.java_hprof";
+constexpr char kJavaHprofOomDataSourceName[] = "android.java_hprof.oom";
 constexpr char kTracedPerfDataSourceName[] = "linux.perf";
 constexpr char kLazyHeapprofdPropertyName[] = "traced.lazy.heapprofd";
 constexpr char kLazyTracedPerfPropertyName[] = "traced.lazy.traced_perf";
+constexpr char kJavaHprofOomActivePropertyName[] = "traced.oome_heap_session.count";
 
 }  // namespace
 
@@ -64,6 +66,8 @@
     SetAndroidProperty(kLazyHeapprofdPropertyName, "");
   if (!lazy_traced_perf_.instance_ids.empty())
     SetAndroidProperty(kLazyTracedPerfPropertyName, "");
+  if (!java_hprof_oome_instances_.empty())
+    SetAndroidProperty(kJavaHprofOomActivePropertyName, "");
 }
 
 void BuiltinProducer::ConnectInProcess(TracingService* svc) {
@@ -102,6 +106,11 @@
     lazy_traced_perf_dsd.set_name(kTracedPerfDataSourceName);
     endpoint_->RegisterDataSource(lazy_traced_perf_dsd);
   }
+  {
+    DataSourceDescriptor java_hprof_oome_dsd;
+    java_hprof_oome_dsd.set_name(kJavaHprofOomDataSourceName);
+    endpoint_->RegisterDataSource(java_hprof_oome_dsd);
+  }
 }
 
 void BuiltinProducer::SetupDataSource(DataSourceInstanceID ds_id,
@@ -120,6 +129,13 @@
     lazy_traced_perf_.instance_ids.emplace(ds_id);
     return;
   }
+
+  if (ds_config.name() == kJavaHprofOomDataSourceName) {
+    java_hprof_oome_instances_.emplace(ds_id);
+    SetAndroidProperty(kJavaHprofOomActivePropertyName,
+                       std::to_string(java_hprof_oome_instances_.size()));
+    return;
+  }
 }
 
 void BuiltinProducer::StartDataSource(DataSourceInstanceID ds_id,
@@ -152,6 +168,13 @@
 
   MaybeInitiateLazyStop(ds_id, &lazy_heapprofd_, kLazyHeapprofdPropertyName);
   MaybeInitiateLazyStop(ds_id, &lazy_traced_perf_, kLazyTracedPerfPropertyName);
+
+  auto oome_it = java_hprof_oome_instances_.find(ds_id);
+  if (oome_it != java_hprof_oome_instances_.end()) {
+    java_hprof_oome_instances_.erase(oome_it);
+    SetAndroidProperty(kJavaHprofOomActivePropertyName,
+                       std::to_string(java_hprof_oome_instances_.size()));
+  }
 }
 
 void BuiltinProducer::MaybeInitiateLazyStop(DataSourceInstanceID ds_id,
diff --git a/src/traced/service/builtin_producer.h b/src/traced/service/builtin_producer.h
index 9873458..7014d44 100644
--- a/src/traced/service/builtin_producer.h
+++ b/src/traced/service/builtin_producer.h
@@ -34,6 +34,7 @@
 // * perfetto metatrace
 // * lazy heapprofd daemon starter (android only)
 // * lazy traced_perf daemon starter (android only)
+// * java_hprof oom data source counter (android only)
 class BuiltinProducer : public Producer {
  public:
   BuiltinProducer(base::TaskRunner* task_runner, uint32_t lazy_stop_delay_ms);
@@ -83,6 +84,7 @@
   MetatraceState metatrace_;
   LazyAndroidDaemonState lazy_heapprofd_;
   LazyAndroidDaemonState lazy_traced_perf_;
+  std::set<DataSourceInstanceID> java_hprof_oome_instances_;
 
   base::WeakPtrFactory<BuiltinProducer> weak_factory_;  // Keep last.
 };
diff --git a/src/tracing/core/trace_buffer.cc b/src/tracing/core/trace_buffer.cc
index 3fddc58..90820a0 100644
--- a/src/tracing/core/trace_buffer.cc
+++ b/src/tracing/core/trace_buffer.cc
@@ -100,13 +100,13 @@
                                      bool chunk_complete,
                                      const uint8_t* src,
                                      size_t size) {
-  TRACE_BUFFER_DLOG("CopyChunk @ %lu, size=%zu", wptr_ - begin(), record_size);
   PERFETTO_CHECK(!read_only_);
 
   // |record_size| = |size| + sizeof(ChunkRecord), rounded up to avoid to end
   // up in a fragmented state where size_to_end() < sizeof(ChunkRecord).
   const size_t record_size =
       base::AlignUp<sizeof(ChunkRecord)>(size + sizeof(ChunkRecord));
+  TRACE_BUFFER_DLOG("CopyChunk @ %lu, size=%zu", wptr_ - begin(), record_size);
   if (PERFETTO_UNLIKELY(record_size > max_chunk_size_)) {
     stats_.set_abi_violations(stats_.abi_violations() + 1);
     PERFETTO_DCHECK(suppress_client_dchecks_for_testing_);
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index d304ada..e98b6a1 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -282,10 +282,10 @@
     case TraceConfig::STATSD_LOGGING_DISABLED:
       return false;
     case TraceConfig::STATSD_LOGGING_UNSPECIFIED:
-      // For backward compatibility with older versions of perfetto_cmd.
-      return cfg.enable_extra_guardrails();
+      break;
   }
-  PERFETTO_FATAL("For GCC");
+  // For backward compatibility with older versions of perfetto_cmd.
+  return cfg.enable_extra_guardrails();
 }
 
 // Appends `data` (which has `size` bytes), to `*packet`. Splits the data in
@@ -836,10 +836,10 @@
 
   if (cfg.write_into_file()) {
     if (!fd ^ !cfg.output_path().empty()) {
-      tracing_sessions_.erase(tsid);
       MaybeLogUploadEvent(
           tracing_session->config, uuid,
           PerfettoStatsdAtom::kTracedEnableTracingInvalidFdOutputFile);
+      tracing_sessions_.erase(tsid);
       return PERFETTO_SVC_ERR(
           "When write_into_file==true either a FD needs to be passed or "
           "output_path must be populated (but not both)");
@@ -868,6 +868,7 @@
 
   // Initialize the log buffers.
   bool did_allocate_all_buffers = true;
+  bool invalid_buffer_config = false;
 
   // Allocate the trace buffers. Also create a map to translate a consumer
   // relative index (TraceConfig.DataSourceConfig.target_buffer) into the
@@ -884,14 +885,24 @@
       break;
     }
     tracing_session->buffers_index.push_back(global_id);
-    const size_t buf_size_bytes = buffer_cfg.size_kb() * 1024u;
-    total_buf_size_kb += buffer_cfg.size_kb();
+    // TraceBuffer size is limited to 32-bit.
+    const uint32_t buf_size_kb = buffer_cfg.size_kb();
+    const uint64_t buf_size_bytes = buf_size_kb * static_cast<uint64_t>(1024);
+    const size_t buf_size = static_cast<size_t>(buf_size_bytes);
+    if (buf_size_bytes == 0 ||
+        buf_size_bytes > std::numeric_limits<uint32_t>::max() ||
+        buf_size != buf_size_bytes) {
+      invalid_buffer_config = true;
+      did_allocate_all_buffers = false;
+      break;
+    }
+    total_buf_size_kb += buf_size_kb;
     TraceBuffer::OverwritePolicy policy =
         buffer_cfg.fill_policy() == TraceConfig::BufferConfig::DISCARD
             ? TraceBuffer::kDiscard
             : TraceBuffer::kOverwrite;
-    auto it_and_inserted = buffers_.emplace(
-        global_id, TraceBuffer::Create(buf_size_bytes, policy));
+    auto it_and_inserted =
+        buffers_.emplace(global_id, TraceBuffer::Create(buf_size, policy));
     PERFETTO_DCHECK(it_and_inserted.second);  // buffers_.count(global_id) == 0.
     std::unique_ptr<TraceBuffer>& trace_buffer = it_and_inserted.first->second;
     if (!trace_buffer) {
@@ -900,25 +911,29 @@
     }
   }
 
-  UpdateMemoryGuardrail();
-
   // This can happen if either:
   // - All the kMaxTraceBufferID slots are taken.
-  // - OOM, or, more relistically, we exhausted virtual memory.
+  // - OOM, or, more realistically, we exhausted virtual memory.
+  // - The buffer size in the config is invalid.
   // In any case, free all the previously allocated buffers and abort.
-  // TODO(fmayer): add a test to cover this case, this is quite subtle.
   if (!did_allocate_all_buffers) {
     for (BufferID global_id : tracing_session->buffers_index) {
       buffer_ids_.Free(global_id);
       buffers_.erase(global_id);
     }
-    tracing_sessions_.erase(tsid);
     MaybeLogUploadEvent(tracing_session->config, uuid,
                         PerfettoStatsdAtom::kTracedEnableTracingOom);
+    tracing_sessions_.erase(tsid);
+    if (invalid_buffer_config) {
+      return PERFETTO_SVC_ERR(
+          "Failed to allocate tracing buffers: Invalid buffer sizes");
+    }
     return PERFETTO_SVC_ERR(
         "Failed to allocate tracing buffers: OOM or too many buffers");
   }
 
+  UpdateMemoryGuardrail();
+
   consumer->tracing_session_id_ = tsid;
 
   // Setup the data sources on the producers without starting them.
@@ -972,9 +987,12 @@
   tracing_session->state = TracingSession::CONFIGURED;
   PERFETTO_LOG(
       "Configured tracing session %" PRIu64
-      ", #sources:%zu, duration:%d ms, #buffers:%d, total "
+      ", #sources:%zu, duration:%d ms%s, #buffers:%d, total "
       "buffer size:%zu KB, total sessions:%zu, uid:%d session name: \"%s\"",
       tsid, cfg.data_sources().size(), tracing_session->config.duration_ms(),
+      tracing_session->config.prefer_suspend_clock_for_duration()
+          ? " (suspend_clock)"
+          : "",
       cfg.buffers_size(), total_buf_size_kb, tracing_sessions_.size(),
       static_cast<unsigned int>(consumer->uid_),
       cfg.unique_session_name().c_str());
@@ -1174,28 +1192,19 @@
   // Trigger delayed task if the trace is time limited.
   const uint32_t trace_duration_ms = tracing_session->config.duration_ms();
   if (trace_duration_ms > 0) {
-    task_runner_->PostDelayedTask(
-        [weak_this, tsid] {
-          // Skip entirely the flush if the trace session doesn't exist anymore.
-          // This is to prevent misleading error messages to be logged.
-          if (!weak_this)
-            return;
-          auto* tracing_session_ptr = weak_this->GetTracingSession(tsid);
-          if (!tracing_session_ptr)
-            return;
-          // If this trace was using STOP_TRACING triggers and we've seen
-          // one, then the trigger overrides the normal timeout. In this
-          // case we just return and let the other task clean up this trace.
-          if (tracing_session_ptr->config.trigger_config().trigger_mode() ==
-                  TraceConfig::TriggerConfig::STOP_TRACING &&
-              !tracing_session_ptr->received_triggers.empty())
-            return;
-          // In all other cases (START_TRACING or no triggers) we flush
-          // after |trace_duration_ms| unconditionally.
-          weak_this->FlushAndDisableTracing(tsid);
-        },
-        trace_duration_ms);
-  }
+    auto stop_task =
+        std::bind(&TracingServiceImpl::StopOnDurationMsExpiry, weak_this, tsid);
+    if (tracing_session->config.prefer_suspend_clock_for_duration()) {
+      base::PeriodicTask::Args stop_args;
+      stop_args.use_suspend_aware_timer = true;
+      stop_args.period_ms = trace_duration_ms;
+      stop_args.one_shot = true;
+      stop_args.task = std::move(stop_task);
+      tracing_session->timed_stop_task.Start(stop_args);
+    } else {
+      task_runner_->PostDelayedTask(std::move(stop_task), trace_duration_ms);
+    }
+  }  // if (trace_duration_ms > 0).
 
   // Start the periodic drain tasks if we should to save the trace into a file.
   if (tracing_session->config.write_into_file()) {
@@ -1232,6 +1241,29 @@
   return base::OkStatus();
 }
 
+// static
+void TracingServiceImpl::StopOnDurationMsExpiry(
+    base::WeakPtr<TracingServiceImpl> weak_this,
+    TracingSessionID tsid) {
+  // Skip entirely the flush if the trace session doesn't exist anymore.
+  // This is to prevent misleading error messages to be logged.
+  if (!weak_this)
+    return;
+  auto* tracing_session_ptr = weak_this->GetTracingSession(tsid);
+  if (!tracing_session_ptr)
+    return;
+  // If this trace was using STOP_TRACING triggers and we've seen
+  // one, then the trigger overrides the normal timeout. In this
+  // case we just return and let the other task clean up this trace.
+  if (tracing_session_ptr->config.trigger_config().trigger_mode() ==
+          TraceConfig::TriggerConfig::STOP_TRACING &&
+      !tracing_session_ptr->received_triggers.empty())
+    return;
+  // In all other cases (START_TRACING or no triggers) we flush
+  // after |trace_duration_ms| unconditionally.
+  weak_this->FlushAndDisableTracing(tsid);
+}
+
 void TracingServiceImpl::StartDataSourceInstance(
     ProducerEndpointImpl* producer,
     TracingSession* tracing_session,
@@ -1601,9 +1633,9 @@
   }  // for (trigger_name : triggers)
 }
 
-// Always invoked kDataSourceStopTimeoutMs after DisableTracing(). In nominal
-// conditions all data sources should have acked the stop and this will early
-// out.
+// Always invoked TraceConfig.data_source_stop_timeout_ms (by default
+// kDataSourceStopTimeoutMs) after DisableTracing(). In nominal conditions all
+// data sources should have acked the stop and this will early out.
 void TracingServiceImpl::OnDisableTracingTimeout(TracingSessionID tsid) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
   TracingSession* tracing_session = GetTracingSession(tsid);
@@ -2704,6 +2736,16 @@
 
   DataSourceConfig& ds_config = ds_instance->config;
   ds_config.set_trace_duration_ms(tracing_session->config.duration_ms());
+
+  // Rationale for `if (prefer) set_prefer(true)`, rather than `set(prefer)`:
+  // ComputeStartupConfigHash() in tracing_muxer_impl.cc compares hashes of the
+  // DataSourceConfig and expects to know (and clear) the fields generated by
+  // the tracing service. Unconditionally adding a new field breaks backward
+  // compatibility of startup tracing with older SDKs, because the serialization
+  // also propagates unkonwn fields, breaking the hash matching check.
+  if (tracing_session->config.prefer_suspend_clock_for_duration())
+    ds_config.set_prefer_suspend_clock_for_duration(true);
+
   ds_config.set_stop_timeout_ms(tracing_session->data_source_stop_timeout_ms());
   ds_config.set_enable_extra_guardrails(
       tracing_session->config.enable_extra_guardrails());
@@ -4269,7 +4311,8 @@
       consumer_maybe_null(consumer),
       consumer_uid(consumer->uid_),
       config(new_config),
-      snapshot_periodic_task(task_runner) {
+      snapshot_periodic_task(task_runner),
+      timed_stop_task(task_runner) {
   // all_data_sources_flushed is special because we store up to 64 events of
   // this type. Other events will go through the default case in
   // SnapshotLifecycleEvent() where they will be given a max history of 1.
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index 77bdb6b..4800559 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -641,6 +641,11 @@
     // etc)
     base::PeriodicTask snapshot_periodic_task;
 
+    // Deferred task that stops the trace when |duration_ms| expires. This is
+    // to handle the case of |prefer_suspend_clock_for_duration| which cannot
+    // use PostDelayedTask.
+    base::PeriodicTask timed_stop_task;
+
     // When non-NULL the packets should be post-processed using the filter.
     std::unique_ptr<protozero::MessageFilter> trace_filter;
     uint64_t filter_input_packets = 0;
@@ -759,6 +764,8 @@
                             const std::string& trigger_name);
   size_t PurgeExpiredAndCountTriggerInWindow(int64_t now_ns,
                                              uint64_t trigger_name_hash);
+  static void StopOnDurationMsExpiry(base::WeakPtr<TracingServiceImpl>,
+                                     TracingSessionID);
 
   base::TaskRunner* const task_runner_;
   std::unique_ptr<SharedMemory::Factory> shm_factory_;
diff --git a/src/tracing/core/tracing_service_impl_unittest.cc b/src/tracing/core/tracing_service_impl_unittest.cc
index 3a12a75..8b8c62c 100644
--- a/src/tracing/core/tracing_service_impl_unittest.cc
+++ b/src/tracing/core/tracing_service_impl_unittest.cc
@@ -54,10 +54,12 @@
 using ::testing::AssertionResult;
 using ::testing::AssertionSuccess;
 using ::testing::Contains;
+using ::testing::DoAll;
 using ::testing::Each;
 using ::testing::ElementsAreArray;
 using ::testing::Eq;
 using ::testing::ExplainMatchResult;
+using ::testing::HasSubstr;
 using ::testing::InSequence;
 using ::testing::Invoke;
 using ::testing::InvokeWithoutArgs;
@@ -66,6 +68,7 @@
 using ::testing::Ne;
 using ::testing::Not;
 using ::testing::Property;
+using ::testing::SaveArg;
 using ::testing::StrictMock;
 using ::testing::StringMatchResultListener;
 using ::testing::StrNe;
@@ -4026,4 +4029,25 @@
               Each(Property(&protos::gen::TracePacket::has_timestamp, false)));
 }
 
+TEST_F(TracingServiceImplTest, InvalidBufferSizes) {
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(128);
+  trace_config.add_buffers()->set_size_kb(256);
+  trace_config.add_buffers()->set_size_kb(4 * 1024 * 1024);
+  auto* ds = trace_config.add_data_sources();
+  auto* ds_config = ds->mutable_config();
+  ds_config->set_name("data_source");
+  consumer->EnableTracing(trace_config);
+
+  std::string error;
+  auto checkpoint = task_runner.CreateCheckpoint("tracing_disabled");
+  EXPECT_CALL(*consumer, OnTracingDisabled(_))
+      .WillOnce(DoAll(SaveArg<0>(&error), checkpoint));
+  task_runner.RunUntilCheckpoint("tracing_disabled");
+  EXPECT_THAT(error, HasSubstr("Invalid buffer sizes"));
+}
+
 }  // namespace perfetto
diff --git a/src/tracing/data_source.cc b/src/tracing/data_source.cc
index 0f4a5d1..902c803 100644
--- a/src/tracing/data_source.cc
+++ b/src/tracing/data_source.cc
@@ -15,6 +15,7 @@
  */
 
 #include "perfetto/tracing/data_source.h"
+#include "perfetto/base/logging.h"
 
 namespace perfetto {
 
@@ -26,4 +27,36 @@
 void DataSourceBase::WillClearIncrementalState(
     const ClearIncrementalStateArgs&) {}
 
+namespace internal {
+
+void DataSourceType::PopulateTlsInst(
+    DataSourceInstanceThreadLocalState* tls_inst,
+    DataSourceState* instance_state,
+    uint32_t instance_index) {
+  auto* tracing_impl = TracingMuxer::Get();
+  tls_inst->muxer_id_for_testing = instance_state->muxer_id_for_testing;
+  tls_inst->backend_id = instance_state->backend_id;
+  tls_inst->backend_connection_id = instance_state->backend_connection_id;
+  tls_inst->buffer_id = instance_state->buffer_id;
+  tls_inst->startup_target_buffer_reservation =
+      instance_state->startup_target_buffer_reservation.load(
+          std::memory_order_relaxed);
+  tls_inst->data_source_instance_id = instance_state->data_source_instance_id;
+  tls_inst->is_intercepted = instance_state->interceptor_id != 0;
+  tls_inst->trace_writer = tracing_impl->CreateTraceWriter(
+      &state_, instance_index, instance_state, buffer_exhausted_policy_);
+  if (create_incremental_state_fn_) {
+    PERFETTO_DCHECK(!tls_inst->incremental_state);
+    CreateIncrementalState(tls_inst, instance_index);
+  }
+  if (create_custom_tls_fn_) {
+    tls_inst->data_source_custom_tls =
+        create_custom_tls_fn_(tls_inst, instance_index, user_arg_);
+  }
+  // Even in the case of out-of-IDs, SharedMemoryArbiterImpl returns a
+  // NullTraceWriter. The returned pointer should never be null.
+  PERFETTO_DCHECK(tls_inst->trace_writer);
+}
+
+}  // namespace internal
 }  // namespace perfetto
diff --git a/src/tracing/internal/system_tracing_backend.cc b/src/tracing/internal/system_tracing_backend.cc
index 824311d..18c3e08 100644
--- a/src/tracing/internal/system_tracing_backend.cc
+++ b/src/tracing/internal/system_tracing_backend.cc
@@ -34,10 +34,17 @@
 
 namespace perfetto {
 namespace internal {
-namespace {
 
-std::unique_ptr<ProducerEndpoint> CreateProducerEndpoint(
-    const TracingBackend::ConnectProducerArgs& args) {
+// static
+TracingProducerBackend* SystemProducerTracingBackend::GetInstance() {
+  static auto* instance = new SystemProducerTracingBackend();
+  return instance;
+}
+
+SystemProducerTracingBackend::SystemProducerTracingBackend() {}
+
+std::unique_ptr<ProducerEndpoint> SystemProducerTracingBackend::ConnectProducer(
+    const ConnectProducerArgs& args) {
   PERFETTO_DCHECK(args.task_runner->RunsTasksOnCurrentThread());
 
   std::unique_ptr<SharedMemory> shm;
@@ -67,22 +74,15 @@
   return endpoint;
 }
 
-}  // namespace
-
 // static
-TracingBackend* SystemTracingBackend::GetInstance() {
-  static auto* instance = new SystemTracingBackend();
+TracingConsumerBackend* SystemConsumerTracingBackend::GetInstance() {
+  static auto* instance = new SystemConsumerTracingBackend();
   return instance;
 }
 
-SystemTracingBackend::SystemTracingBackend() {}
+SystemConsumerTracingBackend::SystemConsumerTracingBackend() {}
 
-std::unique_ptr<ProducerEndpoint> SystemTracingBackend::ConnectProducer(
-    const ConnectProducerArgs& args) {
-  return CreateProducerEndpoint(args);
-}
-
-std::unique_ptr<ConsumerEndpoint> SystemTracingBackend::ConnectConsumer(
+std::unique_ptr<ConsumerEndpoint> SystemConsumerTracingBackend::ConnectConsumer(
     const ConnectConsumerArgs& args) {
 #if PERFETTO_BUILDFLAG(PERFETTO_SYSTEM_CONSUMER)
   auto endpoint = ConsumerIPCClient::Connect(GetConsumerSocket(), args.consumer,
@@ -96,29 +96,5 @@
 #endif
 }
 
-// static
-TracingBackend* SystemTracingProducerOnlyBackend::GetInstance() {
-  static auto* instance = new SystemTracingProducerOnlyBackend();
-  return instance;
-}
-
-SystemTracingProducerOnlyBackend::SystemTracingProducerOnlyBackend() {}
-
-std::unique_ptr<ProducerEndpoint>
-SystemTracingProducerOnlyBackend::ConnectProducer(
-    const ConnectProducerArgs& args) {
-  return CreateProducerEndpoint(args);
-}
-
-std::unique_ptr<ConsumerEndpoint>
-SystemTracingProducerOnlyBackend::ConnectConsumer(
-    const ConnectConsumerArgs& args) {
-  base::ignore_result(args);
-  PERFETTO_FATAL(
-      "System backend consumer support disabled. "
-      "TracingInitArgs::enable_system_consumer was false");
-  return nullptr;
-}
-
 }  // namespace internal
 }  // namespace perfetto
diff --git a/src/tracing/internal/system_tracing_backend_fake.cc b/src/tracing/internal/system_tracing_backend_fake.cc
index ea992a1..7e94809 100644
--- a/src/tracing/internal/system_tracing_backend_fake.cc
+++ b/src/tracing/internal/system_tracing_backend_fake.cc
@@ -22,13 +22,13 @@
 namespace internal {
 
 // static
-TracingBackend* SystemTracingBackend::GetInstance() {
+TracingProducerBackend* SystemProducerTracingBackend::GetInstance() {
   PERFETTO_FATAL("System tracing not implemented");
   return nullptr;
 }
 
 // static
-TracingBackend* SystemTracingProducerOnlyBackend::GetInstance() {
+TracingConsumerBackend* SystemConsumerTracingBackend::GetInstance() {
   PERFETTO_FATAL("System tracing not implemented");
   return nullptr;
 }
diff --git a/src/tracing/internal/tracing_muxer_impl.cc b/src/tracing/internal/tracing_muxer_impl.cc
index 463d0d4..ac97ec8 100644
--- a/src/tracing/internal/tracing_muxer_impl.cc
+++ b/src/tracing/internal/tracing_muxer_impl.cc
@@ -164,6 +164,28 @@
   return hasher.digest();
 }
 
+template <typename RegisteredBackend>
+struct CompareBackendByType {
+  static int BackendTypePriority(BackendType type) {
+    switch (type) {
+      case kSystemBackend:
+        return 0;
+      case kInProcessBackend:
+        return 1;
+      case kCustomBackend:
+        return 2;
+      // The UnspecifiedBackend has the highest priority so that
+      // TracingBackendFake is the last one on the backend lists.
+      case kUnspecifiedBackend:
+        break;
+    }
+    return 3;
+  }
+  bool operator()(BackendType type, const RegisteredBackend& b) {
+    return BackendTypePriority(type) < BackendTypePriority(b.type);
+  }
+};
+
 }  // namespace
 
 // ----- Begin of TracingMuxerImpl::ProducerImpl
@@ -353,12 +375,8 @@
 // ----- Begin of TracingMuxerImpl::ConsumerImpl
 TracingMuxerImpl::ConsumerImpl::ConsumerImpl(TracingMuxerImpl* muxer,
                                              BackendType backend_type,
-                                             TracingBackendId backend_id,
                                              TracingSessionGlobalID session_id)
-    : muxer_(muxer),
-      backend_type_(backend_type),
-      backend_id_(backend_id),
-      session_id_(session_id) {}
+    : muxer_(muxer), backend_type_(backend_type), session_id_(session_id) {}
 
 TracingMuxerImpl::ConsumerImpl::~ConsumerImpl() {
   muxer_ = nullptr;
@@ -852,7 +870,10 @@
       this, platform_->CreateTaskRunner(std::move(tr_args))));
 
   // Run the initializer on that thread.
-  task_runner_->PostTask([this, args] { Initialize(args); });
+  task_runner_->PostTask([this, args] {
+    Initialize(args);
+    AddBackends(args);
+  });
 }
 
 void TracingMuxerImpl::Initialize(const TracingInitArgs& args) {
@@ -862,56 +883,146 @@
   supports_multiple_data_source_instances_ =
       args.supports_multiple_data_source_instances;
 
-  auto add_backend = [this, &args](TracingBackend* backend, BackendType type) {
-    if (!backend) {
-      // We skip the log in release builds because the *_backend_fake.cc code
-      // has already an ELOG before returning a nullptr.
-      PERFETTO_DLOG("Backend creation failed, type %d", static_cast<int>(type));
-      return;
-    }
-    TracingBackendId backend_id = backends_.size();
-    backends_.emplace_back();
-    RegisteredBackend& rb = backends_.back();
-    rb.backend = backend;
-    rb.id = backend_id;
-    rb.type = type;
-    rb.consumer_enabled = type != kSystemBackend || args.enable_system_consumer;
-    rb.producer.reset(new ProducerImpl(this, backend_id,
-                                       args.shmem_batch_commits_duration_ms));
-    rb.producer_conn_args.producer = rb.producer.get();
-    rb.producer_conn_args.producer_name = platform_->GetCurrentProcessName();
-    rb.producer_conn_args.task_runner = task_runner_.get();
-    rb.producer_conn_args.shmem_size_hint_bytes =
-        args.shmem_size_hint_kb * 1024;
-    rb.producer_conn_args.shmem_page_size_hint_bytes =
-        args.shmem_page_size_hint_kb * 1024;
-    rb.producer->Initialize(rb.backend->ConnectProducer(rb.producer_conn_args));
-  };
+  // Fallback backend for producer creation for an unsupported backend type.
+  PERFETTO_CHECK(producer_backends_.empty());
+  AddProducerBackend(internal::TracingBackendFake::GetInstance(),
+                     BackendType::kUnspecifiedBackend, args);
+  // Fallback backend for consumer creation for an unsupported backend type.
+  // This backend simply fails any attempt to start a tracing session.
+  PERFETTO_CHECK(consumer_backends_.empty());
+  AddConsumerBackend(internal::TracingBackendFake::GetInstance(),
+                     BackendType::kUnspecifiedBackend);
+}
 
+void TracingMuxerImpl::AddConsumerBackend(TracingConsumerBackend* backend,
+                                          BackendType type) {
+  if (!backend) {
+    // We skip the log in release builds because the *_backend_fake.cc code
+    // has already an ELOG before returning a nullptr.
+    PERFETTO_DLOG("Consumer backend creation failed, type %d",
+                  static_cast<int>(type));
+    return;
+  }
+  // Keep the backends sorted by type.
+  auto it =
+      std::upper_bound(consumer_backends_.begin(), consumer_backends_.end(),
+                       type, CompareBackendByType<RegisteredConsumerBackend>());
+  it = consumer_backends_.emplace(it);
+
+  RegisteredConsumerBackend& rb = *it;
+  rb.backend = backend;
+  rb.type = type;
+}
+
+void TracingMuxerImpl::AddProducerBackend(TracingProducerBackend* backend,
+                                          BackendType type,
+                                          const TracingInitArgs& args) {
+  if (!backend) {
+    // We skip the log in release builds because the *_backend_fake.cc code
+    // has already an ELOG before returning a nullptr.
+    PERFETTO_DLOG("Producer backend creation failed, type %d",
+                  static_cast<int>(type));
+    return;
+  }
+  TracingBackendId backend_id = producer_backends_.size();
+  // Keep the backends sorted by type.
+  auto it =
+      std::upper_bound(producer_backends_.begin(), producer_backends_.end(),
+                       type, CompareBackendByType<RegisteredProducerBackend>());
+  it = producer_backends_.emplace(it);
+
+  RegisteredProducerBackend& rb = *it;
+  rb.backend = backend;
+  rb.id = backend_id;
+  rb.type = type;
+  rb.producer.reset(
+      new ProducerImpl(this, backend_id, args.shmem_batch_commits_duration_ms));
+  rb.producer_conn_args.producer = rb.producer.get();
+  rb.producer_conn_args.producer_name = platform_->GetCurrentProcessName();
+  rb.producer_conn_args.task_runner = task_runner_.get();
+  rb.producer_conn_args.shmem_size_hint_bytes = args.shmem_size_hint_kb * 1024;
+  rb.producer_conn_args.shmem_page_size_hint_bytes =
+      args.shmem_page_size_hint_kb * 1024;
+  rb.producer->Initialize(rb.backend->ConnectProducer(rb.producer_conn_args));
+}
+
+TracingMuxerImpl::RegisteredProducerBackend*
+TracingMuxerImpl::FindProducerBackendById(TracingBackendId id) {
+  for (RegisteredProducerBackend& b : producer_backends_) {
+    if (b.id == id) {
+      return &b;
+    }
+  }
+  return nullptr;
+}
+
+TracingMuxerImpl::RegisteredProducerBackend*
+TracingMuxerImpl::FindProducerBackendByType(BackendType type) {
+  for (RegisteredProducerBackend& b : producer_backends_) {
+    if (b.type == type) {
+      return &b;
+    }
+  }
+  return nullptr;
+}
+
+TracingMuxerImpl::RegisteredConsumerBackend*
+TracingMuxerImpl::FindConsumerBackendByType(BackendType type) {
+  for (RegisteredConsumerBackend& b : consumer_backends_) {
+    if (b.type == type) {
+      return &b;
+    }
+  }
+  return nullptr;
+}
+
+void TracingMuxerImpl::AddBackends(const TracingInitArgs& args) {
   if (args.backends & kSystemBackend) {
-    PERFETTO_CHECK(args.system_backend_factory_);
-    add_backend(args.system_backend_factory_(), kSystemBackend);
+    PERFETTO_CHECK(args.system_producer_backend_factory_);
+    if (FindProducerBackendByType(kSystemBackend) == nullptr) {
+      AddProducerBackend(args.system_producer_backend_factory_(),
+                         kSystemBackend, args);
+    }
+    if (args.enable_system_consumer) {
+      PERFETTO_CHECK(args.system_consumer_backend_factory_);
+      if (FindConsumerBackendByType(kSystemBackend) == nullptr) {
+        AddConsumerBackend(args.system_consumer_backend_factory_(),
+                           kSystemBackend);
+      }
+    }
   }
 
   if (args.backends & kInProcessBackend) {
-    PERFETTO_CHECK(args.in_process_backend_factory_);
-    add_backend(args.in_process_backend_factory_(), kInProcessBackend);
+    TracingBackend* b = nullptr;
+    if (FindProducerBackendByType(kInProcessBackend) == nullptr) {
+      if (!b) {
+        PERFETTO_CHECK(args.in_process_backend_factory_);
+        b = args.in_process_backend_factory_();
+      }
+      AddProducerBackend(b, kInProcessBackend, args);
+    }
+    if (FindConsumerBackendByType(kInProcessBackend) == nullptr) {
+      if (!b) {
+        PERFETTO_CHECK(args.in_process_backend_factory_);
+        b = args.in_process_backend_factory_();
+      }
+      AddConsumerBackend(b, kInProcessBackend);
+    }
   }
 
   if (args.backends & kCustomBackend) {
     PERFETTO_CHECK(args.custom_backend);
-    add_backend(args.custom_backend, kCustomBackend);
+    if (FindProducerBackendByType(kCustomBackend) == nullptr) {
+      AddProducerBackend(args.custom_backend, kCustomBackend, args);
+    }
+    if (FindConsumerBackendByType(kCustomBackend) == nullptr) {
+      AddConsumerBackend(args.custom_backend, kCustomBackend);
+    }
   }
 
   if (args.backends & ~(kSystemBackend | kInProcessBackend | kCustomBackend)) {
     PERFETTO_FATAL("Unsupported tracing backend type");
   }
-
-  // Fallback backend for consumer creation for an unsupported backend type.
-  // This backend simply fails any attempt to start a tracing session.
-  // NOTE: This backend instance has to be added last.
-  add_backend(internal::TracingBackendFake::GetInstance(),
-              BackendType::kUnspecifiedBackend);
 }
 
 // Can be called from any thread (but not concurrently).
@@ -1019,7 +1130,7 @@
   base::TimeMillis expire_time =
       base::GetWallTimeMs() + base::TimeMillis(ttl_ms);
   task_runner_->PostTask([this, triggers, expire_time] {
-    for (RegisteredBackend& backend : backends_) {
+    for (RegisteredProducerBackend& backend : producer_backends_) {
       if (backend.producer->connected_) {
         backend.producer->service_->ActivateTriggers(triggers);
       } else {
@@ -1171,7 +1282,7 @@
     internal_state->muxer_id_for_testing = muxer_id_for_testing_;
 
     if (startup_session_id) {
-      RegisteredBackend& backend = backends_[backend_id];
+      RegisteredProducerBackend& backend = *FindProducerBackendById(backend_id);
       uint16_t& last_reservation =
           backend.producer->last_startup_target_buffer_reservation_;
       if (last_reservation == std::numeric_limits<uint16_t>::max()) {
@@ -1257,7 +1368,7 @@
       ds.internal_state->startup_target_buffer_reservation.load(
           std::memory_order_relaxed);
   if (startup_reservation) {
-    RegisteredBackend& backend = backends_[backend_id];
+    RegisteredProducerBackend& backend = *FindProducerBackendById(backend_id);
     TracingSessionGlobalID session_id = ds.internal_state->startup_session_id;
     auto session_it = std::find_if(
         backend.startup_sessions.begin(), backend.startup_sessions.end(),
@@ -1314,7 +1425,8 @@
   std::unique_lock<std::recursive_mutex> lock(ds.internal_state->lock);
   if (ds.internal_state->interceptor)
     ds.internal_state->interceptor->OnStart({});
-  ds.internal_state->trace_lambda_enabled = true;
+  ds.internal_state->trace_lambda_enabled.store(true,
+                                                std::memory_order_relaxed);
   PERFETTO_DCHECK(ds.internal_state->data_source != nullptr);
 
   if (!ds.requires_callbacks_under_lock)
@@ -1408,7 +1520,8 @@
   TracingSessionGlobalID startup_session_id;
   {
     std::lock_guard<std::recursive_mutex> guard(ds.internal_state->lock);
-    ds.internal_state->trace_lambda_enabled = false;
+    ds.internal_state->trace_lambda_enabled.store(false,
+                                                  std::memory_order_relaxed);
     ds.internal_state->data_source.reset();
     ds.internal_state->interceptor.reset();
     startup_buffer_reservation =
@@ -1422,10 +1535,10 @@
 
   TracingMuxer::generation_++;
 
-  // |backends_| is append-only, Backend instances are always valid.
-  PERFETTO_CHECK(backend_id < backends_.size());
-  RegisteredBackend& backend = backends_[backend_id];
-  ProducerImpl* producer = backends_[backend_id].producer.get();
+  // |producer_backends_| is append-only, Backend instances are always valid.
+  PERFETTO_CHECK(backend_id < producer_backends_.size());
+  RegisteredProducerBackend& backend = *FindProducerBackendById(backend_id);
+  ProducerImpl* producer = backend.producer.get();
   if (!producer)
     return;
 
@@ -1515,9 +1628,9 @@
     task_runner_->PostTask([this, &mutex, &cv, &countdown] {
       {
         std::unique_lock<std::mutex> countdown_lock(mutex);
-        countdown = backends_.size();
+        countdown = producer_backends_.size();
       }
-      for (auto& backend : backends_) {
+      for (auto& backend : producer_backends_) {
         auto* producer = backend.producer.get();
         producer->service_->Sync([&mutex, &cv, &countdown] {
           std::unique_lock<std::mutex> countdown_lock(mutex);
@@ -1537,7 +1650,7 @@
   bool done = false;
   bool all_producers_connected = true;
   task_runner_->PostTask([this, &mutex, &cv, &done, &all_producers_connected] {
-    for (auto& backend : backends_)
+    for (auto& backend : producer_backends_)
       all_producers_connected &= backend.producer->connected_;
     std::unique_lock<std::mutex> lock(mutex);
     done = true;
@@ -1608,7 +1721,7 @@
 void TracingMuxerImpl::UpdateDataSourceOnAllBackends(RegisteredDataSource& rds,
                                                      bool is_changed) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  for (RegisteredBackend& backend : backends_) {
+  for (RegisteredProducerBackend& backend : producer_backends_) {
     // We cannot call RegisterDataSource on the backend before it connects.
     if (!backend.producer->connected_)
       continue;
@@ -1752,7 +1865,7 @@
 void TracingMuxerImpl::DestroyTracingSession(
     TracingSessionGlobalID session_id) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  for (RegisteredBackend& backend : backends_) {
+  for (RegisteredConsumerBackend& backend : consumer_backends_) {
     // We need to find the consumer (if any) and call Disconnect as we destroy
     // the tracing session. We can't call Disconnect() inside this for loop
     // because in the in-process case this will end up to a synchronous call to
@@ -1845,7 +1958,7 @@
 void TracingMuxerImpl::SetBatchCommitsDurationForTesting(
     uint32_t batch_commits_duration_ms,
     BackendType backend_type) {
-  for (RegisteredBackend& backend : backends_) {
+  for (RegisteredProducerBackend& backend : producer_backends_) {
     if (backend.producer && backend.producer->connected_ &&
         backend.type == backend_type) {
       backend.producer->service_->MaybeSharedMemoryArbiter()
@@ -1856,7 +1969,7 @@
 
 bool TracingMuxerImpl::EnableDirectSMBPatchingForTesting(
     BackendType backend_type) {
-  for (RegisteredBackend& backend : backends_) {
+  for (RegisteredProducerBackend& backend : producer_backends_) {
     if (backend.producer && backend.producer->connected_ &&
         backend.type == backend_type &&
         !backend.producer->service_->MaybeSharedMemoryArbiter()
@@ -1870,27 +1983,31 @@
 TracingMuxerImpl::ConsumerImpl* TracingMuxerImpl::FindConsumer(
     TracingSessionGlobalID session_id) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  for (RegisteredBackend& backend : backends_) {
+  return FindConsumerAndBackend(session_id).first;
+}
+
+std::pair<TracingMuxerImpl::ConsumerImpl*,
+          TracingMuxerImpl::RegisteredConsumerBackend*>
+TracingMuxerImpl::FindConsumerAndBackend(TracingSessionGlobalID session_id) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  for (RegisteredConsumerBackend& backend : consumer_backends_) {
     for (auto& consumer : backend.consumers) {
       if (consumer->session_id_ == session_id) {
-        return consumer.get();
+        return {consumer.get(), &backend};
       }
     }
   }
-  return nullptr;
+  return {nullptr, nullptr};
 }
 
 void TracingMuxerImpl::InitializeConsumer(TracingSessionGlobalID session_id) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
 
-  auto* consumer = FindConsumer(session_id);
-  if (!consumer)
+  auto res = FindConsumerAndBackend(session_id);
+  if (!res.first || !res.second)
     return;
-
-  TracingBackendId backend_id = consumer->backend_id_;
-  // |backends_| is append-only, Backend instances are always valid.
-  PERFETTO_CHECK(backend_id < backends_.size());
-  RegisteredBackend& backend = backends_[backend_id];
+  TracingMuxerImpl::ConsumerImpl* consumer = res.first;
+  RegisteredConsumerBackend& backend = *res.second;
 
   TracingBackend::ConnectConsumerArgs conn_args;
   conn_args.consumer = consumer;
@@ -1900,7 +2017,7 @@
 
 void TracingMuxerImpl::OnConsumerDisconnected(ConsumerImpl* consumer) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  for (RegisteredBackend& backend : backends_) {
+  for (RegisteredConsumerBackend& backend : consumer_backends_) {
     auto pred = [consumer](const std::unique_ptr<ConsumerImpl>& con) {
       return con.get() == consumer;
     };
@@ -1916,7 +2033,7 @@
 
 void TracingMuxerImpl::OnProducerDisconnected(ProducerImpl* producer) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  for (RegisteredBackend& backend : backends_) {
+  for (RegisteredProducerBackend& backend : producer_backends_) {
     if (backend.producer.get() != producer)
       continue;
     // Try reconnecting the disconnected producer. If the connection succeeds,
@@ -1959,7 +2076,7 @@
     TracingBackendId backend_id,
     DataSourceInstanceID instance_id) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  auto& backend = backends_[backend_id];
+  RegisteredProducerBackend& backend = *FindProducerBackendById(backend_id);
   for (const auto& rds : data_sources_) {
     DataSourceStaticState* static_state = rds.static_state;
     for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
@@ -1995,7 +2112,8 @@
         interceptor.tls_factory(static_state, data_source_instance_index),
         interceptor.packet_callback, static_state, data_source_instance_index));
   }
-  ProducerImpl* producer = backends_[data_source->backend_id].producer.get();
+  ProducerImpl* producer =
+      FindProducerBackendById(data_source->backend_id)->producer.get();
   // Atomically load the current service endpoint. We keep the pointer as a
   // shared pointer on the stack to guard against it from being concurrently
   // modified on the thread by ProducerImpl::Initialize() swapping in a
@@ -2038,34 +2156,34 @@
 // This is called via the public API Tracing::NewTrace().
 // Can be called from any thread.
 std::unique_ptr<TracingSession> TracingMuxerImpl::CreateTracingSession(
-    BackendType requested_backend_type) {
+    BackendType requested_backend_type,
+    TracingConsumerBackend* (*system_backend_factory)()) {
   TracingSessionGlobalID session_id = ++next_tracing_session_id_;
 
   // |backend_type| can only specify one backend, not an OR-ed mask.
   PERFETTO_CHECK((requested_backend_type & (requested_backend_type - 1)) == 0);
 
   // Capturing |this| is fine because the TracingMuxer is a leaky singleton.
-  task_runner_->PostTask([this, requested_backend_type, session_id] {
-    for (RegisteredBackend& backend : backends_) {
+  task_runner_->PostTask([this, requested_backend_type, session_id,
+                          system_backend_factory] {
+    if (requested_backend_type == kSystemBackend && system_backend_factory &&
+        !FindConsumerBackendByType(kSystemBackend)) {
+      AddConsumerBackend(system_backend_factory(), kSystemBackend);
+    }
+    for (RegisteredConsumerBackend& backend : consumer_backends_) {
       if (requested_backend_type && backend.type &&
           backend.type != requested_backend_type) {
         continue;
       }
 
-      if (!backend.consumer_enabled) {
-        continue;
-      }
-
-      TracingBackendId backend_id = backend.id;
-
       // Create the consumer now, even if we have to ask the embedder below, so
       // that any other tasks executing after this one can find the consumer and
       // change its pending attributes.
       backend.consumers.emplace_back(
-          new ConsumerImpl(this, backend.type, backend.id, session_id));
+          new ConsumerImpl(this, backend.type, session_id));
 
-      // The last registered backend in |backends_| is the unsupported backend
-      // without a valid type.
+      // The last registered backend in |consumer_backends_| is the unsupported
+      // backend without a valid type.
       if (!backend.type) {
         PERFETTO_ELOG(
             "No tracing backend ready for type=%d, consumer will disconnect",
@@ -2081,10 +2199,11 @@
         return;
       }
 
+      BackendType type = backend.type;
       TracingPolicy::ShouldAllowConsumerSessionArgs args;
       args.backend_type = backend.type;
-      args.result_callback = [this, backend_id, session_id](bool allow) {
-        task_runner_->PostTask([this, backend_id, session_id, allow] {
+      args.result_callback = [this, type, session_id](bool allow) {
+        task_runner_->PostTask([this, type, session_id, allow] {
           if (allow) {
             InitializeConsumer(session_id);
             return;
@@ -2093,7 +2212,7 @@
           PERFETTO_ELOG(
               "Consumer session for backend type type=%d forbidden, "
               "consumer will disconnect",
-              backends_[backend_id].type);
+              type);
 
           auto* consumer = FindConsumer(session_id);
           if (!consumer)
@@ -2129,15 +2248,15 @@
 
   // Capturing |this| is fine because the TracingMuxer is a leaky singleton.
   task_runner_->PostTask([this, config, opts, backend_type, session_id] {
-    for (RegisteredBackend& backend : backends_) {
+    for (RegisteredProducerBackend& backend : producer_backends_) {
       if (backend_type && backend.type && backend.type != backend_type) {
         continue;
       }
 
       TracingBackendId backend_id = backend.id;
 
-      // The last registered backend in |backends_| is the unsupported backend
-      // without a valid type.
+      // The last registered backend in |producer_backends_| is the unsupported
+      // backend without a valid type.
       if (!backend.type) {
         PERFETTO_ELOG(
             "No tracing backend initialized for type=%d, startup tracing "
@@ -2257,7 +2376,7 @@
     BackendType backend_type) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
 
-  for (RegisteredBackend& backend : backends_) {
+  for (RegisteredProducerBackend& backend : producer_backends_) {
     if (backend_type != backend.type)
       continue;
 
@@ -2328,8 +2447,13 @@
 }
 
 void TracingMuxerImpl::InitializeInstance(const TracingInitArgs& args) {
-  if (instance_ != TracingMuxerFake::Get())
-    PERFETTO_FATAL("Tracing already initialized");
+  if (instance_ != TracingMuxerFake::Get()) {
+    // The tracing muxer was already initialized. We might need to initialize
+    // additional backends that were not configured earlier.
+    auto* muxer = static_cast<TracingMuxerImpl*>(instance_);
+    muxer->task_runner_->PostTask([muxer, args] { muxer->AddBackends(args); });
+    return;
+  }
   // If we previously had a TracingMuxerImpl instance which was reset,
   // reinitialize and reuse it instead of trying to create a new one. See
   // ResetForTesting().
@@ -2337,7 +2461,10 @@
     auto* muxer = g_prev_instance;
     g_prev_instance = nullptr;
     instance_ = muxer;
-    muxer->task_runner_->PostTask([muxer, args] { muxer->Initialize(args); });
+    muxer->task_runner_->PostTask([muxer, args] {
+      muxer->Initialize(args);
+      muxer->AddBackends(args);
+    });
   } else {
     new TracingMuxerImpl(args);
   }
@@ -2368,21 +2495,24 @@
     // Unregister all data sources so they don't interfere with any future
     // tracing sessions.
     for (RegisteredDataSource& rds : muxer->data_sources_) {
-      for (RegisteredBackend& backend : muxer->backends_) {
+      for (RegisteredProducerBackend& backend : muxer->producer_backends_) {
         if (!backend.producer->service_)
           continue;
         backend.producer->service_->UnregisterDataSource(rds.descriptor.name());
       }
     }
-    for (auto& backend : muxer->backends_) {
+    for (auto& backend : muxer->consumer_backends_) {
       // Check that no consumer session is currently active on any backend.
       for (auto& consumer : backend.consumers)
         PERFETTO_CHECK(!consumer->service_);
+    }
+    for (auto& backend : muxer->producer_backends_) {
       backend.producer->muxer_ = nullptr;
       backend.producer->DisposeConnection();
       muxer->dead_backends_.push_back(std::move(backend));
     }
-    muxer->backends_.clear();
+    muxer->consumer_backends_.clear();
+    muxer->producer_backends_.clear();
     muxer->interceptors_.clear();
 
     for (auto& ds : muxer->data_sources_) {
@@ -2431,7 +2561,7 @@
     // Check that no consumer session is currently active on any backend.
     // Producers will be automatically disconnected as a part of deleting the
     // muxer below.
-    for (auto& backend : muxer->backends_) {
+    for (auto& backend : muxer->consumer_backends_) {
       for (auto& consumer : backend.consumers) {
         PERFETTO_CHECK(!consumer->service_);
       }
diff --git a/src/tracing/internal/tracing_muxer_impl.h b/src/tracing/internal/tracing_muxer_impl.h
index 4d83ab9..dc89bec 100644
--- a/src/tracing/internal/tracing_muxer_impl.h
+++ b/src/tracing/internal/tracing_muxer_impl.h
@@ -26,6 +26,7 @@
 #include <list>
 #include <map>
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "perfetto/base/time.h"
@@ -34,6 +35,7 @@
 #include "perfetto/ext/tracing/core/basic_types.h"
 #include "perfetto/ext/tracing/core/consumer.h"
 #include "perfetto/ext/tracing/core/producer.h"
+#include "perfetto/tracing/backend_type.h"
 #include "perfetto/tracing/core/data_source_descriptor.h"
 #include "perfetto/tracing/core/forward_decls.h"
 #include "perfetto/tracing/core/trace_config.h"
@@ -133,7 +135,9 @@
 
   void ActivateTriggers(const std::vector<std::string>&, uint32_t) override;
 
-  std::unique_ptr<TracingSession> CreateTracingSession(BackendType);
+  std::unique_ptr<TracingSession> CreateTracingSession(
+      BackendType,
+      TracingConsumerBackend* (*system_backend_factory)());
   std::unique_ptr<StartupTracingSession> CreateStartupTracingSession(
       const TraceConfig& config,
       Tracing::SetupStartupTracingOpts);
@@ -272,10 +276,7 @@
   // tracing sessions.
   class ConsumerImpl : public Consumer {
    public:
-    ConsumerImpl(TracingMuxerImpl*,
-                 BackendType,
-                 TracingBackendId,
-                 TracingSessionGlobalID);
+    ConsumerImpl(TracingMuxerImpl*, BackendType, TracingSessionGlobalID);
     ~ConsumerImpl() override;
 
     void Initialize(std::unique_ptr<ConsumerEndpoint> endpoint);
@@ -300,7 +301,6 @@
 
     TracingMuxerImpl* muxer_;
     BackendType const backend_type_;
-    TracingBackendId const backend_id_;
     TracingSessionGlobalID const session_id_;
     bool connected_ = false;
 
@@ -425,29 +425,42 @@
     std::function<void()> on_adopted;
   };
 
-  struct RegisteredBackend {
+  struct RegisteredProducerBackend {
     // Backends are supposed to have static lifetime.
-    TracingBackend* backend = nullptr;
+    TracingProducerBackend* backend = nullptr;
     TracingBackendId id = 0;
     BackendType type{};
 
     TracingBackend::ConnectProducerArgs producer_conn_args;
     std::unique_ptr<ProducerImpl> producer;
 
+    std::vector<RegisteredStartupSession> startup_sessions;
+  };
+
+  struct RegisteredConsumerBackend {
+    // Backends are supposed to have static lifetime.
+    TracingConsumerBackend* backend = nullptr;
+    BackendType type{};
     // The calling code can request more than one concurrently active tracing
     // session for the same backend. We need to create one consumer per session.
     std::vector<std::unique_ptr<ConsumerImpl>> consumers;
-
-    std::vector<RegisteredStartupSession> startup_sessions;
-
-    bool consumer_enabled = true;
   };
 
   void UpdateDataSourceOnAllBackends(RegisteredDataSource& rds,
                                      bool is_changed);
   explicit TracingMuxerImpl(const TracingInitArgs&);
   void Initialize(const TracingInitArgs& args);
+  void AddBackends(const TracingInitArgs& args);
+  void AddConsumerBackend(TracingConsumerBackend* backend, BackendType type);
+  void AddProducerBackend(TracingProducerBackend* backend,
+                          BackendType type,
+                          const TracingInitArgs& args);
   ConsumerImpl* FindConsumer(TracingSessionGlobalID session_id);
+  std::pair<ConsumerImpl*, RegisteredConsumerBackend*> FindConsumerAndBackend(
+      TracingSessionGlobalID session_id);
+  RegisteredProducerBackend* FindProducerBackendById(TracingBackendId id);
+  RegisteredProducerBackend* FindProducerBackendByType(BackendType type);
+  RegisteredConsumerBackend* FindConsumerBackendByType(BackendType type);
   void InitializeConsumer(TracingSessionGlobalID session_id);
   void OnConsumerDisconnected(ConsumerImpl* consumer);
   void OnProducerDisconnected(ProducerImpl* producer);
@@ -493,7 +506,11 @@
   // WARNING: If you add new state here, be sure to update ResetForTesting.
   std::unique_ptr<base::TaskRunner> task_runner_;
   std::vector<RegisteredDataSource> data_sources_;
-  std::vector<RegisteredBackend> backends_;
+  // These lists can only have one backend per BackendType. The elements are
+  // sorted by BackendType priority (see BackendTypePriority). They always
+  // contain a fake low-priority kUnspecifiedBackend at the end.
+  std::list<RegisteredProducerBackend> producer_backends_;
+  std::list<RegisteredConsumerBackend> consumer_backends_;
   std::vector<RegisteredInterceptor> interceptors_;
   TracingPolicy* policy_ = nullptr;
 
@@ -512,7 +529,7 @@
   // After ResetForTesting() is called, holds tracing backends which needs to be
   // kept alive until all inbound references have gone away. See
   // SweepDeadBackends().
-  std::list<RegisteredBackend> dead_backends_;
+  std::list<RegisteredProducerBackend> dead_backends_;
 
   PERFETTO_THREAD_CHECKER(thread_checker_)
 };
diff --git a/src/tracing/internal/track_event_internal.cc b/src/tracing/internal/track_event_internal.cc
index 736e2dd..67b1fda 100644
--- a/src/tracing/internal/track_event_internal.cc
+++ b/src/tracing/internal/track_event_internal.cc
@@ -386,9 +386,7 @@
     const TrackEventTlsState& tls_state,
     const TraceTimestamp& timestamp) {
   auto sequence_timestamp = timestamp;
-  if (timestamp.clock_id !=
-          static_cast<uint32_t>(TrackEventInternal::GetClockId()) &&
-      timestamp.clock_id != kClockIdIncremental) {
+  if (timestamp.clock_id != kClockIdIncremental) {
     sequence_timestamp = TrackEventInternal::GetTraceTime();
   }
 
diff --git a/src/tracing/test/aligned_buffer_test.cc b/src/tracing/test/aligned_buffer_test.cc
index 6a603ea..082e130 100644
--- a/src/tracing/test/aligned_buffer_test.cc
+++ b/src/tracing/test/aligned_buffer_test.cc
@@ -20,8 +20,10 @@
 
 namespace perfetto {
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // static
 constexpr size_t AlignedBufferTest::kNumPages;
+#endif
 
 void AlignedBufferTest::SetUp() {
   page_size_ = GetParam();
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index f0f2f5d..a821280 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -201,12 +201,15 @@
 using perfetto::internal::TrackEventIncrementalState;
 using perfetto::internal::TrackEventInternal;
 using ::testing::_;
+using ::testing::AllOf;
 using ::testing::ContainerEq;
 using ::testing::Contains;
+using ::testing::Each;
 using ::testing::ElementsAre;
 using ::testing::HasSubstr;
 using ::testing::Invoke;
 using ::testing::InvokeWithoutArgs;
+using ::testing::IsEmpty;
 using ::testing::NiceMock;
 using ::testing::Not;
 using ::testing::Property;
@@ -721,28 +724,28 @@
     perfetto::Tracing::ResetForTesting();
   }
 
-  template <typename DataSourceType>
+  template <typename DerivedDataSource>
   TestDataSourceHandle* RegisterDataSource(std::string name) {
     perfetto::DataSourceDescriptor dsd;
     dsd.set_name(name);
-    return RegisterDataSource<DataSourceType>(dsd);
+    return RegisterDataSource<DerivedDataSource>(dsd);
   }
 
-  template <typename DataSourceType>
+  template <typename DerivedDataSource>
   TestDataSourceHandle* RegisterDataSource(
       const perfetto::DataSourceDescriptor& dsd) {
     EXPECT_EQ(data_sources_.count(dsd.name()), 0u);
     TestDataSourceHandle* handle = &data_sources_[dsd.name()];
-    DataSourceType::Register(dsd);
+    DerivedDataSource::Register(dsd);
     return handle;
   }
 
-  template <typename DataSourceType>
+  template <typename DerivedDataSource>
   TestDataSourceHandle* UpdateDataSource(
       const perfetto::DataSourceDescriptor& dsd) {
     EXPECT_EQ(data_sources_.count(dsd.name()), 1u);
     TestDataSourceHandle* handle = &data_sources_[dsd.name()];
-    DataSourceType::UpdateDescriptor(dsd);
+    DerivedDataSource::UpdateDescriptor(dsd);
     return handle;
   }
 
@@ -4554,6 +4557,51 @@
           "[track=0]Legacy_e(unscoped_id=9001):cat.LegacyAsync3"));
 }
 
+TEST_P(PerfettoApiTest, LegacyTraceEventsAndClockSnapshots) {
+  auto* tracing_session = NewTraceWithCategories({"cat"});
+  tracing_session->get()->StartBlocking();
+
+  {
+    TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("cat", "LegacyAsync", 5678);
+
+    perfetto::test::TracingMuxerImplInternalsForTest::ClearIncrementalState();
+
+    TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
+        "cat", "LegacyAsyncWithTimestamp", 5678, MyTimestamp{1});
+    TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
+        "cat", "LegacyAsyncWithTimestamp", 5678, MyTimestamp{2});
+
+    perfetto::test::TracingMuxerImplInternalsForTest::ClearIncrementalState();
+
+    TRACE_EVENT_NESTABLE_ASYNC_END0("cat", "LegacyAsync", 5678);
+  }
+
+  auto trace = StopSessionAndReturnParsedTrace(tracing_session);
+
+  // Check that clock snapshots are monotonic and don't contain timestamps from
+  // trace events with explicit timestamps.
+  std::unordered_map<uint64_t, uint64_t> last_clock_ts;
+  for (const auto& packet : trace.packet()) {
+    if (packet.has_clock_snapshot()) {
+      for (auto& clock : packet.clock_snapshot().clocks()) {
+        if (!clock.is_incremental()) {
+          uint64_t ts = clock.timestamp();
+          uint64_t id = clock.clock_id();
+          EXPECT_LE(last_clock_ts[id], ts);
+          last_clock_ts[id] = ts;
+        }
+      }
+
+      // Events that don't use explicit timestamps should have exactly the same
+      // timestamp as in the snapshot (i.e. the relative ts of 0).
+      // Here we assume that timestamps are incremental by default.
+      if (!packet.has_timestamp_clock_id()) {
+        EXPECT_EQ(packet.timestamp(), 0u);
+      }
+    }
+  }
+}
+
 TEST_P(PerfettoApiTest, LegacyTraceEventsWithCustomAnnotation) {
   // Create a new trace session.
   auto* tracing_session = NewTraceWithCategories({"cat"});
@@ -5550,6 +5598,54 @@
                             "trigger1")))));
 }
 
+TEST_P(PerfettoApiTest, StartTracingWhileExecutingTracepoint) {
+  perfetto::TraceConfig cfg;
+  auto* buffer = cfg.add_buffers();
+  buffer->set_size_kb(64);
+  auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+  ds_cfg->set_name("my_data_source");
+
+  std::atomic<bool> quit{false};
+  WaitableTestEvent outside_tracing;
+  WaitableTestEvent tracing;
+  std::thread t([&] {
+    while (!quit) {
+      MockDataSource::Trace([&](MockDataSource::TraceContext ctx) {
+        {
+          auto packet = ctx.NewTracePacket();
+          packet->set_for_testing()->set_str("My String");
+        }
+        { auto packet = ctx.NewTracePacket(); }
+        tracing.Notify();
+      });
+      outside_tracing.Notify();
+      std::this_thread::yield();
+    }
+  });
+  outside_tracing.Wait();
+
+  auto* tracing_session = NewTrace(cfg);
+  tracing_session->get()->StartBlocking();
+  tracing.Wait();
+  tracing_session->get()->StopBlocking();
+
+  // The data source instance should be stopped.
+  auto* data_source = &data_sources_["my_data_source"];
+  data_source->on_stop.Wait();
+
+  quit = true;
+  t.join();
+
+  auto trace = StopSessionAndReturnParsedTrace(tracing_session);
+  std::vector<std::string> test_strings;
+  for (auto& trace_packet : trace.packet()) {
+    if (trace_packet.has_for_testing()) {
+      test_strings.push_back(trace_packet.for_testing().str());
+    }
+  }
+  EXPECT_THAT(test_strings, AllOf(Not(IsEmpty()), Each("My String")));
+}
+
 class PerfettoStartupTracingApiTest : public PerfettoApiTest {
  public:
   using SetupStartupTracingOpts = perfetto::Tracing::SetupStartupTracingOpts;
@@ -5935,8 +6031,12 @@
   void TearDown() override { perfetto::Tracing::ResetForTesting(); }
 
   static std::unique_ptr<perfetto::TracingSession> StartTracing(
-      perfetto::BackendType backend_type) {
+      perfetto::BackendType backend_type,
+      bool short_stop_timeout = false) {
     perfetto::TraceConfig cfg;
+    if (short_stop_timeout) {
+      cfg.set_data_source_stop_timeout_ms(500);
+    }
     cfg.add_buffers()->set_size_kb(1024);
     auto* ds_cfg = cfg.add_data_sources()->mutable_config();
     ds_cfg->set_name("track_event");
@@ -5988,7 +6088,8 @@
   auto tracing_session1 = StartTracing(perfetto::kInProcessBackend);
   TRACE_EVENT_BEGIN("test", "DrawGame1");
 
-  auto tracing_session2 = StartTracing(perfetto::kInProcessBackend);
+  auto tracing_session2 =
+      StartTracing(perfetto::kInProcessBackend, /*short_stop_timeout=*/true);
   TRACE_EVENT_BEGIN("test", "DrawGame2");
 
   auto slices1 = StopTracing(std::move(tracing_session1));
@@ -6030,18 +6131,105 @@
 
   perfetto::test::DisableReconnectLimit();
 
-  std::unique_ptr<perfetto::TracingSession> ts =
-      perfetto::Tracing::NewTrace(perfetto::kSystemBackend);
+  // Creating the consumer with kUnspecifiedBackend should cause a connection
+  // error: there's no consumer backend.
+  {
+    std::unique_ptr<perfetto::TracingSession> ts =
+        perfetto::Tracing::NewTrace(perfetto::kUnspecifiedBackend);
 
-  // Creating the consumer should cause an asynchronous disconnect error.
-  WaitableTestEvent got_error;
-  ts->SetOnErrorCallback([&](perfetto::TracingError error) {
-    EXPECT_EQ(perfetto::TracingError::kDisconnected, error.code);
-    EXPECT_FALSE(error.message.empty());
-    got_error.Notify();
-  });
-  got_error.Wait();
-  ts.reset();
+    WaitableTestEvent got_error;
+    ts->SetOnErrorCallback([&](perfetto::TracingError error) {
+      EXPECT_EQ(perfetto::TracingError::kDisconnected, error.code);
+      EXPECT_FALSE(error.message.empty());
+      got_error.Notify();
+    });
+    got_error.Wait();
+  }
+
+  // Creating the consumer with kSystemBackend should create a system consumer
+  // backend on the spot.
+  EXPECT_TRUE(perfetto::Tracing::NewTrace(perfetto::kSystemBackend)
+                  ->QueryServiceStateBlocking()
+                  .success);
+
+  // Now even a consumer with kUnspecifiedBackend should succeed, because the
+  // backend has been created.
+  EXPECT_TRUE(perfetto::Tracing::NewTrace(perfetto::kUnspecifiedBackend)
+                  ->QueryServiceStateBlocking()
+                  .success);
+
+  perfetto::Tracing::ResetForTesting();
+}
+
+TEST(PerfettoApiInitTest, SeparateInitializations) {
+  auto system_service = perfetto::test::SystemService::Start();
+  // If the system backend isn't supported, skip
+  if (!system_service.valid()) {
+    GTEST_SKIP();
+  }
+
+  {
+    EXPECT_FALSE(perfetto::Tracing::IsInitialized());
+    TracingInitArgs args;
+    args.backends = perfetto::kInProcessBackend;
+    perfetto::Tracing::Initialize(args);
+  }
+
+  // If this wasn't the first test to run in this process, any producers
+  // connected to the old system service will have been disconnected by the
+  // service restarting above. Wait for all producers to connect again before
+  // proceeding with the test.
+  perfetto::test::SyncProducers();
+
+  perfetto::test::DisableReconnectLimit();
+
+  {
+    perfetto::DataSourceDescriptor dsd;
+    dsd.set_name("CustomDataSource");
+    CustomDataSource::Register(dsd);
+  }
+
+  {
+    std::unique_ptr<perfetto::TracingSession> tracing_session =
+        perfetto::Tracing::NewTrace(perfetto::kInProcessBackend);
+    auto result = tracing_session->QueryServiceStateBlocking();
+    perfetto::protos::gen::TracingServiceState state;
+    ASSERT_TRUE(result.success);
+    ASSERT_TRUE(state.ParseFromArray(result.service_state_data.data(),
+                                     result.service_state_data.size()));
+    EXPECT_THAT(state.data_sources(),
+                Contains(Property(
+                    &perfetto::protos::gen::TracingServiceState::DataSource::
+                        ds_descriptor,
+                    Property(&perfetto::protos::gen::DataSourceDescriptor::name,
+                             "CustomDataSource"))));
+  }
+
+  {
+    EXPECT_TRUE(perfetto::Tracing::IsInitialized());
+    TracingInitArgs args;
+    args.backends = perfetto::kSystemBackend;
+    args.enable_system_consumer = false;
+    perfetto::Tracing::Initialize(args);
+  }
+
+  perfetto::test::SyncProducers();
+
+  {
+    std::unique_ptr<perfetto::TracingSession> tracing_session =
+        perfetto::Tracing::NewTrace(perfetto::kSystemBackend);
+    auto result = tracing_session->QueryServiceStateBlocking();
+    perfetto::protos::gen::TracingServiceState state;
+    ASSERT_TRUE(result.success);
+    ASSERT_TRUE(state.ParseFromArray(result.service_state_data.data(),
+                                     result.service_state_data.size()));
+    EXPECT_THAT(state.data_sources(),
+                Contains(Property(
+                    &perfetto::protos::gen::TracingServiceState::DataSource::
+                        ds_descriptor,
+                    Property(&perfetto::protos::gen::DataSourceDescriptor::name,
+                             "CustomDataSource"))));
+  }
 
   perfetto::Tracing::ResetForTesting();
 }
diff --git a/src/tracing/test/api_test_support.cc b/src/tracing/test/api_test_support.cc
index 064f259..6246a6b 100644
--- a/src/tracing/test/api_test_support.cc
+++ b/src/tracing/test/api_test_support.cc
@@ -147,41 +147,54 @@
   TestTempFile res{};
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
   base::StackString<255> temp_file("%s\\perfetto-XXXXXX", getenv("TMP"));
-  PERFETTO_CHECK(_mktemp_s(temp_file.c_str(), temp_file.len() + 1) == 0);
+  PERFETTO_CHECK(_mktemp_s(temp_file.mutable_data(), temp_file.len() + 1) == 0);
   HANDLE handle =
-      ::CreateFileA(temp_file, GENERIC_READ | GENERIC_WRITE,
+      ::CreateFileA(temp_file.c_str(), GENERIC_READ | GENERIC_WRITE,
                     FILE_SHARE_DELETE | FILE_SHARE_READ, nullptr, CREATE_ALWAYS,
                     FILE_ATTRIBUTE_TEMPORARY, nullptr);
   PERFETTO_CHECK(handle && handle != INVALID_HANDLE_VALUE);
   res.fd = _open_osfhandle(reinterpret_cast<intptr_t>(handle), 0);
+  res.path = temp_file.ToStdString();
 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
   char temp_file[] = "/data/local/tmp/perfetto-XXXXXXXX";
   res.fd = mkstemp(temp_file);
+  res.path = temp_file;
 #else
   char temp_file[] = "/tmp/perfetto-XXXXXXXX";
   res.fd = mkstemp(temp_file);
-#endif
   res.path = temp_file;
+#endif
   PERFETTO_CHECK(res.fd > 0);
   return res;
 }
 
 // static
 bool TracingMuxerImplInternalsForTest::DoesSystemBackendHaveSMB() {
-  using RegisteredBackend = TracingMuxerImpl::RegisteredBackend;
+  using RegisteredProducerBackend = TracingMuxerImpl::RegisteredProducerBackend;
   // Ideally we should be doing dynamic_cast and a DCHECK(muxer != nullptr);
   auto* muxer =
       reinterpret_cast<TracingMuxerImpl*>(TracingMuxerImpl::instance_);
-  const auto& backends = muxer->backends_;
-  const auto& backend = std::find_if(backends.begin(), backends.end(),
-                                     [](const RegisteredBackend& r_backend) {
-                                       return r_backend.type == kSystemBackend;
-                                     });
+  const auto& backends = muxer->producer_backends_;
+  const auto& backend =
+      std::find_if(backends.begin(), backends.end(),
+                   [](const RegisteredProducerBackend& r_backend) {
+                     return r_backend.type == kSystemBackend;
+                   });
   if (backend == backends.end())
     return false;
   const auto& service = backend->producer->service_;
   return service && service->shared_memory();
 }
 
+// static
+void TracingMuxerImplInternalsForTest::ClearIncrementalState() {
+  auto* muxer =
+      reinterpret_cast<TracingMuxerImpl*>(TracingMuxerImpl::instance_);
+  for (const auto& data_source : muxer->data_sources_) {
+    data_source.static_state->incremental_state_generation.fetch_add(
+        1, std::memory_order_relaxed);
+  }
+}
+
 }  // namespace test
 }  // namespace perfetto
diff --git a/src/tracing/test/api_test_support.h b/src/tracing/test/api_test_support.h
index 3fbc039..3a882e7 100644
--- a/src/tracing/test/api_test_support.h
+++ b/src/tracing/test/api_test_support.h
@@ -78,6 +78,7 @@
 class TracingMuxerImplInternalsForTest {
  public:
   static bool DoesSystemBackendHaveSMB();
+  static void ClearIncrementalState();
 };
 
 }  // namespace test
diff --git a/src/tracing/test/tracing_module.cc b/src/tracing/test/tracing_module.cc
index d2ade28..d6dff5d 100644
--- a/src/tracing/test/tracing_module.cc
+++ b/src/tracing/test/tracing_module.cc
@@ -25,9 +25,8 @@
 // combined into the same program.
 
 PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE(tracing_module);
-PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE_WITH_ATTRS(
-    tracing_extra,
-    PERFETTO_SDK_EXPORT);
+PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE_WITH_ATTRS(tracing_extra,
+                                                            [[maybe_unused]]);
 
 namespace tracing_extra {
 namespace {
diff --git a/src/tracing/test/tracing_module_categories.h b/src/tracing/test/tracing_module_categories.h
index 9b1dd7b..5b80a74 100644
--- a/src/tracing/test/tracing_module_categories.h
+++ b/src/tracing/test/tracing_module_categories.h
@@ -43,7 +43,7 @@
 // linkage attributes.
 PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS(
     tracing_extra,
-    PERFETTO_SDK_EXPORT,
+    [[maybe_unused]],
     perfetto::Category("extra"),
     perfetto::Category("extra2"));
 
diff --git a/src/tracing/tracing.cc b/src/tracing/tracing.cc
index 030f49c..7fbdfd1 100644
--- a/src/tracing/tracing.cc
+++ b/src/tracing/tracing.cc
@@ -39,32 +39,27 @@
 // static
 void Tracing::InitializeInternal(const TracingInitArgs& args) {
   std::unique_lock<std::mutex> lock(InitializedMutex());
-  static TracingInitArgs init_args;
-  if (g_was_initialized) {
-    if (!(init_args == args)) {
-      PERFETTO_ELOG(
-          "Tracing::Initialize() called more than once with different args. "
-          "This is not supported, only the first call will have effect.");
-      PERFETTO_DCHECK(false);
+  // If it's the first time Initialize is called, set some global params.
+  if (!g_was_initialized) {
+    // Make sure the headers and implementation files agree on the build config.
+    PERFETTO_CHECK(args.dcheck_is_on_ == PERFETTO_DCHECK_IS_ON());
+    if (args.log_message_callback) {
+      base::SetLogMessageCallback(args.log_message_callback);
     }
-    return;
-  }
 
-  // Make sure the headers and implementation files agree on the build config.
-  PERFETTO_CHECK(args.dcheck_is_on_ == PERFETTO_DCHECK_IS_ON());
-  if (args.log_message_callback) {
-    base::SetLogMessageCallback(args.log_message_callback);
-  }
-
-  if (args.use_monotonic_raw_clock) {
-    internal::TrackEventInternal::SetClockId(
-        protos::pbzero::BUILTIN_CLOCK_MONOTONIC_RAW);
+    if (args.use_monotonic_clock) {
+      PERFETTO_CHECK(!args.use_monotonic_raw_clock);
+      internal::TrackEventInternal::SetClockId(
+          protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+    } else if (args.use_monotonic_raw_clock) {
+      internal::TrackEventInternal::SetClockId(
+          protos::pbzero::BUILTIN_CLOCK_MONOTONIC_RAW);
+    }
   }
 
   internal::TracingMuxerImpl::InitializeInstance(args);
   internal::TrackRegistry::InitializeInstance();
   g_was_initialized = true;
-  init_args = args;
 }
 
 // static
@@ -94,9 +89,11 @@
 }
 
 //  static
-std::unique_ptr<TracingSession> Tracing::NewTrace(BackendType backend) {
+std::unique_ptr<TracingSession> Tracing::NewTraceInternal(
+    BackendType backend,
+    TracingConsumerBackend* (*system_backend_factory)()) {
   return static_cast<internal::TracingMuxerImpl*>(internal::TracingMuxer::Get())
-      ->CreateTracingSession(backend);
+      ->CreateTracingSession(backend, system_backend_factory);
 }
 
 //  static
diff --git a/src/tracing/track.cc b/src/tracing/track.cc
index 6c35754..dc3006f 100644
--- a/src/tracing/track.cc
+++ b/src/tracing/track.cc
@@ -161,8 +161,6 @@
 
 // static
 void TrackRegistry::InitializeInstance() {
-  // TODO(eseckler): Chrome may call this more than once. Once Chrome doesn't
-  // call this directly anymore, bring back DCHECK(!instance_) instead.
   if (instance_)
     return;
   instance_ = new TrackRegistry();
diff --git a/src/tracing/virtual_destructors.cc b/src/tracing/virtual_destructors.cc
index 9a555fe..19620dd 100644
--- a/src/tracing/virtual_destructors.cc
+++ b/src/tracing/virtual_destructors.cc
@@ -45,6 +45,8 @@
 
 }  // namespace internal
 
+TracingProducerBackend::~TracingProducerBackend() = default;
+TracingConsumerBackend::~TracingConsumerBackend() = default;
 TracingBackend::~TracingBackend() = default;
 
 }  // namespace perfetto
diff --git a/test/cts/Android.bp b/test/cts/Android.bp
index 3eef756..1400a6e 100644
--- a/test/cts/Android.bp
+++ b/test/cts/Android.bp
@@ -17,9 +17,11 @@
     "reporter_test_cts.cc",
     "traced_perf_test_cts.cc",
     ":perfetto_protos_perfetto_config_cpp_gen",
+    ":perfetto_protos_perfetto_common_cpp_gen"
   ],
   generated_headers: [
     "perfetto_protos_perfetto_config_cpp_gen_headers",
+    "perfetto_protos_perfetto_common_cpp_gen_headers",
   ],
   static_libs: [
     "libgmock",
diff --git a/test/cts/BUILD.gn b/test/cts/BUILD.gn
index e821de8..9294cdb 100644
--- a/test/cts/BUILD.gn
+++ b/test/cts/BUILD.gn
@@ -26,6 +26,7 @@
     "../../gn:gtest_and_gmock",
     "../../include/perfetto/ext/tracing/core",
     "../../protos/perfetto/config:cpp",
+    "../../protos/perfetto/config/process_stats:cpp",
     "../../protos/perfetto/config/profiling:cpp",
     "../../protos/perfetto/trace:cpp",
     "../../protos/perfetto/trace/profiling:cpp",
diff --git a/test/cts/heapprofd_java_test_cts.cc b/test/cts/heapprofd_java_test_cts.cc
index c2f3f77..ca60161 100644
--- a/test/cts/heapprofd_java_test_cts.cc
+++ b/test/cts/heapprofd_java_test_cts.cc
@@ -235,16 +235,27 @@
 }
 
 TEST(HeapprofdJavaCtsTest, DebuggableAppOom) {
-  if (IsUserBuild()) return;
-
   std::string app_name = "android.perfetto.cts.app.debuggable";
   const auto& packets = TriggerOomHeapDump(app_name, "*");
   AssertGraphPresent(packets);
 }
 
-TEST(HeapprofdJavaCtsTest, DebuggableAppOomNotSelected) {
-  if (IsUserBuild()) return;
+TEST(HeapprofdJavaCtsTest, ProfileableAppOom) {
+  std::string app_name = "android.perfetto.cts.app.profileable";
+  const auto& packets = TriggerOomHeapDump(app_name, "*");
+  AssertGraphPresent(packets);
+}
 
+TEST(HeapprofdJavaCtsTest, ReleaseAppOom) {
+  std::string app_name = "android.perfetto.cts.app.release";
+  const auto& packets = TriggerOomHeapDump(app_name, "*");
+  if (!IsUserBuild())
+    AssertGraphPresent(packets);
+  else
+    AssertNoProfileContents(packets);
+}
+
+TEST(HeapprofdJavaCtsTest, DebuggableAppOomNotSelected) {
   std::string app_name = "android.perfetto.cts.app.debuggable";
   const auto& packets = TriggerOomHeapDump(app_name, "not.this.app");
   AssertNoProfileContents(packets);
diff --git a/test/cts/heapprofd_test_cts.cc b/test/cts/heapprofd_test_cts.cc
index 510bc7b..fb5e78f 100644
--- a/test/cts/heapprofd_test_cts.cc
+++ b/test/cts/heapprofd_test_cts.cc
@@ -28,6 +28,7 @@
 #include "test/gtest_and_gmock.h"
 #include "test/test_helper.h"
 
+#include "protos/perfetto/config/process_stats/process_stats_config.gen.h"
 #include "protos/perfetto/config/profiling/heapprofd_config.gen.h"
 #include "protos/perfetto/trace/profiling/profile_common.gen.h"
 #include "protos/perfetto/trace/profiling/profile_packet.gen.h"
@@ -210,9 +211,7 @@
   ASSERT_TRUE(found_alloc);
 }
 
-// Check that `packets` contain some allocations performed by
-// kJavaAllocActivity.
-void AssertExpectedArtAllocsPresent(
+void AssertHasSampledAllocs(
     const std::vector<protos::gen::TracePacket>& packets) {
   ASSERT_GT(packets.size(), 0u);
 
@@ -322,7 +321,7 @@
   std::string app_name = "android.perfetto.cts.app.debuggable";
   const auto& packets = ProfileRuntime(app_name, kJavaAllocActivity,
                                        /*heap_names=*/{"com.android.art"});
-  AssertExpectedArtAllocsPresent(packets);
+  AssertHasSampledAllocs(packets);
   StopApp(app_name);
 }
 
@@ -330,9 +329,60 @@
   std::string app_name = "android.perfetto.cts.app.debuggable";
   const auto& packets = ProfileStartup(app_name, kJavaAllocActivity,
                                        /*heap_names=*/{"com.android.art"});
-  AssertExpectedArtAllocsPresent(packets);
+  AssertHasSampledAllocs(packets);
   StopApp(app_name);
 }
 
+TEST(HeapprofdCtsTest, ProfilePlatformProcess) {
+  int target_pid = PidForProcessName("/system/bin/traced_probes");
+  ASSERT_GT(target_pid, 0) << "failed to find pid for target process";
+
+  // Construct config.
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(20 * 1024);
+  trace_config.set_duration_ms(3000);
+  trace_config.set_data_source_stop_timeout_ms(8000);
+  trace_config.set_unique_session_name(RandomSessionName().c_str());
+
+  // process.stats to cause work in traced_probes
+  protos::gen::ProcessStatsConfig ps_config;
+  ps_config.set_proc_stats_poll_ms(100);
+  ps_config.set_record_thread_names(true);
+
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("linux.process_stats");
+  ds_config->set_process_stats_config_raw(ps_config.SerializeAsString());
+
+  // profile native heap of traced_probes
+  protos::gen::HeapprofdConfig heapprofd_config;
+  heapprofd_config.set_sampling_interval_bytes(kTestSamplingInterval);
+  heapprofd_config.add_pid(static_cast<uint64_t>(target_pid));
+  heapprofd_config.set_block_client(true);
+
+  ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("android.heapprofd");
+  ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+
+  // Collect trace.
+  base::TestTaskRunner task_runner;
+  TestHelper helper(&task_runner);
+  helper.ConnectConsumer();
+  helper.WaitForConsumerConnect();
+
+  helper.StartTracing(trace_config);
+  helper.WaitForTracingDisabled(15000 /*ms*/);
+  helper.ReadData();
+  helper.WaitForReadData();
+  auto packets = helper.trace();
+
+  int target_pid_after = PidForProcessName("/system/bin/traced_probes");
+  ASSERT_EQ(target_pid, target_pid_after) << "traced_probes died during test";
+
+  if (IsUserBuild())
+    AssertNoProfileContents(packets);
+  else
+    AssertHasSampledAllocs(packets);
+}
+
 }  // namespace
 }  // namespace perfetto
diff --git a/test/cts/producer/src/android/perfetto/producer/ProducerActivity.java b/test/cts/producer/src/android/perfetto/producer/ProducerActivity.java
index 5a8e0c9..0572fa6 100644
--- a/test/cts/producer/src/android/perfetto/producer/ProducerActivity.java
+++ b/test/cts/producer/src/android/perfetto/producer/ProducerActivity.java
@@ -47,7 +47,8 @@
         manager.createNotificationChannel(isolatedChannel);
 
         startForegroundService(new Intent(ProducerActivity.this, ProducerService.class));
-        startForegroundService(new Intent(ProducerActivity.this, ProducerIsolatedService.class));
+        // isolated services cannot be foreground
+        startService(new Intent(ProducerActivity.this, ProducerIsolatedService.class));
 
         System.loadLibrary("perfettocts_jni");
 
diff --git a/test/cts/producer/src/android/perfetto/producer/ProducerIsolatedService.java b/test/cts/producer/src/android/perfetto/producer/ProducerIsolatedService.java
index 943cab9..27bbea4 100644
--- a/test/cts/producer/src/android/perfetto/producer/ProducerIsolatedService.java
+++ b/test/cts/producer/src/android/perfetto/producer/ProducerIsolatedService.java
@@ -22,17 +22,9 @@
 import android.content.Intent;
 
 public class ProducerIsolatedService extends Service {
-    private static final int NOTIFICATION_ID = 123;
 
     @Override
     public void onCreate() {
-        Notification.Builder builder = new Notification.Builder(this, "isolated_service");
-        builder.setContentTitle("Perfetto isolated service")
-                .setContentText("Perfetto isolated service")
-                .setSmallIcon(R.mipmap.ic_launcher);
-
-        startForeground(NOTIFICATION_ID, builder.build());
-
         System.loadLibrary("perfettocts_jni");
         new Thread(new Runnable() {
             @Override
diff --git a/test/cts/test_apps/AndroidManifest_profileable.xml b/test/cts/test_apps/AndroidManifest_profileable.xml
index bac8a66..077fd95 100755
--- a/test/cts/test_apps/AndroidManifest_profileable.xml
+++ b/test/cts/test_apps/AndroidManifest_profileable.xml
@@ -59,6 +59,19 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
+        <activity
+          android:name="android.perfetto.cts.app.JavaOomActivity"
+          android:exported="true">
+        </activity>
+        <activity-alias
+          android:name="android.perfetto.cts.app.profileable.JavaOomActivity"
+          android:targetActivity="android.perfetto.cts.app.JavaOomActivity"
+          android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
     </application>
 </manifest>
 
diff --git a/test/cts/test_apps/AndroidManifest_release.xml b/test/cts/test_apps/AndroidManifest_release.xml
index 5bc0f5b..417a539 100755
--- a/test/cts/test_apps/AndroidManifest_release.xml
+++ b/test/cts/test_apps/AndroidManifest_release.xml
@@ -58,5 +58,18 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
+        <activity
+          android:name="android.perfetto.cts.app.JavaOomActivity"
+          android:exported="true">
+        </activity>
+        <activity-alias
+          android:name="android.perfetto.cts.app.release.JavaOomActivity"
+          android:targetActivity="android.perfetto.cts.app.JavaOomActivity"
+          android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
     </application>
 </manifest>
diff --git a/test/cts/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java b/test/cts/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java
index 4165a92..f73c135 100644
--- a/test/cts/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java
+++ b/test/cts/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java
@@ -19,36 +19,15 @@
 import android.app.Activity;
 import android.os.Bundle;
 
-import java.util.ArrayList;
-
 public class JavaOomActivity extends Activity {
     @Override
     public void onCreate(Bundle state) {
         super.onCreate(state);
-
-        new Thread(new Runnable() {
-            public void run() {
-                try {
-                    runAllocationLoop();
-                } catch (Exception ex) {
-                    ex.printStackTrace();
-                }
+        new Thread(() -> {
+            try {
+                byte[] alloc = new byte[Integer.MAX_VALUE];
+            } catch (OutOfMemoryError e) {
             }
         }).start();
     }
-
-    private static void runAllocationLoop() {
-        ArrayList<byte[]> leaky = new ArrayList<>();
-        try {
-            for (;;) {
-                leaky.add(new byte[1024 * 1024]);
-                try {
-                    Thread.sleep(10);
-                } catch (InterruptedException ignored) {
-                }
-            }
-        } catch (OutOfMemoryError e) {
-          leaky.clear();
-        }
-    }
 }
diff --git a/test/cts/traced_perf_test_cts.cc b/test/cts/traced_perf_test_cts.cc
index d7bd60e..807c8e0 100644
--- a/test/cts/traced_perf_test_cts.cc
+++ b/test/cts/traced_perf_test_cts.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
 
@@ -21,12 +22,15 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/android_utils.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/tracing/core/data_source_config.h"
 #include "src/base/test/test_task_runner.h"
 #include "test/android_test_utils.h"
 #include "test/gtest_and_gmock.h"
 #include "test/test_helper.h"
 
+#include "protos/perfetto/common/perf_events.gen.h"
+#include "protos/perfetto/config/process_stats/process_stats_config.gen.h"
 #include "protos/perfetto/config/profiling/perf_event_config.gen.h"
 #include "protos/perfetto/trace/profiling/profile_common.gen.h"
 #include "protos/perfetto/trace/profiling/profile_packet.gen.h"
@@ -54,6 +58,20 @@
   return result;
 }
 
+std::vector<protos::gen::TracePacket> CollectTrace(
+    base::TestTaskRunner* task_runner,
+    const TraceConfig& trace_config) {
+  TestHelper helper(task_runner);
+  helper.ConnectConsumer();
+  helper.WaitForConsumerConnect();
+
+  helper.StartTracing(trace_config);
+  helper.WaitForTracingDisabled(15000 /*ms*/);
+  helper.ReadData();
+  helper.WaitForReadData();
+  return helper.trace();
+}
+
 std::vector<protos::gen::TracePacket> ProfileSystemWide(std::string app_name) {
   base::TestTaskRunner task_runner;
 
@@ -67,11 +85,7 @@
                    /*delay_ms=*/100);
   task_runner.RunUntilCheckpoint("target.app.running", 10000 /*ms*/);
 
-  // set up tracing
-  TestHelper helper(&task_runner);
-  helper.ConnectConsumer();
-  helper.WaitForConsumerConnect();
-
+  // build config
   TraceConfig trace_config;
   trace_config.add_buffers()->set_size_kb(20 * 1024);
   trace_config.set_duration_ms(3000);
@@ -83,18 +97,11 @@
   ds_config->set_target_buffer(0);
 
   protos::gen::PerfEventConfig perf_config;
-
   perf_config.set_all_cpus(true);
   perf_config.set_sampling_frequency(10);  // Hz
   ds_config->set_perf_event_config_raw(perf_config.SerializeAsString());
 
-  // start tracing
-  helper.StartTracing(trace_config);
-  helper.WaitForTracingDisabled(15000 /*ms*/);
-  helper.ReadData();
-  helper.WaitForReadData();
-
-  return helper.trace();
+  return CollectTrace(&task_runner, trace_config);
 }
 
 void AssertHasSampledStacksForPid(std::vector<protos::gen::TracePacket> packets,
@@ -132,12 +139,15 @@
       target_samples++;
   }
 
-  EXPECT_GT(target_samples, 0)
-      << "target_pid: " << target_pid << ", packets.size(): " << packets.size()
-      << ", total_perf_packets: " << total_perf_packets
-      << ", full_samples: " << full_samples
-      << ", lost_records_packets: " << lost_records_packets
-      << ", target_skipped_samples: " << target_skipped_samples << "\n";
+  // log summary even if successful
+  base::StackString<512> log(
+      "target_pid: %d, packets.size(): %zu, total_perf_packets: %d, "
+      "full_samples: %d, lost_records_packets: %d, target_skipped_samples: %d",
+      target_pid, packets.size(), total_perf_packets, full_samples,
+      lost_records_packets, target_skipped_samples);
+  PERFETTO_LOG("%s", log.c_str());
+
+  EXPECT_GT(target_samples, 0) << log.c_str() << "\n";
 }
 
 void AssertNoStacksForPid(std::vector<protos::gen::TracePacket> packets,
@@ -216,5 +226,58 @@
   StopApp(app_name);
 }
 
+// Loads a platform process with work (we use traced_probes which runs as
+// AID_NOBODY), and profiles it.
+TEST(TracedPerfCtsTest, ProfilePlatformProcess) {
+  if (!HasPerfLsmHooks())
+    GTEST_SKIP() << "skipped due to lack of perf_event_open LSM hooks";
+
+  int target_pid = PidForProcessName("/system/bin/traced_probes");
+  ASSERT_GT(target_pid, 0) << "failed to find pid for target process";
+
+  // Construct config.
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(64);
+  trace_config.add_buffers()->set_size_kb(1024);
+  trace_config.set_duration_ms(3000);
+  trace_config.set_data_source_stop_timeout_ms(8000);
+  trace_config.set_unique_session_name(RandomSessionName().c_str());
+
+  // process.stats to cause work in traced_probes
+  protos::gen::ProcessStatsConfig ps_config;
+  ps_config.set_proc_stats_poll_ms(100);
+  ps_config.set_record_thread_names(true);
+
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("linux.process_stats");
+  ds_config->set_process_stats_config_raw(ps_config.SerializeAsString());
+
+  // capture callstacks of traced_probes descheduling
+  protos::gen::PerfEventConfig perf_config;
+  auto* timebase = perf_config.mutable_timebase();
+  timebase->set_counter(protos::gen::PerfEvents::SW_CONTEXT_SWITCHES);
+  timebase->set_period(1);
+  auto* callstacks = perf_config.mutable_callstack_sampling();
+  auto* scope = callstacks->mutable_scope();
+  scope->add_target_pid(target_pid);
+
+  ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("linux.perf");
+  ds_config->set_target_buffer(1);
+  ds_config->set_perf_event_config_raw(perf_config.SerializeAsString());
+
+  // Collect trace.
+  base::TestTaskRunner task_runner;
+  auto packets = CollectTrace(&task_runner, trace_config);
+
+  int target_pid_after = PidForProcessName("/system/bin/traced_probes");
+  ASSERT_EQ(target_pid, target_pid_after) << "traced_probes died during test";
+
+  if (!IsUserBuild())
+    AssertHasSampledStacksForPid(packets, target_pid);
+  else
+    AssertNoStacksForPid(packets, target_pid);
+}
+
 }  // namespace
 }  // namespace perfetto
diff --git a/test/ftrace_integrationtest.cc b/test/ftrace_integrationtest.cc
index 068ebf7..ab86dd5 100644
--- a/test/ftrace_integrationtest.cc
+++ b/test/ftrace_integrationtest.cc
@@ -110,7 +110,7 @@
   helper.WaitForConsumerConnect();
 
   TraceConfig trace_config;
-  trace_config.add_buffers()->set_size_kb(1024);
+  trace_config.add_buffers()->set_size_kb(64);
   trace_config.set_duration_ms(3000);
 
   auto* ds_config = trace_config.add_data_sources()->mutable_config();
diff --git a/test/trace_processor/diff_tests/fuchsia/tests.py b/test/trace_processor/diff_tests/fuchsia/tests.py
index caa44f7..86d08a7 100644
--- a/test/trace_processor/diff_tests/fuchsia/tests.py
+++ b/test/trace_processor/diff_tests/fuchsia/tests.py
@@ -107,11 +107,11 @@
         """,
         out=Csv("""
         "ts","value","name"
-        20329439768,30.331177,"cpu_usage:average_cpu_percentage"
-        21331281870,7.829745,"cpu_usage:average_cpu_percentage"
-        22332302017,9.669818,"cpu_usage:average_cpu_percentage"
-        23332974162,6.421237,"cpu_usage:average_cpu_percentage"
-        24333405767,12.079849,"cpu_usage:average_cpu_percentage"
+        20329439768,30.331177,"cpu_usage:average_cpu_percentage:0"
+        21331281870,7.829745,"cpu_usage:average_cpu_percentage:0"
+        22332302017,9.669818,"cpu_usage:average_cpu_percentage:0"
+        23332974162,6.421237,"cpu_usage:average_cpu_percentage:0"
+        24333405767,12.079849,"cpu_usage:average_cpu_percentage:0"
         """))
 
   def test_fuchsia_smoke_flow(self):
@@ -157,7 +157,7 @@
         2,"[NULL]","thread_track"
         3,"[NULL]","thread_track"
         4,"[NULL]","thread_track"
-        5,"cpu_usage:average_cpu_percentage","process_counter_track"
+        5,"cpu_usage:average_cpu_percentage:0","process_counter_track"
         6,"[NULL]","thread_track"
         7,"[NULL]","thread_track"
         8,"[NULL]","thread_track"
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index 09e084e..6f9e525 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -51,11 +51,14 @@
 from diff_tests.memory.tests_metrics import MemoryMetrics
 from diff_tests.network.tests import Network
 from diff_tests.parsing.tests import Parsing
+from diff_tests.parsing.tests_debug_annotation import ParsingDebugAnnotation
 from diff_tests.parsing.tests_memory_counters import ParsingMemoryCounters
 from diff_tests.parsing.tests_rss_stats import ParsingRssStats
 from diff_tests.performance.tests import Performance
 from diff_tests.power.tests import Power
 from diff_tests.power.tests_energy_breakdown import PowerEnergyBreakdown
+from diff_tests.power.tests_entity_state_residency import EntityStateResidency
+from diff_tests.power.tests_linux_sysfs_power import LinuxSysfsPower
 from diff_tests.power.tests_power_rails import PowerPowerRails
 from diff_tests.power.tests_voltage_and_scaling import PowerVoltageAndScaling
 from diff_tests.process_tracking.tests import ProcessTracking
@@ -105,6 +108,8 @@
       *Chrome(index_path, 'chrome', 'Chrome').fetch(),
       *Cros(index_path, 'cros', 'Cros').fetch(),
       *Dynamic(index_path, 'dynamic', 'Dynamic').fetch(),
+      *EntityStateResidency(index_path, 'power',
+                            'EntityStateResidency').fetch(),
       *Fs(index_path, 'fs', 'Fs').fetch(),
       *Fuchsia(index_path, 'fuchsia', 'Fuchsia').fetch(),
       *Functions(index_path, 'functions', 'Functions').fetch(),
@@ -113,10 +118,13 @@
       *GraphicsDrmRelatedFtraceEvents(index_path, 'graphics',
                                       'GraphicsDrmRelatedFtraceEvents').fetch(),
       *Ufs(index_path, 'ufs', 'Ufs').fetch(),
+      *LinuxSysfsPower(index_path, 'power', 'LinuxSysfsPower').fetch(),
       *Memory(index_path, 'memory', 'Memory').fetch(),
       *MemoryMetrics(index_path, 'memory', 'MemoryMetrics').fetch(),
       *Network(index_path, 'network', 'Network').fetch(),
       *Parsing(index_path, 'parsing', 'Parsing').fetch(),
+      *ParsingDebugAnnotation(index_path, 'parsing',
+                              'ParsingDebugAnnotation').fetch(),
       *ParsingRssStats(index_path, 'parsing', 'ParsingRssStats').fetch(),
       *ParsingMemoryCounters(index_path, 'parsing',
                              'ParsingMemoryCounters').fetch(),
diff --git a/test/trace_processor/diff_tests/parsing/tests_debug_annotation.py b/test/trace_processor/diff_tests/parsing/tests_debug_annotation.py
new file mode 100644
index 0000000..fab40cb
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/tests_debug_annotation.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ParsingDebugAnnotation(TestSuite):
+  # Verify parsing of interned_string_value in DebugAnnotation proto.
+  def test_interned_string_value(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          trusted_packet_sequence_id: 1
+          incremental_state_cleared: true
+          track_descriptor {
+            uuid: 1
+            thread {
+              pid: 5
+              tid: 1
+            }
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 2000
+          interned_data {
+            debug_annotation_names {
+                iid: 1
+                name: "key"
+            }
+            debug_annotation_string_values {
+                iid: 1
+                str: "value"
+            }
+          }
+          track_event {
+            track_uuid: 1
+            type: TYPE_INSTANT
+            name: "slice1"
+            debug_annotations {
+              name_iid: 1
+              string_value_iid: 1
+            }
+          }
+        }
+        """),
+        query="""
+          SELECT EXTRACT_ARG(s.arg_set_id, 'debug.key') AS value
+          FROM slice s;
+        """,
+        out=Csv("""
+        "value"
+        "value"
+        """))
diff --git a/test/trace_processor/diff_tests/power/entity_state_residency.textproto b/test/trace_processor/diff_tests/power/entity_state_residency.textproto
new file mode 100644
index 0000000..08ace7f
--- /dev/null
+++ b/test/trace_processor/diff_tests/power/entity_state_residency.textproto
@@ -0,0 +1,76 @@
+packet {
+  timestamp: 1
+
+  entity_state_residency {
+    power_entity_state {
+      entity_index: 0
+      state_index: 0
+      entity_name: "Bluetooth"
+      state_name: "Idle"
+    }
+    power_entity_state {
+      entity_index: 0
+      state_index: 1
+      entity_name: "Bluetooth"
+      state_name: "Active"
+    }
+    power_entity_state {
+      entity_index: 1
+      state_index: 0
+      entity_name: "PCIe-Modem"
+      state_name: "UP"
+    }
+    power_entity_state {
+      entity_index: 1
+      state_index: 1
+      entity_name: "PCIe-Modem"
+      state_name: "DOWN"
+    }
+    residency {
+      entity_index: 0
+      state_index: 0
+      total_time_in_state_ms: 1000
+    }
+    residency {
+      entity_index: 0
+      state_index: 1
+      total_time_in_state_ms: 2000
+    }
+    residency {
+      entity_index: 1
+      state_index: 0
+      total_time_in_state_ms: 10000
+    }
+    residency {
+      entity_index: 1
+      state_index: 1
+      total_time_in_state_ms: 20000
+    }
+  }
+}
+packet {
+  timestamp: 2
+
+  entity_state_residency {
+    residency {
+      entity_index: 0
+      state_index: 0
+      total_time_in_state_ms: 3000
+    }
+    residency {
+      entity_index: 0
+      state_index: 1
+      total_time_in_state_ms: 4000
+    }
+    residency {
+      entity_index: 1
+      state_index: 0
+      total_time_in_state_ms: 30000
+    }
+    residency {
+      entity_index: 1
+      state_index: 1
+      total_time_in_state_ms: 40000
+    }
+  }
+}
diff --git a/test/trace_processor/diff_tests/power/tests_entity_state_residency.py b/test/trace_processor/diff_tests/power/tests_entity_state_residency.py
new file mode 100644
index 0000000..be41142
--- /dev/null
+++ b/test/trace_processor/diff_tests/power/tests_entity_state_residency.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class EntityStateResidency(TestSuite):
+
+  def test_track(self):
+    return DiffTestBlueprint(
+        trace=Path('entity_state_residency.textproto'),
+        query="""
+        select ts, ct.name, cast(c.value as int) value 
+        from counter_track ct join counter c on ct.id = c.track_id 
+        """,
+        out=Csv("""
+        "ts","name","value"
+        1,"Entity residency: Bluetooth is Idle",1000
+        2,"Entity residency: Bluetooth is Idle",3000
+        1,"Entity residency: Bluetooth is Active",2000
+        2,"Entity residency: Bluetooth is Active",4000
+        1,"Entity residency: PCIe-Modem is UP",10000
+        2,"Entity residency: PCIe-Modem is UP",30000
+        1,"Entity residency: PCIe-Modem is DOWN",20000
+        2,"Entity residency: PCIe-Modem is DOWN",40000
+        """))
diff --git a/test/trace_processor/diff_tests/power/tests_linux_sysfs_power.py b/test/trace_processor/diff_tests/power/tests_linux_sysfs_power.py
new file mode 100644
index 0000000..fce058e
--- /dev/null
+++ b/test/trace_processor/diff_tests/power/tests_linux_sysfs_power.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class LinuxSysfsPower(TestSuite):
+
+  # Test basic battery counters.
+  def test_counters(self):
+    return DiffTestBlueprint(
+        trace=TextProto("""
+        packet {
+          timestamp: 3000000
+          battery {
+            charge_counter_uah: 3005000
+            capacity_percent: 100.000000
+            current_ua: 0
+          }
+        }
+        """),
+        query="""
+        SELECT * FROM (
+          (SELECT AVG(value) AS capacity_percent FROM counters
+           WHERE name='batt.capacity_pct'),
+          (SELECT AVG(value) AS charge_uah FROM counters
+           WHERE name='batt.charge_uah'),
+          (SELECT AVG(value) AS current_ua FROM counters
+           WHERE name='batt.current_ua')
+        );
+        """,
+        out=Csv("""
+        "capacity_percent","charge_uah","current_ua"
+        100.000000,3005000.000000,0.000000
+        """))
+
+  # Test multiple batteries.
+  def test_multiple_batteries(self):
+    return DiffTestBlueprint(
+        trace=TextProto("""
+        packet {
+          timestamp: 3000000
+          battery {
+            charge_counter_uah: 3005000
+            capacity_percent: 100.000000
+            current_ua: 0
+            name: "BAT0"
+          }
+        }
+        packet {
+          timestamp: 3000000
+          battery {
+            capacity_percent: 90.000000
+            name: "BAT1"
+          }
+        }
+        """),
+        query="""
+        SELECT name, value FROM counters WHERE name like "batt.%" ORDER BY name
+        """,
+        out=Csv("""
+        "name","value"
+        "batt.BAT0.capacity_pct",100.000000
+        "batt.BAT0.charge_uah",3005000.000000
+        "batt.BAT0.current_ua",0.000000
+        "batt.BAT1.capacity_pct",90.000000
+        """))
+
+  # Test convertion to charge counter from energy and voltage.
+  def test_charge_from_energy_and_voltage(self):
+    return DiffTestBlueprint(
+        trace=TextProto("""
+        packet {
+          timestamp: 3000000
+          battery {
+            energy_counter_uwh: 56680000
+            voltage_uv: 17356000
+          }
+        }
+        packet {
+          timestamp: 4000000
+          battery {
+            energy_counter_uwh: 56600000
+            voltage_uv: 17356000
+          }
+        }
+        """),
+        query="""
+        SELECT value
+        FROM counters
+        WHERE name = "batt.charge_uah"
+        """,
+        out=Csv("""
+        "value"
+        3265729.000000
+        3261120.000000
+        """))
+
+  # Test convertion to charge counter from energy and voltage: bad voltage
+  # value.
+  def test_charge_from_energy_and_bad_voltage(self):
+    return DiffTestBlueprint(
+        trace=TextProto("""
+        packet {
+          timestamp: 3000000
+          battery {
+            energy_counter_uwh: 56680000
+            voltage_uv: 0
+          }
+        }
+        """),
+        query="""
+        SELECT value
+        FROM counters
+        WHERE name = "batt.charge_uah"
+        """,
+        out=Csv("""
+        "value"
+        """))
diff --git a/test/trace_processor/diff_tests/startup/android_startup.out b/test/trace_processor/diff_tests/startup/android_startup.out
index 752db7b..f804a34 100644
--- a/test/trace_processor/diff_tests/startup/android_startup.out
+++ b/test/trace_processor/diff_tests/startup/android_startup.out
@@ -8,7 +8,7 @@
       dur_ns: 108
       main_thread_by_task_state {
         running_dur_ns: 10
-        runnable_dur_ns: 90
+        runnable_dur_ns: 80
         uninterruptible_sleep_dur_ns: 0
         interruptible_sleep_dur_ns: 10
         uninterruptible_io_sleep_dur_ns: 0
diff --git a/test/trace_processor/diff_tests/startup/android_startup_breakdown.out b/test/trace_processor/diff_tests/startup/android_startup_breakdown.out
index 8b35bb5..132aad8 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_breakdown.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_breakdown.out
@@ -8,7 +8,7 @@
       dur_ns: 108000000000
       main_thread_by_task_state {
         running_dur_ns: 25000000000
-        runnable_dur_ns: 30000000000
+        runnable_dur_ns: 5000000000
         uninterruptible_sleep_dur_ns: 0
         interruptible_sleep_dur_ns: 0
         uninterruptible_io_sleep_dur_ns: 0
diff --git a/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out b/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out
index 3ed8ce3..2974f88 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out
@@ -8,7 +8,7 @@
       dur_ns: 108000000000
       main_thread_by_task_state {
         running_dur_ns: 25000000000
-        runnable_dur_ns: 30000000000
+        runnable_dur_ns: 5000000000
         uninterruptible_sleep_dur_ns: 0
         interruptible_sleep_dur_ns: 0
         uninterruptible_io_sleep_dur_ns: 0
diff --git a/test/trace_processor/diff_tests/startup/android_startup_slow.out b/test/trace_processor/diff_tests/startup/android_startup_slow.out
index 333883a..22f2d90 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_slow.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_slow.out
@@ -8,7 +8,7 @@
       dur_ns: 108000000000
       main_thread_by_task_state {
         running_dur_ns: 10000000000
-        runnable_dur_ns: 90000000000
+        runnable_dur_ns: 80000000000
         uninterruptible_sleep_dur_ns: 5000000000
         interruptible_sleep_dur_ns: 5000000000
         uninterruptible_io_sleep_dur_ns: 5000000000
diff --git a/tools/cpu_profile b/tools/cpu_profile
index 943d6fb..ccce9d2 100755
--- a/tools/cpu_profile
+++ b/tools/cpu_profile
@@ -361,6 +361,25 @@
       metavar="DURATION",
       type=int,
       default=0)
+  # Profiling using hardware counters.
+  parser.add_argument(
+      "-e",
+      "--event",
+      help="Use the specified hardware counter event for sampling.",
+      metavar="EVENT",
+      action="append",
+      # See: '//perfetto/protos/perfetto/trace/perfetto_trace.proto'.
+      choices=['HW_CPU_CYCLES', 'HW_INSTRUCTIONS', 'HW_CACHE_REFERENCES',
+               'HW_CACHE_MISSES', 'HW_BRANCH_INSTRUCTIONS', 'HW_BRANCH_MISSES',
+               'HW_BUS_CYCLES', 'HW_STALLED_CYCLES_FRONTEND',
+               'HW_STALLED_CYCLES_BACKEND'],
+      default=[])
+  parser.add_argument(
+      "-k",
+      "--kernel-frames",
+      help="Collect kernel frames.  Default: false.",
+      action="store_true",
+      default=False)
   parser.add_argument(
       "-n",
       "--name",
@@ -399,8 +418,11 @@
       default=None)
 
   args = parser.parse_args()
-  if args.config is not None and args.name is not None:
-    sys.exit("--name/-n should not be provided when --config/-c is provided.")
+  if args.config is not None:
+    if args.name is not None:
+      sys.exit("--name/-n should not be specified with --config/-c.")
+    elif args.event is not None:
+      sys.exit("-e/--event should not be specified with --config/-c.")
   elif args.config is None and args.name is None:
     sys.exit("One of --names/-n or --config/-c is required.")
 
@@ -449,7 +471,7 @@
     except IOError as error:
       sys.exit("Unable to read config file: {}".format(error))
 
-  CONFIG_INDENT = '      '
+  CONFIG_INDENT = '          '
   CONFIG = textwrap.dedent('''\
   buffers {{
     size_kb: 2048
@@ -469,18 +491,6 @@
     }}
   }}
 
-  data_sources {{
-    config {{
-      name: "linux.perf"
-      target_buffer: 1
-      perf_event_config {{
-        all_cpus: true
-        sampling_frequency: {frequency}
-  {target_config}
-      }}
-    }}
-  }}
-
   duration_ms: {duration}
   write_into_file: true
   flush_timeout_ms: 30000
@@ -498,6 +508,35 @@
   target_config = "\n".join(
       [f'{CONFIG_INDENT}target_cmdline: "{p}"' for p in matching_processes])
 
+  events = args.event or ['SW_CPU_CLOCK']
+  for event in events:
+    CONFIG += (textwrap.dedent('''
+    data_sources {{
+      config {{
+        name: "linux.perf"
+        target_buffer: 1
+        perf_event_config {{
+          timebase {{
+            counter: %s
+            frequency: {frequency}
+            timestamp_clock: PERF_CLOCK_MONOTONIC
+          }}
+          callstack_sampling {{
+            scope {{
+    {target_config}
+            }}
+            kernel_frames: {kernel_config}
+          }}
+        }}
+      }}
+    }}
+    ''') % (event))
+
+  if args.kernel_frames:
+    kernel_config = "true"
+  else:
+    kernel_config = "false"
+
   if not args.print_config:
     print("Configured profiling for these processes:\n")
     for matching_process in matching_processes:
@@ -507,7 +546,8 @@
   config = CONFIG.format(
       frequency=args.frequency,
       duration=args.duration,
-      target_config=target_config)
+      target_config=target_config,
+      kernel_config=kernel_config)
 
   return config
 
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index e8a5677..b7da65d 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -117,6 +117,12 @@
     ],
 }
 
+needs_libfts = [
+    '//:perfetto_unittests',
+    '//src/trace_processor:trace_processor_shell',
+    '//src/traceconv:traceconv',
+]
+
 # All module names are prefixed with this string to avoid collisions.
 module_prefix = 'perfetto_'
 
@@ -507,6 +513,7 @@
     self.tool_files: Optional[List[str]] = None
     self.android = Target('android')
     self.host = Target('host')
+    self.musl = Target('musl')
     self.lto: Optional[bool] = None
     self.stl = None
     self.dist = dict()
@@ -568,6 +575,7 @@
     target_out = []
     self._output_field(target_out, 'android')
     self._output_field(target_out, 'host')
+    self._output_field(target_out, 'musl')
     if target_out:
       output.append('    target: {')
       for line in target_out:
@@ -985,6 +993,9 @@
       for src in target.sources
       if is_supported_source_file(src))
 
+  if name_without_toolchain in needs_libfts:
+    module.musl.static_libs.add('libfts')
+
   if target.type in gn_utils.LINKER_UNIT_TYPES:
     module.cflags.update(_get_cflags(target))
 
diff --git a/tools/gen_grpc_build_gn.py b/tools/gen_grpc_build_gn.py
new file mode 100755
index 0000000..98e9083
--- /dev/null
+++ b/tools/gen_grpc_build_gn.py
@@ -0,0 +1,182 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import os
+import subprocess
+import sys
+from typing import Any, Dict, List
+import yaml
+
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+GRPC_GN_HEADER = '''
+#
+# DO NOT EDIT. AUTOGENERATED file
+#
+# This file is generated with the command:
+# tools/gen_grpc_build_gn.py > buildtools/grpc/BUILD.gn
+#
+
+import("../../gn/perfetto.gni")
+
+# Prevent the gRPC from being depended upon without explicitly being opted in.
+assert(enable_perfetto_grpc)
+
+# BoringSSL has assembly code which is tied to platform-specific. For now, we
+# only care about Linux x64 so assert this as the case.
+assert(is_linux && current_cpu == "x64")
+'''
+
+TARGET_TEMPLATE = """
+{target_type}("{name}") {{
+  sources = {srcs}
+  public_deps = {deps}
+  public_configs = ["..:{config_name}"]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+}}"""
+
+LIBRARY_IGNORE_LIST = set([
+    'grpcpp_channelz',
+    'grpc++_reflection',
+    'benchmark_helpers',
+    'boringssl_test_util',
+])
+
+TARGET_ALLOW_LIST = set([
+    'grpc_cpp_plugin',
+])
+
+STATIC_LIBRARY_TARGETS = set([
+    'upb',
+    're2',
+    'boringssl',
+    'grpc++',
+])
+
+
+def grpc_relpath(*segments: str) -> str:
+  '''From path segments to GRPC root, returns the absolute path.'''
+  return os.path.join(ROOT_DIR, 'buildtools', 'grpc', 'src', *segments)
+
+
+GRPC_BUILD_YAML = grpc_relpath('build_autogenerated.yaml')
+ABSL_GEN_BUILD_YAML = grpc_relpath('src', 'abseil-cpp', 'gen_build_yaml.py')
+UPB_GEN_BUILD_YAML = grpc_relpath('src', 'upb', 'gen_build_yaml.py')
+RE2_GEN_BUILD_YAML = grpc_relpath('src', 're2', 'gen_build_yaml.py')
+BSSL_GEN_BUILD_YAML = grpc_relpath('src', 'boringssl', 'gen_build_yaml.py')
+
+
+def gen_grpc_dep_yaml(gen_path: str) -> Dict[str, Any]:
+  '''Invokes a gen_build_yaml.py file for creating YAML for gRPC deps.'''
+  return yaml.safe_load(subprocess.check_output(['python3', gen_path]))
+
+
+def bazel_label_to_gn_target(dep: str) -> str:
+  '''Converts a Bazel label name into a gn target name.'''
+  if dep == 'libssl':
+    return 'boringssl'
+  return dep.replace('/', '_').replace(':', '_')
+
+
+def get_deps_for_target(target: str) -> List[str]:
+  if target == 'grpc_plugin_support':
+    return ['..:protoc_lib']
+  if target == 'grpc':
+    return [':re2', '../../gn:zlib']
+  if target == 'grpc_authorization_provider':
+    return [':re2']
+  return []
+
+
+def get_library_target_type(target: str) -> str:
+  if target in STATIC_LIBRARY_TARGETS:
+    return 'static_library'
+  return 'source_set'
+
+
+def yaml_to_gn_targets(desc: Dict[str, Any], build_types: list[str],
+                       config_name: str) -> List[str]:
+  '''Given a gRPC YAML description of the build graph, generates GN targets.'''
+  out = []
+  for lib in desc['libs']:
+    if lib['build'] not in build_types:
+      continue
+    if lib['name'] in LIBRARY_IGNORE_LIST:
+      continue
+    srcs = [f'src/{file}' for file in lib['src'] + lib['headers']]
+    if 'asm_src' in lib:
+      srcs += [f'src/{file}' for file in lib['asm_src']['crypto_linux_x86_64']]
+    deps = [f':{bazel_label_to_gn_target(dep)}' for dep in lib.get('deps', [])
+           ] + get_deps_for_target(lib['name'])
+    library_target = TARGET_TEMPLATE.format(
+        name=bazel_label_to_gn_target(lib['name']),
+        config_name=config_name,
+        srcs=json.dumps(srcs),
+        deps=json.dumps(deps),
+        target_type=get_library_target_type(lib['name']))
+    out.append(library_target)
+
+  for bin in desc.get('targets', []):
+    if bin['build'] not in build_types:
+      continue
+    if bin['name'] not in TARGET_ALLOW_LIST:
+      continue
+    srcs = json.dumps([f'src/{file}' for file in bin['src'] + bin['headers']])
+    deps = [f':{bazel_label_to_gn_target(dep)}' for dep in bin.get('deps', [])
+           ] + get_deps_for_target(bin['name'])
+    binary_target = TARGET_TEMPLATE.format(
+        name=bazel_label_to_gn_target(bin['name']),
+        config_name=config_name,
+        srcs=srcs,
+        deps=json.dumps(deps),
+        target_type='executable')
+    out.append(binary_target)
+  return out
+
+
+def main():
+  out: List[str] = []
+
+  # Generate absl rules
+  absl_yaml = gen_grpc_dep_yaml(ABSL_GEN_BUILD_YAML)
+  out.extend(yaml_to_gn_targets(absl_yaml, ['private'], 'grpc_absl_config'))
+
+  # Generate upb rules
+  upb_yaml = gen_grpc_dep_yaml(UPB_GEN_BUILD_YAML)
+  out.extend(yaml_to_gn_targets(upb_yaml, ['all'], 'grpc_upb_config'))
+
+  # Generate re2 rules
+  re2_yaml = gen_grpc_dep_yaml(RE2_GEN_BUILD_YAML)
+  out.extend(yaml_to_gn_targets(re2_yaml, ['private'], 'grpc_re2_config'))
+
+  # Generate boringssl rules
+  boringssl_yaml = gen_grpc_dep_yaml(BSSL_GEN_BUILD_YAML)
+  out.extend(
+      yaml_to_gn_targets(boringssl_yaml, ['private'], 'grpc_boringssl_config'))
+
+  # Generate grpc rules
+  with open(GRPC_BUILD_YAML, 'r', encoding='utf-8') as f:
+    grpc_yaml = yaml.safe_load(f.read())
+  out.extend(
+      yaml_to_gn_targets(grpc_yaml, ['all', 'protoc'], 'grpc_internal_config'))
+
+  print(GRPC_GN_HEADER)
+  print('\n'.join(out))
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 3ad1026..ddafffc 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -14,6 +14,7 @@
 # limitations under the License.
 
 import argparse
+import dataclasses as dc
 import hashlib
 import logging
 import os
@@ -27,6 +28,7 @@
 from collections import namedtuple
 from platform import system, machine
 
+
 # The format for the deps below is the following:
 # (target_folder, source_url, sha1, target_os, target_arch)
 # |source_url| can be either a git repo or a http url.
@@ -41,9 +43,15 @@
 # in both cases the dep only applies on matching platforms
 # |target_arch| can be 'all' when 'target_os' is not 'all' for example in the
 # case of MacOS universal binaries.
-Dependency = namedtuple(
-    'Dependency',
-    ['target_folder', 'source_url', 'checksum', 'target_os', 'target_arch'])
+@dc.dataclass
+class Dependency:
+  target_folder: str
+  source_url: str
+  checksum: str
+  target_os: str
+  target_arch: str
+  submodules: bool = False
+
 
 # This is to remove old directories when build tools get {re,}moved. This is to
 # avoid accidentally referring to stale dir in custom user scripts.
@@ -146,8 +154,8 @@
     # Keep in sync with Chromium's //third_party/protobuf.
     Dependency(
         'buildtools/protobuf',
-        'https://chromium.googlesource.com/external/github.com/google/protobuf.git',
-        '6a59a2ad1f61d9696092f79b6d74368b4d7970a3',  # refs/tags/v3.9.0
+        'https://chromium.googlesource.com/external/github.com/protocolbuffers/protobuf.git',
+        'fe271ab76f2ad2b2b28c10443865d2af21e27e0e',  # refs/tags/v3.20.3
         'all',
         'all'),
 
@@ -357,6 +365,14 @@
         TYPEFACES_SHA256, TYPEFACES_SHA256, 'all', 'all')
 ]
 
+# Dependencies to build gRPC.
+GRPC_DEPS = [
+    Dependency(
+        'buildtools/grpc/src',
+        'https://chromium.googlesource.com/external/github.com/grpc/grpc.git',
+        '6943c1841f57cac4666b165aea4f618fe73b3ff1', 'all', 'all', True),
+]
+
 # Sysroots required to cross-compile Linux targets (linux-arm{,64}).
 # These are taken from Chromium's build/linux/sysroot_scripts/sysroots.json.
 BUILD_DEPS_LINUX_CROSS_SYSROOTS = [
@@ -467,6 +483,9 @@
   subprocess.check_call(
       ['git', 'fetch', '--quiet', '--depth', '1', git_url, revision], cwd=path)
   subprocess.check_call(['git', 'checkout', revision, '--quiet'], cwd=path)
+  subprocess.check_call(
+      ['git', 'submodule', 'update', '--init', '--recursive', '--quiet'],
+      cwd=path)
   assert (IsGitRepoCheckoutOutAtRevision(path, revision))
   return True
 
@@ -533,6 +552,10 @@
       '--ui',
       action='store_true',
       help='Node and NPM packages to Build the Web-based UI via ./ui/build')
+  parser.add_argument(
+      '--grpc',
+      action='store_true',
+      help='Packages to build gRPC')
   parser.add_argument('--check-only')
   parser.add_argument('--filter', action='append')
   parser.add_argument('--verify', help='Check all URLs', action='store_true')
@@ -557,6 +580,8 @@
     deps += BUILD_DEPS_LINUX_CROSS_SYSROOTS
   if args.ui:
     deps += UI_DEPS
+  if args.grpc:
+    deps += GRPC_DEPS
   deps_updated = False
   nodejs_updated = False
 
diff --git a/tools/java_heap_dump b/tools/java_heap_dump
index eebf074..c14dde4 100755
--- a/tools/java_heap_dump
+++ b/tools/java_heap_dump
@@ -35,7 +35,7 @@
 }
 '''
 
-CFG_IDENT = '      '
+CFG_INDENT = '      '
 CFG = '''buffers {{
   size_kb: {size_kb}
   fill_policy: DISCARD
@@ -63,6 +63,9 @@
 data_sources: {{
     config {{
         name: "android.java_hprof.oom"
+        java_hprof_config {{
+{process_cfg}
+        }}
     }}
 }}
 
@@ -133,12 +136,12 @@
       except ValueError:
         print("FATAL: invalid PID %s" % pid, file=sys.stderr)
         fail = True
-      target_cfg += '{}pid: {}\n'.format(CFG_IDENT, pid)
+      target_cfg += '{}pid: {}\n'.format(CFG_INDENT, pid)
   if args.name:
     for name in args.name.split(','):
-      target_cfg += '{}process_cmdline: "{}"\n'.format(CFG_IDENT, name)
+      target_cfg += '{}process_cmdline: "{}"\n'.format(CFG_INDENT, name)
   if args.dump_smaps:
-    target_cfg += '{}dump_smaps: true\n'.format(CFG_IDENT)
+    target_cfg += '{}dump_smaps: true\n'.format(CFG_INDENT)
 
   if fail:
     return None
@@ -168,8 +171,13 @@
         file=sys.stderr)
     return None
 
-  if args.pid or args.name:
-    print("FATAL: Specifying process not supported in OOM mode",
+  if args.pid:
+    print("FATAL: Specifying pid not supported in OOM mode",
+        file=sys.stderr)
+    return None
+
+  if not args.name:
+    print("FATAL: Must specify process in OOM mode (use --name '*' to match all)",
         file=sys.stderr)
     return None
 
@@ -183,9 +191,14 @@
         file=sys.stderr)
     return None
 
+  process_cfg = ''
+  for name in args.name.split(','):
+    process_cfg += '{}process_cmdline: "{}"\n'.format(CFG_INDENT, name)
+
   return OOM_CFG.format(
       size_kb=convert_size_to_kb(args.buffer_size),
-      wait_duration_ms=args.oom_wait_seconds * 1000)
+      wait_duration_ms=args.oom_wait_seconds * 1000,
+      process_cfg=process_cfg)
 
 
 def main(argv):
diff --git a/ui/package-lock.json b/ui/package-lock.json
index b515d19..361b7f1 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -9,6 +9,7 @@
       "version": "1.0.0",
       "license": "Apache-2.0",
       "dependencies": {
+        "@popperjs/core": "^2.11.6",
         "@types/chrome": "0.0.186",
         "@types/color-convert": "^1.9.0",
         "@types/filesystem": "^0.0.32",
@@ -1039,6 +1040,15 @@
         "node": ">=10"
       }
     },
+    "node_modules/@popperjs/core": {
+      "version": "2.11.6",
+      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
+      "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/popperjs"
+      }
+    },
     "node_modules/@protobufjs/aspromise": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
@@ -10914,6 +10924,11 @@
         }
       }
     },
+    "@popperjs/core": {
+      "version": "2.11.6",
+      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
+      "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw=="
+    },
     "@protobufjs/aspromise": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
diff --git a/ui/package.json b/ui/package.json
index dbe9bc7..98a2bd6 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -7,6 +7,7 @@
   "author": "Perfetto Team",
   "license": "Apache-2.0",
   "dependencies": {
+    "@popperjs/core": "^2.11.6",
     "@types/chrome": "0.0.186",
     "@types/color-convert": "^1.9.0",
     "@types/filesystem": "^0.0.32",
diff --git a/ui/release/channels.json b/ui/release/channels.json
index 6d4559f..8864b8a 100644
--- a/ui/release/channels.json
+++ b/ui/release/channels.json
@@ -2,11 +2,11 @@
   "channels": [
     {
       "name": "stable",
-      "rev": "cf5c9753c2729027e283f52e5291e457f814f704"
+      "rev": "8adbb1e97c506f8394acc93b881d8e116c787238"
     },
     {
       "name": "canary",
-      "rev": "14a08703e3e391b713cf627d24a1ab9b30be99c2"
+      "rev": "8ba16d3e77b34ef1b2892ba2a5724da4af44c5e1"
     },
     {
       "name": "autopush",
diff --git a/ui/release/roll_branch.py b/ui/release/roll_branch.py
new file mode 100644
index 0000000..25a1e59
--- /dev/null
+++ b/ui/release/roll_branch.py
@@ -0,0 +1,47 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys
+import json
+
+
+def main():
+  [_, channel_name, rev] = sys.argv
+
+  with open('channels.json', 'r') as f:
+    channels_json = json.load(f)
+
+  found = False
+  for channel in channels_json['channels']:
+    if channel['name'] == channel_name:
+      if channel['rev'] == rev:
+        print(f'Channel {channel_name} is already at {rev}!')
+        return
+      channel['rev'] = rev
+      found = True
+      break
+
+  if not found:
+    print(f'Failed to find channel {channel_name}!')
+    return
+
+  with open('channels.json', 'w') as f:
+    json.dump(channels_json, f, indent=2)
+    # Right now channels.json ends with two line breaks,
+    # keep it that way to minimise diffs.
+    f.write('\n\n')
+
+
+if __name__ == "__main__":
+  main()
diff --git a/ui/release/roll_canary.sh b/ui/release/roll_canary.sh
new file mode 100755
index 0000000..bb233b1
--- /dev/null
+++ b/ui/release/roll_canary.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+RELEASE_DIR="$(cd -P ${BASH_SOURCE[0]%/*}; pwd)"
+
+cd $RELEASE_DIR
+git remote update
+python3 roll_branch.py canary $(git rev-parse origin/ui-canary)
diff --git a/ui/release/roll_stable.sh b/ui/release/roll_stable.sh
new file mode 100755
index 0000000..9aaca68
--- /dev/null
+++ b/ui/release/roll_stable.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+RELEASE_DIR="$(cd -P ${BASH_SOURCE[0]%/*}; pwd)"
+
+cd $RELEASE_DIR
+git remote update
+python3 roll_branch.py stable $(git rev-parse origin/ui-stable)
diff --git a/ui/src/assets/common.scss b/ui/src/assets/common.scss
index 5470643..d64576a 100644
--- a/ui/src/assets/common.scss
+++ b/ui/src/assets/common.scss
@@ -85,6 +85,7 @@
   padding: 0;
   margin: 0;
   overscroll-behavior: none;
+  overflow: hidden;
 }
 
 pre,
@@ -211,37 +212,57 @@
 
 $table-border-color: rgba(60, 76, 92, 0.4);
 
-.pivot-table {
+$bottom-tab-padding: 10px;
+
+@mixin table-component {
   @include bottom-panel-font;
   @include table-font-size;
 
   width: 100%;
   border-collapse: collapse;
 
-  thead,
-  i {
-    cursor: pointer;
-  }
   thead {
     font-weight: normal;
   }
+
+  tr:hover td {
+    background-color: $table-hover-color;
+  }
+
+  tr.header {
+    border-bottom: 1px solid $table-border-color;
+    text-align: center;
+  }
+
   td {
     padding: 2px 1px;
+
+    i.material-icons {
+      // Shrink the icons inside the table cells to increase the information
+      // density a little bit.
+      font-size: 16px;
+    }
+  }
+}
+
+.generic-table {
+  @include table-component;
+}
+
+.pivot-table {
+  @include table-component;
+
+  thead,
+  i {
+    cursor: pointer;
   }
   td.first {
     border-left: 1px solid $table-border-color;
     padding-left: 6px;
   }
-  tr.header {
-    border-bottom: 1px solid $table-border-color;
-    text-align: center;
-  }
   thead td.reorderable-cell {
     cursor: grab;
   }
-  tr:hover td {
-    background-color: $table-hover-color;
-  }
   .disabled {
     cursor: default;
   }
@@ -792,9 +813,9 @@
   user-select: none;
 }
 
-.pivot-table-redux {
+.pivot-table {
   user-select: text;
-  padding: 10px;
+  padding: $bottom-tab-padding;
 
   button.mode-button {
     border-radius: 10px;
@@ -826,9 +847,6 @@
     // a separate line.
     white-space: pre;
 
-    i.material-icons {
-      font-size: 16px;
-    }
   }
 }
 
diff --git a/ui/src/assets/details.scss b/ui/src/assets/details.scss
index 3faaadd..3a007c6 100644
--- a/ui/src/assets/details.scss
+++ b/ui/src/assets/details.scss
@@ -120,6 +120,12 @@
     padding: 10px 0 5px 0;
     position: sticky;
     top: 0;
+
+    // Relative/absolute/etc.. (non static) elements stack on top of this sticky
+    // header, so setting the z-index here to 1 forces this header to render
+    // over the top of other elements in the underlying panels.
+    z-index: 1;
+
     display: flex;
     background: white;
     &.aggregation {
@@ -282,6 +288,37 @@
     margin-top: 12px;
     margin-left: 10px;
   }
+
+  .slice-details-latency-panel {
+    // This panel is set to relative to make this panel a positioned element
+    // This is to allow the absolute text panels below to be positioned relative
+    // to this panel and not our parent.
+    position: relative;
+    font-size: 13px;
+    user-select: text;
+
+    .text-detail {
+      font-size: 10px;
+    }
+
+    .slice-details-wakeup-text {
+      position: absolute;
+      left: 40px;
+      top: 20px;
+    }
+
+    .slice-details-latency-text {
+      position: absolute;
+      left: 106px;
+      top: 90px;
+    }
+
+    .slice-details-image {
+      user-select: none;
+      width: 360px;
+      height: 300px;
+    }
+  }
 }
 
 .details-table-multicolumn {
@@ -430,16 +467,13 @@
     .tag-container {
       height: auto;
       min-height: 34px;
-      border: 2px solid #737679;
-      padding: 8px;
-      margin: 8px;
+      padding: 2px;
+      margin: 2px;
       cursor: text;
       border-radius: 3px;
       display: flex;
       align-items: center;
 
-      box-shadow: 0 2px 6px rgba(25, 25, 25, 0.2);
-
       .chips .chip {
         display: inline-block;
         width: auto;
diff --git a/ui/src/assets/perfetto.scss b/ui/src/assets/perfetto.scss
index c6d03b4..cb3c14e 100644
--- a/ui/src/assets/perfetto.scss
+++ b/ui/src/assets/perfetto.scss
@@ -25,3 +25,10 @@
 @import "trace_info_page";
 @import "flags_page";
 @import "hiring_banner";
+@import "widgets_page";
+@import "widgets/button";
+@import "widgets/checkbox";
+@import "widgets/text_input";
+@import "widgets/empty_state";
+@import "widgets/anchor";
+@import "widgets/popup";
diff --git a/ui/src/assets/scheduling_latency.png b/ui/src/assets/scheduling_latency.png
new file mode 100644
index 0000000..2a28074
--- /dev/null
+++ b/ui/src/assets/scheduling_latency.png
Binary files differ
diff --git a/ui/src/assets/widgets/anchor.scss b/ui/src/assets/widgets/anchor.scss
new file mode 100644
index 0000000..425a16b
--- /dev/null
+++ b/ui/src/assets/widgets/anchor.scss
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+@import "theme";
+
+.pf-anchor {
+  // Converting this element to a block type here ensures this element is the
+  // containing box for the icon when when floating the icon.
+  display: inline-block;
+  line-height: 1;
+  text-decoration: none; // Remove the default underling if href exists.
+  color: $pf-primary-background;
+  cursor: pointer;
+  border-bottom: dotted 1px $pf-primary-background;
+  transition: box-shadow $pf-anim-timing, background $pf-anim-timing;
+
+  & > .material-icons {
+    // For some reason, floating this icon results in the most pleasing vertical
+    // alignment.
+    float: right;
+    margin: 0;
+    font-size: inherit;
+    line-height: inherit;
+    color: inherit;
+  }
+
+  &:hover {
+    // Gently darken the background and thicken the underline.
+    border-bottom-style: solid;
+    background: $pf-minimal-background-hover;
+    box-shadow: 0 1px 0 $pf-primary-background;
+  }
+
+  &:focus-visible {
+    @include focus;
+  }
+}
diff --git a/ui/src/assets/widgets/button.scss b/ui/src/assets/widgets/button.scss
new file mode 100644
index 0000000..5a0fe86
--- /dev/null
+++ b/ui/src/assets/widgets/button.scss
@@ -0,0 +1,94 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+@import "theme";
+
+.pf-button {
+  font-family: $pf-font;
+  line-height: 1;
+  user-select: none;
+  color: $pf-primary-foreground;
+  background: $pf-primary-background;
+  transition: background $pf-anim-timing, box-shadow $pf-anim-timing;
+  border-radius: $pf-border-radius;
+  padding: 4px 8px;
+
+  & > .material-icons {
+    font-size: inherit;
+    line-height: inherit;
+    float: left;
+    margin-right: 4px; // Make some room between the icon and label
+  }
+
+  &:hover {
+    background: $pf-primary-background-hover;
+  }
+
+  &:active,
+  &.pf-active {
+    transition: none;
+    background: $pf-primary-background-active;
+    box-shadow: inset 1px 1px 4px #00000040;
+  }
+
+  &:focus-visible {
+    @include focus
+  }
+
+  &[disabled] {
+    background: $pf-primary-background-disabled;
+    color: $pf-primary-foreground-disabled;
+    box-shadow: none;
+    cursor: not-allowed;
+  }
+
+  // Remove default background in minimal mode, showing only the text
+  &.pf-minimal {
+    background: $pf-minimal-background;
+    color: $pf-minimal-foreground;
+
+    &:hover {
+      background: $pf-minimal-background-hover;
+    }
+
+    &:active,
+    &.pf-active {
+      background: $pf-minimal-background-active;
+    }
+
+    &[disabled] {
+      color: $pf-minimal-foreground-disabled;
+      background: $pf-minimal-background-disabled;
+      cursor: not-allowed;
+    }
+  }
+
+  // Reduce padding when compact
+  &.pf-compact {
+    padding: 2px 4px;
+  }
+
+  // Reduce padding when we are icon-only
+  &.pf-icon-only {
+    & > i {
+      margin: 0;
+    }
+
+    padding: 4px 4px;
+
+    &.pf-compact {
+      padding: 0;
+    }
+  }
+}
diff --git a/ui/src/assets/widgets/checkbox.scss b/ui/src/assets/widgets/checkbox.scss
new file mode 100644
index 0000000..b33a073
--- /dev/null
+++ b/ui/src/assets/widgets/checkbox.scss
@@ -0,0 +1,137 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+@import "theme";
+
+// This checkbox element is expected to contain a checkbox type input followed
+// by an empty span element.
+// The input is completely hidden and an entirely new checkbox is drawn inside
+// the span element. This allows us to style it how we like, and also add some
+// fancy transitions.
+// The box of the checkbox is a fixed sized span element. The tick is also a
+// fixed sized rectange rotated 45 degrees with only the bottom and right
+// borders visible.
+// When unchecked, the tick size and border width is 0, so the tick is
+// completely invsible. When we transition to checked, the border size on the
+// bottom and right sides is immmdiately set to full width, and the tick morphs
+// into view first by expanding along the x axis first, then expanding up the
+// y-axis. This has the effect of making the tick look like it's being drawn
+// onto the page with a pen.
+// When transitioning from checked to unchecked, the animation plays in reverse,
+// and the border width is set to 0 right at the end in order to make the tick
+// completely invisible again.
+.pf-checkbox {
+  $tick-anim-time-width: 100ms;
+  $tick-anim-time-height: 150ms;
+  $tick-anim-time: $tick-anim-time-width + $tick-anim-time-height;
+  $tick-easing: linear;
+
+  $box-size: 18px;
+  $tick-height: 9px;
+  $tick-width: 5px;
+  $box-label-padding: 6px;
+
+  display: inline-block;
+  position: relative; // Turns this container into a positioned element
+  font-family: $pf-font;
+  font-size: inherit;
+  color: $pf-minimal-foreground;
+  user-select: none;
+  cursor: pointer;
+  padding-left: $box-size + $box-label-padding;
+
+  // Hide the default checkbox
+  input {
+    position: absolute;
+    opacity: 0;
+    pointer-events: none;
+  }
+
+  // The span forms the "box" of the checkbox
+  span {
+    position: absolute;
+    left: 0;
+    top: 0;
+    bottom: 0;
+    margin-top: auto;
+    margin-bottom: auto;
+    height: $box-size;
+    width: $box-size;
+    border-radius: $pf-border-radius;
+    border: solid 2px $pf-minimal-foreground;
+    transition: background $pf-anim-timing;
+    background: none;
+
+    // The :after element forms the "tick" of the checkbox
+    &:after {
+      content: "";
+      display: block;
+      position: absolute;
+      bottom: 7.5px;
+      left: 1px;
+      width: 0px;
+      height: 0px;
+      border-color: $pf-primary-foreground;
+      border-style: solid;
+      border-width: 0;
+      transform-origin: 0% 100%; // Put the origin at the short edge of the tick
+      transform: rotate(45deg);
+      transition: height $tick-anim-time-height $tick-easing,
+        width $tick-anim-time-width $tick-anim-time-height $tick-easing,
+        border-width 0ms $tick-anim-time;
+    }
+  }
+
+  &:hover {
+    span {
+      background: $pf-minimal-background-hover;
+    }
+  }
+
+  input:checked + span {
+    border-color: $pf-primary-background;
+    background: $pf-primary-background;
+  }
+
+  input:focus-visible + span {
+    @include focus;
+  }
+
+  input:checked + span:after {
+    width: $tick-width;
+    height: $tick-height;
+    border-width: 0 2px 2px 0;
+    transition: width $tick-anim-time-height $tick-easing,
+      height $tick-anim-time-height $tick-anim-time-width $tick-easing,
+      border-width 0ms;
+  }
+
+  &.pf-disabled {
+    cursor: not-allowed;
+    color: $pf-minimal-foreground-disabled;
+
+    span {
+      border-color: $pf-minimal-foreground-disabled;
+      background: none;
+      &:after {
+        border-color: $pf-primary-foreground;
+      }
+    }
+
+    input:checked ~ span {
+      border-color: $pf-primary-background-disabled;
+      background: $pf-primary-background-disabled;
+    }
+  }
+}
diff --git a/ui/src/assets/widgets/empty_state.scss b/ui/src/assets/widgets/empty_state.scss
new file mode 100644
index 0000000..46ab9bc
--- /dev/null
+++ b/ui/src/assets/widgets/empty_state.scss
@@ -0,0 +1,39 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+@import "theme";
+
+.pf-empty-state {
+  display: inline-flex;
+  flex-direction: column;
+  align-items: center;
+  margin: 10px;
+  user-select: none;
+
+  & > i {
+    margin: auto;
+    font-size: 5em; // Size of the icon is relative to the font size
+    color: $pf-minimal-foreground;
+    margin-bottom: 10px;
+  }
+
+  .pf-empty-state-header {
+    margin-bottom: 10px;
+    color: $pf-minimal-foreground;
+  }
+
+  .pf-empty-state-detail {
+    margin: auto;
+  }
+}
diff --git a/ui/src/assets/widgets/popup.scss b/ui/src/assets/widgets/popup.scss
new file mode 100644
index 0000000..4433409
--- /dev/null
+++ b/ui/src/assets/widgets/popup.scss
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+@import "theme";
+
+.pf-popup {
+  background: white;
+  border: solid 1px gray;
+  border-radius: $pf-border-radius;
+  padding: 8px;
+  box-shadow: 2px 2px 16px rgba(0, 0, 0, 0.2);
+  position: absolute;
+  z-index: 10; // Hack to show popups over certain other elements
+}
+
+.pf-popup-arrow,
+.pf-popup-arrow::before {
+  position: absolute;
+  width: 8px;
+  height: 8px;
+  background: inherit;
+  border: inherit;
+}
+
+.pf-popup-arrow {
+  visibility: hidden;
+}
+
+.pf-popup-arrow::before {
+  visibility: visible;
+  content: "";
+  transform: rotate(45deg);
+}
+
+.pf-popup[data-popper-placement^="top"] > .pf-popup-arrow {
+  bottom: -4px;
+  border-top: none;
+  border-left: none;
+}
+
+.pf-popup[data-popper-placement^="bottom"] > .pf-popup-arrow {
+  top: -6px;
+  border-bottom: none;
+  border-right: none;
+}
+
+.pf-popup[data-popper-placement^="left"] > .pf-popup-arrow {
+  right: -4px;
+  border-bottom: none;
+  border-left: none;
+}
+
+.pf-popup[data-popper-placement^="right"] > .pf-popup-arrow {
+  left: -6px;
+  border-top: none;
+  border-right: none;
+}
diff --git a/ui/src/assets/widgets/text_input.scss b/ui/src/assets/widgets/text_input.scss
new file mode 100644
index 0000000..40c4b68
--- /dev/null
+++ b/ui/src/assets/widgets/text_input.scss
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+@import "theme";
+
+.pf-text-input {
+  font-family: $pf-font;
+  font-size: inherit;
+  outline: none; // Disable the default outline
+  border: none; // Disable the default border
+  border-bottom: solid 1px $pf-minimal-foreground; // Thin underline
+  background: none;
+  transition: border $pf-anim-timing, box-shadow $pf-anim-timing,
+    background $pf-anim-timing;
+
+  // Round only the top corners to avoid rounding the edges of the underline
+  border-radius: $pf-border-radius $pf-border-radius 0 0;
+
+  // The gentle hover effect indicates this component is interactive
+  &:hover {
+    background: $pf-minimal-background-hover;
+  }
+
+  &:focus {
+    background: $pf-minimal-background-hover;
+    border-bottom: solid 1px $pf-primary-background;
+
+    // The box-shadow thickens the bottom border, without adding to the height.
+    // This is the same technique used by materializecss:
+    // See https://materializecss.com/text-inputs.html
+    box-shadow: 0 1px 0 $pf-primary-background;
+  }
+
+  &[disabled] {
+    border-bottom-color: $pf-minimal-foreground-disabled;
+    color: $pf-minimal-foreground-disabled;
+    background: $pf-minimal-background-disabled;
+    cursor: not-allowed;
+  }
+}
diff --git a/ui/src/assets/widgets/theme.scss b/ui/src/assets/widgets/theme.scss
new file mode 100644
index 0000000..3902dc4
--- /dev/null
+++ b/ui/src/assets/widgets/theme.scss
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Standard theme settings for widgets
+
+$pf-font: "Roboto Condensed", sans-serif;
+$pf-border-radius: 3px;
+$pf-anim-timing: 250ms cubic-bezier(0.4, 0, 0.2, 1);
+
+// Here we describe two colour schemes: primary and minimal
+// It is assumed widgets exist on a light background
+// Primary is to be used for things like buttons and checkboxes
+// Minimal is to be used for things like inputs and labels
+// Some controls (i.e. checkboxes) may mix and match both in the same widget.
+// Other controls might use the primary scheme by default, but have a minimal
+// configuration which makes them use the minimal colour scheme.
+
+$pf-primary-foreground: #fff;
+$pf-primary-foreground-disabled: #aaa;
+$pf-primary-background: #3d5688;
+$pf-primary-background-hover: #4966a2;
+$pf-primary-background-active: #243e71;
+$pf-primary-background-disabled: #666;
+
+$pf-minimal-foreground: #19212b;
+$pf-minimal-foreground-disabled: #aaa;
+$pf-minimal-background: none;
+$pf-minimal-background-hover: #0001;
+$pf-minimal-background-active: #0002;
+$pf-minimal-background-disabled: none;
+
+@mixin focus {
+    outline: 2px auto #64b5f6;
+}
diff --git a/ui/src/assets/widgets_page.scss b/ui/src/assets/widgets_page.scss
new file mode 100644
index 0000000..beff40c
--- /dev/null
+++ b/ui/src/assets/widgets_page.scss
@@ -0,0 +1,72 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+.widgets-page {
+  padding: 20px;
+  font-size: 16px;
+  overflow: auto;
+
+  h1 {
+    margin: 32px 0 0 0;
+    font-size: 28px;
+  }
+
+  h2 {
+    margin: 16px 0 0 0;
+    font-size: 24px;
+  }
+
+  ul {
+    margin-block-start: 5px;
+    margin-block-end: 0px;
+    padding-inline-start: 0px;
+  }
+
+  li {
+    list-style-type: none;
+    margin: 2px 0 0 0;
+  }
+
+  .widget-row {
+    margin: 10px;
+
+    & > * {
+      margin-right: 5px;
+    }
+  }
+
+  .widget-block {
+    display: flex;
+    flex-direction: row;
+  }
+
+  .widget-controls {
+    margin: 10px;
+  }
+
+  .widget-container {
+    display: flex;
+    width: 300px;
+    height: 250px;
+    border-radius: 3px;
+    box-shadow: inset 2px 2px 10px #00000020;
+    border: dashed 1px gray;
+    margin: 10px 0 10px 0;
+
+    & > * {
+      margin: auto;
+      vertical-align: middle;
+    }
+  }
+}
diff --git a/ui/src/base/array_utils.ts b/ui/src/base/array_utils.ts
new file mode 100644
index 0000000..a3a9980
--- /dev/null
+++ b/ui/src/base/array_utils.ts
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// A function similar to Python's `range`.
+export function range(n: number): number[] {
+  if (n < 0) {
+    throw new Error('range size should be non-negative!');
+  }
+
+  const result = new Array<number>(n);
+
+  for (let i = 0; i < n; i++) {
+    result[i] = i;
+  }
+
+  return result;
+}
+
+// Checks whether all the strings in the array are unique.
+export function allUnique(x: string[]): boolean {
+  return x.length == new Set(x).size;
+}
diff --git a/ui/src/base/array_utils_unittest.ts b/ui/src/base/array_utils_unittest.ts
new file mode 100644
index 0000000..fcc8f36
--- /dev/null
+++ b/ui/src/base/array_utils_unittest.ts
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {allUnique, range} from './array_utils';
+
+describe('range', () => {
+  it('returns array of elements in range [0; n)', () => {
+    expect(range(3)).toEqual([0, 1, 2]);
+    expect(range(5)).toEqual([0, 1, 2, 3, 4]);
+  });
+
+  it('returns empty array on n = 0', () => {
+    expect(range(0)).toEqual([]);
+  });
+
+  it('throws an error on negative input', () => {
+    expect(() => {
+      range(-10);
+    }).toThrowError();
+  });
+});
+
+describe('allUnique', () => {
+  it('returns true on array with unique elements', () => {
+    expect(allUnique(['a', 'b', 'c'])).toBeTruthy();
+  });
+
+  it('returns false on array with repeated elements', () => {
+    expect(allUnique(['a', 'a', 'b'])).toBeFalsy();
+  });
+
+  // Couple of corner cases
+  it('returns true on an empty array', () => {
+    expect(allUnique([])).toBeTruthy();
+  });
+
+  it('returns true on an array with one element', () => {
+    expect(allUnique(['test'])).toBeTruthy();
+  });
+});
diff --git a/ui/src/base/comparison_utils.ts b/ui/src/base/comparison_utils.ts
new file mode 100644
index 0000000..ea6110a
--- /dev/null
+++ b/ui/src/base/comparison_utils.ts
@@ -0,0 +1,90 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {ColumnType} from '../common/query_result';
+
+export type ComparisonFn<X> = (a: X, b: X) => number;
+
+export type SortDirection = 'DESC'|'ASC';
+
+// Having a comparison function of type S and a getter that returns value of
+// type S from value of type T, values of type T can be compared.
+export function comparingBy<T, S>(
+    getter: (t: T) => S, comparison: ComparisonFn<S>): ComparisonFn<T> {
+  return (x, y) => {
+    return comparison(getter(x), getter(y));
+  };
+}
+
+export function withDirection<T>(
+    comparison: ComparisonFn<T>,
+    sortDirection?: SortDirection): ComparisonFn<T> {
+  if (sortDirection !== 'DESC') {
+    return comparison;
+  }
+
+  return (x, y) => {
+    return comparison(y, x);
+  };
+}
+
+export type SortableValue = ColumnType|undefined;
+
+function columnTypeKind(a: SortableValue): number {
+  if (a === undefined) {
+    return 0;
+  }
+  if (a === null) {
+    return 1;
+  }
+  if (typeof a === 'number') {
+    return 2;
+  }
+  if (typeof a === 'string') {
+    return 3;
+  }
+  // a instanceof Uint8Array
+  return 4;
+}
+
+export function compareUniversal(a: SortableValue, b: SortableValue): number {
+  if (a === undefined && b === undefined) {
+    return 0;
+  }
+  if (a === null && b === null) {
+    return 0;
+  }
+  if (typeof a === 'number' && typeof b === 'number') {
+    return a - b;
+  }
+  if (typeof a === 'string' && typeof b === 'string') {
+    return a.localeCompare(b);
+  }
+  if (a instanceof Uint8Array && b instanceof Uint8Array) {
+    // Do the lexicographical comparison
+    for (let i = 0; i < a.length && i < b.length; i++) {
+      if (a[i] < b[i]) {
+        return -1;
+      }
+      if (a[i] > b[i]) {
+        return 1;
+      }
+    }
+    // No discrepancies found in the common prefix, compare lengths of arrays.
+    return a.length - b.length;
+  }
+
+  // Values are of different kinds, compare the kinds
+  return columnTypeKind(a) - columnTypeKind(b);
+}
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 2909c73..38e9d44 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -23,7 +23,7 @@
   TableColumn,
   tableColumnEquals,
   toggleEnabled,
-} from '../frontend/pivot_table_redux_types';
+} from '../frontend/pivot_table_types';
 
 import {randomColor} from './colorizer';
 import {
@@ -44,7 +44,7 @@
   LogsPagination,
   NewEngineMode,
   OmniboxState,
-  PivotTableReduxResult,
+  PivotTableResult,
   PrimaryTrackSortKey,
   ProfileType,
   RecordingTarget,
@@ -1031,25 +1031,23 @@
     }
   },
 
-  togglePivotTableRedux(state: StateDraft, args: {areaId: string|null}) {
-    state.nonSerializableState.pivotTableRedux.selectionArea =
-        args.areaId === null ?
+  togglePivotTable(state: StateDraft, args: {areaId: string|null}) {
+    state.nonSerializableState.pivotTable.selectionArea = args.areaId === null ?
         undefined :
         {areaId: args.areaId, tracks: globals.state.areas[args.areaId].tracks};
     if (args.areaId !==
-        state.nonSerializableState.pivotTableRedux.selectionArea?.areaId) {
-      state.nonSerializableState.pivotTableRedux.queryResult = null;
+        state.nonSerializableState.pivotTable.selectionArea?.areaId) {
+      state.nonSerializableState.pivotTable.queryResult = null;
     }
   },
 
   setPivotStateQueryResult(
-      state: StateDraft, args: {queryResult: PivotTableReduxResult|null}) {
-    state.nonSerializableState.pivotTableRedux.queryResult = args.queryResult;
+      state: StateDraft, args: {queryResult: PivotTableResult|null}) {
+    state.nonSerializableState.pivotTable.queryResult = args.queryResult;
   },
 
-  setPivotTableReduxConstrainToArea(
-      state: StateDraft, args: {constrain: boolean}) {
-    state.nonSerializableState.pivotTableRedux.constrainToArea = args.constrain;
+  setPivotTableConstrainToArea(state: StateDraft, args: {constrain: boolean}) {
+    state.nonSerializableState.pivotTable.constrainToArea = args.constrain;
   },
 
   dismissFlamegraphModal(state: StateDraft, _: {}) {
@@ -1058,41 +1056,40 @@
 
   addPivotTableAggregation(
       state: StateDraft, args: {aggregation: Aggregation, after: number}) {
-    state.nonSerializableState.pivotTableRedux.selectedAggregations.splice(
+    state.nonSerializableState.pivotTable.selectedAggregations.splice(
         args.after, 0, args.aggregation);
   },
 
   removePivotTableAggregation(state: StateDraft, args: {index: number}) {
-    state.nonSerializableState.pivotTableRedux.selectedAggregations.splice(
+    state.nonSerializableState.pivotTable.selectedAggregations.splice(
         args.index, 1);
   },
 
   setPivotTableQueryRequested(
       state: StateDraft, args: {queryRequested: boolean}) {
-    state.nonSerializableState.pivotTableRedux.queryRequested =
-        args.queryRequested;
+    state.nonSerializableState.pivotTable.queryRequested = args.queryRequested;
   },
 
   setPivotTablePivotSelected(
       state: StateDraft, args: {column: TableColumn, selected: boolean}) {
     toggleEnabled(
         tableColumnEquals,
-        state.nonSerializableState.pivotTableRedux.selectedPivots,
+        state.nonSerializableState.pivotTable.selectedPivots,
         args.column,
         args.selected);
   },
 
   setPivotTableAggregationFunction(
       state: StateDraft, args: {index: number, function: AggregationFunction}) {
-    state.nonSerializableState.pivotTableRedux.selectedAggregations[args.index]
+    state.nonSerializableState.pivotTable.selectedAggregations[args.index]
         .aggregationFunction = args.function;
   },
 
   setPivotTableSortColumn(
       state: StateDraft,
       args: {aggregationIndex: number, order: SortDirection}) {
-    state.nonSerializableState.pivotTableRedux.selectedAggregations =
-        state.nonSerializableState.pivotTableRedux.selectedAggregations.map(
+    state.nonSerializableState.pivotTable.selectedAggregations =
+        state.nonSerializableState.pivotTable.selectedAggregations.map(
             (agg, index) => ({
               column: agg.column,
               aggregationFunction: agg.aggregationFunction,
@@ -1114,26 +1111,24 @@
 
   setPivotTableArgumentNames(
       state: StateDraft, args: {argumentNames: string[]}) {
-    state.nonSerializableState.pivotTableRedux.argumentNames =
-        args.argumentNames;
+    state.nonSerializableState.pivotTable.argumentNames = args.argumentNames;
   },
 
   changePivotTablePivotOrder(
       state: StateDraft,
       args: {from: number, to: number, direction: DropDirection}) {
-    const pivots = state.nonSerializableState.pivotTableRedux.selectedPivots;
-    state.nonSerializableState.pivotTableRedux.selectedPivots =
-        performReordering(
-            computeIntervals(pivots.length, args.from, args.to, args.direction),
-            pivots);
+    const pivots = state.nonSerializableState.pivotTable.selectedPivots;
+    state.nonSerializableState.pivotTable.selectedPivots = performReordering(
+        computeIntervals(pivots.length, args.from, args.to, args.direction),
+        pivots);
   },
 
   changePivotTableAggregationOrder(
       state: StateDraft,
       args: {from: number, to: number, direction: DropDirection}) {
     const aggregations =
-        state.nonSerializableState.pivotTableRedux.selectedAggregations;
-    state.nonSerializableState.pivotTableRedux.selectedAggregations =
+        state.nonSerializableState.pivotTable.selectedAggregations;
+    state.nonSerializableState.pivotTable.selectedAggregations =
         performReordering(
             computeIntervals(
                 aggregations.length, args.from, args.to, args.direction),
diff --git a/ui/src/common/colorizer.ts b/ui/src/common/colorizer.ts
index cbcf70a..dd4e109 100644
--- a/ui/src/common/colorizer.ts
+++ b/ui/src/common/colorizer.ts
@@ -51,6 +51,15 @@
   l: 62,
 };
 
+// A piece of wisdom from a long forgotten blog post: "Don't make
+// colors you want to change something normal like grey."
+export const UNEXPECTED_PINK_COLOR: Color = {
+  c: '#ff69b4',
+  h: 330,
+  s: 1.0,
+  l: 0.706,
+};
+
 function hash(s: string, max: number): number {
   let hash = 0x811c9dc5 & 0xfffffff;
   for (let i = 0; i < s.length; i++) {
diff --git a/ui/src/common/empty_state.ts b/ui/src/common/empty_state.ts
index 72b7f8b..4267be2 100644
--- a/ui/src/common/empty_state.ts
+++ b/ui/src/common/empty_state.ts
@@ -15,7 +15,7 @@
 import {createEmptyRecordConfig} from '../controller/record_config_types';
 import {
   Aggregation,
-} from '../frontend/pivot_table_redux_types';
+} from '../frontend/pivot_table_types';
 import {
   autosaveConfigStore,
   recordTargetStore,
@@ -57,7 +57,7 @@
 
 export function createEmptyNonSerializableState(): NonSerializableState {
   return {
-    pivotTableRedux: {
+    pivotTable: {
       queryResult: null,
       selectedPivots: [{kind: 'regular', table: 'slice', column: 'name'}],
       selectedAggregations: [
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index b16eba6..ae9c631 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -16,9 +16,8 @@
 import {
   Aggregation,
   PivotTree,
-  RegularColumn,
   TableColumn,
-} from '../frontend/pivot_table_redux_types';
+} from '../frontend/pivot_table_types';
 
 /**
  * A plain js object, holding objects of type |Class| keyed by string id.
@@ -403,47 +402,48 @@
 // Auxiliary metadata needed to parse the query result, as well as to render it
 // correctly. Generated together with the text of query and passed without the
 // change to the query response.
-export interface PivotTableReduxQueryMetadata {
+export interface PivotTableQueryMetadata {
   pivotColumns: TableColumn[];
   aggregationColumns: Aggregation[];
+  countIndex: number;
 }
 
 // Everything that's necessary to run the query for pivot table
-export interface PivotTableReduxQuery {
+export interface PivotTableQuery {
   text: string;
-  metadata: PivotTableReduxQueryMetadata;
+  metadata: PivotTableQueryMetadata;
 }
 
 // Pivot table query result
-export interface PivotTableReduxResult {
+export interface PivotTableResult {
   // Hierarchical pivot structure on top of rows
   tree: PivotTree;
   // Copy of the query metadata from the request, bundled up with the query
   // result to ensure the correct rendering.
-  metadata: PivotTableReduxQueryMetadata;
+  metadata: PivotTableQueryMetadata;
 }
 
 // Input parameters to check whether the pivot table needs to be re-queried.
-export interface PivotTableReduxAreaState {
+export interface PivotTableAreaState {
   areaId: string;
   tracks: string[];
 }
 
 export type SortDirection = 'DESC'|'ASC';
 
-export interface PivotTableReduxState {
+export interface PivotTableState {
   // Currently selected area, if null, pivot table is not going to be visible.
-  selectionArea?: PivotTableReduxAreaState;
+  selectionArea?: PivotTableAreaState;
 
   // Query response
-  queryResult: PivotTableReduxResult|null;
+  queryResult: PivotTableResult|null;
 
   // Selected pivots for tables other than slice.
   // Because of the query generation, pivoting happens first on non-slice
   // pivots; therefore, those can't be put after slice pivots. In order to
   // maintain the separation more clearly, slice and non-slice pivots are
   // located in separate arrays.
-  selectedPivots: RegularColumn[];
+  selectedPivots: TableColumn[];
 
   // Selected aggregation columns. Stored same way as pivots.
   selectedAggregations: Aggregation[];
@@ -476,7 +476,7 @@
     LoadedConfigNone|LoadedConfigAutomatic|LoadedConfigNamed;
 
 export interface NonSerializableState {
-  pivotTableRedux: PivotTableReduxState;
+  pivotTable: PivotTableState;
 }
 
 export interface LogFilteringCriteria {
diff --git a/ui/src/common/worker_messages.ts b/ui/src/common/worker_messages.ts
index b90f6b2..166b1cc 100644
--- a/ui/src/common/worker_messages.ts
+++ b/ui/src/common/worker_messages.ts
@@ -28,17 +28,3 @@
   // (see resetEngineWorker()).
   enginePort: MessagePort;
 }
-
-// Frontend -> Controller initialization message.
-export interface ControllerWorkerInitMessage {
-  // For receiving dispatch() commands from the frontend. This is where most of
-  // the frontend <> controller interaction happens.
-  controllerPort: MessagePort;
-
-  // For controller <> Chrome extension communication.
-  extensionPort: MessagePort;
-
-  // For reporting errors back to the frontend. This is a dedicated port to
-  // reduce depdencies on the business logic behind the other ports.
-  errorReportingPort: MessagePort;
-}
diff --git a/ui/src/controller/adb_base_controller.ts b/ui/src/controller/adb_base_controller.ts
index 4113247..bbafbc3 100644
--- a/ui/src/controller/adb_base_controller.ts
+++ b/ui/src/controller/adb_base_controller.ts
@@ -15,10 +15,10 @@
 import {extractDurationFromTraceConfig} from '../base/trace_config_utils';
 import {extractTraceConfig} from '../base/trace_config_utils';
 import {isAdbTarget} from '../common/state';
+import {globals} from '../frontend/globals';
 
 import {Adb} from './adb_interfaces';
 import {ReadBuffersResponse} from './consumer_port_types';
-import {globals} from './globals';
 import {Consumer, RpcConsumerPort} from './record_controller_interfaces';
 
 export enum AdbConnectionState {
diff --git a/ui/src/controller/aggregation/aggregation_controller.ts b/ui/src/controller/aggregation/aggregation_controller.ts
index 4fa1ecc..76cefeb 100644
--- a/ui/src/controller/aggregation/aggregation_controller.ts
+++ b/ui/src/controller/aggregation/aggregation_controller.ts
@@ -21,10 +21,10 @@
 import {Engine} from '../../common/engine';
 import {NUM} from '../../common/query_result';
 import {Area, Sorting} from '../../common/state';
+import {globals} from '../../frontend/globals';
 import {publishAggregateData} from '../../frontend/publish';
 import {AreaSelectionHandler} from '../area_selection_handler';
 import {Controller} from '../controller';
-import {globals} from '../globals';
 
 export interface AggregationControllerArgs {
   engine: Engine;
diff --git a/ui/src/controller/aggregation/counter_aggregation_controller.ts b/ui/src/controller/aggregation/counter_aggregation_controller.ts
index 295f9d5..dd7f14a 100644
--- a/ui/src/controller/aggregation/counter_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/counter_aggregation_controller.ts
@@ -16,8 +16,8 @@
 import {Engine} from '../../common/engine';
 import {Area, Sorting} from '../../common/state';
 import {toNs} from '../../common/time';
+import {globals} from '../../frontend/globals';
 import {Config, COUNTER_TRACK_KIND} from '../../tracks/counter';
-import {globals} from '../globals';
 
 import {AggregationController} from './aggregation_controller';
 
diff --git a/ui/src/controller/aggregation/cpu_aggregation_controller.ts b/ui/src/controller/aggregation/cpu_aggregation_controller.ts
index e98e94b..94d3950 100644
--- a/ui/src/controller/aggregation/cpu_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/cpu_aggregation_controller.ts
@@ -16,8 +16,8 @@
 import {Engine} from '../../common/engine';
 import {Area, Sorting} from '../../common/state';
 import {toNs} from '../../common/time';
+import {globals} from '../../frontend/globals';
 import {Config, CPU_SLICE_TRACK_KIND} from '../../tracks/cpu_slices';
-import {globals} from '../globals';
 
 import {AggregationController} from './aggregation_controller';
 
diff --git a/ui/src/controller/aggregation/cpu_by_process_aggregation_controller.ts b/ui/src/controller/aggregation/cpu_by_process_aggregation_controller.ts
index 59e633f..b28e496 100644
--- a/ui/src/controller/aggregation/cpu_by_process_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/cpu_by_process_aggregation_controller.ts
@@ -16,8 +16,8 @@
 import {Engine} from '../../common/engine';
 import {Area, Sorting} from '../../common/state';
 import {toNs} from '../../common/time';
+import {globals} from '../../frontend/globals';
 import {Config, CPU_SLICE_TRACK_KIND} from '../../tracks/cpu_slices';
-import {globals} from '../globals';
 
 import {AggregationController} from './aggregation_controller';
 
diff --git a/ui/src/controller/aggregation/frame_aggregation_controller.ts b/ui/src/controller/aggregation/frame_aggregation_controller.ts
index 0ce5c1d..97e1f86 100644
--- a/ui/src/controller/aggregation/frame_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/frame_aggregation_controller.ts
@@ -16,11 +16,11 @@
 import {Engine} from '../../common/engine';
 import {Area, Sorting} from '../../common/state';
 import {toNs} from '../../common/time';
+import {globals} from '../../frontend/globals';
 import {
   ACTUAL_FRAMES_SLICE_TRACK_KIND,
   Config,
 } from '../../tracks/actual_frames';
-import {globals} from '../globals';
 
 import {AggregationController} from './aggregation_controller';
 
diff --git a/ui/src/controller/aggregation/slice_aggregation_controller.ts b/ui/src/controller/aggregation/slice_aggregation_controller.ts
index d55e8ec..ebb8000 100644
--- a/ui/src/controller/aggregation/slice_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/slice_aggregation_controller.ts
@@ -16,6 +16,7 @@
 import {Engine} from '../../common/engine';
 import {Area, Sorting} from '../../common/state';
 import {toNs} from '../../common/time';
+import {globals} from '../../frontend/globals';
 import {
   ASYNC_SLICE_TRACK_KIND,
   Config as AsyncSliceConfig,
@@ -24,7 +25,6 @@
   Config as SliceConfig,
   SLICE_TRACK_KIND,
 } from '../../tracks/chrome_slices';
-import {globals} from '../globals';
 
 import {AggregationController} from './aggregation_controller';
 
diff --git a/ui/src/controller/aggregation/thread_aggregation_controller.ts b/ui/src/controller/aggregation/thread_aggregation_controller.ts
index 1f620d4..ddfadae 100644
--- a/ui/src/controller/aggregation/thread_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/thread_aggregation_controller.ts
@@ -18,11 +18,11 @@
 import {Area, Sorting} from '../../common/state';
 import {translateState} from '../../common/thread_state';
 import {toNs} from '../../common/time';
+import {globals} from '../../frontend/globals';
 import {
   Config,
   THREAD_STATE_TRACK_KIND,
 } from '../../tracks/thread_state';
-import {globals} from '../globals';
 
 import {AggregationController} from './aggregation_controller';
 
diff --git a/ui/src/controller/app_controller.ts b/ui/src/controller/app_controller.ts
index c711559..580e330 100644
--- a/ui/src/controller/app_controller.ts
+++ b/ui/src/controller/app_controller.ts
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 import {RECORDING_V2_FLAG} from '../common/feature_flags';
-import {globals} from '../controller/globals';
+import {globals} from '../frontend/globals';
 
 import {Child, Controller, ControllerInitializerAny} from './controller';
 import {PermalinkController} from './permalink_controller';
@@ -44,9 +44,7 @@
         [Child('permalink', PermalinkController, {})];
     if (!RECORDING_V2_FLAG.get()) {
       childControllers.push(Child(
-          'record',
-          RecordController,
-          {app: globals, extensionPort: this.extensionPort}));
+          'record', RecordController, {extensionPort: this.extensionPort}));
     }
     if (globals.state.engine !== undefined) {
       const engineCfg = globals.state.engine;
diff --git a/ui/src/controller/area_selection_handler.ts b/ui/src/controller/area_selection_handler.ts
index 3f5368d..b1d1c7e 100644
--- a/ui/src/controller/area_selection_handler.ts
+++ b/ui/src/controller/area_selection_handler.ts
@@ -13,18 +13,18 @@
 // limitations under the License.
 
 import {Area, AreaById} from '../common/state';
-import {globals as frontendGlobals} from '../frontend/globals';
+import {globals} from '../frontend/globals';
 
 export class AreaSelectionHandler {
   private previousArea?: Area;
 
   getAreaChange(): [boolean, AreaById|undefined] {
-    const currentSelection = frontendGlobals.state.currentSelection;
+    const currentSelection = globals.state.currentSelection;
     if (currentSelection === null || currentSelection.kind !== 'AREA') {
       return [false, undefined];
     }
 
-    const selectedArea = frontendGlobals.state.areas[currentSelection.areaId];
+    const selectedArea = globals.state.areas[currentSelection.areaId];
     // Area is considered changed if:
     // 1. The new area is defined and the old area undefined.
     // 2. The new area is undefined and the old area defined (viceversa from 1).
diff --git a/ui/src/controller/area_selection_handler_unittest.ts b/ui/src/controller/area_selection_handler_unittest.ts
index 1b49d87..c5a27c0 100644
--- a/ui/src/controller/area_selection_handler_unittest.ts
+++ b/ui/src/controller/area_selection_handler_unittest.ts
@@ -12,18 +12,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {createEmptyState} from '../common/empty_state';
 import {AreaById} from '../common/state';
-import {globals as frontendGlobals} from '../frontend/globals';
+import {globals} from '../frontend/globals';
 
 import {AreaSelectionHandler} from './area_selection_handler';
-import {createEmptyState} from '../common/empty_state';
 
 test('validAreaAfterUndefinedArea', () => {
   const areaId = '0';
   const latestArea: AreaById = {startSec: 0, endSec: 1, tracks: [], id: areaId};
-  frontendGlobals.state = createEmptyState();
-  frontendGlobals.state.currentSelection = {kind: 'AREA', areaId};
-  frontendGlobals.state.areas[areaId] = latestArea;
+  globals.state = createEmptyState();
+  globals.state.currentSelection = {kind: 'AREA', areaId};
+  globals.state.areas[areaId] = latestArea;
 
   const areaSelectionHandler = new AreaSelectionHandler();
   const [hasAreaChanged, selectedArea] = areaSelectionHandler.getAreaChange();
@@ -36,17 +36,17 @@
   const previousAreaId = '0';
   const previous:
       AreaById = {startSec: 0, endSec: 1, tracks: [], id: previousAreaId};
-  frontendGlobals.state = createEmptyState();
-  frontendGlobals.state.currentSelection = {
+  globals.state = createEmptyState();
+  globals.state.currentSelection = {
     kind: 'AREA',
     areaId: previousAreaId,
   };
-  frontendGlobals.state.areas[previousAreaId] = previous;
+  globals.state.areas[previousAreaId] = previous;
   const areaSelectionHandler = new AreaSelectionHandler();
   areaSelectionHandler.getAreaChange();
 
   const currentAreaId = '1';
-  frontendGlobals.state.currentSelection = {
+  globals.state.currentSelection = {
     kind: 'AREA',
     areaId: currentAreaId,
   };
@@ -57,11 +57,11 @@
 });
 
 test('UndefinedAreaAfterUndefinedArea', () => {
-  frontendGlobals.state.currentSelection = {kind: 'AREA', areaId: '0'};
+  globals.state.currentSelection = {kind: 'AREA', areaId: '0'};
   const areaSelectionHandler = new AreaSelectionHandler();
   areaSelectionHandler.getAreaChange();
 
-  frontendGlobals.state.currentSelection = {kind: 'AREA', areaId: '1'};
+  globals.state.currentSelection = {kind: 'AREA', areaId: '1'};
   const [hasAreaChanged, selectedArea] = areaSelectionHandler.getAreaChange();
 
   expect(hasAreaChanged).toEqual(true);
@@ -72,23 +72,23 @@
   const previousAreaId = '0';
   const previous:
       AreaById = {startSec: 0, endSec: 1, tracks: [], id: previousAreaId};
-  frontendGlobals.state = createEmptyState();
-  frontendGlobals.state.currentSelection = {
+  globals.state = createEmptyState();
+  globals.state.currentSelection = {
     kind: 'AREA',
     areaId: previousAreaId,
   };
-  frontendGlobals.state.areas[previousAreaId] = previous;
+  globals.state.areas[previousAreaId] = previous;
   const areaSelectionHandler = new AreaSelectionHandler();
   areaSelectionHandler.getAreaChange();
 
   const currentAreaId = '1';
   const current:
       AreaById = {startSec: 1, endSec: 2, tracks: [], id: currentAreaId};
-  frontendGlobals.state.currentSelection = {
+  globals.state.currentSelection = {
     kind: 'AREA',
     areaId: currentAreaId,
   };
-  frontendGlobals.state.areas[currentAreaId] = current;
+  globals.state.areas[currentAreaId] = current;
   const [hasAreaChanged, selectedArea] = areaSelectionHandler.getAreaChange();
 
   expect(hasAreaChanged).toEqual(true);
@@ -99,23 +99,23 @@
   const previousAreaId = '0';
   const previous:
       AreaById = {startSec: 0, endSec: 1, tracks: [], id: previousAreaId};
-  frontendGlobals.state = createEmptyState();
-  frontendGlobals.state.currentSelection = {
+  globals.state = createEmptyState();
+  globals.state.currentSelection = {
     kind: 'AREA',
     areaId: previousAreaId,
   };
-  frontendGlobals.state.areas[previousAreaId] = previous;
+  globals.state.areas[previousAreaId] = previous;
   const areaSelectionHandler = new AreaSelectionHandler();
   areaSelectionHandler.getAreaChange();
 
   const currentAreaId = '0';
   const current:
       AreaById = {startSec: 0, endSec: 1, tracks: [], id: currentAreaId};
-  frontendGlobals.state.currentSelection = {
+  globals.state.currentSelection = {
     kind: 'AREA',
     areaId: currentAreaId,
   };
-  frontendGlobals.state.areas[currentAreaId] = current;
+  globals.state.areas[currentAreaId] = current;
   const [hasAreaChanged, selectedArea] = areaSelectionHandler.getAreaChange();
 
   expect(hasAreaChanged).toEqual(false);
@@ -123,11 +123,11 @@
 });
 
 test('NonAreaSelectionAfterUndefinedArea', () => {
-  frontendGlobals.state.currentSelection = {kind: 'AREA', areaId: '0'};
+  globals.state.currentSelection = {kind: 'AREA', areaId: '0'};
   const areaSelectionHandler = new AreaSelectionHandler();
   areaSelectionHandler.getAreaChange();
 
-  frontendGlobals.state
+  globals.state
       .currentSelection = {kind: 'COUNTER', leftTs: 0, rightTs: 0, id: 1};
   const [hasAreaChanged, selectedArea] = areaSelectionHandler.getAreaChange();
 
diff --git a/ui/src/controller/cpu_profile_controller.ts b/ui/src/controller/cpu_profile_controller.ts
index db695ac..0f61cb2 100644
--- a/ui/src/controller/cpu_profile_controller.ts
+++ b/ui/src/controller/cpu_profile_controller.ts
@@ -15,11 +15,10 @@
 import {Engine} from '../common/engine';
 import {NUM, STR} from '../common/query_result';
 import {CallsiteInfo, CpuProfileSampleSelection} from '../common/state';
-import {CpuProfileDetails} from '../frontend/globals';
+import {CpuProfileDetails, globals} from '../frontend/globals';
 import {publishCpuProfileDetails} from '../frontend/publish';
 
 import {Controller} from './controller';
-import {globals} from './globals';
 
 export interface CpuProfileControllerArgs {
   engine: Engine;
diff --git a/ui/src/controller/flamegraph_controller.ts b/ui/src/controller/flamegraph_controller.ts
index 6d4f36d..e94531d 100644
--- a/ui/src/controller/flamegraph_controller.ts
+++ b/ui/src/controller/flamegraph_controller.ts
@@ -28,10 +28,7 @@
 import {NUM, STR} from '../common/query_result';
 import {CallsiteInfo, FlamegraphState, ProfileType} from '../common/state';
 import {toNs} from '../common/time';
-import {
-  FlamegraphDetails,
-  globals as frontendGlobals,
-} from '../frontend/globals';
+import {FlamegraphDetails, globals} from '../frontend/globals';
 import {publishFlamegraphDetails} from '../frontend/publish';
 import {
   Config as PerfSampleConfig,
@@ -40,7 +37,6 @@
 
 import {AreaSelectionHandler} from './area_selection_handler';
 import {Controller} from './controller';
-import {globals} from './globals';
 
 export function profileType(s: string): ProfileType {
   if (isProfileType(s)) {
@@ -131,11 +127,11 @@
       const upids = [];
       if (!area) {
         this.checkCompletionAndPublishFlamegraph(
-            {...frontendGlobals.flamegraphDetails, isInAreaSelection: false});
+            {...globals.flamegraphDetails, isInAreaSelection: false});
         return;
       }
       for (const trackId of area.tracks) {
-        const trackState = frontendGlobals.state.tracks[trackId];
+        const trackState = globals.state.tracks[trackId];
         if (!trackState ||
             trackState.kind !== PERF_SAMPLES_PROFILE_TRACK_KIND) {
           continue;
@@ -144,10 +140,10 @@
       }
       if (upids.length === 0) {
         this.checkCompletionAndPublishFlamegraph(
-            {...frontendGlobals.flamegraphDetails, isInAreaSelection: false});
+            {...globals.flamegraphDetails, isInAreaSelection: false});
         return;
       }
-      frontendGlobals.dispatch(Actions.openFlamegraph({
+      globals.dispatch(Actions.openFlamegraph({
         upids,
         startNs: toNs(area.startSec),
         endNs: toNs(area.endSec),
@@ -155,7 +151,7 @@
         viewingOption: PERF_SAMPLES_KEY,
       }));
     }
-    const selection = frontendGlobals.state.currentFlamegraphState;
+    const selection = globals.state.currentFlamegraphState;
     if (!selection || !this.shouldRequestData(selection)) {
       return;
     }
diff --git a/ui/src/controller/flow_events_controller.ts b/ui/src/controller/flow_events_controller.ts
index 33fd8bb..d89b514 100644
--- a/ui/src/controller/flow_events_controller.ts
+++ b/ui/src/controller/flow_events_controller.ts
@@ -17,7 +17,7 @@
 import {NUM, STR_NULL} from '../common/query_result';
 import {Area} from '../common/state';
 import {fromNs, toNs} from '../common/time';
-import {Flow} from '../frontend/globals';
+import {Flow, globals} from '../frontend/globals';
 import {publishConnectedFlows, publishSelectedFlows} from '../frontend/publish';
 import {
   ACTUAL_FRAMES_SLICE_TRACK_KIND,
@@ -29,7 +29,6 @@
 } from '../tracks/chrome_slices';
 
 import {Controller} from './controller';
-import {globals} from './globals';
 
 export interface FlowEventsControllerArgs {
   engine: Engine;
diff --git a/ui/src/controller/globals.ts b/ui/src/controller/globals.ts
deleted file mode 100644
index 5cd8f67..0000000
--- a/ui/src/controller/globals.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {applyPatches, Patch} from 'immer';
-
-import {assertExists} from '../base/logging';
-import {createEmptyState} from '../common/empty_state';
-import {State} from '../common/state';
-
-import {ControllerAny} from './controller';
-
-export interface App {
-  state: State;
-}
-
-/**
- * Global accessors for state/dispatch in the controller.
- */
-class Globals implements App {
-  private _state?: State;
-  private _rootController?: ControllerAny;
-  private _runningControllers = false;
-
-  initialize(rootController: ControllerAny) {
-    this._rootController = rootController;
-    this._state = createEmptyState();
-  }
-
-  // This is called by the frontend logic which now owns and handle the
-  // source-of-truth state, to give us an update on the newer state updates.
-  patchState(patches: Patch[]): void {
-    this._state = applyPatches(assertExists(this._state), patches);
-    this.runControllers();
-  }
-
-  private runControllers() {
-    if (this._runningControllers) throw new Error('Re-entrant call detected');
-
-    // Run controllers locally until all state machines reach quiescence.
-    let runAgain = true;
-    for (let iter = 0; runAgain; iter++) {
-      if (iter > 100) throw new Error('Controllers are stuck in a livelock');
-      this._runningControllers = true;
-      try {
-        runAgain = assertExists(this._rootController).invoke();
-      } finally {
-        this._runningControllers = false;
-      }
-    }
-  }
-
-  get state(): State {
-    return assertExists(this._state);
-  }
-
-  resetForTesting() {
-    this._state = undefined;
-    this._rootController = undefined;
-  }
-}
-
-export const globals = new Globals();
diff --git a/ui/src/controller/index.ts b/ui/src/controller/index.ts
index 3c43918..ffb481b 100644
--- a/ui/src/controller/index.ts
+++ b/ui/src/controller/index.ts
@@ -15,21 +15,31 @@
 import '../gen/all_tracks';
 import '../common/recordingV2/target_factories';
 
-import {assertTrue} from '../base/logging';
-import {ControllerWorkerInitMessage} from '../common/worker_messages';
-import {AppController} from './app_controller';
-import {globals} from './globals';
+import {assertExists, assertTrue} from '../base/logging';
 
-let initialized = false;
-export function initController(init: ControllerWorkerInitMessage) {
-  assertTrue(!initialized);
-  initialized = true;
-  const controllerPort = init.controllerPort;
-  const extensionPort = init.extensionPort;
-  controllerPort.onmessage = ({data}) => globals.patchState(data);
-  globals.initialize(new AppController(extensionPort));
+import {AppController} from './app_controller';
+import {ControllerAny} from './controller';
+
+let rootController: ControllerAny;
+let runningControllers = false;
+
+export function initController(extensionPort: MessagePort) {
+  assertTrue(rootController === undefined);
+  rootController = new AppController(extensionPort);
 }
 
+export function runControllers() {
+  if (runningControllers) throw new Error('Re-entrant call detected');
 
-// For devtools-based debugging.
-(self as {} as {controllerGlobals: {}}).controllerGlobals = globals;
+  // Run controllers locally until all state machines reach quiescence.
+  let runAgain = true;
+  for (let iter = 0; runAgain; iter++) {
+    if (iter > 100) throw new Error('Controllers are stuck in a livelock');
+    runningControllers = true;
+    try {
+      runAgain = assertExists(rootController).invoke();
+    } finally {
+      runningControllers = false;
+    }
+  }
+}
diff --git a/ui/src/controller/logs_controller.ts b/ui/src/controller/logs_controller.ts
index 336007a..d8f9b3a 100644
--- a/ui/src/controller/logs_controller.ts
+++ b/ui/src/controller/logs_controller.ts
@@ -24,10 +24,10 @@
 import {escapeGlob, escapeQuery} from '../common/query_utils';
 import {LogFilteringCriteria} from '../common/state';
 import {fromNs, TimeSpan, toNsCeil, toNsFloor} from '../common/time';
+import {globals} from '../frontend/globals';
 import {publishTrackData} from '../frontend/publish';
 
 import {Controller} from './controller';
-import {App, globals} from './globals';
 
 async function updateLogBounds(
     engine: Engine, span: TimeSpan): Promise<LogBounds> {
@@ -164,7 +164,6 @@
 
 export interface LogsControllerArgs {
   engine: Engine;
-  app: App;
 }
 
 /**
@@ -179,7 +178,6 @@
  * and keeps it up to date.
  */
 export class LogsController extends Controller<'main'> {
-  private app: App;
   private engine: Engine;
   private span: TimeSpan;
   private pagination: Pagination;
@@ -190,7 +188,6 @@
 
   constructor(args: LogsControllerArgs) {
     super('main');
-    this.app = args.app;
     this.engine = args.engine;
     this.span = new TimeSpan(0, 10);
     this.pagination = new Pagination(0, 0);
@@ -229,11 +226,11 @@
   }
 
   private async updateLogTracks() {
-    const traceTime = this.app.state.frontendLocalState.visibleState;
+    const traceTime = globals.state.frontendLocalState.visibleState;
     const newSpan = new TimeSpan(traceTime.startSec, traceTime.endSec);
     const oldSpan = this.span;
 
-    const pagination = this.app.state.logsPagination;
+    const pagination = globals.state.logsPagination;
     // This can occur when loading old traces.
     // TODO(hjd): Fix the problem of accessing state from a previous version of
     // the UI in a general way.
diff --git a/ui/src/controller/metrics_controller.ts b/ui/src/controller/metrics_controller.ts
index 7c7c9be..594d04c 100644
--- a/ui/src/controller/metrics_controller.ts
+++ b/ui/src/controller/metrics_controller.ts
@@ -15,11 +15,10 @@
 import {Actions} from '../common/actions';
 import {Engine} from '../common/engine';
 import {QueryError} from '../common/query_result';
-import {globals as frontendGlobals} from '../frontend/globals';
+import {globals} from '../frontend/globals';
 import {publishMetricResult} from '../frontend/publish';
 
 import {Controller} from './controller';
-import {globals} from './globals';
 
 export class MetricsController extends Controller<'main'> {
   private engine: Engine;
@@ -49,7 +48,7 @@
         throw e;
       }
     }
-    frontendGlobals.dispatch(Actions.resetMetricRequest({name}));
+    globals.dispatch(Actions.resetMetricRequest({name}));
     this.currentlyRunningMetric = undefined;
   }
 
diff --git a/ui/src/controller/permalink_controller.ts b/ui/src/controller/permalink_controller.ts
index 22cf3de..a382a40 100644
--- a/ui/src/controller/permalink_controller.ts
+++ b/ui/src/controller/permalink_controller.ts
@@ -30,12 +30,11 @@
   saveTrace,
   toSha256,
 } from '../common/upload_utils';
-import {globals as frontendGlobals} from '../frontend/globals';
+import {globals} from '../frontend/globals';
 import {publishConversionJobStatusUpdate} from '../frontend/publish';
 import {Router} from '../frontend/router';
 
 import {Controller} from './controller';
-import {globals} from './globals';
 import {RecordConfig, recordConfigValidator} from './record_config_types';
 import {runValidator} from './validators';
 
@@ -79,7 +78,7 @@
 
       PermalinkController.createPermalink(isRecordingConfig)
           .then((hash) => {
-            frontendGlobals.dispatch(Actions.setPermalink({requestId, hash}));
+            globals.dispatch(Actions.setPermalink({requestId, hash}));
           })
           .finally(() => {
             publishConversionJobStatusUpdate({
@@ -99,12 +98,11 @@
             const validConfig =
                 runValidator(recordConfigValidator, stateOrConfig as unknown)
                     .result;
-            frontendGlobals.dispatch(
-                Actions.setRecordConfig({config: validConfig}));
+            globals.dispatch(Actions.setRecordConfig({config: validConfig}));
             Router.navigate('#!/record');
             return;
           }
-          frontendGlobals.dispatch(Actions.setState({newState: stateOrConfig}));
+          globals.dispatch(Actions.setState({newState: stateOrConfig}));
           this.lastRequestId = stateOrConfig.permalink.requestId;
         });
   }
@@ -156,7 +154,7 @@
     if (isRecordingConfig) {
       uploadState = globals.state.recordConfig;
     } else {
-      const engine = assertExists(frontendGlobals.getCurrentEngine());
+      const engine = assertExists(globals.getCurrentEngine());
       let dataToUpload: File|ArrayBuffer|undefined = undefined;
       let traceName = `trace ${engine.id}`;
       if (engine.source.type === 'FILE') {
@@ -216,7 +214,7 @@
 
   private static updateStatus(msg: string): void {
     // TODO(hjd): Unify loading updates.
-    frontendGlobals.dispatch(Actions.updateStatus({
+    globals.dispatch(Actions.updateStatus({
       msg,
       timestamp: Date.now() / 1000,
     }));
diff --git a/ui/src/controller/pivot_table_redux_controller.ts b/ui/src/controller/pivot_table_controller.ts
similarity index 72%
rename from ui/src/controller/pivot_table_redux_controller.ts
rename to ui/src/controller/pivot_table_controller.ts
index 8658822..cc33e3e 100644
--- a/ui/src/controller/pivot_table_redux_controller.ts
+++ b/ui/src/controller/pivot_table_controller.ts
@@ -21,46 +21,53 @@
 import {ColumnType, STR} from '../common/query_result';
 import {
   AreaSelection,
-  PivotTableReduxQuery,
-  PivotTableReduxQueryMetadata,
-  PivotTableReduxResult,
-  PivotTableReduxState,
+  PivotTableQuery,
+  PivotTableQueryMetadata,
+  PivotTableResult,
+  PivotTableState,
 } from '../common/state';
-import {globals as frontendGlobals} from '../frontend/globals';
+import {globals} from '../frontend/globals';
 import {
   aggregationIndex,
   generateQueryFromState,
-} from '../frontend/pivot_table_redux_query_generator';
-import {
-  Aggregation,
-  PivotTree,
-} from '../frontend/pivot_table_redux_types';
+} from '../frontend/pivot_table_query_generator';
+import {Aggregation, PivotTree} from '../frontend/pivot_table_types';
 
 import {Controller} from './controller';
-import {globals} from './globals';
 
 export const PIVOT_TABLE_REDUX_FLAG = featureFlags.register({
-  id: 'pivotTableRedux',
+  id: 'pivotTable',
   name: 'Pivot tables V2',
   description: 'Second version of pivot table',
   // Enabled in canary and autopush by default.
   defaultValue: getCurrentChannel() !== DEFAULT_CHANNEL,
 });
 
-// Auxiliary class to build the tree from query response.
-class TreeBuilder {
-  private readonly root: PivotTree;
-  pivotColumns: number;
-  aggregateColumns: Aggregation[];
+function expectNumber(value: ColumnType): number {
+  if (typeof value === 'number') {
+    return value;
+  }
+  throw new Error(`Number was expected, got ${typeof value}`);
+}
 
-  constructor(
-      pivotColumns: number, aggregateColumns: Aggregation[],
-      firstRow: ColumnType[]) {
-    this.pivotColumns = pivotColumns;
-    this.aggregateColumns = aggregateColumns;
+// Auxiliary class to build the tree from query response.
+export class PivotTableTreeBuilder {
+  private readonly root: PivotTree;
+  queryMetadata: PivotTableQueryMetadata;
+
+  get pivotColumnsCount(): number {
+    return this.queryMetadata.pivotColumns.length;
+  }
+
+  get aggregateColumns(): Aggregation[] {
+    return this.queryMetadata.aggregationColumns;
+  }
+
+  constructor(queryMetadata: PivotTableQueryMetadata, firstRow: ColumnType[]) {
+    this.queryMetadata = queryMetadata;
     this.root = this.createNode(firstRow);
     let tree = this.root;
-    for (let i = 0; i + 1 < this.pivotColumns; i++) {
+    for (let i = 0; i + 1 < this.pivotColumnsCount; i++) {
       const value = firstRow[i];
       tree = this.insertChild(tree, value, this.createNode(firstRow));
     }
@@ -71,7 +78,7 @@
   ingestRow(row: ColumnType[]) {
     let tree = this.root;
     this.updateAggregates(tree, row);
-    for (let i = 0; i + 1 < this.pivotColumns; i++) {
+    for (let i = 0; i + 1 < this.pivotColumnsCount; i++) {
       const nextTree = tree.children.get(row[i]);
       if (nextTree === undefined) {
         // Insert the new node into the tree, and make variable `tree` point
@@ -90,15 +97,23 @@
   }
 
   updateAggregates(tree: PivotTree, row: ColumnType[]) {
+    const countIndex = this.queryMetadata.countIndex;
+    const treeCount =
+        countIndex >= 0 ? expectNumber(tree.aggregates[countIndex]) : 0;
+    const rowCount = countIndex >= 0 ?
+        expectNumber(
+            row[aggregationIndex(this.pivotColumnsCount, countIndex)]) :
+        0;
+
     for (let i = 0; i < this.aggregateColumns.length; i++) {
       const agg = this.aggregateColumns[i];
 
       const currAgg = tree.aggregates[i];
-      const childAgg = row[aggregationIndex(this.pivotColumns, i)];
+      const childAgg = row[aggregationIndex(this.pivotColumnsCount, i)];
       if (typeof currAgg === 'number' && typeof childAgg === 'number') {
         switch (agg.aggregationFunction) {
-          case 'COUNT':
           case 'SUM':
+          case 'COUNT':
             tree.aggregates[i] = currAgg + childAgg;
             break;
           case 'MAX':
@@ -107,9 +122,16 @@
           case 'MIN':
             tree.aggregates[i] = Math.min(currAgg, childAgg);
             break;
+          case 'AVG': {
+            const currSum = currAgg * treeCount;
+            const addSum = childAgg * rowCount;
+            tree.aggregates[i] = (currSum + addSum) / (treeCount + rowCount);
+            break;
+          }
         }
       }
     }
+    tree.aggregates[this.aggregateColumns.length] = treeCount + rowCount;
   }
 
   // Helper method that inserts child node into the tree and returns it, used
@@ -126,8 +148,10 @@
     const aggregates = [];
 
     for (let j = 0; j < this.aggregateColumns.length; j++) {
-      aggregates.push(row[aggregationIndex(this.pivotColumns, j)]);
+      aggregates.push(row[aggregationIndex(this.pivotColumnsCount, j)]);
     }
+    aggregates.push(row[aggregationIndex(
+        this.pivotColumnsCount, this.aggregateColumns.length)]);
 
     return {
       isCollapsed: false,
@@ -138,8 +162,8 @@
   }
 }
 
-function createEmptyQueryResult(metadata: PivotTableReduxQueryMetadata):
-    PivotTableReduxResult {
+function createEmptyQueryResult(metadata: PivotTableQueryMetadata):
+    PivotTableResult {
   return {
     tree: {
       aggregates: [],
@@ -151,10 +175,9 @@
   };
 }
 
-
 // Controller responsible for showing the panel with pivot table, as well as
 // executing its queries and post-processing query results.
-export class PivotTableReduxController extends Controller<{}> {
+export class PivotTableController extends Controller<{}> {
   static detailsCount = 0;
   engine: Engine;
   lastQueryAreaId = '';
@@ -181,7 +204,7 @@
     return true;
   }
 
-  shouldRerun(state: PivotTableReduxState, selection: AreaSelection) {
+  shouldRerun(state: PivotTableState, selection: AreaSelection) {
     if (state.selectionArea === undefined) {
       return false;
     }
@@ -196,7 +219,7 @@
     return false;
   }
 
-  async processQuery(query: PivotTableReduxQuery) {
+  async processQuery(query: PivotTableQuery) {
     const result = await this.engine.query(query.text);
     try {
       await result.waitAllRows();
@@ -220,22 +243,19 @@
     if (!it.valid()) {
       // Iterator is invalid after creation; means that there are no rows
       // satisfying filtering criteria. Return an empty tree.
-      frontendGlobals.dispatch(Actions.setPivotStateQueryResult(
+      globals.dispatch(Actions.setPivotStateQueryResult(
           {queryResult: createEmptyQueryResult(query.metadata)}));
       return;
     }
 
-    const treeBuilder = new TreeBuilder(
-        query.metadata.pivotColumns.length,
-        query.metadata.aggregationColumns,
-        nextRow());
+    const treeBuilder = new PivotTableTreeBuilder(query.metadata, nextRow());
     while (it.valid()) {
       treeBuilder.ingestRow(nextRow());
     }
 
-    frontendGlobals.dispatch(Actions.setPivotStateQueryResult(
+    globals.dispatch(Actions.setPivotStateQueryResult(
         {queryResult: {tree: treeBuilder.build(), metadata: query.metadata}}));
-    frontendGlobals.dispatch(Actions.setCurrentTab({tab: 'pivot_table_redux'}));
+    globals.dispatch(Actions.setCurrentTab({tab: 'pivot_table'}));
   }
 
   async requestArgumentNames() {
@@ -251,8 +271,7 @@
       it.next();
     }
 
-    frontendGlobals.dispatch(
-        Actions.setPivotTableArgumentNames({argumentNames}));
+    globals.dispatch(Actions.setPivotTableArgumentNames({argumentNames}));
   }
 
 
@@ -265,25 +284,23 @@
       this.requestArgumentNames();
     }
 
-    const pivotTableState = globals.state.nonSerializableState.pivotTableRedux;
+    const pivotTableState = globals.state.nonSerializableState.pivotTable;
     const selection = globals.state.currentSelection;
 
     if (pivotTableState.queryRequested ||
         (selection !== null && selection.kind === 'AREA' &&
          this.shouldRerun(pivotTableState, selection))) {
-      frontendGlobals.dispatch(
+      globals.dispatch(
           Actions.setPivotTableQueryRequested({queryRequested: false}));
       // Need to re-run the existing query, clear the current result.
-      frontendGlobals.dispatch(
-          Actions.setPivotStateQueryResult({queryResult: null}));
+      globals.dispatch(Actions.setPivotStateQueryResult({queryResult: null}));
       this.processQuery(generateQueryFromState(pivotTableState));
     }
 
     if (selection !== null && selection.kind === 'AREA' &&
         (pivotTableState.selectionArea === undefined ||
          pivotTableState.selectionArea.areaId !== selection.areaId)) {
-      frontendGlobals.dispatch(
-          Actions.togglePivotTableRedux({areaId: selection.areaId}));
+      globals.dispatch(Actions.togglePivotTable({areaId: selection.areaId}));
     }
   }
 }
diff --git a/ui/src/controller/pivot_table_tree_builder_unittest.ts b/ui/src/controller/pivot_table_tree_builder_unittest.ts
new file mode 100644
index 0000000..5ab93f9
--- /dev/null
+++ b/ui/src/controller/pivot_table_tree_builder_unittest.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {PivotTableTreeBuilder} from './pivot_table_controller';
+
+describe('Pivot Table tree builder', () => {
+  test('aggregates averages correctly', () => {
+    const builder = new PivotTableTreeBuilder(
+        {
+          pivotColumns: [
+            {kind: 'regular', table: 'slice', column: 'category'},
+            {kind: 'regular', table: 'slice', column: 'name'},
+          ],
+          aggregationColumns: [{
+            aggregationFunction: 'AVG',
+            column: {kind: 'regular', table: 'slice', column: 'dur'},
+          }],
+          countIndex: 1,
+        },
+        ['cat1', 'name1', 80.0, 2]);
+
+    builder.ingestRow(['cat1', 'name2', 20.0, 1]);
+    builder.ingestRow(['cat2', 'name3', 20.0, 1]);
+
+    // With two rows of average value 80.0, and two of average value 20.0;
+    // the total sum is 80.0 * 2 + 20.0 + 20.0 = 200.0 over four slices. The
+    // average value should be 200.0 / 4 = 50.0
+    expect(builder.build().aggregates[0]).toBeCloseTo(50.0);
+  });
+});
diff --git a/ui/src/controller/query_controller.ts b/ui/src/controller/query_controller.ts
index 3a896d8..488a863 100644
--- a/ui/src/controller/query_controller.ts
+++ b/ui/src/controller/query_controller.ts
@@ -16,11 +16,10 @@
 import {Actions} from '../common/actions';
 import {Engine} from '../common/engine';
 import {runQuery} from '../common/queries';
-import {globals as frontendGlobals} from '../frontend/globals';
+import {globals} from '../frontend/globals';
 import {publishQueryResult} from '../frontend/publish';
 
 import {Controller} from './controller';
-import {globals} from './globals';
 
 export interface QueryControllerArgs {
   queryId: string;
@@ -40,7 +39,7 @@
             .then((result) => {
               console.log(`Query ${config.query} took ${result.durationMs} ms`);
               publishQueryResult({id: this.args.queryId, data: result});
-              frontendGlobals.dispatch(
+              globals.dispatch(
                   Actions.deleteQuery({queryId: this.args.queryId}));
             });
         this.setState('querying');
diff --git a/ui/src/controller/record_controller.ts b/ui/src/controller/record_controller.ts
index 3ce567e..3f6eb05 100644
--- a/ui/src/controller/record_controller.ts
+++ b/ui/src/controller/record_controller.ts
@@ -29,7 +29,7 @@
   isChromeTarget,
   RecordingTarget,
 } from '../common/state';
-import {globals as frontendGlobals} from '../frontend/globals';
+import {globals} from '../frontend/globals';
 import {publishBufferUsage, publishTrackData} from '../frontend/publish';
 
 import {AdbOverWebUsb} from './adb';
@@ -46,7 +46,6 @@
   isReadBuffersResponse,
 } from './consumer_port_types';
 import {Controller} from './controller';
-import {App, globals} from './globals';
 import {RecordConfig} from './record_config_types';
 import {Consumer, RpcConsumerPort} from './record_controller_interfaces';
 
@@ -177,7 +176,6 @@
 }
 
 export class RecordController extends Controller<'main'> implements Consumer {
-  private app: App;
   private config: RecordConfig|null = null;
   private readonly extensionPort: MessagePort;
   private recordingInProgress = false;
@@ -194,9 +192,8 @@
   // char, it is the 'targetOS'
   private controllerPromises = new Map<string, Promise<RpcConsumerPort>>();
 
-  constructor(args: {app: App, extensionPort: MessagePort}) {
+  constructor(args: {extensionPort: MessagePort}) {
     super('main');
-    this.app = args.app;
     this.consumerPort = ConsumerPort.create(this.rpcImpl.bind(this));
     this.extensionPort = args.extensionPort;
   }
@@ -204,22 +201,21 @@
   run() {
     // TODO(eseckler): Use ConsumerPort's QueryServiceState instead
     // of posting a custom extension message to retrieve the category list.
-    if (this.app.state.fetchChromeCategories && !this.fetchedCategories) {
+    if (globals.state.fetchChromeCategories && !this.fetchedCategories) {
       this.fetchedCategories = true;
-      if (this.app.state.extensionInstalled) {
+      if (globals.state.extensionInstalled) {
         this.extensionPort.postMessage({method: 'GetCategories'});
       }
-      frontendGlobals.dispatch(
-          Actions.setFetchChromeCategories({fetch: false}));
+      globals.dispatch(Actions.setFetchChromeCategories({fetch: false}));
     }
-    if (this.app.state.recordConfig === this.config &&
-        this.app.state.recordingInProgress === this.recordingInProgress) {
+    if (globals.state.recordConfig === this.config &&
+        globals.state.recordingInProgress === this.recordingInProgress) {
       return;
     }
-    this.config = this.app.state.recordConfig;
+    this.config = globals.state.recordConfig;
 
     const configProto =
-        genConfigProto(this.config, this.app.state.recordingTarget);
+        genConfigProto(this.config, globals.state.recordingTarget);
     const configProtoText = toPbtxt(configProto);
     const configProtoBase64 = base64Encode(configProto);
     const commandline = `
@@ -229,7 +225,7 @@
       adb pull /data/misc/perfetto-traces/trace /tmp/trace
     `;
     const traceConfig =
-        convertToRecordingV2Input(this.config, this.app.state.recordingTarget);
+        convertToRecordingV2Input(this.config, globals.state.recordingTarget);
     // TODO(hjd): This should not be TrackData after we unify the stores.
     publishTrackData({
       id: 'config',
@@ -243,8 +239,8 @@
 
     // If the recordingInProgress boolean state is different, it means that we
     // have to start or stop recording a trace.
-    if (this.app.state.recordingInProgress === this.recordingInProgress) return;
-    this.recordingInProgress = this.app.state.recordingInProgress;
+    if (globals.state.recordingInProgress === this.recordingInProgress) return;
+    this.recordingInProgress = globals.state.recordingInProgress;
 
     if (this.recordingInProgress) {
       this.startRecordTrace(traceConfig);
@@ -304,15 +300,15 @@
 
   onTraceComplete() {
     this.consumerPort.freeBuffers({});
-    frontendGlobals.dispatch(Actions.setRecordingStatus({status: undefined}));
+    globals.dispatch(Actions.setRecordingStatus({status: undefined}));
     if (globals.state.recordingCancelled) {
-      frontendGlobals.dispatch(
+      globals.dispatch(
           Actions.setLastRecordingError({error: 'Recording cancelled.'}));
       this.traceBuffer = [];
       return;
     }
     const trace = this.generateTrace();
-    frontendGlobals.dispatch(Actions.openTraceFromBuffer({
+    globals.dispatch(Actions.openTraceFromBuffer({
       title: 'Recorded trace',
       buffer: trace.buffer,
       fileName: `recorded_trace${this.recordedTraceSuffix}`,
@@ -348,13 +344,13 @@
   onError(message: string) {
     // TODO(octaviant): b/204998302
     console.error('Error in record controller: ', message);
-    frontendGlobals.dispatch(
+    globals.dispatch(
         Actions.setLastRecordingError({error: message.substr(0, 150)}));
-    frontendGlobals.dispatch(Actions.stopRecording({}));
+    globals.dispatch(Actions.stopRecording({}));
   }
 
   onStatus(message: string) {
-    frontendGlobals.dispatch(Actions.setRecordingStatus({status: message}));
+    globals.dispatch(Actions.setRecordingStatus({status: message}));
   }
 
   // Depending on the recording target, different implementation of the
@@ -417,7 +413,7 @@
       method: RPCImplMethod, requestData: Uint8Array,
       _callback: RPCImplCallback) {
     try {
-      const state = this.app.state;
+      const state = globals.state;
       // TODO(hjd): This is a bit weird. We implicity send each RPC message to
       // whichever target is currently selected (creating that target if needed)
       // it would be nicer if the setup/teardown was more explicit.
diff --git a/ui/src/controller/search_controller.ts b/ui/src/controller/search_controller.ts
index fde726d..d1df094 100644
--- a/ui/src/controller/search_controller.ts
+++ b/ui/src/controller/search_controller.ts
@@ -18,20 +18,18 @@
 import {escapeSearchQuery} from '../common/query_utils';
 import {CurrentSearchResults, SearchSummary} from '../common/search_data';
 import {TimeSpan} from '../common/time';
+import {toNs} from '../common/time';
+import {globals} from '../frontend/globals';
 import {publishSearch, publishSearchResult} from '../frontend/publish';
 
 import {Controller} from './controller';
-import {App} from './globals';
-import {toNs} from '../common/time';
 
 export interface SearchControllerArgs {
   engine: Engine;
-  app: App;
 }
 
 export class SearchController extends Controller<'main'> {
   private engine: Engine;
-  private app: App;
   private previousSpan: TimeSpan;
   private previousResolution: number;
   private previousSearch: string;
@@ -41,7 +39,6 @@
   constructor(args: SearchControllerArgs) {
     super('main');
     this.engine = args.engine;
-    this.app = args.app;
     this.previousSpan = new TimeSpan(0, 1);
     this.previousSearch = '';
     this.updateInProgress = false;
@@ -67,8 +64,8 @@
       return;
     }
 
-    const visibleState = this.app.state.frontendLocalState.visibleState;
-    const omniboxState = this.app.state.omniboxState;
+    const visibleState = globals.state.frontendLocalState.visibleState;
+    const omniboxState = globals.state.omniboxState;
     if (visibleState === undefined || omniboxState === undefined ||
         omniboxState.mode === 'COMMAND') {
       return;
@@ -210,7 +207,7 @@
     // TODO(hjd): we should avoid recomputing this every time. This will be
     // easier once the track table has entries for all the tracks.
     const cpuToTrackId = new Map();
-    for (const track of Object.values(this.app.state.tracks)) {
+    for (const track of Object.values(globals.state.tracks)) {
       if (track.kind === 'CpuSliceTrack') {
         cpuToTrackId.set((track.config as {cpu: number}).cpu, track.id);
         continue;
@@ -286,9 +283,9 @@
       if (it.source === 'cpu') {
         trackId = cpuToTrackId.get(it.sourceId);
       } else if (it.source === 'track') {
-        trackId = this.app.state.uiTrackIdByTraceTrackId[it.sourceId];
+        trackId = globals.state.uiTrackIdByTraceTrackId[it.sourceId];
       } else if (it.source === 'log') {
-        const logTracks = Object.values(this.app.state.tracks)
+        const logTracks = Object.values(globals.state.tracks)
                               .filter((t) => t.kind === 'AndroidLogTrack');
         if (logTracks.length > 0) {
           trackId = logTracks[0].id;
diff --git a/ui/src/controller/selection_controller.ts b/ui/src/controller/selection_controller.ts
index 4b4ee82..66cf3d0 100644
--- a/ui/src/controller/selection_controller.ts
+++ b/ui/src/controller/selection_controller.ts
@@ -22,9 +22,9 @@
   STR_NULL,
 } from '../common/query_result';
 import {ChromeSliceSelection} from '../common/state';
-import {translateState} from '../common/thread_state';
 import {fromNs, toNs} from '../common/time';
 import {SliceDetails, ThreadStateDetails} from '../frontend/globals';
+import {globals} from '../frontend/globals';
 import {
   publishCounterDetails,
   publishSliceDetails,
@@ -34,7 +34,6 @@
 
 import {parseArgs} from './args_parser';
 import {Controller} from './controller';
-import {globals} from './globals';
 
 export interface SelectionControllerArgs {
   engine: Engine;
@@ -311,19 +310,16 @@
     return trackId;
   }
 
+  // TODO(altimin): We currently rely on the ThreadStateDetails for supporting
+  // marking the area (the rest goes is handled by ThreadStateTab
+  // directly. Refactor it to be plugin-friendly and remove this.
   async threadStateDetails(id: number) {
     const query = `
       SELECT
         ts,
-        thread_state.dur as dur,
-        state,
-        io_wait as ioWait,
-        thread_state.utid as utid,
-        thread_state.cpu as cpu,
-        sched.id as id,
-        thread_state.blocked_function as blockedFunction
+        thread_state.dur as dur
       from thread_state
-      left join sched using(ts) where thread_state.id = ${id}
+      where thread_state.id = ${id}
     `;
     const result = await this.args.engine.query(query);
 
@@ -332,25 +328,11 @@
       const row = result.firstRow({
         ts: NUM,
         dur: NUM,
-        state: STR_NULL,
-        ioWait: NUM_NULL,
-        utid: NUM,
-        cpu: NUM_NULL,
-        id: NUM_NULL,
-        blockedFunction: STR_NULL,
       });
       const ts = row.ts;
       const timeFromStart = fromNs(ts) - globals.state.traceTime.startSec;
       const dur = fromNs(row.dur);
-      const ioWait = row.ioWait === null ? undefined : row.ioWait > 0;
-      const state = translateState(row.state || undefined, ioWait);
-      const utid = row.utid;
-      const cpu = row.cpu === null ? undefined : row.cpu;
-      const sliceId = row.id === null ? undefined : row.id;
-      const blockedFunction =
-          row.blockedFunction === null ? undefined : row.blockedFunction;
-      const selected: ThreadStateDetails =
-          {ts: timeFromStart, dur, state, utid, cpu, sliceId, blockedFunction};
+      const selected: ThreadStateDetails = {ts: timeFromStart, dur};
       publishThreadStateDetails(selected);
     }
   }
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index a6c8771..7fd41ed 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -32,7 +32,7 @@
 import {resetEngineWorker, WasmEngineProxy} from '../common/wasm_engine_proxy';
 import {BottomTabList} from '../frontend/bottom_tab';
 import {
-  globals as frontendGlobals,
+  globals,
   QuantizedLoad,
   ThreadDesc,
 } from '../frontend/globals';
@@ -77,14 +77,13 @@
   FlowEventsController,
   FlowEventsControllerArgs,
 } from './flow_events_controller';
-import {globals} from './globals';
 import {LoadingManager} from './loading_manager';
 import {LogsController} from './logs_controller';
 import {MetricsController} from './metrics_controller';
 import {
   PIVOT_TABLE_REDUX_FLAG,
-  PivotTableReduxController,
-} from './pivot_table_redux_controller';
+  PivotTableController,
+} from './pivot_table_controller';
 import {QueryController, QueryControllerArgs} from './query_controller';
 import {SearchController} from './search_controller';
 import {
@@ -207,7 +206,7 @@
       case 'init':
         this.loadTrace()
             .then((mode) => {
-              frontendGlobals.dispatch(Actions.setEngineReady({
+              globals.dispatch(Actions.setEngineReady({
                 engineId: this.engineId,
                 ready: true,
                 mode,
@@ -312,7 +311,7 @@
           app: globals,
         }));
         childControllers.push(
-          Child('pivot_table_redux', PivotTableReduxController, {engine}));
+            Child('pivot_table', PivotTableController, {engine}));
 
         childControllers.push(Child('logs', LogsController, {
           engine,
@@ -331,7 +330,7 @@
   }
 
   onDestroy() {
-    frontendGlobals.engines.delete(this.engineId);
+    globals.engines.delete(this.engineId);
   }
 
   private async loadTrace(): Promise<EngineMode> {
@@ -349,7 +348,7 @@
       engineMode = 'HTTP_RPC';
       engine = new HttpRpcEngine(this.engineId, LoadingManager.getInstance);
       engine.errorHandler = (err) => {
-        frontendGlobals.dispatch(
+        globals.dispatch(
             Actions.setEngineFailed({mode: 'HTTP_RPC', failure: `${err}`}));
         throw err;
       };
@@ -371,11 +370,10 @@
       this.engine.enableMetatrace(
         assertExists(getEnabledMetatracingCategories()));
     }
-    frontendGlobals.bottomTabList =
-      new BottomTabList(engine.getProxy('BottomTabList'));
+    globals.bottomTabList = new BottomTabList(engine.getProxy('BottomTabList'));
 
-    frontendGlobals.engines.set(this.engineId, engine);
-    frontendGlobals.dispatch(Actions.setEngineReady({
+    globals.engines.set(this.engineId, engine);
+    globals.dispatch(Actions.setEngineReady({
       engineId: this.engineId,
       ready: false,
       mode: engineMode,
@@ -445,7 +443,7 @@
     if (!shownJsonWarning) {
       // When in embedded mode, the host app will control which trace format
       // it passes to Perfetto, so we don't need to show this warning.
-      if (isJsonTrace && !frontendGlobals.embeddedMode) {
+      if (isJsonTrace && !globals.embeddedMode) {
         showJsonWarning();
         // Save that the warning has been shown. Value is irrelevant since only
         // the presence of key is going to be checked.
@@ -455,7 +453,7 @@
 
     const emptyOmniboxState = {
       omnibox: '',
-      mode: frontendGlobals.state.omniboxState.mode || 'SEARCH',
+      mode: globals.state.omniboxState.mode || 'SEARCH',
     };
 
     const actions: DeferredAction[] = [
@@ -476,7 +474,7 @@
       resolution,
     }));
 
-    frontendGlobals.dispatchMultiple(actions);
+    globals.dispatchMultiple(actions);
     Router.navigate(`#!/viewer?local_cache_key=${traceUuid}`);
 
     // Make sure the helper views are available before we start adding tracks.
@@ -510,9 +508,9 @@
       publishHasFtrace(hasFtrace);
     }
 
-    frontendGlobals.dispatch(Actions.removeDebugTrack({}));
-    frontendGlobals.dispatch(Actions.sortThreadTracks({}));
-    frontendGlobals.dispatch(Actions.maybeExpandOnlyTrackGroup({}));
+    globals.dispatch(Actions.removeDebugTrack({}));
+    globals.dispatch(Actions.sortThreadTracks({}));
+    globals.dispatch(Actions.maybeExpandOnlyTrackGroup({}));
 
     await this.selectFirstHeapProfile();
     if (PERF_SAMPLE_FLAG.get()) {
@@ -531,7 +529,7 @@
     if (!isJsonTrace && ENABLE_CHROME_RELIABLE_RANGE_ANNOTATION_FLAG.get()) {
       const reliableRangeStart = await computeTraceReliableRangeStart(engine);
       if (reliableRangeStart > 0) {
-        frontendGlobals.dispatch(Actions.addAutomaticNote({
+        globals.dispatch(Actions.addAutomaticNote({
           timestamp: reliableRangeStart,
           color: '#ff0000',
           text: 'Reliable Range Start',
@@ -554,7 +552,7 @@
     const upid = row.upid;
     const leftTs = toNs(globals.state.traceTime.startSec);
     const rightTs = toNs(globals.state.traceTime.endSec);
-    frontendGlobals.dispatch(Actions.selectPerfSamples(
+    globals.dispatch(Actions.selectPerfSamples(
         {id: 0, upid, leftTs, rightTs, type: ProfileType.PERF_SAMPLE}));
   }
 
@@ -576,15 +574,14 @@
     const ts = row.ts;
     const type = profileType(row.type);
     const upid = row.upid;
-    frontendGlobals.dispatch(
-        Actions.selectHeapProfile({id: 0, upid, ts, type}));
+    globals.dispatch(Actions.selectHeapProfile({id: 0, upid, ts, type}));
   }
 
   private async listTracks() {
     this.updateStatus('Loading tracks');
     const engine = assertExists<Engine>(this.engine);
     const actions = await decideTracks(this.engineId, engine);
-    frontendGlobals.dispatchMultiple(actions);
+    globals.dispatchMultiple(actions);
   }
 
   private async listThreads() {
@@ -777,7 +774,7 @@
     for (const it = metricsResult.iter({name: STR}); it.valid(); it.next()) {
       availableMetrics.push(it.name);
     }
-    frontendGlobals.dispatch(Actions.setAvailableMetrics({availableMetrics}));
+    globals.dispatch(Actions.setAvailableMetrics({availableMetrics}));
 
     const availableMetricsSet = new Set<string>(availableMetrics);
     for (const [flag, metric] of FLAGGED_METRICS) {
@@ -896,7 +893,7 @@
   }
 
   private updateStatus(msg: string): void {
-    frontendGlobals.dispatch(Actions.updateStatus({
+    globals.dispatch(Actions.updateStatus({
       msg,
       timestamp: Date.now() / 1000,
     }));
diff --git a/ui/src/controller/track_controller.ts b/ui/src/controller/track_controller.ts
index 9f8593a..cf9c3fd 100644
--- a/ui/src/controller/track_controller.ts
+++ b/ui/src/controller/track_controller.ts
@@ -18,11 +18,11 @@
 import {TraceTime, TrackState} from '../common/state';
 import {fromNs, toNs} from '../common/time';
 import {LIMIT, TrackData} from '../common/track_data';
+import {globals} from '../frontend/globals';
 import {publishTrackData} from '../frontend/publish';
 
 import {Controller} from './controller';
 import {ControllerFactory} from './controller';
-import {globals} from './globals';
 
 interface TrackConfig {}
 
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index 14a77e8..b577210 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -93,6 +93,8 @@
 const KERNEL_WAKELOCK_GROUP = 'Kernel wakelocks';
 const NETWORK_TRACK_REGEX = new RegExp('^.* (Received|Transmitted)( KB)?$');
 const NETWORK_TRACK_GROUP = 'Networking';
+const ENTITY_RESIDENCY_REGEX = new RegExp('^Entity residency: (.*)$');
+const ENTITY_RESIDENCY_GROUP = 'Entity residency';
 
 // Sets the default 'scale' for counter tracks. If the regex matches
 // then the paired mode is used. Entries are in priority order so the
@@ -103,7 +105,9 @@
   // value.
   [new RegExp('^power\..*$'), 'RATE'],
   // Same for network counters.
-  [new RegExp('^.* (Received|Transmitted) KB$'), 'RATE'],
+  [NETWORK_TRACK_REGEX, 'RATE'],
+  // Entity residency
+  [ENTITY_RESIDENCY_REGEX, 'RATE'],
 ];
 
 function getCounterScale(name: string): CounterScaleOptions|undefined {
@@ -628,11 +632,17 @@
     }
   }
 
-  async groupTracksByRegex(regex: RegExp, groupName: string): Promise<void> {
+  async groupTracksByRegex(
+      regex: RegExp, groupName: string,
+      renameToCapturingGroup?: number): Promise<void> {
     let groupUuid = undefined;
 
     for (const track of this.tracksToAdd) {
-      if (regex.test(track.name)) {
+      const matches = regex.exec(track.name);
+      if (matches !== null) {
+        if (renameToCapturingGroup) {
+          track.name = matches[renameToCapturingGroup];
+        }
         if (groupUuid === undefined) {
           groupUuid = uuidv4();
         }
@@ -1684,6 +1694,8 @@
     await this.groupGlobalBuddyInfoTracks();
     await this.groupTracksByRegex(KERNEL_WAKELOCK_REGEX, KERNEL_WAKELOCK_GROUP);
     await this.groupTracksByRegex(NETWORK_TRACK_REGEX, NETWORK_TRACK_GROUP);
+    await this.groupTracksByRegex(
+        ENTITY_RESIDENCY_REGEX, ENTITY_RESIDENCY_GROUP, 1);
 
     // Pre-group all kernel "threads" (actually processes) if this is a linux
     // system trace. Below, addProcessTrackGroups will skip them due to an
diff --git a/ui/src/controller/visualised_args_controller.ts b/ui/src/controller/visualised_args_controller.ts
index ec1d67c..8e709f5 100644
--- a/ui/src/controller/visualised_args_controller.ts
+++ b/ui/src/controller/visualised_args_controller.ts
@@ -18,13 +18,12 @@
 import {Engine} from '../common/engine';
 import {NUM} from '../common/query_result';
 import {InThreadTrackSortKey} from '../common/state';
-import {globals as frontendGlobals} from '../frontend/globals';
+import {globals} from '../frontend/globals';
 import {
   VISUALISED_ARGS_SLICE_TRACK_KIND,
 } from '../tracks/visualised_args/index';
 
 import {Controller} from './controller';
-import {globals} from './globals';
 
 export interface VisualisedArgControllerArgs {
   argName: string;
@@ -49,7 +48,7 @@
 
   onDestroy() {
     this.engine.query(`drop table if exists ${this.tableName}`);
-    frontendGlobals.dispatch(
+    globals.dispatch(
         Actions.removeVisualisedArgTracks({trackIds: this.addedTrackIds}));
   }
 
@@ -114,8 +113,8 @@
         },
       });
     }
-    frontendGlobals.dispatch(Actions.addTracks({tracks: tracksToAdd}));
-    frontendGlobals.dispatch(Actions.sortThreadTracks({}));
+    globals.dispatch(Actions.addTracks({tracks: tracksToAdd}));
+    globals.dispatch(Actions.sortThreadTracks({}));
   }
 
   run() {
diff --git a/ui/src/frontend/anchor.ts b/ui/src/frontend/anchor.ts
new file mode 100644
index 0000000..873f786
--- /dev/null
+++ b/ui/src/frontend/anchor.ts
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import * as m from 'mithril';
+
+interface AnchorAttrs {
+  // Optional icon to show at the end of the content.
+  icon?: string;
+  // Remaining items.
+  [htmlAttrs: string]: any;
+}
+
+export class Anchor implements m.ClassComponent<AnchorAttrs> {
+  view({attrs, children}: m.CVnode<AnchorAttrs>) {
+    const {icon, ...htmlAttrs} = attrs;
+
+    return m(
+        'a.pf-anchor',
+        htmlAttrs,
+        icon && m('i.material-icons', icon),
+        children,
+    );
+  }
+}
diff --git a/ui/src/frontend/base_slice_track.ts b/ui/src/frontend/base_slice_track.ts
index dd34c92..cd6f234 100644
--- a/ui/src/frontend/base_slice_track.ts
+++ b/ui/src/frontend/base_slice_track.ts
@@ -15,8 +15,12 @@
 import {assertExists} from '../base/logging';
 import {Actions} from '../common/actions';
 import {cropText, drawIncompleteSlice} from '../common/canvas_utils';
-import {colorCompare, colorToStr, GRAY_COLOR} from '../common/colorizer';
-import {NUM, QueryResult} from '../common/query_result';
+import {
+  colorCompare,
+  colorToStr,
+  UNEXPECTED_PINK_COLOR,
+} from '../common/colorizer';
+import {NUM} from '../common/query_result';
 import {SelectionKind} from '../common/state';
 import {fromNs, toNs} from '../common/time';
 
@@ -25,6 +29,7 @@
 import {Slice} from './slice';
 import {DEFAULT_SLICE_LAYOUT, SliceLayout} from './slice_layout';
 import {NewTrackArgs, SliceRect, Track} from './track';
+import {BUCKETS_PER_PIXEL, CacheKey, TrackCache} from './track_cache';
 
 // The common class that underpins all tracks drawing slices.
 
@@ -33,12 +38,9 @@
 
 // Slices smaller than this don't get any text:
 const SLICE_MIN_WIDTH_FOR_TEXT_PX = 5;
-// Slices smaller than this aren't rendered at all.
-const SLICE_MIN_WIDTH_PX = 0.1;
+const SLICE_MIN_WIDTH_PX = 1 / BUCKETS_PER_PIXEL;
 const CHEVRON_WIDTH_PX = 10;
-const DEFAULT_SLICE_COLOR = GRAY_COLOR;
-
-// TODO(hjd): Implement caching.
+const DEFAULT_SLICE_COLOR = UNEXPECTED_PINK_COLOR;
 
 // Exposed and standalone to allow for testing without making this
 // visible to subclasses.
@@ -131,11 +133,12 @@
 // If you need temporally overlapping slices, look at AsyncSliceTrack, which
 // merges several tracks into one visual track.
 export const BASE_SLICE_ROW = {
-  id: NUM,     // The slice ID, for selection / lookups.
-  tsq: NUM,    // Quantized |ts|. This class owns the quantization logic.
-  ts: NUM,     // Start time in nanoseconds.
-  dur: NUM,    // Duration in nanoseconds. -1 = incomplete, 0 = instant.
-  depth: NUM,  // Vertical depth.
+  id: NUM,      // The slice ID, for selection / lookups.
+  tsq: NUM,     // Quantized |ts|. This class owns the quantization logic.
+  tsqEnd: NUM,  // Quantized |ts+dur|. The end bucket.
+  ts: NUM,      // Start time in nanoseconds.
+  dur: NUM,     // Duration in nanoseconds. -1 = incomplete, 0 = instant.
+  depth: NUM,   // Vertical depth.
 };
 
 export type BaseSliceRow = typeof BASE_SLICE_ROW;
@@ -166,14 +169,17 @@
 export abstract class BaseSliceTrack<T extends BaseSliceTrackTypes =
                                                    BaseSliceTrackTypes> extends
     Track<T['config']> {
-  // This is the slice cache.
-  private slices = new Array<CastInternal<T['slice']>>();
   protected sliceLayout: SliceLayout = {...DEFAULT_SLICE_LAYOUT};
 
-  // These are the over-skirted cached bounds.
-  private slicesStartNs = -1;
-  private slicesEndNs = -1;
-  private slicesBucketNs = -1;
+  // This is the over-skirted cached bounds:
+  private slicesKey: CacheKey = CacheKey.zero();
+
+  // This is the currently 'cached' slices:
+  private slices = new Array<CastInternal<T['slice']>>();
+
+  // This is the slices cache:
+  private cache: TrackCache<Array<CastInternal<T['slice']>>> =
+      new TrackCache(5);
 
   private readonly tableName: string;
   private maxDurNs = 0;
@@ -216,7 +222,15 @@
   onSliceOver(_args: OnSliceOverArgs<T['slice']>): void {}
   onSliceOut(_args: OnSliceOutArgs<T['slice']>): void {}
   onSliceClick(_args: OnSliceClickArgs<T['slice']>): void {}
-  prepareSlices(slices: Array<T['slice']>): void {
+
+  // The API contract of onUpdatedSlices() is:
+  //  - I am going to draw these slices in the near future.
+  //  - I am not going to draw any slice that I haven't passed here first.
+  //  - This is guaranteed to be called at least once on every global
+  //    state update.
+  //  - This is NOT guaranteed to be called on every frame. For instance you
+  //    cannot use this to do some colour-based animation.
+  onUpdatedSlices(slices: Array<T['slice']>): void {
     this.highlightHovererdAndSameTitle(slices);
   }
 
@@ -227,6 +241,8 @@
   constructor(args: NewTrackArgs) {
     super(args);
     this.frontendOnly = true;  // Disable auto checkerboarding.
+    // TODO(hjd): Handle pinned tracks, which current cause a crash
+    // since the tableName we generate is the same for both.
     this.tableName = `track_${this.trackId}`.replace(/[^a-zA-Z0-9_]+/g, '_');
 
     // Work out the extra columns.
@@ -246,53 +262,29 @@
   }
 
   onFullRedraw(): void {
-    // TODO(hjd): Call this only when cache changes. See discussion:
-    // What we want to do here is give the Impl a chance to colour the slice,
-    // e.g. depending on the currently selected thread or process.
-    // Here's an interesting thought. We have two options here:
-    //   A) We could pass only the vizSlices, but then we'd have to call this
-    //      @ 60FPS (because vizSlices changes as we pan).
-    //   B) We could call this only on full redraws (when the state changes),
-    //      but then the track needs to process *all* cached slices, not just
-    //      the visible ones. It's okay now (it's a 2x factor) but might get
-    //      worse if we cache several layers of slices at various resolutions.
-    // But there's an escape, I think. I think the right thing to do is:
-    // - For now call it on the full slices, but only on full redraws.
-    // - When we get caching, call it every time we switch "cached quantization
-    //  level", which is a way in the middle between 60FPS and full redraws..
-    // Overall the API contract of this prepareSlices() call is:
-    //  - I am going to draw these slices in the near future.
-    //  - I am not going to draw any slice that I haven't passed here first.
-    //  - This is guaranteed to be called at least on every state change.
-    //  - This is NOT guaranteed to be called on every frame. For instance you
-    //    cannot use this to do some colour-based animation.
-
     // Give a chance to the embedder to change colors and other stuff.
-    this.prepareSlices(this.slices);
+    this.onUpdatedSlices(this.slices);
   }
 
   renderCanvas(ctx: CanvasRenderingContext2D): void {
     // TODO(hjd): fonts and colors should come from the CSS and not hardcoded
     // here.
-    const {timeScale} = globals.frontendLocalState;
+    const timeScale = globals.frontendLocalState.timeScale;
     const vizTime = globals.frontendLocalState.visibleWindowTime;
 
-    // If the visible time range is outside the cached area, requests
-    // asynchronously new data from the SQL engine.
-    this.maybeRequestData();
+    {
+      const windowSizePx = Math.max(1, timeScale.endPx - timeScale.startPx);
+      const rawStartNs = toNs(vizTime.start);
+      const rawEndNs = toNs(vizTime.end);
+      const rawSlicesKey = CacheKey.create(rawStartNs, rawEndNs, windowSizePx);
+
+      // If the visible time range is outside the cached area, requests
+      // asynchronously new data from the SQL engine.
+      this.maybeRequestData(rawSlicesKey);
+    }
 
     // In any case, draw whatever we have (which might be stale/incomplete).
 
-    // If the cached trace slices don't fully cover the visible time range,
-    // show a gray rectangle with a "Loading..." label.
-    checkerboardExcept(
-        ctx,
-        this.getHeight(),
-        timeScale.timeToPx(vizTime.start),
-        timeScale.timeToPx(vizTime.end),
-        timeScale.timeToPx(fromNs(this.slicesStartNs)),
-        timeScale.timeToPx(fromNs(this.slicesEndNs)));
-
     let charWidth = this.charWidth;
     if (charWidth < 0) {
       // TODO(hjd): Centralize font measurement/invalidation.
@@ -376,8 +368,9 @@
       } else if (slice.flags & SLICE_FLAGS_INCOMPLETE) {
         const w = Math.max(slice.w - 2, 2);
         drawIncompleteSlice(ctx, slice.x, y, w, sliceHeight);
-      } else if (slice.w > SLICE_MIN_WIDTH_PX) {
-        ctx.fillRect(slice.x, y, slice.w, sliceHeight);
+      } else {
+        const w = Math.max(slice.w, SLICE_MIN_WIDTH_PX);
+        ctx.fillRect(slice.x, y, w, sliceHeight);
       }
     }
 
@@ -428,6 +421,16 @@
       ctx.closePath();
     }
 
+    // If the cached trace slices don't fully cover the visible time range,
+    // show a gray rectangle with a "Loading..." label.
+    checkerboardExcept(
+        ctx,
+        this.getHeight(),
+        timeScale.timeToPx(vizTime.start),
+        timeScale.timeToPx(vizTime.end),
+        timeScale.timeToPx(fromNs(this.slicesKey.startNs)),
+        timeScale.timeToPx(fromNs(this.slicesKey.endNs)));
+
     // TODO(hjd): Remove this.
     // The only thing this does is drawing the sched latency arrow. We should
     // have some abstraction for that arrow (ideally the same we'd use for
@@ -443,7 +446,7 @@
       } else {
         this.drawTrackHoverTooltip(ctx, this.hoverPos, tooltip[0], tooltip[1]);
       }
-    }  // if (howSlice)
+    }  // if (hoveredSlice)
   }
 
   onDestroy() {
@@ -455,7 +458,7 @@
   // This method figures out if the visible window is outside the bounds of
   // the cached data and if so issues new queries (i.e. sorta subsumes the
   // onBoundsChange).
-  async maybeRequestData() {
+  private async maybeRequestData(rawSlicesKey: CacheKey) {
     // Important: this method is async and is invoked on every frame. Care
     // must be taken to avoid piling up queries on every frame, hence the FSM.
     if (this.sqlState === 'UNINITIALIZED') {
@@ -480,23 +483,39 @@
       return;
     }
 
-    const resolutionNs = toNs(globals.getCurResolution());
-    const vizTime = globals.frontendLocalState.visibleWindowTime;
-
-    const startNs = toNs(vizTime.start);
-    const endNs = toNs(vizTime.end);
-
-    // TODO(hjd): figure out / centralize the resolution steps.
-    // Will handle this at the same time as cacheing.
-    const bucketNs = resolutionNs;
-
-    if (startNs >= this.slicesStartNs && endNs <= this.slicesEndNs &&
-        bucketNs === this.slicesBucketNs) {
+    if (rawSlicesKey.isCoveredBy(this.slicesKey)) {
       return;  // We have the data already, no need to re-query
     }
 
+    // Determine the cache key:
+    const slicesKey = rawSlicesKey.normalize();
+    if (!rawSlicesKey.isCoveredBy(slicesKey)) {
+      throw new Error(`Normalization error ${slicesKey.toString()} ${
+          rawSlicesKey.toString()}`);
+    }
+
+    const maybeCachedSlices = this.cache.lookup(slicesKey);
+    if (maybeCachedSlices) {
+      this.slicesKey = slicesKey;
+      this.onUpdatedSlices(maybeCachedSlices);
+      this.slices = maybeCachedSlices;
+      return;
+    }
+
     this.sqlState = 'QUERY_PENDING';
-    const queryTsq = `(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs}`;
+    const bucketNs = slicesKey.bucketNs;
+    let queryTsq;
+    let queryTsqEnd;
+    // When we're zoomed into the level of single ns there is no point
+    // doing quantization (indeed it causes bad artifacts) so instead
+    // we use ts / ts+dur directly.
+    if (bucketNs === 1) {
+      queryTsq = 'ts';
+      queryTsqEnd = 'ts + dur';
+    } else {
+      queryTsq = `(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs}`;
+      queryTsqEnd = `(ts + dur + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs}`;
+    }
 
     const extraCols = this.extraSqlColumns.join(',');
     let depthCol = 'depth';
@@ -518,12 +537,16 @@
     // - Avoid the union if we know we don't have any -1 slices.
     // - Maybe we don't need the union at all and can deal in TS?
     if (this.isDestroyed) {
+      this.sqlState = 'QUERY_DONE';
       return;
     }
+    // TODO(hjd): Count and expose the number of slices summarized in
+    // each bucket?
     const queryRes = await this.engine.query(`
     with q1 as (
       select
         ${queryTsq} as tsq,
+        ${queryTsqEnd} as tsqEnd,
         ts,
         max(dur) as dur,
         id,
@@ -531,13 +554,14 @@
         ${extraCols ? ',' + extraCols : ''}
       from ${this.tableName}
       where
-        ts >= ${startNs - this.maxDurNs /* - durNs */} and
-        ts <= ${endNs /* + durNs */}
+        ts >= ${slicesKey.startNs - this.maxDurNs /* - durNs */} and
+        ts <= ${slicesKey.endNs /* + durNs */}
       group by ${maybeGroupByDepth} tsq
       order by tsq),
     q2 as (
       select
         ${queryTsq} as tsq,
+        ${queryTsqEnd} as tsqEnd,
         ts,
         -1 as dur,
         id,
@@ -552,32 +576,29 @@
       group by ${maybeGroupByDepth} tsq
       order by tsq
     `);
-    this.convertQueryResultToSlices(queryRes, startNs, endNs, bucketNs);
-    this.sqlState = 'QUERY_DONE';
-    globals.rafScheduler.scheduleRedraw();
-  }
 
-  // Here convert each row to a Slice. We do what we can do generically
-  // in the base class, and delegate the rest to the impl via that rowToSlice()
-  // abstract call.
-  convertQueryResultToSlices(
-      queryRes: QueryResult, startNs: number, endNs: number, bucketNs: number) {
+    // Here convert each row to a Slice. We do what we can do
+    // generically in the base class, and delegate the rest to the impl
+    // via that rowToSlice() abstract call.
     const slices = new Array<CastInternal<T['slice']>>(queryRes.numRows());
     const it = queryRes.iter(this.getRowSpec());
 
     let maxDataDepth = this.maxDataDepth;
-    this.slicesStartNs = startNs;
-    this.slicesEndNs = endNs;
-    this.slicesBucketNs = bucketNs;
+    this.slicesKey = slicesKey;
     for (let i = 0; it.valid(); it.next(), ++i) {
       maxDataDepth = Math.max(maxDataDepth, it.depth);
-
-      // Construct the base slice. The Impl will construct and return the full
-      // derived T["slice"] (e.g. CpuSlice) in the rowToSlice() method.
+      // Construct the base slice. The Impl will construct and return
+      // the full derived T["slice"] (e.g. CpuSlice) in the
+      // rowToSlice() method.
       slices[i] = this.rowToSliceInternal(it);
     }
     this.maxDataDepth = maxDataDepth;
+    this.onUpdatedSlices(slices);
+    this.cache.insert(slicesKey, slices);
     this.slices = slices;
+
+    this.sqlState = 'QUERY_DONE';
+    globals.rafScheduler.scheduleRedraw();
   }
 
   private rowToSliceInternal(row: T['row']): CastInternal<T['slice']> {
@@ -589,20 +610,13 @@
 
   rowToSlice(row: T['row']): T['slice'] {
     const startNsQ = row.tsq;
-    const startNs = row.ts;
+    const endNsQ = row.tsqEnd;
     let flags = 0;
-    let durNs: number;
     if (row.dur === -1) {
-      durNs = toNs(globals.state.traceTime.endSec) - startNs;
       flags |= SLICE_FLAGS_INCOMPLETE;
-    } else {
-      flags |= (row.dur === 0) ? SLICE_FLAGS_INSTANT : 0;
-      durNs = row.dur;
+    } else if (row.dur === 0) {
+      flags |= SLICE_FLAGS_INSTANT;
     }
-    const endNs = startNs + durNs;
-    const bucketNs = this.slicesBucketNs;
-    let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
-    endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
 
     return {
       id: row.id,
@@ -742,9 +756,9 @@
     ctx.fill();
   }
 
-  // This is a good default implemenation for highlighting slices. By default
-  // prepareSlices() calls this. However, if the XxxSliceTrack impl overrides
-  // prepareSlices() this gives them a chance to call the highlighting witout
+  // This is a good default implementation for highlighting slices. By default
+  // onUpdatedSlices() calls this. However, if the XxxSliceTrack impl overrides
+  // onUpdatedSlices() this gives them a chance to call the highlighting without
   // having to reimplement it.
   protected highlightHovererdAndSameTitle(slices: Slice[]) {
     for (const slice of slices) {
diff --git a/ui/src/frontend/base_slice_track_unittest.ts b/ui/src/frontend/base_slice_track_unittest.ts
index c5a0a36..7dd109d 100644
--- a/ui/src/frontend/base_slice_track_unittest.ts
+++ b/ui/src/frontend/base_slice_track_unittest.ts
@@ -89,6 +89,7 @@
   expect(filterVisibleSlices(
              [
                s(0, 100),
+
              ],
              10,
              90))
diff --git a/ui/src/frontend/chrome_slice_panel.ts b/ui/src/frontend/chrome_slice_panel.ts
index f5a45e8..c9d1f21 100644
--- a/ui/src/frontend/chrome_slice_panel.ts
+++ b/ui/src/frontend/chrome_slice_panel.ts
@@ -202,7 +202,8 @@
       }
 
       defaultBuilder.add(
-          'Slice ID', sliceInfo.id ? sliceInfo.id.toString() : 'Unknown');
+          'Slice ID',
+          (sliceInfo.id !== undefined) ? sliceInfo.id.toString() : 'Unknown');
       if (sliceInfo.description) {
         for (const [key, value] of sliceInfo.description) {
           defaultBuilder.add(key, value);
@@ -391,7 +392,7 @@
               contents));
         const value = row.contents.value;
         if (typeof value === 'string') {
-          renderedRow.push(m('td.value', value));
+          renderedRow.push(m('td.value', this.mayLinkify(value)));
         } else {
           // Type of value being a record is not propagated into the callback
           // for some reason, extracting necessary parts as constants instead.
@@ -422,4 +423,11 @@
 
     return m(`table.auto-layout${additionalClasses}`, rows);
   }
+
+  private mayLinkify(what: string): string|m.Vnode {
+    if (what.startsWith('http://') || what.startsWith('https://')) {
+      return m('a', {href: what, target: '_blank'}, what);
+    }
+    return what;
+  }
 }
diff --git a/ui/src/frontend/classnames.ts b/ui/src/frontend/classnames.ts
new file mode 100644
index 0000000..b2f6007
--- /dev/null
+++ b/ui/src/frontend/classnames.ts
@@ -0,0 +1,25 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// It's common to want to have a class depending on a boolean flag, in which
+// case we use `flag && className` which evaluates to either false or a string,
+// which is why false is included in definition of ArgType.
+type ArgType = string|false|undefined|ArgType[];
+
+// Join class names together into valid HTML class attributes
+// Falsey elements are ignored
+// Nested arrays are flattened
+export function classNames(...args: ArgType[]): string {
+  return args.flat().filter((x) => x).join(' ');
+}
diff --git a/ui/src/frontend/classnames_unittest.ts b/ui/src/frontend/classnames_unittest.ts
new file mode 100644
index 0000000..29546db
--- /dev/null
+++ b/ui/src/frontend/classnames_unittest.ts
@@ -0,0 +1,46 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {classNames} from './classnames';
+
+test('classnames', () => {
+  expect(classNames('foo', 'bar')).toEqual('foo bar');
+  expect(classNames('foo', '', 'bar')).toEqual('foo bar');
+  expect(classNames(false, 'foo', 'bar')).toEqual('foo bar');
+  expect(classNames(undefined, 'foo', 'bar')).toEqual('foo bar');
+  expect(classNames('foo', 'bar', ['baz', 'qux'])).toEqual('foo bar baz qux');
+  expect(classNames('foo bar', 'baz')).toEqual('foo bar baz');
+});
+
+test('example usecase with flags', () => {
+  const foo = true;
+  const bar = false;
+  const baz = true;
+  expect(classNames(
+             foo && 'foo',
+             bar && 'bar',
+             baz && 'baz',
+             ))
+      .toEqual('foo baz');
+});
+
+test('example usecase with possibly undefined classnames', () => {
+  let fooClass: string|undefined;
+  const barClass = 'bar';
+  expect(classNames(
+             fooClass,
+             barClass,
+             ))
+      .toEqual('bar');
+});
diff --git a/ui/src/frontend/details_panel.ts b/ui/src/frontend/details_panel.ts
index 9b7e617..2c73385 100644
--- a/ui/src/frontend/details_panel.ts
+++ b/ui/src/frontend/details_panel.ts
@@ -36,10 +36,10 @@
 import {LogPanel} from './logs_panel';
 import {NotesEditorTab} from './notes_panel';
 import {AnyAttrsVnode, PanelContainer} from './panel_container';
-import {PivotTableRedux} from './pivot_table_redux';
+import {PivotTable} from './pivot_table';
 import {QueryTable} from './query_table';
 import {SliceDetailsPanel} from './slice_details_panel';
-import {ThreadStatePanel} from './thread_state_panel';
+import {ThreadStateTab} from './thread_state_tab';
 
 const UP_ICON = 'keyboard_arrow_up';
 const DOWN_ICON = 'keyboard_arrow_down';
@@ -230,6 +230,15 @@
         });
       }
       break;
+    case 'THREAD_STATE':
+      bottomTabList.addTab({
+        kind: ThreadStateTab.kind,
+        tag: currentSelectionTag,
+        config: {
+          id: newSelection.id,
+        },
+      });
+      break;
     default:
       bottomTabList.closeTabByTag(currentSelectionTag);
   }
@@ -315,13 +324,6 @@
             vnode: m(ChromeSliceDetailsPanel, {key: 'chrome_slice'}),
           });
           break;
-        case 'THREAD_STATE':
-          detailsPanels.push({
-            key: 'current_selection',
-            name: 'Current Selection',
-            vnode: m(ThreadStatePanel, {key: 'thread_state'}),
-          });
-          break;
         default:
           break;
       }
@@ -353,15 +355,15 @@
     }
 
 
-    if (globals.state.nonSerializableState.pivotTableRedux.selectionArea !==
+    if (globals.state.nonSerializableState.pivotTable.selectionArea !==
         undefined) {
       detailsPanels.push({
-        key: 'pivot_table_redux',
+        key: 'pivot_table',
         name: 'Pivot Table',
-        vnode: m(PivotTableRedux, {
-          key: 'pivot_table_redux',
+        vnode: m(PivotTable, {
+          key: 'pivot_table',
           selectionArea:
-              globals.state.nonSerializableState.pivotTableRedux.selectionArea,
+              globals.state.nonSerializableState.pivotTable.selectionArea,
         }),
       });
     }
diff --git a/ui/src/frontend/frontend_local_state.ts b/ui/src/frontend/frontend_local_state.ts
index d406835..b24f69a 100644
--- a/ui/src/frontend/frontend_local_state.ts
+++ b/ui/src/frontend/frontend_local_state.ts
@@ -74,7 +74,6 @@
   scrollToTrackId?: string|number;
   httpRpcState: HttpRpcState = {connected: false};
   newVersionAvailable = false;
-  showPivotTable = false;
 
   // This is used to calculate the tracks within a Y range for area selection.
   areaY: Range = {};
@@ -126,11 +125,6 @@
     }
   }
 
-  togglePivotTable() {
-    this.showPivotTable = !this.showPivotTable;
-    globals.rafScheduler.scheduleFullRedraw();
-  }
-
   mergeState(state: FrontendState): void {
     // This is unfortunately subtle. This class mutates this._visibleState.
     // Since we may not mutate |state| (in order to make immer's immutable
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index 0e5de55..b49e6cd 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -114,11 +114,6 @@
 export interface ThreadStateDetails {
   ts?: number;
   dur?: number;
-  state?: string;
-  utid?: number;
-  cpu?: number;
-  sliceId?: number;
-  blockedFunction?: string;
 }
 
 export interface FlamegraphDetails {
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index 35cf958..94a1ec5 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -25,11 +25,10 @@
 import {onSelectionChanged} from '../common/selection_observer';
 import {State} from '../common/state';
 import {initWasm} from '../common/wasm_engine_proxy';
-import {ControllerWorkerInitMessage} from '../common/worker_messages';
 import {
   isGetCategoriesResponse,
 } from '../controller/chrome_proxy_record_controller';
-import {initController} from '../controller/index';
+import {initController, runControllers} from '../controller/index';
 
 import {AnalyzePage} from './analyze_page';
 import {initCssConstants} from './css_constants';
@@ -48,16 +47,15 @@
 import {TraceInfoPage} from './trace_info_page';
 import {maybeOpenTraceFromRoute} from './trace_url_handler';
 import {ViewerPage} from './viewer_page';
+import {WidgetsPage} from './widgets_page';
 
 const EXTENSION_ID = 'lfmkphfpdbjijhpomgecfikhfohaoine';
 
 class FrontendApi {
-  private port: MessagePort;
   private state: State;
 
-  constructor(port: MessagePort) {
+  constructor() {
     this.state = createEmptyState();
-    this.port = port;
   }
 
   dispatchMultiple(actions: DeferredAction[]) {
@@ -104,7 +102,11 @@
     }
 
     if (patches.length > 0) {
-      this.port.postMessage(patches);
+      // Need to avoid reentering the controller so move this to a
+      // separate task.
+      setTimeout(() => {
+        runControllers();
+      }, 0);
     }
   }
 
@@ -221,23 +223,11 @@
   window.addEventListener('error', (e) => reportError(e));
   window.addEventListener('unhandledrejection', (e) => reportError(e));
 
-  const controllerChannel = new MessageChannel();
   const extensionLocalChannel = new MessageChannel();
-  const errorReportingChannel = new MessageChannel();
-
-  errorReportingChannel.port2.onmessage = (e) =>
-      maybeShowErrorDialog(`${e.data}`);
-
-  const msg: ControllerWorkerInitMessage = {
-    controllerPort: controllerChannel.port1,
-    extensionPort: extensionLocalChannel.port1,
-    errorReportingPort: errorReportingChannel.port1,
-  };
 
   initWasm(globals.root);
   initializeImmerJs();
-
-  initController(msg);
+  initController(extensionLocalChannel.port1);
 
   const dispatch = (action: DeferredAction) => {
     frontendApi.dispatchMultiple([action]);
@@ -251,6 +241,7 @@
     '/flags': FlagsPage,
     '/metrics': MetricsPage,
     '/info': TraceInfoPage,
+    '/widgets': WidgetsPage,
   });
   router.onRouteChanged = (route) => {
     globals.rafScheduler.scheduleFullRedraw();
@@ -264,7 +255,7 @@
   globals.initialize(dispatch, router);
   globals.serviceWorkerController.install();
 
-  const frontendApi = new FrontendApi(controllerChannel.port2);
+  const frontendApi = new FrontendApi();
   globals.publishRedraw = () => globals.rafScheduler.scheduleFullRedraw();
 
   // We proxy messages between the extension and the controller because the
diff --git a/ui/src/frontend/overview_timeline_panel.ts b/ui/src/frontend/overview_timeline_panel.ts
index 578fa7f..adc06d9 100644
--- a/ui/src/frontend/overview_timeline_panel.ts
+++ b/ui/src/frontend/overview_timeline_panel.ts
@@ -85,7 +85,7 @@
 
     const timeScale = new TimeScale(timeSpan, [TRACK_SHELL_WIDTH, this.width]);
 
-    if (timeScale.widthPx > 0) {
+    if (timeScale.timeSpan.duration > 0 && timeScale.widthPx > 0) {
       const tickGen = new TickGenerator(timeScale);
 
       // Draw time labels on the top header.
diff --git a/ui/src/frontend/pivot_table_redux.ts b/ui/src/frontend/pivot_table.ts
similarity index 84%
rename from ui/src/frontend/pivot_table_redux.ts
rename to ui/src/frontend/pivot_table.ts
index f17bb24..f8e02a5 100644
--- a/ui/src/frontend/pivot_table_redux.ts
+++ b/ui/src/frontend/pivot_table.ts
@@ -23,36 +23,34 @@
 import {ColumnType} from '../common/query_result';
 import {
   Area,
-  PivotTableReduxAreaState,
-  PivotTableReduxResult,
+  PivotTableAreaState,
+  PivotTableResult,
   SortDirection,
 } from '../common/state';
 import {fromNs, timeToCode} from '../common/time';
 import {
-  PivotTableReduxController,
-} from '../controller/pivot_table_redux_controller';
+  PivotTableController,
+} from '../controller/pivot_table_controller';
 
 import {globals} from './globals';
-import {fullscreenModalContainer, ModalDefinition} from './modal';
 import {Panel} from './panel';
-import {AnyAttrsVnode} from './panel_container';
-import {ArgumentPopup} from './pivot_table_redux_argument_popup';
 import {
   aggregationIndex,
   areaFilter,
   extractArgumentExpression,
   sliceAggregationColumns,
   tables,
-} from './pivot_table_redux_query_generator';
+} from './pivot_table_query_generator';
 import {
   Aggregation,
   AggregationFunction,
   columnKey,
   PivotTree,
   TableColumn,
-} from './pivot_table_redux_types';
-import {PopupMenuButton, PopupMenuItem} from './popup_menu';
+} from './pivot_table_types';
+import {PopupMenuButton, popupMenuIcon, PopupMenuItem} from './popup_menu';
 import {ReorderableCell, ReorderableCellGroup} from './reorderable_cells';
+import {AttributeModalHolder} from './tables/attribute_modal_holder';
 
 
 interface PathItem {
@@ -60,8 +58,8 @@
   nextKey: ColumnType;
 }
 
-interface PivotTableReduxAttrs {
-  selectionArea: PivotTableReduxAreaState;
+interface PivotTableAttrs {
+  selectionArea: PivotTableAreaState;
 }
 
 interface DrillFilter {
@@ -107,12 +105,24 @@
   return '';
 }
 
-export class PivotTableRedux extends Panel<PivotTableReduxAttrs> {
+export class PivotTable extends Panel<PivotTableAttrs> {
+  constructor() {
+    super();
+    this.attributeModalHolder = new AttributeModalHolder((arg) => {
+      globals.dispatch(Actions.setPivotTablePivotSelected({
+        column: {kind: 'argument', argument: arg},
+        selected: true,
+      }));
+      globals.dispatch(
+          Actions.setPivotTableQueryRequested({queryRequested: true}));
+    });
+  }
+
   get pivotState() {
-    return globals.state.nonSerializableState.pivotTableRedux;
+    return globals.state.nonSerializableState.pivotTable;
   }
   get constrainToArea() {
-    return globals.state.nonSerializableState.pivotTableRedux.constrainToArea;
+    return globals.state.nonSerializableState.pivotTable.constrainToArea;
   }
 
   renderCanvas(): void {}
@@ -139,7 +149,7 @@
               // custom query is a temporary one, replace with a proper UI.
               globals.dispatch(Actions.executeQuery({
                 queryId: `pivot_table_details_${
-                    PivotTableReduxController.detailsCount++}`,
+                    PivotTableController.detailsCount++}`,
                 query,
               }));
             },
@@ -149,7 +159,7 @@
 
   renderSectionRow(
       area: Area, path: PathItem[], tree: PivotTree,
-      result: PivotTableReduxResult): m.Vnode {
+      result: PivotTableResult): m.Vnode {
     const renderedCells = [];
     for (let j = 0; j + 1 < path.length; j++) {
       renderedCells.push(m('td', m('span.indent', ' '), `${path[j].nextKey}`));
@@ -171,7 +181,7 @@
     renderedCells.push(
         m('td', {colspan}, button, `${path[path.length - 1].nextKey}`));
 
-    for (let i = 0; i < tree.aggregates.length; i++) {
+    for (let i = 0; i < result.metadata.aggregationColumns.length; i++) {
       const renderedValue = this.renderCell(
           result.metadata.aggregationColumns[i].column, tree.aggregates[i]);
       renderedCells.push(m('td' + markFirst(i), renderedValue));
@@ -200,8 +210,8 @@
   }
 
   renderTree(
-      area: Area, path: PathItem[], tree: PivotTree,
-      result: PivotTableReduxResult, sink: m.Vnode[]) {
+      area: Area, path: PathItem[], tree: PivotTree, result: PivotTableResult,
+      sink: m.Vnode[]) {
     if (tree.isCollapsed) {
       sink.push(this.renderSectionRow(area, path, tree, result));
       return;
@@ -251,12 +261,12 @@
     }
   }
 
-  renderTotalsRow(queryResult: PivotTableReduxResult) {
+  renderTotalsRow(queryResult: PivotTableResult) {
     const overallValuesRow =
         [m('td.total-values',
            {'colspan': queryResult.metadata.pivotColumns.length},
            m('strong', 'Total values:'))];
-    for (let i = 0; i < queryResult.tree.aggregates.length; i++) {
+    for (let i = 0; i < queryResult.metadata.aggregationColumns.length; i++) {
       overallValuesRow.push(
           m('td' + markFirst(i),
             this.renderCell(
@@ -328,8 +338,7 @@
       aggregation: Aggregation, index: number,
       removeItem: boolean): ReorderableCell {
     const popupItems: PopupMenuItem[] = [];
-    const state = globals.state.nonSerializableState.pivotTableRedux;
-    let icon = 'more_horiz';
+    const state = globals.state.nonSerializableState.pivotTable;
     if (aggregation.sortDirection === undefined) {
       popupItems.push(
           this.sortingItem(index, 'DESC'), this.sortingItem(index, 'ASC'));
@@ -338,10 +347,8 @@
       // opposite direction.
       popupItems.push(this.sortingItem(
           index, aggregation.sortDirection === 'DESC' ? 'ASC' : 'DESC'));
-      icon = aggregation.sortDirection === 'DESC' ? 'arrow_drop_down' :
-                                                    'arrow_drop_up';
     }
-    const otherAggs: AggregationFunction[] = ['SUM', 'MAX', 'MIN'];
+    const otherAggs: AggregationFunction[] = ['SUM', 'MAX', 'MIN', 'AVG'];
     if (aggregation.aggregationFunction !== 'COUNT') {
       for (const otherAgg of otherAggs) {
         if (aggregation.aggregationFunction === otherAgg) {
@@ -396,52 +403,23 @@
       content: [
         this.readableAggregationName(aggregation),
         m(PopupMenuButton, {
-          icon,
+          icon: popupMenuIcon(aggregation.sortDirection),
           items: popupItems,
         }),
       ],
     };
   }
 
-  showModal = false;
-  typedArgument = '';
-
-  renderModal(): ModalDefinition {
-    return {
-      title: 'Enter argument name',
-      content: m(ArgumentPopup, {
-                 knownArguments: globals.state.nonSerializableState
-                                     .pivotTableRedux.argumentNames,
-                 onArgumentChange: (arg) => {
-                   this.typedArgument = arg;
-                 },
-               }) as AnyAttrsVnode,
-      buttons: [
-        {
-          text: 'Add',
-          action: () => {
-            globals.dispatch(Actions.setPivotTablePivotSelected({
-              column: {kind: 'argument', argument: this.typedArgument},
-              selected: true,
-            }));
-            globals.dispatch(
-                Actions.setPivotTableQueryRequested({queryRequested: true}));
-          },
-        },
-      ],
-    };
-  }
+  attributeModalHolder: AttributeModalHolder;
 
   renderPivotColumnHeader(
-      queryResult: PivotTableReduxResult, pivot: TableColumn,
+      queryResult: PivotTableResult, pivot: TableColumn,
       selectedPivots: Set<string>): ReorderableCell {
     const items: PopupMenuItem[] = [{
       itemType: 'regular',
       text: 'Add argument pivot',
       callback: () => {
-        this.showModal = true;
-        this.typedArgument = '';
-        fullscreenModalContainer.createNew(this.renderModal());
+        this.attributeModalHolder.start();
       },
     }];
     if (queryResult.metadata.pivotColumns.length > 1) {
@@ -496,12 +474,12 @@
     };
   }
 
-  renderResultsTable(attrs: PivotTableReduxAttrs) {
-    const state = globals.state.nonSerializableState.pivotTableRedux;
+  renderResultsTable(attrs: PivotTableAttrs) {
+    const state = globals.state.nonSerializableState.pivotTable;
     if (state.queryResult === null) {
       return m('div', 'Loading...');
     }
-    const queryResult: PivotTableReduxResult = state.queryResult;
+    const queryResult: PivotTableResult = state.queryResult;
 
     const renderedRows: m.Vnode[] = [];
     const tree = state.queryResult.tree;
@@ -566,7 +544,7 @@
                       'Query data for the whole timeline' :
                       'Constrain to selected area',
                   callback: () => {
-                    globals.dispatch(Actions.setPivotTableReduxConstrainToArea(
+                    globals.dispatch(Actions.setPivotTableConstrainToArea(
                         {constrain: !state.constrainToArea}));
                     globals.dispatch(Actions.setPivotTableQueryRequested(
                         {queryRequested: true}));
@@ -576,11 +554,9 @@
         m('tbody', this.renderTotalsRow(state.queryResult), renderedRows));
   }
 
-  view({attrs}: m.Vnode<PivotTableReduxAttrs>): m.Children {
-    if (this.showModal) {
-      fullscreenModalContainer.updateVdom(this.renderModal());
-    }
+  view({attrs}: m.Vnode<PivotTableAttrs>): m.Children {
+    this.attributeModalHolder.update();
 
-    return m('.pivot-table-redux', this.renderResultsTable(attrs));
+    return m('.pivot-table', this.renderResultsTable(attrs));
   }
 }
diff --git a/ui/src/frontend/pivot_table_redux_argument_popup.ts b/ui/src/frontend/pivot_table_argument_popup.ts
similarity index 100%
rename from ui/src/frontend/pivot_table_redux_argument_popup.ts
rename to ui/src/frontend/pivot_table_argument_popup.ts
diff --git a/ui/src/frontend/pivot_table_redux_query_generator.ts b/ui/src/frontend/pivot_table_query_generator.ts
similarity index 91%
rename from ui/src/frontend/pivot_table_redux_query_generator.ts
rename to ui/src/frontend/pivot_table_query_generator.ts
index 4ff1c97..0c61f56 100644
--- a/ui/src/frontend/pivot_table_redux_query_generator.ts
+++ b/ui/src/frontend/pivot_table_query_generator.ts
@@ -17,8 +17,8 @@
 import {sqliteString} from '../base/string_utils';
 import {
   Area,
-  PivotTableReduxQuery,
-  PivotTableReduxState,
+  PivotTableQuery,
+  PivotTableState,
 } from '../common/state';
 import {toNs} from '../common/time';
 import {
@@ -29,7 +29,7 @@
 import {
   Aggregation,
   TableColumn,
-} from './pivot_table_redux_types';
+} from './pivot_table_types';
 
 export interface Table {
   name: string;
@@ -111,7 +111,7 @@
     case 'regular':
       return `${column.table}.${column.column}`;
     case 'argument':
-      return extractArgumentExpression(column.argument);
+      return extractArgumentExpression(column.argument, 'slice');
   }
 }
 
@@ -132,9 +132,8 @@
   return pivotColumns + aggregationNo;
 }
 
-export function generateQueryFromState(
-    state: PivotTableReduxState,
-    ): PivotTableReduxQuery {
+export function generateQueryFromState(state: PivotTableState):
+    PivotTableQuery {
   if (state.selectionArea === undefined) {
     throw new QueryGeneratorError('Should not be called without area');
   }
@@ -149,9 +148,11 @@
   const aggregations = sliceTableAggregations.map(
       (agg, index) =>
           `${aggregationExpression(agg)} as ${aggregationAlias(index)}`);
+  const countIndex = aggregations.length;
+  // Extra count aggregation, needed in order to compute combined averages.
+  aggregations.push('COUNT() as hidden_count');
 
-  const renderedPivots =
-      pivots.map((pivot) => `${pivot.table}.${pivot.column}`);
+  const renderedPivots = pivots.map(expression);
   const sortClauses: string[] = [];
   for (let i = 0; i < sliceTableAggregations.length; i++) {
     const sortDirection = sliceTableAggregations[i].sortDirection;
@@ -184,6 +185,7 @@
     metadata: {
       pivotColumns: pivots,
       aggregationColumns: sliceTableAggregations,
+      countIndex,
     },
   };
 }
diff --git a/ui/src/frontend/pivot_table_redux_types.ts b/ui/src/frontend/pivot_table_types.ts
similarity index 97%
rename from ui/src/frontend/pivot_table_redux_types.ts
rename to ui/src/frontend/pivot_table_types.ts
index 6daeb30..0e4913b 100644
--- a/ui/src/frontend/pivot_table_redux_types.ts
+++ b/ui/src/frontend/pivot_table_types.ts
@@ -31,7 +31,7 @@
   rows: ColumnType[][];
 }
 
-export type AggregationFunction = 'COUNT'|'SUM'|'MIN'|'MAX';
+export type AggregationFunction = 'COUNT'|'SUM'|'MIN'|'MAX'|'AVG';
 
 // Queried "table column" is either:
 // 1. A real one, represented as object with table and column name.
diff --git a/ui/src/frontend/popup_menu.ts b/ui/src/frontend/popup_menu.ts
index 863ddd1..cdb0dab 100644
--- a/ui/src/frontend/popup_menu.ts
+++ b/ui/src/frontend/popup_menu.ts
@@ -13,6 +13,9 @@
 // limitations under the License.
 
 import * as m from 'mithril';
+
+import {SortDirection} from '../common/state';
+
 import {globals} from './globals';
 
 export interface RegularPopupMenuItem {
@@ -23,6 +26,16 @@
   callback: () => void;
 }
 
+// Helper function for simplifying defining menus.
+export function menuItem(
+    text: string, action: () => void): RegularPopupMenuItem {
+  return {
+    itemType: 'regular',
+    text,
+    callback: action,
+  };
+}
+
 export interface GroupPopupMenuItem {
   itemType: 'group';
   text: string;
@@ -32,7 +45,7 @@
 
 export type PopupMenuItem = RegularPopupMenuItem|GroupPopupMenuItem;
 
-interface PopupMenuButtonAttrs {
+export interface PopupMenuButtonAttrs {
   // Icon for button opening a menu
   icon: string;
   // List of popup menu items
@@ -90,6 +103,21 @@
 // Singleton instance of PopupHolder
 const popupHolder = new PopupHolder();
 
+// For a table column that can be sorted; the standard popup icon should
+// reflect the current sorting direction. This function returns an icon
+// corresponding to optional SortDirection according to which the column is
+// sorted. (Optional because column might be unsorted)
+export function popupMenuIcon(sortDirection?: SortDirection) {
+  switch (sortDirection) {
+    case undefined:
+      return 'more_horiz';
+    case 'DESC':
+      return 'arrow_drop_down';
+    case 'ASC':
+      return 'arrow_drop_up';
+  }
+}
+
 // Component that displays a button that shows a popup menu on click.
 export class PopupMenuButton implements m.ClassComponent<PopupMenuButtonAttrs> {
   popupShown = false;
diff --git a/ui/src/frontend/slice_details_panel.ts b/ui/src/frontend/slice_details_panel.ts
index 23e54d2..7ae27c4 100644
--- a/ui/src/frontend/slice_details_panel.ts
+++ b/ui/src/frontend/slice_details_panel.ts
@@ -15,12 +15,9 @@
 import * as m from 'mithril';
 
 import {Actions} from '../common/actions';
-import {drawDoubleHeadedArrow} from '../common/canvas_utils';
 import {translateState} from '../common/thread_state';
 import {timeToCode, toNs} from '../common/time';
-
 import {globals, SliceDetails, ThreadDesc} from './globals';
-import {PanelSize} from './panel';
 import {scrollToTrackAndTs} from './scroll_helper';
 import {SlicePanel} from './slice_panel';
 
@@ -32,15 +29,72 @@
 
     return m(
         '.details-panel',
-        m('.details-panel-heading',
-          m('h2.split', `Slice Details`),
-          (sliceInfo.wakeupTs && sliceInfo.wakerUtid) ?
-              m('h2.split', 'Scheduling Latency') :
-              ''),
-        this.getDetails(sliceInfo, threadInfo));
+        m(
+            '.details-panel-heading',
+            m('h2.split', `Slice Details`),
+            this.hasSchedLatencyInfo(sliceInfo) &&
+                m('h2.split', 'Scheduling Latency'),
+            ),
+        this.renderDetails(sliceInfo, threadInfo));
   }
 
-  getDetails(sliceInfo: SliceDetails, threadInfo: ThreadDesc|undefined) {
+  private renderSchedLatencyInfo(sliceInfo: SliceDetails): m.Children {
+    if (!this.hasSchedLatencyInfo(sliceInfo)) {
+      return null;
+    }
+    return m(
+        '.half-width-panel.slice-details-latency-panel',
+        m('img.slice-details-image', {
+          src: `${globals.root}assets/scheduling_latency.png`,
+        }),
+        this.renderWakeupText(sliceInfo),
+        this.renderDisplayLatencyText(sliceInfo),
+    );
+  }
+
+  private renderWakeupText(sliceInfo: SliceDetails): m.Children {
+    if (sliceInfo.wakerUtid === undefined) {
+      return null;
+    }
+    const threadInfo = globals.threads.get(sliceInfo.wakerUtid!);
+    if (!threadInfo) {
+      return null;
+    }
+    const timestamp = timeToCode(
+        sliceInfo.wakeupTs! - globals.state.traceTime.startSec,
+    );
+    return m(
+        '.slice-details-wakeup-text',
+        m('', `Wakeup @ ${timestamp} on CPU ${sliceInfo.wakerCpu} by`),
+        m('', `P: ${threadInfo.procName} [${threadInfo.pid}]`),
+        m('', `T: ${threadInfo.threadName} [${threadInfo.tid}]`),
+    );
+  }
+
+  private renderDisplayLatencyText(sliceInfo: SliceDetails): m.Children {
+    if (sliceInfo.ts === undefined || sliceInfo.wakeupTs === undefined) {
+      return null;
+    }
+
+    const latency = timeToCode(
+        sliceInfo.ts - (sliceInfo.wakeupTs - globals.state.traceTime.startSec),
+    );
+    return m(
+        '.slice-details-latency-text',
+        m('', `Scheduling latency: ${latency}`),
+        m('.text-detail',
+          `This is the interval from when the task became eligible to run
+        (e.g. because of notifying a wait queue it was suspended on) to
+        when it started running.`),
+    );
+  }
+
+  private hasSchedLatencyInfo({wakeupTs, wakerUtid}: SliceDetails): boolean {
+    return wakeupTs !== undefined && wakerUtid !== undefined;
+  }
+
+  private renderDetails(sliceInfo: SliceDetails, threadInfo?: ThreadDesc):
+      m.Children {
     if (!threadInfo || sliceInfo.ts === undefined ||
         sliceInfo.dur === undefined) {
       return null;
@@ -74,7 +128,9 @@
           m('td', translateState(sliceInfo.endState))),
         m('tr',
           m('th', `Slice ID`),
-          m('td', sliceInfo.id ? sliceInfo.id.toString() : 'Unknown')),
+          m('td',
+            (sliceInfo.id !== undefined) ? sliceInfo.id.toString() :
+                                           'Unknown')),
       ];
 
       for (const [key, value] of this.getProcessThreadDetails(sliceInfo)) {
@@ -84,8 +140,9 @@
       }
 
       return m(
-          '.details-table',
+          '.details-table-multicolumn',
           m('table.half-width-panel', tableRows),
+          this.renderSchedLatencyInfo(sliceInfo),
       );
     }
   }
@@ -120,57 +177,5 @@
     }
   }
 
-
-  renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
-    const details = globals.sliceDetails;
-    // Show expanded details on the scheduling of the currently selected slice.
-    if (details.wakeupTs && details.wakerUtid !== undefined) {
-      const threadInfo = globals.threads.get(details.wakerUtid);
-      // Draw diamond and vertical line.
-      const startDraw = {x: size.width / 2 + 20, y: 52};
-      ctx.beginPath();
-      ctx.moveTo(startDraw.x, startDraw.y + 28);
-      ctx.fillStyle = 'black';
-      ctx.lineTo(startDraw.x + 6, startDraw.y + 20);
-      ctx.lineTo(startDraw.x, startDraw.y + 12);
-      ctx.lineTo(startDraw.x - 6, startDraw.y + 20);
-      ctx.fill();
-      ctx.closePath();
-      ctx.fillRect(startDraw.x - 1, startDraw.y, 2, 100);
-
-      // Wakeup explanation text.
-      ctx.font = '13px Roboto Condensed';
-      ctx.fillStyle = '#3c4b5d';
-      if (threadInfo) {
-        const displayText = `Wakeup @ ${
-            timeToCode(
-                details.wakeupTs - globals.state.traceTime.startSec)} on CPU ${
-            details.wakerCpu} by`;
-        const processText = `P: ${threadInfo.procName} [${threadInfo.pid}]`;
-        const threadText = `T: ${threadInfo.threadName} [${threadInfo.tid}]`;
-        ctx.fillText(displayText, startDraw.x + 20, startDraw.y + 20);
-        ctx.fillText(processText, startDraw.x + 20, startDraw.y + 37);
-        ctx.fillText(threadText, startDraw.x + 20, startDraw.y + 55);
-      }
-
-      // Draw latency arrow and explanation text.
-      drawDoubleHeadedArrow(ctx, startDraw.x, startDraw.y + 80, 60, true);
-      if (details.ts) {
-        const displayLatency = `Scheduling latency: ${
-            timeToCode(
-                details.ts -
-                (details.wakeupTs - globals.state.traceTime.startSec))}`;
-        ctx.fillText(displayLatency, startDraw.x + 70, startDraw.y + 86);
-        const explain1 =
-            'This is the interval from when the task became eligible to run';
-        const explain2 =
-            '(e.g. because of notifying a wait queue it was suspended on) to';
-        const explain3 = 'when it started running.';
-        ctx.font = '10px Roboto Condensed';
-        ctx.fillText(explain1, startDraw.x + 70, startDraw.y + 86 + 16);
-        ctx.fillText(explain2, startDraw.x + 70, startDraw.y + 86 + 16 + 12);
-        ctx.fillText(explain3, startDraw.x + 70, startDraw.y + 86 + 16 + 24);
-      }
-    }
-  }
+  renderCanvas() {}
 }
diff --git a/ui/src/frontend/sql_types.ts b/ui/src/frontend/sql_types.ts
new file mode 100644
index 0000000..a2159e3
--- /dev/null
+++ b/ui/src/frontend/sql_types.ts
@@ -0,0 +1,67 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {fromNs} from '../common/time';
+import {globals} from './globals';
+
+// Type-safe aliases for various flavours of ints Trace Processor exposes
+// (e.g. timestamp or ids into a given SQL table) and functions to work with
+// them.
+//
+// These rely on TypeScript's type branding: extending a number with additional
+// compile-time-only type information, which prevents "implicit" conversions
+// between different ids.
+
+// Timestamp (in nanoseconds) in the same time domain as Trace Processor is
+// exposing.
+export type TPTimestamp = number&{
+  __type: 'TPTimestamp'
+}
+
+// TODO: unify this with common/time.ts.
+export function toTraceTime(ts: TPTimestamp): number {
+  return fromNs(ts) - globals.state.traceTime.startSec;
+}
+
+// Unique id for a process, id into |process| table.
+export type Upid = number&{
+  __type: 'Upid'
+}
+
+export function asUpid(v: number): Upid;
+export function asUpid(v?: number): Upid|undefined;
+export function asUpid(v?: number): Upid|undefined {
+  return v as (Upid | undefined);
+}
+
+// Unique id for a thread, id into |thread| table.
+export type Utid = number&{
+  __type: 'Utid'
+}
+
+export function asUtid(v: number): Utid;
+export function asUtid(v?: number): Utid|undefined;
+export function asUtid(v?: number): Utid|undefined {
+  return v as (Utid | undefined);
+}
+
+// Id into |sched| SQL table.
+export type SchedSqlId = number&{
+  __type: 'SchedSqlId'
+}
+
+// Id into |thread_state| SQL table.
+export type ThreadStateSqlId = number&{
+  __type: 'ThreadStateSqlId'
+}
diff --git a/ui/src/frontend/sql_utils.ts b/ui/src/frontend/sql_utils.ts
new file mode 100644
index 0000000..422e70f
--- /dev/null
+++ b/ui/src/frontend/sql_utils.ts
@@ -0,0 +1,58 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {SortDirection} from '../common/state';
+
+interface OrderClause {
+  fieldName: string;
+  direction?: SortDirection;
+}
+
+// Interface for defining constraints which can be passed to a SQL query.
+export interface SQLConstraints {
+  filters?: string[];
+  orderBy?: OrderClause[];
+  limit?: number;
+}
+
+// Formatting given constraints into a string which can be injected into
+// SQL query.
+export function constraintsToQueryFragment(c: SQLConstraints): string {
+  const result: string[] = [];
+  if (c.filters && c.filters.length > 0) {
+    result.push(`WHERE ${c.filters.join(' and ')}`);
+  }
+  if (c.orderBy && c.orderBy.length > 0) {
+    const orderBys = c.orderBy.map((clause) => {
+      const direction = clause.direction ? ` ${clause.direction}` : '';
+      return `${clause.fieldName}${direction}`;
+    });
+    result.push(`ORDER BY ${orderBys.join(', ')}`);
+  }
+  if (c.limit) {
+    result.push(`LIMIT ${c.limit}`);
+  }
+  return result.join('\n');
+}
+
+// Trace Processor returns number | null for NUM_NULL, while most of the UI
+// code uses number | undefined. This functions provides a short-hand
+// conversion.
+// TODO(altimin): Support NUM_UNDEFINED as a first-class citizen.
+export function fromNumNull(n: number|null): number|undefined {
+  if (n === null) {
+    return undefined;
+  }
+  return n;
+}
diff --git a/ui/src/frontend/sql_utils_unittest.ts b/ui/src/frontend/sql_utils_unittest.ts
new file mode 100644
index 0000000..0e5dc76
--- /dev/null
+++ b/ui/src/frontend/sql_utils_unittest.ts
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {constraintsToQueryFragment} from './sql_utils';
+
+// Clean up repeated whitespaces to allow for easier testing.
+function normalize(s: string): string {
+  return s.replace(/\s+/g, ' ');
+}
+
+test('constraintsToQueryFragment: where', () => {
+  expect(normalize(constraintsToQueryFragment({
+    filters: ['ts > 1000', 'dur != 0'],
+  }))).toEqual('WHERE ts > 1000 and dur != 0');
+});
+
+test('constraintsToQueryFragment: order by', () => {
+  expect(normalize(constraintsToQueryFragment({
+    orderBy: [{fieldName: 'name'}, {fieldName: 'count', direction: 'DESC'}],
+  }))).toEqual('ORDER BY name, count DESC');
+});
+
+test('constraintsToQueryFragment: limit', () => {
+  expect(normalize(constraintsToQueryFragment({limit: 3}))).toEqual('LIMIT 3');
+});
+
+test('constraintsToQueryFragment: all', () => {
+  expect(normalize(constraintsToQueryFragment({
+    filters: ['id != 1'],
+    orderBy: [{fieldName: 'ts'}],
+    limit: 1,
+  }))).toEqual('WHERE id != 1 ORDER BY ts LIMIT 1');
+});
diff --git a/ui/src/frontend/tables/attribute_modal_holder.ts b/ui/src/frontend/tables/attribute_modal_holder.ts
new file mode 100644
index 0000000..2ed2be9
--- /dev/null
+++ b/ui/src/frontend/tables/attribute_modal_holder.ts
@@ -0,0 +1,71 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import * as m from 'mithril';
+
+import {globals} from '../globals';
+import {fullscreenModalContainer, ModalDefinition} from '../modal';
+import {AnyAttrsVnode} from '../panel_container';
+import {ArgumentPopup} from '../pivot_table_argument_popup';
+
+export class AttributeModalHolder {
+  showModal = false;
+  typedArgument = '';
+
+  callback: (arg: string) => void;
+
+  constructor(callback: (arg: string) => void) {
+    this.callback = callback;
+  }
+
+  start() {
+    this.showModal = true;
+    fullscreenModalContainer.createNew(this.renderModal());
+    globals.rafScheduler.scheduleFullRedraw();
+  }
+
+  private renderModal(): ModalDefinition {
+    return {
+      title: 'Enter argument name',
+      content:
+          m(ArgumentPopup, {
+            knownArguments:
+                globals.state.nonSerializableState.pivotTable.argumentNames,
+            onArgumentChange: (arg) => {
+              this.typedArgument = arg;
+            },
+          }) as AnyAttrsVnode,
+      buttons: [
+        {
+          text: 'Add',
+          action: () => {
+            this.callback(this.typedArgument);
+            this.typedArgument = '';
+          },
+        },
+      ],
+      onClose: () => {
+        this.showModal = false;
+      },
+    };
+  }
+
+  // A method that should be called in `view` method of whatever component is
+  // using the attribute modal.
+  update() {
+    if (this.showModal) {
+      fullscreenModalContainer.updateVdom(this.renderModal());
+    }
+  }
+}
diff --git a/ui/src/frontend/tables/table.ts b/ui/src/frontend/tables/table.ts
new file mode 100644
index 0000000..5a6c099
--- /dev/null
+++ b/ui/src/frontend/tables/table.ts
@@ -0,0 +1,233 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use size file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import * as m from 'mithril';
+
+import {allUnique, range} from '../../base/array_utils';
+import {
+  compareUniversal,
+  comparingBy,
+  ComparisonFn,
+  SortableValue,
+  SortDirection,
+  withDirection,
+} from '../../base/comparison_utils';
+import {globals} from '../globals';
+import {
+  menuItem,
+  PopupMenuButton,
+  popupMenuIcon,
+  PopupMenuItem,
+} from '../popup_menu';
+
+export interface ColumnDescriptorAttrs<T> {
+  // Context menu items displayed on the column header.
+  contextMenu?: PopupMenuItem[];
+
+  // Unique column ID, used to identify which column is currently sorted.
+  columnId?: string;
+
+  // Sorting predicate: if provided, column would be sortable.
+  ordering?: ComparisonFn<T>;
+
+  // Simpler way to provide a sorting: instead of full predicate, the function
+  // can map the row for "sorting key" associated with the column.
+  sortKey?: (value: T) => SortableValue;
+}
+
+export class ColumnDescriptor<T> {
+  name: string;
+  render: (row: T) => m.Child;
+  id: string;
+  contextMenu?: PopupMenuItem[];
+  ordering?: ComparisonFn<T>;
+
+  constructor(
+      name: string, render: (row: T) => m.Child,
+      attrs?: ColumnDescriptorAttrs<T>) {
+    this.name = name;
+    this.render = render;
+    this.id = attrs?.columnId === undefined ? name : attrs.columnId;
+
+    if (attrs === undefined) {
+      return;
+    }
+
+    if (attrs.sortKey !== undefined && attrs.ordering !== undefined) {
+      throw new Error('only one way to order a column should be specified');
+    }
+
+    if (attrs.sortKey !== undefined) {
+      this.ordering = comparingBy(attrs.sortKey, compareUniversal);
+    }
+    if (attrs.ordering !== undefined) {
+      this.ordering = attrs.ordering;
+    }
+  }
+}
+
+export function numberColumn<T>(
+    name: string, getter: (t: T) => number, contextMenu?: PopupMenuItem[]):
+    ColumnDescriptor<T> {
+  return new ColumnDescriptor<T>(name, getter, {contextMenu, sortKey: getter});
+}
+
+export function stringColumn<T>(
+    name: string, getter: (t: T) => string, contextMenu?: PopupMenuItem[]):
+    ColumnDescriptor<T> {
+  return new ColumnDescriptor<T>(name, getter, {contextMenu, sortKey: getter});
+}
+
+interface SortingInfo<T> {
+  columnId: string;
+  direction: SortDirection;
+  // TODO(ddrone): figure out if storing this can be avoided.
+  ordering: ComparisonFn<T>;
+}
+
+// Encapsulated table data, that contains the input to be displayed, as well as
+// some helper information to allow sorting.
+export class TableData<T> {
+  data: T[];
+  private _sortingInfo?: SortingInfo<T>;
+  private permutation: number[];
+
+  constructor(data: T[]) {
+    this.data = data;
+    this.permutation = range(data.length);
+  }
+
+  * iterateItems(): Generator<T> {
+    for (const index of this.permutation) {
+      yield this.data[index];
+    }
+  }
+
+  items(): T[] {
+    return Array.from(this.iterateItems());
+  }
+
+  setItems(newItems: T[]) {
+    this.data = newItems;
+    this.permutation = range(newItems.length);
+    if (this._sortingInfo !== undefined) {
+      this.reorder(this._sortingInfo);
+    }
+    globals.rafScheduler.scheduleFullRedraw();
+  }
+
+  resetOrder() {
+    this.permutation = range(this.data.length);
+    this._sortingInfo = undefined;
+    globals.rafScheduler.scheduleFullRedraw();
+  }
+
+  get sortingInfo(): SortingInfo<T>|undefined {
+    return this._sortingInfo;
+  }
+
+  reorder(info: SortingInfo<T>) {
+    this._sortingInfo = info;
+    this.permutation.sort(withDirection(
+        comparingBy((index: number) => this.data[index], info.ordering),
+        info.direction));
+    globals.rafScheduler.scheduleFullRedraw();
+  }
+}
+
+export interface TableAttrs<T> {
+  data: TableData<T>;
+  columns: ColumnDescriptor<T>[];
+}
+
+function directionOnIndex(
+    columnId: string, info?: SortingInfo<any>): SortDirection|undefined {
+  if (info === undefined) {
+    return undefined;
+  }
+  return info.columnId === columnId ? info.direction : undefined;
+}
+
+export class Table implements m.ClassComponent<TableAttrs<any>> {
+  renderColumnHeader(
+      vnode: m.Vnode<TableAttrs<any>>, column: ColumnDescriptor<any>): m.Child {
+    let currDirection: SortDirection|undefined = undefined;
+
+    let items = column.contextMenu;
+    if (column.ordering !== undefined) {
+      const ordering = column.ordering;
+      currDirection = directionOnIndex(column.id, vnode.attrs.data.sortingInfo);
+      const newItems: PopupMenuItem[] = [];
+      if (currDirection !== 'ASC') {
+        newItems.push(menuItem('Sort ascending', () => {
+          vnode.attrs.data.reorder(
+              {columnId: column.id, direction: 'ASC', ordering});
+        }));
+      }
+      if (currDirection !== 'DESC') {
+        newItems.push(menuItem('Sort descending', () => {
+          vnode.attrs.data.reorder({
+            columnId: column.id,
+            direction: 'DESC',
+            ordering,
+          });
+        }));
+      }
+      if (currDirection !== undefined) {
+        newItems.push(menuItem('Restore original order', () => {
+          vnode.attrs.data.resetOrder();
+        }));
+      }
+      items = [
+        ...newItems,
+        ...(items ?? []),
+      ];
+    }
+
+    return m(
+        'td', column.name, items === undefined ? null : m(PopupMenuButton, {
+          icon: popupMenuIcon(currDirection),
+          items,
+        }));
+  }
+
+  checkValid(attrs: TableAttrs<any>) {
+    if (!allUnique(attrs.columns.map((c) => c.id))) {
+      throw new Error('column IDs should be unique');
+    }
+  }
+
+  oncreate(vnode: m.VnodeDOM<TableAttrs<any>, this>) {
+    this.checkValid(vnode.attrs);
+  }
+
+  onupdate(vnode: m.VnodeDOM<TableAttrs<any>, this>) {
+    this.checkValid(vnode.attrs);
+  }
+
+  view(vnode: m.Vnode<TableAttrs<any>>): m.Child {
+    const attrs = vnode.attrs;
+
+    return m(
+        'table.generic-table',
+        m('thead',
+          m('tr.header',
+            attrs.columns.map(
+                (column) => this.renderColumnHeader(vnode, column)))),
+        attrs.data.items().map(
+            (row) =>
+                m('tr',
+                  attrs.columns.map((column) => m('td', column.render(row))))));
+  }
+}
diff --git a/ui/src/frontend/tables/table_showcase.ts b/ui/src/frontend/tables/table_showcase.ts
new file mode 100644
index 0000000..49fcebe
--- /dev/null
+++ b/ui/src/frontend/tables/table_showcase.ts
@@ -0,0 +1,70 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import * as m from 'mithril';
+
+import {
+  ColumnDescriptor,
+  numberColumn,
+  stringColumn,
+  Table,
+  TableData,
+} from './table';
+
+// This file serves as an example of a table component present in the widgets
+// showcase. Since table is somewhat complicated component that requires some
+// setup spread across several declarations, all the necessary code resides in a
+// separate file (this one) and provides a no-argument wrapper component that
+// can be used in the widgets showcase directly.
+
+interface ProgrammingLanguage {
+  id: number;
+  name: string;
+  year: number;
+}
+
+const languagesList: ProgrammingLanguage[] = [
+  {
+    id: 1,
+    name: 'TypeScript',
+    year: 2012,
+  },
+  {
+    id: 2,
+    name: 'JavaScript',
+    year: 1995,
+  },
+  {
+    id: 3,
+    name: 'Lean',
+    year: 2013,
+  },
+];
+
+const columns: ColumnDescriptor<ProgrammingLanguage>[] = [
+  numberColumn('ID', (x) => x.id),
+  stringColumn('Name', (x) => x.name),
+  numberColumn('Year', (x) => x.year),
+];
+
+export class TableShowcase implements m.ClassComponent {
+  data = new TableData(languagesList);
+
+  view(): m.Child {
+    return m(Table, {
+      data: this.data,
+      columns,
+    });
+  }
+}
diff --git a/ui/src/frontend/thread_and_process_info.ts b/ui/src/frontend/thread_and_process_info.ts
new file mode 100644
index 0000000..d524e02
--- /dev/null
+++ b/ui/src/frontend/thread_and_process_info.ts
@@ -0,0 +1,121 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {EngineProxy} from '../common/engine';
+import {NUM, NUM_NULL, STR, STR_NULL} from '../common/query_result';
+import {Upid, Utid} from './sql_types';
+import {fromNumNull} from './sql_utils';
+
+// Interface definitions for process and thread-related information
+// and functions to extract them from SQL.
+
+// TODO(altimin): Current implementation ends up querying process and thread
+// information separately for each thread. Given that there is a limited
+// numer of threads and processes, it might be easier to fetch this information
+// once when loading the trace and then just look it up synchronously.
+
+export interface ProcessInfo {
+  upid: Upid;
+  pid?: number;
+  name?: string;
+  uid?: number;
+  packageName?: string;
+  versionCode?: number;
+}
+
+async function getProcessInfo(
+    engine: EngineProxy, upid: Upid): Promise<ProcessInfo> {
+  const it = (await engine.query(`
+              SELECT pid, name, uid FROM process WHERE upid = ${upid};
+            `)).iter({pid: NUM, name: STR_NULL, uid: NUM_NULL});
+  if (!it.valid()) {
+    return {upid};
+  }
+  const result: ProcessInfo = {
+    upid,
+    pid: it.pid,
+    name: it.name || undefined,
+  };
+
+  if (it.pid === null) {
+    return result;
+  }
+  result.pid = it.pid || undefined;
+
+  if (it.uid === undefined) {
+    return result;
+  }
+
+  const packageResult = await engine.query(`
+                SELECT
+                  package_name as packageName,
+                  version_code as versionCode
+                FROM package_list WHERE uid = ${it.uid};
+              `);
+  // The package_list table is not populated in some traces so we need to
+  // check if the result has returned any rows.
+  if (packageResult.numRows() > 0) {
+    const packageDetails = packageResult.firstRow({
+      packageName: STR,
+      versionCode: NUM,
+    });
+    result.packageName = packageDetails.packageName;
+    result.versionCode = packageDetails.versionCode || undefined;
+  }
+  return result;
+}
+
+function getDisplayName(name: string|undefined, id: number|undefined): string|
+    undefined {
+  if (name === undefined) {
+    return id === undefined ? undefined : `${id}`;
+  }
+  return id === undefined ? name : `${name} [${id}]`;
+}
+
+export function getProcessName(info?: ProcessInfo): string|undefined {
+  return getDisplayName(info?.name, info?.pid);
+}
+
+export interface ThreadInfo {
+  utid: Utid;
+  tid?: number;
+  name?: string;
+  process?: ProcessInfo;
+}
+
+export async function getThreadInfo(
+    engine: EngineProxy, utid: Utid): Promise<ThreadInfo> {
+  const it = (await engine.query(`
+        SELECT tid, name, upid
+        FROM thread
+        WHERE utid = ${utid};
+    `)).iter({tid: NUM, name: STR_NULL, upid: NUM_NULL});
+  if (!it.valid()) {
+    return {
+      utid,
+    };
+  }
+  const upid = fromNumNull(it.upid) as (Upid | undefined);
+  return {
+    utid,
+    tid: it.tid,
+    name: it.name || undefined,
+    process: upid ? await getProcessInfo(engine, upid) : undefined,
+  };
+}
+
+export function getThreadName(info?: ThreadInfo): string|undefined {
+  return getDisplayName(info?.name, info?.tid);
+}
diff --git a/ui/src/frontend/thread_state.ts b/ui/src/frontend/thread_state.ts
new file mode 100644
index 0000000..1a4f558
--- /dev/null
+++ b/ui/src/frontend/thread_state.ts
@@ -0,0 +1,206 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use size file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {Actions} from '../common/actions';
+import {EngineProxy} from '../common/engine';
+import {NUM, NUM_NULL, STR_NULL} from '../common/query_result';
+import {translateState} from '../common/thread_state';
+import {fromNs, timeToCode} from '../common/time';
+
+import {copyToClipboard} from './clipboard';
+import {globals} from './globals';
+import {menuItem} from './popup_menu';
+import {scrollToTrackAndTs} from './scroll_helper';
+import {
+  asUtid,
+  SchedSqlId,
+  ThreadStateSqlId,
+  toTraceTime,
+  TPTimestamp,
+} from './sql_types';
+import {
+  constraintsToQueryFragment,
+  fromNumNull,
+  SQLConstraints,
+} from './sql_utils';
+import {
+  getProcessName,
+  getThreadInfo,
+  getThreadName,
+  ThreadInfo,
+} from './thread_and_process_info';
+import {dict, Dict, maybeValue, Value, value} from './value';
+
+// Representation of a single thread state object, corresponding to
+// a row for the |thread_slice| table.
+export interface ThreadState {
+  // Id into |thread_state| table.
+  threadStateSqlId: ThreadStateSqlId;
+  // Id of the corresponding entry in the |sched| table.
+  schedSqlId?: SchedSqlId;
+  // Timestamp of the the beginning of this thread state in nanoseconds.
+  ts: TPTimestamp;
+  // Duration of this thread state in nanoseconds.
+  dur: number;
+  // CPU id if this thread state corresponds to a thread running on the CPU.
+  cpu?: number;
+  // Human-readable name of this thread state.
+  state: string;
+  blockedFunction?: string;
+
+  thread?: ThreadInfo;
+  wakerThread?: ThreadInfo;
+}
+
+// Gets a list of thread state objects from Trace Processor with given
+// constraints.
+export async function getThreadStateFromConstraints(
+    engine: EngineProxy, constraints: SQLConstraints): Promise<ThreadState[]> {
+  const query = await engine.query(`
+    SELECT
+      thread_state.id as threadStateSqlId,
+      (select sched.id
+        from sched
+        where sched.ts=thread_state.ts and sched.utid=thread_state.utid
+        limit 1
+       ) as schedSqlId,
+      ts,
+      thread_state.dur as dur,
+      thread_state.cpu as cpu,
+      state,
+      thread_state.blocked_function as blockedFunction,
+      io_wait as ioWait,
+      thread_state.utid as utid,
+      waker_utid as wakerUtid
+    FROM thread_state
+    ${constraintsToQueryFragment(constraints)}`);
+  const it = query.iter({
+    threadStateSqlId: NUM,
+    schedSqlId: NUM_NULL,
+    ts: NUM,
+    dur: NUM,
+    cpu: NUM_NULL,
+    state: STR_NULL,
+    blockedFunction: STR_NULL,
+    ioWait: NUM_NULL,
+    utid: NUM,
+    wakerUtid: NUM_NULL,
+  });
+
+  const result: ThreadState[] = [];
+
+  for (; it.valid(); it.next()) {
+    const ioWait = it.ioWait === null ? undefined : it.ioWait > 0;
+    const wakerUtid = asUtid(it.wakerUtid || undefined);
+
+    // TODO(altimin): Consider fetcing thread / process info using a single
+    // query instead of one per row.
+    result.push({
+      threadStateSqlId: it.threadStateSqlId as ThreadStateSqlId,
+      schedSqlId: fromNumNull(it.schedSqlId) as (SchedSqlId | undefined),
+      ts: it.ts as TPTimestamp,
+      dur: it.dur,
+      cpu: fromNumNull(it.cpu),
+      state: translateState(it.state || undefined, ioWait),
+      blockedFunction: it.blockedFunction || undefined,
+      thread: await getThreadInfo(engine, asUtid(it.utid)),
+      wakerThread: wakerUtid ? await getThreadInfo(engine, wakerUtid) :
+                               undefined,
+    });
+  }
+  return result;
+}
+
+export async function getThreadState(
+    engine: EngineProxy, id: number): Promise<ThreadState|undefined> {
+  const result = await getThreadStateFromConstraints(engine, {
+    filters: [`id=${id}`],
+  });
+  if (result.length > 1) {
+    throw new Error(`thread_state table has more than one row with id ${id}`);
+  }
+  if (result.length === 0) {
+    return undefined;
+  }
+  return result[0];
+}
+
+export function goToSchedSlice(cpu: number, id: SchedSqlId, ts: TPTimestamp) {
+  let trackId: string|undefined;
+  for (const track of Object.values(globals.state.tracks)) {
+    if (track.kind === 'CpuSliceTrack' &&
+        (track.config as {cpu: number}).cpu === cpu) {
+      trackId = track.id;
+    }
+  }
+  if (trackId === undefined) {
+    return;
+  }
+  globals.makeSelection(Actions.selectSlice({id, trackId}));
+  scrollToTrackAndTs(trackId, ts);
+}
+
+function stateToValue(
+    state: string,
+    cpu: number|undefined,
+    id: SchedSqlId|undefined,
+    ts: TPTimestamp): Value|null {
+  if (!state) {
+    return null;
+  }
+  if (id === undefined || cpu === undefined) {
+    return value(state);
+  }
+  return value(`${state} on CPU ${cpu}`, {
+    rightButton: {
+      action: () => {
+        goToSchedSlice(cpu, id, ts);
+      },
+      hoverText: 'Go to CPU slice',
+    },
+  });
+}
+
+export function threadStateToDict(state: ThreadState): Dict {
+  const result: {[name: string]: Value|null} = {};
+
+  result['Start time'] = value(timeToCode(toTraceTime(state.ts)));
+  result['Duration'] = value(timeToCode(fromNs(state.dur)));
+  result['State'] =
+      stateToValue(state.state, state.cpu, state.schedSqlId, state.ts);
+  result['Blocked function'] = maybeValue(state.blockedFunction);
+  const process = state?.thread?.process;
+  result['Process'] = maybeValue(process ? getProcessName(process) : undefined);
+  const thread = state?.thread;
+  result['Thread'] = maybeValue(thread ? getThreadName(thread) : undefined);
+  if (state.wakerThread) {
+    const process = state.wakerThread.process;
+    result['Waker'] = dict({
+      'Process': maybeValue(process ? getProcessName(process) : undefined),
+      'Thread': maybeValue(getThreadName(state.wakerThread)),
+    });
+  }
+  result['SQL id'] = value(`thread_state[${state.threadStateSqlId}]`, {
+    contextMenu: [
+      menuItem(
+          'Copy SQL query',
+          () => {
+            copyToClipboard(`select * from thread_state where id=${
+                state.threadStateSqlId}`);
+          }),
+    ],
+  });
+
+  return dict(result);
+}
diff --git a/ui/src/frontend/thread_state_panel.ts b/ui/src/frontend/thread_state_panel.ts
deleted file mode 100644
index 337832a..0000000
--- a/ui/src/frontend/thread_state_panel.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use size file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import * as m from 'mithril';
-
-import {Actions} from '../common/actions';
-import {timeToCode, toNs} from '../common/time';
-
-import {globals} from './globals';
-import {Panel, PanelSize} from './panel';
-import {scrollToTrackAndTs} from './scroll_helper';
-
-
-export class ThreadStatePanel extends Panel {
-  view() {
-    const threadState = globals.threadStateDetails;
-    if (threadState === undefined || threadState.utid === undefined ||
-        threadState.ts === undefined || threadState.dur === undefined ||
-        threadState.state === undefined) {
-      return m('.details-panel');
-    }
-    const threadInfo = globals.threads.get(threadState.utid);
-    if (threadInfo) {
-      return m(
-          '.details-panel',
-          m('.details-panel-heading', m('h2', 'Thread State')),
-          m('.details-table', [m('table', [
-              m('tr',
-                m('th', `Start time`),
-                m('td', `${timeToCode(threadState.ts)}`)),
-              m('tr',
-                m('th', `Duration`),
-                m(
-                    'td',
-                    `${timeToCode(threadState.dur)} `,
-                    )),
-              m('tr',
-                m('th', `State`),
-                m('td',
-                  this.getStateContent(
-                      threadState.state,
-                      threadState.cpu,
-                      threadState.sliceId,
-                      threadState.ts))),
-              m('tr',
-                m('th', `Process`),
-                m('td', `${threadInfo.procName} [${threadInfo.pid}]`)),
-              this.getBlockedFunctionContent(threadState.blockedFunction),
-            ])]));
-    }
-    return m('.details-panel');
-  }
-
-  renderCanvas(_ctx: CanvasRenderingContext2D, _size: PanelSize) {}
-
-  // If it is the running state, we want to show which CPU and a button to
-  // go to the sched slice. Otherwise, just show the state.
-  getStateContent(
-      state: string, cpu: number|undefined, sliceId: number|undefined,
-      ts: number) {
-    if (sliceId === undefined || cpu === undefined) {
-      return [state];
-    }
-
-    return [
-      `${state} on CPU ${cpu}`,
-      m(
-          'i.material-icons.grey',
-          {
-            onclick: () => {
-              // TODO(hjd): Use trackId from TP.
-              let trackId;
-              for (const track of Object.values(globals.state.tracks)) {
-                if (track.kind === 'CpuSliceTrack' &&
-                    (track.config as {cpu: number}).cpu === cpu) {
-                  trackId = track.id;
-                }
-              }
-              if (trackId) {
-                globals.makeSelection(
-                    Actions.selectSlice({id: sliceId, trackId}));
-                scrollToTrackAndTs(
-                    trackId, toNs(ts + globals.state.traceTime.startSec));
-              }
-            },
-            title: 'Go to CPU slice',
-          },
-          'call_made'),
-    ];
-  }
-
-  getBlockedFunctionContent(blockedFunction: string|undefined) {
-    if (blockedFunction === undefined) {
-      return null;
-    }
-    return m('tr', m('th', `Blocked Function`), m('td', blockedFunction));
-  }
-}
diff --git a/ui/src/frontend/thread_state_tab.ts b/ui/src/frontend/thread_state_tab.ts
new file mode 100644
index 0000000..df8a775
--- /dev/null
+++ b/ui/src/frontend/thread_state_tab.ts
@@ -0,0 +1,75 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use size file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import * as m from 'mithril';
+
+import {BottomTab, bottomTabRegistry, NewBottomTabArgs} from './bottom_tab';
+import {globals} from './globals';
+import {ThreadStateSqlId} from './sql_types';
+import {getThreadState, ThreadState, threadStateToDict} from './thread_state';
+import {renderDict} from './value';
+
+interface ThreadStateTabConfig {
+  // Id into |thread_state| sql table.
+  readonly id: ThreadStateSqlId;
+}
+
+export class ThreadStateTab extends BottomTab<ThreadStateTabConfig> {
+  static readonly kind = 'org.perfetto.ThreadStateTab';
+
+  state?: ThreadState;
+  loaded: boolean = false;
+
+  static create(args: NewBottomTabArgs): ThreadStateTab {
+    return new ThreadStateTab(args);
+  }
+
+  constructor(args: NewBottomTabArgs) {
+    super(args);
+
+    getThreadState(this.engine, this.config.id).then((state?: ThreadState) => {
+      this.loaded = true;
+      this.state = state;
+      globals.rafScheduler.scheduleFullRedraw();
+    });
+  }
+
+  getTitle() {
+    // TODO(altimin): Support dynamic titles here.
+    return 'Current Selection';
+  }
+
+  renderTabContents(): m.Child {
+    if (!this.loaded) {
+      return m('h2', 'Loading');
+    }
+    if (!this.state) {
+      return m('h2', `Thread state ${this.config.id} does not exist`);
+    }
+    return renderDict(threadStateToDict(this.state));
+  }
+
+  viewTab() {
+    // TODO(altimin): Create a reusable component for showing the header and
+    // differentiate between "Current Selection" and "Pinned" views.
+    return m(
+        'div.details-panel',
+        m('header.overview', m('span', 'Thread State')),
+        this.renderTabContents());
+  }
+
+  renderTabCanvas(): void {}
+}
+
+bottomTabRegistry.register(ThreadStateTab);
diff --git a/ui/src/frontend/track_cache.ts b/ui/src/frontend/track_cache.ts
new file mode 100644
index 0000000..049cbf4
--- /dev/null
+++ b/ui/src/frontend/track_cache.ts
@@ -0,0 +1,177 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {assertTrue} from '../base/logging';
+
+export const BUCKETS_PER_PIXEL = 2;
+
+// CacheKey is a specific region of the timeline defined by the
+// following four properties:
+// - startNs
+// - endNs
+// - bucketNs
+// - windowSizePx
+// startNs is the beginning of the region in ns
+// endNs is the end of the region in ns
+// bucketNs is the size of a single bucket within the region which is
+//          used for quantizing the timeline.
+// windowSizePx is the size of the whole window in pixels.
+//
+// In the nominal case bucketNs is
+// set so that 1px of the screen corresponds to N bucketNs worth of
+// time where 1 < N < 10. This ensures that we show the maximum
+// amount of data given the available screen real estate.
+// We shouldn't rely on this property when rendering however since in
+// some situations (i.e. after zooming before new data has loaded) it
+// may not be the case.
+//
+// CacheKey's can be 'normalized' - rounding the interval up and the
+// bucket size down. For a given CacheKey key ('foo') the normalized
+// version ('normal') has the properties:
+//   normal.startNs <= foo.startNs
+//   normal.endNs => foo.endNs
+//   normal.bucketNs <= foo.bucketNs
+//   normal.windowSizePx ~= windowSizePx (we round to the nearest 100px)
+//   foo.isCoveredBy(foo) == true
+//   foo.isCoveredBy(normal) == true
+//   normal.isCoveredBy(normal) == true
+//   normal.isCoveredBy(foo) == false unless normal == foo
+//   normalize(normal) == normal
+//
+// In other words the normal window is a superset of the data of the
+// non-normal window at a higher resolution. Normalization is used to
+// avoid re-fetching data on tiny zooms/moves/resizes.
+export class CacheKey {
+  readonly startNs: number;
+  readonly endNs: number;
+  readonly bucketNs: number;
+  readonly windowSizePx: number;
+
+  static create(startNs: number, endNs: number, windowSizePx: number):
+      CacheKey {
+    const bucketNs = (endNs - startNs) / (windowSizePx * BUCKETS_PER_PIXEL);
+    return new CacheKey(startNs, endNs, bucketNs, windowSizePx);
+  }
+
+  private constructor(
+      startNs: number, endNs: number, bucketNs: number, windowSizePx: number) {
+    this.startNs = startNs;
+    this.endNs = endNs;
+    this.bucketNs = bucketNs;
+    this.windowSizePx = windowSizePx;
+  }
+
+  static zero(): CacheKey {
+    return new CacheKey(0, 0, 0, 100);
+  }
+
+  get normalizedBucketNs(): number {
+    // Round bucketNs down to the nearest smaller power of 2 (minimum 1):
+    return Math.max(1, Math.pow(2, Math.floor(Math.log2(this.bucketNs))));
+  }
+
+  get normalizedWindowSizePx(): number {
+    return Math.max(100, Math.round(this.windowSizePx / 100) * 100);
+  }
+
+  normalize(): CacheKey {
+    const windowSizePx = this.normalizedWindowSizePx;
+    const bucketNs = this.normalizedBucketNs;
+    const windowNs = windowSizePx * BUCKETS_PER_PIXEL * bucketNs;
+    const startNs = Math.floor(this.startNs / windowNs) * windowNs;
+    const endNs = Math.ceil(this.endNs / windowNs) * windowNs;
+    return new CacheKey(startNs, endNs, bucketNs, windowSizePx);
+  }
+
+  isNormalized(): boolean {
+    return this.toString() === this.normalize().toString();
+  }
+
+  isCoveredBy(other: CacheKey): boolean {
+    let r = true;
+    r = r && other.startNs <= this.startNs;
+    r = r && other.endNs >= this.endNs;
+    r = r && other.normalizedBucketNs === this.normalizedBucketNs;
+    r = r && other.normalizedWindowSizePx === this.normalizedWindowSizePx;
+    return r;
+  }
+
+  // toString is 'load bearing' in that it's used to key e.g. caches
+  // with CacheKey's.
+  toString() {
+    const start = this.startNs;
+    const end = this.endNs;
+    const bucket = this.bucketNs;
+    const size = this.windowSizePx;
+    return `CacheKey<${start}, ${end}, ${bucket}, ${size}>`;
+  }
+}
+
+
+interface CacheItem<T> {
+  t: T;
+  lastAccessId: number;
+}
+
+
+// LRU cache for the tracks.
+// T is all the data needed for a displaying the track in a given
+// CacheKey area - generally an array of slices.
+export class TrackCache<T> {
+  private cacheSize: number;
+  private cache: Map<string, CacheItem<T>>;
+  private lastAccessId: number;
+
+  constructor(cacheSize: number) {
+    assertTrue(cacheSize >= 2);
+    this.cacheSize = cacheSize;
+    this.cache = new Map();
+    this.lastAccessId = 0;
+  }
+
+  insert(cacheKey: CacheKey, t: T): void {
+    assertTrue(cacheKey.isNormalized());
+    const key = cacheKey.toString();
+    this.cache.set(key, {
+      t,
+      lastAccessId: this.lastAccessId++,
+    });
+    this.updateLru();
+  }
+
+  lookup(cacheKey: CacheKey): undefined|T {
+    assertTrue(cacheKey.isNormalized());
+    const key = cacheKey.toString();
+    const item = this.cache.get(key);
+    if (item) {
+      item.lastAccessId = this.lastAccessId++;
+      this.updateLru();
+    }
+    return item === undefined ? undefined : item.t;
+  }
+
+  private updateLru(): void {
+    while (this.cache.size > this.cacheSize) {
+      let oldestKey = '';
+      let oldestAccessId = Number.MAX_SAFE_INTEGER;
+      for (const [k, v] of this.cache.entries()) {
+        if (v.lastAccessId < oldestAccessId) {
+          oldestAccessId = v.lastAccessId;
+          oldestKey = k;
+        }
+      }
+      this.cache.delete(oldestKey);
+    }
+  }
+}
diff --git a/ui/src/frontend/track_cache_unittest.ts b/ui/src/frontend/track_cache_unittest.ts
new file mode 100644
index 0000000..546406a
--- /dev/null
+++ b/ui/src/frontend/track_cache_unittest.ts
@@ -0,0 +1,60 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {CacheKey, TrackCache} from './track_cache';
+
+test('cacheKeys', () => {
+  const k = CacheKey.create(201, 302, 123);
+  const n = k.normalize();
+  const n2 = n.normalize();
+  expect(k.isNormalized()).toEqual(false);
+  expect(n.isNormalized()).toEqual(true);
+  expect(n2.isNormalized()).toEqual(true);
+  expect(n).toEqual(n2);
+  expect(n.startNs).toBeLessThanOrEqual(k.startNs);
+  expect(n.endNs).toBeGreaterThanOrEqual(k.startNs);
+  expect(n.bucketNs).toBeGreaterThanOrEqual(k.bucketNs);
+  expect(Math.abs(n.windowSizePx - k.windowSizePx)).toBeLessThanOrEqual(200);
+});
+
+test('cache', () => {
+  const k1 = (CacheKey.create(1000, 1100, 100)).normalize();
+  const k2 = (CacheKey.create(2000, 2100, 100)).normalize();
+  const k3 = (CacheKey.create(3000, 3100, 100)).normalize();
+  const k4 = (CacheKey.create(4000, 4100, 100)).normalize();
+  const k5 = (CacheKey.create(5000, 5100, 100)).normalize();
+  const k6 = (CacheKey.create(6000, 6100, 100)).normalize();
+  const k7 = (CacheKey.create(7000, 7100, 100)).normalize();
+  const cache = new TrackCache<string>(5);
+
+  cache.insert(k1, 'v1');
+  expect(cache.lookup(k1)).toEqual('v1');
+
+  cache.insert(k2, 'v2');
+  cache.insert(k3, 'v3');
+  cache.insert(k4, 'v4');
+  cache.insert(k5, 'v5');
+
+  // Should push k1/v1 out of the cache:
+  cache.insert(k6, 'v6');
+  expect(cache.lookup(k1)).toEqual(undefined);
+
+  // Access k2 then add one more entry:
+  expect(cache.lookup(k2)).toEqual('v2');
+  cache.insert(k7, 'v7');
+
+  // k2/v2 should still be present but k3/v3 should be discarded:
+  expect(cache.lookup(k2)).toEqual('v2');
+  expect(cache.lookup(k3)).toEqual(undefined);
+});
diff --git a/ui/src/frontend/value.ts b/ui/src/frontend/value.ts
new file mode 100644
index 0000000..2f14c07
--- /dev/null
+++ b/ui/src/frontend/value.ts
@@ -0,0 +1,189 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use size file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import * as m from 'mithril';
+
+import {PopupMenuButton, PopupMenuItem} from './popup_menu';
+
+// This file implements a component for rendering JSON-like values (with
+// customisation options like context menu and action buttons).
+//
+// It defines the common Value, StringValue, DictValue, ArrayValue types,
+// to be used as an interchangeable format between different components
+// and `renderValue` function to convert DictValue into vdom nodes.
+
+// Leaf (non-dict and non-array) value which can be displayed to the user
+// together with the rendering customisation parameters.
+type StringValue = {
+  kind: 'STRING',
+  value: string,
+}&StringValueParams;
+
+// Helper function to create a StringValue from string together with optional
+// parameters.
+export function value(value: string, params?: StringValueParams): StringValue {
+  return {
+    kind: 'STRING',
+    value,
+    ...params,
+  };
+}
+
+// Helper function to convert a potentially undefined value to StringValue or
+// null.
+export function maybeValue(v?: string, params?: StringValueParams): StringValue|
+    null {
+  if (!v) {
+    return null;
+  }
+  return value(v, params);
+}
+
+// A basic type for the JSON-like value, comprising a primitive type (string)
+// and composite types (arrays and dicts).
+export type Value = StringValue|Array|Dict;
+
+// Dictionary type.
+export type Dict = {
+  kind: 'DICT',
+  items: {[name: string]: Value},
+}&ValueParams;
+
+// Helper function to simplify creation of an dictionary.
+// This function accepts and filters out nulls as values in the passed
+// dictionary (useful for simplifying the code to render optional values).
+export function dict(
+    items: {[name: string]: Value|null}, params?: ValueParams): Dict {
+  const result: {[name: string]: Value} = {};
+  for (const [name, value] of Object.entries(items)) {
+    if (value !== null) {
+      result[name] = value;
+    }
+  }
+  return {
+    kind: 'DICT',
+    items: result,
+    ...params,
+  };
+}
+
+// Array type.
+export type Array = {
+  kind: 'ARRAY', items: Value[];
+}&ValueParams;
+
+// Helper function to simplify creation of an array.
+// This function accepts and filters out nulls in the passed array (useful for
+// simplifying the code to render optional values).
+export function array(items: (Value|null)[], params?: ValueParams): Array {
+  return {
+    kind: 'ARRAY',
+    items: items.filter((item: Value|null) => item !== null) as Value[],
+    ...params,
+  };
+}
+
+// Parameters for displaying a button next to a value to perform
+// the context-dependent action (i.e. go to the corresponding slice).
+type ButtonParams = {
+  action: () => void;
+  hoverText?: string;
+  icon?: string;
+}
+
+// Customisation parameters which apply to any Value (e.g. context menu).
+interface ValueParams {
+  contextMenu?: PopupMenuItem[];
+}
+
+// Customisation parameters which apply for a primitive value (e.g. showing
+// button next to a string, or making it clickable, or adding onhover effect).
+interface StringValueParams extends ValueParams {
+  leftButton?: ButtonParams;
+  rightButton?: ButtonParams;
+}
+
+export function isArray(value: Value): value is Array {
+  return value.kind === 'ARRAY';
+};
+
+export function isDict(value: Value): value is Dict {
+  return value.kind === 'DICT';
+};
+
+export function isStringValue(value: Value): value is StringValue {
+  return !isArray(value) && !isDict(value);
+};
+
+// Recursively render the given value and its children, returning a list of
+// vnodes corresponding to the nodes of the table.
+function*
+    renderValue(name: string, value: Value, depth: number): Generator<m.Child> {
+  const row = [
+    m('th',
+      {
+        style: `padding-left: ${15 * depth}px`,
+      },
+      name,
+      value.contextMenu ? m(PopupMenuButton, {
+        icon: 'arrow_drop_down',
+        items: value.contextMenu,
+      }) :
+                          null),
+  ];
+  if (isArray(value)) {
+    yield m('tr', row);
+    for (let i = 0; i < value.items.length; ++i) {
+      yield* renderValue(`[${i}]`, value.items[i], depth + 1);
+    }
+    return;
+  } else if (isDict(value)) {
+    yield m('tr', row);
+    for (const key of Object.keys(value.items)) {
+      yield* renderValue(key, value.items[key], depth + 1);
+    }
+    return;
+  }
+  const renderButton = (button?: ButtonParams) => {
+    if (!button) {
+      return null;
+    }
+    return m(
+        'i.material-icons.grey',
+        {
+          onclick: button.action,
+          title: button.hoverText,
+        },
+        button.icon ? button.icon : 'call_made');
+  };
+  if (value.kind === 'STRING') {
+    row.push(
+        m('td',
+          renderButton(value.leftButton),
+          m('span', value.value),
+          renderButton(value.rightButton)));
+  }
+  yield m('tr', row);
+}
+
+// Render a given dictionary into a vnode.
+export function renderDict(dict: Dict): m.Child {
+  const rows: m.Child[] = [];
+  for (const key of Object.keys(dict.items)) {
+    for (const vnode of renderValue(key, dict.items[key], 0)) {
+      rows.push(vnode);
+    }
+  }
+  return m('table.auto-layout', rows);
+}
diff --git a/ui/src/frontend/widgets/button.ts b/ui/src/frontend/widgets/button.ts
new file mode 100644
index 0000000..2e6f3d6
--- /dev/null
+++ b/ui/src/frontend/widgets/button.ts
@@ -0,0 +1,83 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import * as m from 'mithril';
+import {classNames} from '../classnames';
+
+interface CommonAttrs {
+  // Always show the button as if the "active" pseudo class were applied, which
+  // makes the button look permanently pressed.
+  // Useful for when the button represents some toggleable state, such as
+  // showing/hiding a popup menu.
+  // Defaults to false.
+  active?: boolean;
+  // Use minimal padding, reducing the overall size of the button by a few px.
+  // Defaults to false.
+  compact?: boolean;
+  // Reduces button decorations.
+  // Defaults to false.
+  minimal?: boolean;
+  // Make the button appear greyed out block any interaction with it. No events
+  // will be fired.
+  // Defaults to false.
+  disabled?: boolean;
+  // Remaining attributes forwarded to the underlying HTML <button>.
+  [htmlAttrs: string]: any;
+}
+
+interface IconButtonAttrs extends CommonAttrs {
+  // Icon buttons require an icon.
+  icon: string;
+}
+
+interface LabelButtonAttrs extends CommonAttrs {
+  // Label buttons require a label.
+  label: string;
+  // Label buttons can have an optional icon.
+  icon?: string;
+}
+
+export type ButtonAttrs = LabelButtonAttrs|IconButtonAttrs;
+
+export class Button implements m.ClassComponent<ButtonAttrs> {
+  view({attrs}: m.CVnode<ButtonAttrs>) {
+    const {
+      label,
+      icon,
+      active = false,
+      compact = false,
+      minimal = false,
+      disabled = false,
+      ...htmlAttrs
+    } = attrs;
+
+    const classes = classNames(
+        'pf-button',
+        active && 'pf-active',
+        compact && 'pf-compact',
+        minimal && 'pf-minimal',
+        (icon && !label) && 'pf-icon-only',
+    );
+
+    return m(
+        'button' + (disabled ? '[disabled]' : ''),
+        {
+          class: classes,
+          ...htmlAttrs,
+        },
+        icon && m('i.material-icons', icon),
+        label || '\u200B',  // Zero width space keeps button in-flow
+    );
+  }
+}
diff --git a/ui/src/frontend/widgets/checkbox.ts b/ui/src/frontend/widgets/checkbox.ts
new file mode 100644
index 0000000..e9fa7d9
--- /dev/null
+++ b/ui/src/frontend/widgets/checkbox.ts
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import * as m from 'mithril';
+import {classNames} from '../classnames';
+
+export interface CheckboxAttrs {
+  // Optional text to show to the right of the checkbox.
+  label?: string;
+  // Whether the label is checked or not, defaults to false.
+  // If omitted, the checkbox will be uncontrolled.
+  checked?: boolean;
+  // Make the checkbox appear greyed out block any interaction with it. No
+  // events will be fired.
+  // Defaults to false.
+  disabled?: boolean;
+  // Remaining attributes forwarded to the underlying HTML <label>.
+  [htmlAttrs: string]: any;
+}
+
+export class Checkbox implements m.ClassComponent<CheckboxAttrs> {
+  view({attrs}: m.CVnode<CheckboxAttrs>) {
+    const {label, checked, disabled = false, ...htmlAttrs} = attrs;
+
+    const classes = classNames(
+        disabled && 'pf-disabled',
+    );
+
+    // The default checkbox is removed and an entirely new one created inside
+    // the span element in CSS.
+    return m(
+        'label.pf-checkbox',
+        {class: classes, ...htmlAttrs},
+        m('input[type=checkbox]', {disabled, checked}),
+        m('span'),
+        label,
+    );
+  }
+}
diff --git a/ui/src/frontend/widgets/empty_state.ts b/ui/src/frontend/widgets/empty_state.ts
new file mode 100644
index 0000000..255e9ff
--- /dev/null
+++ b/ui/src/frontend/widgets/empty_state.ts
@@ -0,0 +1,42 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import * as m from 'mithril';
+
+export interface EmptyStateAttrs {
+  // Which material icon to show.
+  // Defaults to 'search'.
+  icon?: string;
+  // Some text to show under the icon. No text shown if omitted.
+  header?: string;
+}
+
+// Something to show when there's nothing else to show!
+// Features a large icon, followed by some text explaining what went wrong, and
+// some optional content passed as children elements, usually containing common
+// actions for things you might want to do next (e.g. clear a search box).
+export class EmptyState implements m.ClassComponent<EmptyStateAttrs> {
+  view({attrs, children}: m.Vnode<EmptyStateAttrs, this>): void|m.Children {
+    const {
+      icon = 'search',  // Icon defaults to the search symbol
+      header,
+    } = attrs;
+    return m(
+        '.pf-empty-state',
+        m('i.material-icons', icon),
+        header && m('span.pf-empty-state-header', header),
+        m('div.pf-empty-state-content', children),
+    );
+  }
+}
diff --git a/ui/src/frontend/widgets/popup.ts b/ui/src/frontend/widgets/popup.ts
new file mode 100644
index 0000000..25ff7e7
--- /dev/null
+++ b/ui/src/frontend/widgets/popup.ts
@@ -0,0 +1,232 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {createPopper, Instance, OptionsGeneric} from '@popperjs/core';
+import type {StrictModifiers} from '@popperjs/core';
+import * as m from 'mithril';
+import {globals} from '../globals';
+import {Portal} from './portal';
+
+function isOrContains(container?: HTMLElement, target?: HTMLElement): boolean {
+  if (!container || !target) return false;
+  return container === target || container.contains(target);
+}
+
+function findRef(root: HTMLElement, ref: string): HTMLElement|undefined {
+  const query = `[ref=${ref}]`;
+  if (root.matches(query)) {
+    return root;
+  } else {
+    const result = root.querySelector(query);
+    Element;
+    return result ? result as HTMLElement : undefined;
+  }
+}
+
+// Note: We could just use the Placement type from popper.js instead, which is a
+// union of string literals corresponding to the values in this enum, but having
+// the emun makes it possible to enumerate the possible options, which is a
+// feature used in the widgets page.
+export enum PopupPosition {
+  Auto = 'auto',
+  AutoStart = 'auto-start',
+  AutoEnd = 'auto-end',
+  Top = 'top',
+  TopStart = 'top-start',
+  TopEnd = 'top-end',
+  Bottom = 'bottom',
+  BottomStart = 'bottom-start',
+  BottomEnd = 'bottom-end',
+  Right = 'right',
+  RightStart = 'right-start',
+  RightEnd = 'right-end',
+  Left = 'left',
+  LeftStart = 'left-start',
+  LeftEnd = 'left-end',
+}
+
+type OnChangeCallback = (shouldOpen: boolean) => void;
+
+export interface PopupAttrs {
+  // Which side of the trigger to place to popup.
+  // Defaults to "Auto"
+  position?: PopupPosition;
+  // The element used to open and close the popup, and the target which the near
+  // which the popup should hover.
+  // Beware this element will have its `onclick`, `ref`, and `active` attributes
+  // overwritten.
+  trigger: m.Vnode<any, any>;
+  // Close when the escape key is pressed
+  // Defaults to true.
+  closeOnEscape?: boolean;
+  // Close on mouse down somewhere other than the popup or trigger.
+  // Defaults to true.
+  closeOnOutsideClick?: boolean;
+  // Controls whether the popup is open or not.
+  // If omitted, the popup operates in uncontrolled mode.
+  isOpen?: boolean;
+  // Called when the popup isOpen state should be changed in controlled mode.
+  onChange?: OnChangeCallback;
+}
+
+// A popup is a portal whose position is dynamically updated so that it floats
+// next to a trigger element. It is also styled with a nice backdrop, and
+// a little arrow pointing at the trigger element.
+// Useful for displaying things like popup menus.
+export class Popup implements m.ClassComponent<PopupAttrs> {
+  private isOpen: boolean = false;
+  private triggerElement?: HTMLElement;
+  private popupElement?: HTMLElement;
+  private popper?: Instance;
+  private onChange: OnChangeCallback = (_) => {};
+  private closeOnEscape?: boolean;
+  private closeOnOutsideClick?: boolean;
+
+  private static readonly TRIGGER_REF = 'trigger';
+  private static readonly POPUP_REF = 'popup';
+
+  view({attrs, children}: m.CVnode<PopupAttrs>): m.Children {
+    const {
+      trigger,
+      isOpen = this.isOpen,
+      onChange = (_) => {},
+      closeOnEscape = true,
+      closeOnOutsideClick = true,
+    } = attrs;
+
+    this.isOpen = isOpen;
+    this.onChange = onChange;
+    this.closeOnEscape = closeOnEscape;
+    this.closeOnOutsideClick = closeOnOutsideClick;
+
+    return [
+      this.renderTrigger(trigger),
+      isOpen && this.renderPopup(attrs, children),
+    ];
+  }
+
+  private renderTrigger(trigger: m.Vnode<any, any>): m.Children {
+    trigger.attrs = {
+      ...trigger.attrs,
+      ref: Popup.TRIGGER_REF,
+      onclick: () => this.togglePopup(),
+      active: this.isOpen,
+    };
+    return trigger;
+  }
+
+  private renderPopup(attrs: PopupAttrs, children: any): m.Children {
+    const portalAttrs = {
+      onContentMount: (dom: HTMLElement) => {
+        this.popupElement = findRef(dom, Popup.POPUP_REF);
+        this.createOrUpdatePopper(attrs);
+        document.addEventListener('mousedown', this.handleDocMouseDown);
+        document.addEventListener('keydown', this.handleDocKeyPress);
+      },
+      onContentUpdate: () => {
+        // The content inside the portal has updated, so we call popper to
+        // recompute the popup's position, in case it has changed size.
+        this.popper && this.popper.update();
+      },
+      onContentUnmount: () => {
+        document.removeEventListener('keydown', this.handleDocKeyPress);
+        document.removeEventListener('mousedown', this.handleDocMouseDown);
+        this.popper && this.popper.destroy();
+        this.popper = undefined;
+        this.popupElement = undefined;
+      },
+    };
+    return m(
+        Portal,
+        portalAttrs,
+        m(
+            '.pf-popup',
+            {ref: Popup.POPUP_REF},
+            m('.pf-popup-arrow[data-popper-arrow]'),
+            children,
+            ),
+    );
+  }
+
+  oncreate({dom}: m.VnodeDOM<PopupAttrs, this>) {
+    this.triggerElement = findRef(dom as HTMLElement, Popup.TRIGGER_REF);
+  }
+
+  onupdate({attrs}: m.VnodeDOM<PopupAttrs, this>) {
+    // We might have some new popper options, or the trigger might have changed
+    // size, so we call popper to recompute the popup's position.
+    this.createOrUpdatePopper(attrs);
+  }
+
+  onremove(_: m.VnodeDOM<PopupAttrs, this>) {
+    this.triggerElement = undefined;
+  }
+
+  private createOrUpdatePopper(attrs: PopupAttrs) {
+    const {
+      position = PopupPosition.Auto,
+    } = attrs;
+
+    const options: Partial<OptionsGeneric<StrictModifiers>> = {
+      placement: position,
+      modifiers: [
+        // Move the popup away from the target allowing room for the arrow
+        {name: 'offset', options: {offset: [0, 8]}},
+        // Don't let the popup touch the edge of the viewport
+        {name: 'preventOverflow', options: {padding: 8}},
+        // Don't let the arrow reach the end of the popup, which looks odd when
+        // the popup has rounded corners
+        {name: 'arrow', options: {padding: 8}},
+      ],
+    };
+
+    if (this.popper) {
+      this.popper.setOptions(options);
+    } else {
+      if (this.popupElement && this.triggerElement) {
+        this.popper = createPopper<StrictModifiers>(
+            this.triggerElement, this.popupElement, options);
+      }
+    }
+  }
+
+  private handleDocMouseDown = (e: Event) => {
+    const target = e.target as HTMLElement;
+    const isClickOnTrigger = isOrContains(this.triggerElement, target);
+    const isClickOnPopup = isOrContains(this.popupElement, target);
+    if (this.closeOnOutsideClick && !isClickOnPopup && !isClickOnTrigger) {
+      this.closePopup();
+    }
+  };
+
+  private handleDocKeyPress = (e: KeyboardEvent) => {
+    if (this.closeOnEscape && e.key === 'Escape') {
+      this.closePopup();
+    }
+  };
+
+  private closePopup() {
+    if (this.isOpen) {
+      this.isOpen = false;
+      this.onChange(this.isOpen);
+      globals.rafScheduler.scheduleFullRedraw();
+    }
+  }
+
+  private togglePopup() {
+    this.isOpen = !this.isOpen;
+    this.onChange(this.isOpen);
+    globals.rafScheduler.scheduleFullRedraw();
+  }
+}
diff --git a/ui/src/frontend/widgets/portal.ts b/ui/src/frontend/widgets/portal.ts
new file mode 100644
index 0000000..62b83d8
--- /dev/null
+++ b/ui/src/frontend/widgets/portal.ts
@@ -0,0 +1,107 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import * as m from 'mithril';
+
+type Style = string|Partial<CSSStyleDeclaration>;
+
+export interface PortalAttrs {
+  // Inline styles forwarded to our portal container
+  style?: Style;
+  // Called after content is mounted in the portal
+  onContentMount?: (rootElement: HTMLElement) => void;
+  // Called after the content is unmounted from the portal
+  onContentUnmount?: () => void;
+  // Called when the content is updated
+  onContentUpdate?: () => void;
+}
+
+// A portal renders children into a a div outside of the normal hierarchy of the
+// parent component.
+// For now, this implementation adds a new div to the root of the document, and
+// mounts its children into this div.
+// The main reason for doing this is to draw a floating item over the top of the
+// other elements, such as a popover or a menu.
+// If there are no manually set z-indexes the portal would appear over the top
+// of the rest of the elements on the page by virtue of the fact that it's the
+// last element in the document.
+// If manual z-indexes have been set on other page elements, then this one's
+// z-index might need to be set to be above the others, but at least the
+// stacking contexts are easier to manage.
+export class Portal implements m.ClassComponent<PortalAttrs> {
+  private rootElement?: HTMLElement;
+  private component?: m.Component;
+
+  oncreate({attrs, children}: m.VnodeDOM<PortalAttrs, this>) {
+    const {
+      onContentUpdate = () => {},
+      onContentMount = (_) => {},
+    } = attrs;
+    // Create a new div, assigning styles from our attrs
+    const rootElement = document.createElement('div');
+    Object.assign(rootElement.style, attrs.style);
+
+    // Attach it to the body of the document
+    document.body.appendChild(rootElement);
+
+    this.rootElement = rootElement;
+
+    // Create a proxy component which just returns the children
+    this.component = {
+      view: () => children,
+      onupdate: () => onContentUpdate(),
+    };
+
+    // Mount this component in the root element
+    m.mount(this.rootElement, this.component);
+
+    // At this point, all children of the portal will have had their
+    // oncreate() function calls which the owner of portal has no visibility
+    // into so we can notify it through the callback if one exists
+    onContentMount(this.rootElement);
+  }
+
+  onbeforeupdate({children}: m.Vnode<PortalAttrs>) {
+    // We need to jump in before view is called to replace our pseudo view
+    // component, updating its children to ours...
+    if (!this.component) {
+      return false;
+    }
+    this.component.view = () => children;
+    return true;
+  }
+
+  onupdate({attrs}: m.VnodeDOM<PortalAttrs, this>) {
+    // We can now update the styles
+    Object.assign(this.rootElement!.style, attrs.style);
+  }
+
+  onremove({attrs}: m.VnodeDOM<PortalAttrs, this>) {
+    const {onContentUnmount = () => {}} = attrs;
+    // If the body contains our rootelement, unmount it (mount null?) and
+    // remove the div from the root element
+    if (document.body.contains(this.rootElement!)) {
+      m.mount(this.rootElement!, null);
+      document.body.removeChild(this.rootElement!);
+    }
+
+    onContentUnmount();
+  }
+
+  view(_: m.Vnode<PortalAttrs, this>): void|m.Children {
+    // We can return nothing here, we just want to get involved with
+    // lifecycle events
+    return null;
+  }
+}
diff --git a/ui/src/frontend/widgets/text_input.ts b/ui/src/frontend/widgets/text_input.ts
new file mode 100644
index 0000000..cd5f7e8
--- /dev/null
+++ b/ui/src/frontend/widgets/text_input.ts
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import * as m from 'mithril';
+
+export interface TextInputAttrs {
+  [htmlAttrs: string]: any;
+}
+
+// For now, this component is just a simple wrapper around a plain old input
+// element, which does no more than specify a class. However, in the future we
+// might want to add more features such as an optional icon or button (e.g. a
+// clear button), at which point the benefit of having this as a component would
+// become more apparent.
+export class TextInput implements m.ClassComponent<TextInputAttrs> {
+  view({attrs}: m.CVnode<TextInputAttrs>) {
+    return m('input.pf-text-input', attrs);
+  }
+}
diff --git a/ui/src/frontend/widgets_page.ts b/ui/src/frontend/widgets_page.ts
new file mode 100644
index 0000000..041e663
--- /dev/null
+++ b/ui/src/frontend/widgets_page.ts
@@ -0,0 +1,330 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import * as m from 'mithril';
+
+import {Anchor} from './anchor';
+import {globals} from './globals';
+import {createPage} from './pages';
+import {TableShowcase} from './tables/table_showcase';
+import {Button} from './widgets/button';
+import {Checkbox} from './widgets/checkbox';
+import {EmptyState} from './widgets/empty_state';
+import {Popup, PopupPosition} from './widgets/popup';
+import {Portal} from './widgets/portal';
+import {TextInput} from './widgets/text_input';
+
+function PortalButton() {
+  let portalOpen = false;
+
+  return {
+    view: function({attrs}: any) {
+      const {
+        zIndex = true,
+        absolute = true,
+        top = true,
+      } = attrs;
+      return [
+        m(Button, {
+          label: 'Toggle Portal',
+          onclick: () => {
+            portalOpen = !portalOpen;
+            globals.rafScheduler.scheduleFullRedraw();
+          },
+        }),
+        portalOpen &&
+            m(Portal,
+              {
+                style: {
+                  position: absolute && 'absolute',
+                  top: top && '0',
+                  zIndex: zIndex ? '10' : '0',
+                  background: 'white',
+                },
+              },
+              m('', `A very simple portal - a div rendered outside of the normal
+              flow of the page`)),
+      ];
+    },
+  };
+}
+
+function lorem() {
+  const text =
+      `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
+      veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
+      commodo consequat.Duis aute irure dolor in reprehenderit in voluptate
+      velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
+      cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
+      est laborum.`;
+  return m('', {style: {width: '200px'}}, text);
+}
+
+
+function ControlledPopup() {
+  let popupOpen = false;
+
+  return {
+    view: function() {
+      return m(
+          Popup,
+          {
+            trigger:
+                m(Button, {label: `${popupOpen ? 'Close' : 'Open'} Popup`}),
+            isOpen: popupOpen,
+            onChange: (shouldOpen: boolean) => popupOpen = shouldOpen,
+          },
+          m(Button, {
+            label: 'Close Popup',
+            onclick: () => {
+              popupOpen = !popupOpen;
+              globals.rafScheduler.scheduleFullRedraw();
+            },
+          }),
+      );
+    },
+  };
+}
+
+type Options = {
+  [key: string]: EnumOption|boolean
+};
+
+interface WidgetShowcaseAttrs {
+  initialOpts?: Options;
+  renderWidget: (options: any) => any;
+}
+
+class EnumOption {
+  constructor(public initial: string, public options: string[]) {}
+}
+
+// A little helper class to render any vnode with a dynamic set of options
+class WidgetShowcase implements m.ClassComponent<WidgetShowcaseAttrs> {
+  private optValues: any = {};
+  private opts?: Options;
+
+  renderOptions(listItems: m.Child[]): m.Child {
+    if (listItems.length === 0) {
+      return null;
+    }
+    return m(
+        '.widget-controls',
+        m('h3', 'Options'),
+        m('ul', listItems),
+    );
+  }
+
+  oninit({attrs: {initialOpts: opts}}: m.Vnode<WidgetShowcaseAttrs, this>) {
+    this.opts = opts;
+    if (opts) {
+      // Make the initial options values
+      for (const key in opts) {
+        if (Object.prototype.hasOwnProperty.call(opts, key)) {
+          const option = opts[key];
+          if (option instanceof EnumOption) {
+            this.optValues[key] = option.initial;
+          } else if (typeof option === 'boolean') {
+            this.optValues[key] = option;
+          }
+        }
+      }
+    }
+  }
+
+  view({attrs: {renderWidget}}: m.CVnode<WidgetShowcaseAttrs>) {
+    const listItems = [];
+
+    if (this.opts) {
+      for (const key in this.opts) {
+        if (Object.prototype.hasOwnProperty.call(this.opts, key)) {
+          listItems.push(m('li', this.renderControlForOption(key)));
+        }
+      }
+    }
+
+    return [
+      m(
+          '.widget-block',
+          m(
+              '.widget-container',
+              renderWidget(this.optValues),
+              ),
+          this.renderOptions(listItems),
+          ),
+    ];
+  }
+
+  private renderControlForOption(key: string) {
+    if (!this.opts) return null;
+    const value = this.opts[key];
+    if (value instanceof EnumOption) {
+      return this.renderEnumOption(key, value);
+    } else if (typeof value === 'boolean') {
+      return this.renderBooleanOption(key);
+    } else {
+      return null;
+    }
+  }
+
+  private renderBooleanOption(key: string) {
+    return m(Checkbox, {
+      checked: this.optValues[key],
+      label: key,
+      onchange: () => {
+        this.optValues[key] = !this.optValues[key];
+        globals.rafScheduler.scheduleFullRedraw();
+      },
+    });
+  }
+
+  private renderEnumOption(key: string, opt: EnumOption) {
+    const optionElements = opt.options.map((option: string) => {
+      return m('option', {value: option}, option);
+    });
+    return m(
+        'select',
+        {
+          value: this.optValues[key],
+          onchange: (e: Event) => {
+            const el = e.target as HTMLSelectElement;
+            this.optValues[key] = el.value;
+            globals.rafScheduler.scheduleFullRedraw();
+          },
+        },
+        optionElements);
+  }
+}
+
+
+export const WidgetsPage = createPage({
+  view() {
+    return m(
+        '.widgets-page',
+        m('h1', 'Widgets'),
+        m('h2', 'Button'),
+        m(WidgetShowcase, {
+          renderWidget: ({label, icon, ...rest}) => m(Button, {
+            icon: icon ? 'send' : undefined,
+            label: label ? 'Button' : '',
+            ...rest,
+          }),
+          initialOpts: {
+            label: true,
+            icon: false,
+            disabled: false,
+            minimal: false,
+            active: false,
+            compact: false,
+          },
+        }),
+        m('h2', 'Checkbox'),
+        m(WidgetShowcase, {
+          renderWidget: (opts) => m(Checkbox, {label: 'Checkbox', ...opts}),
+          initialOpts: {
+            disabled: false,
+          },
+        }),
+        m('h2', 'Text Input'),
+        m(WidgetShowcase, {
+          renderWidget: ({placeholder, ...rest}) => m(TextInput, {
+            placeholder: placeholder ? 'Placeholder...' : '',
+            ...rest,
+          }),
+          initialOpts: {
+            placeholder: true,
+            disabled: false,
+          },
+        }),
+        m('h2', 'Empty State'),
+        m(WidgetShowcase, {
+          renderWidget: ({header, content}) =>
+              m(EmptyState,
+                {
+                  header: header && 'No search results found...',
+                },
+                content && m(Button, {label: 'Try again'})),
+          initialOpts: {
+            header: true,
+            content: true,
+          },
+        }),
+        m('h2', 'Anchor'),
+        m(WidgetShowcase, {
+          renderWidget: ({icon}) => m(
+              Anchor,
+              {
+                icon: icon && 'open_in_new',
+                href: 'https://perfetto.dev/docs/',
+                target: '_blank',
+              },
+              'Docs',
+              ),
+          initialOpts: {
+            icon: true,
+          },
+        }),
+        m('h2', 'Table'),
+        m(WidgetShowcase,
+          {renderWidget: () => m(TableShowcase), initialOpts: {}}),
+        m('h2', 'Portal'),
+        m('p', `A portal is a div rendered out of normal flow of the
+        hierarchy.`),
+        m(WidgetShowcase, {
+          renderWidget: (opts) => m(PortalButton, opts),
+          initialOpts: {
+            absolute: true,
+            zIndex: true,
+            top: true,
+          },
+        }),
+        m('h2', 'Popup'),
+        m('p', `A popup is a nicely styled portal element whose position is
+        dynamically updated to appear to float alongside a specific element on
+        the page, even as the element is moved and scrolled around.`),
+        m(WidgetShowcase, {
+          renderWidget: (opts) => m(
+              Popup,
+              {
+                trigger: m(Button, {label: 'Toggle Popup'}),
+                ...opts,
+              },
+              lorem(),
+              ),
+          initialOpts: {
+            position: new EnumOption(
+                PopupPosition.Auto,
+                Object.values(PopupPosition),
+                ),
+            closeOnEscape: true,
+            closeOnOutsideClick: true,
+          },
+        }),
+        m('h2', 'Controlled Popup'),
+        m('p', `The open/close state of a controlled popup is passed in via
+        the 'isOpen' attribute. This means we can get open or close the popup
+        from wherever we like. E.g. from a button inside the popup.
+        Keeping this state external also means we can modify other parts of the
+        page depending on whether the popup is open or not, such as the text
+        on this button.
+        Note, this is the same component as the popup above, but used in
+        controlled mode.`),
+        m(WidgetShowcase, {
+          renderWidget: (opts) => m(ControlledPopup, opts),
+          initialOpts: {},
+        }),
+    );
+  },
+});
diff --git a/ui/tsconfig.json b/ui/tsconfig.json
index 5fd4e3d..35ae460 100644
--- a/ui/tsconfig.json
+++ b/ui/tsconfig.json
@@ -1,6 +1,6 @@
 {
   "extends": "./tsconfig.base.json",
-  "include": [ "src/" ],
+  "include": [ "src/**/*" ],
   "exclude": [
     "./node_modules/",
     "./src/service_worker/",