Merge "[ui] Linkifies slice details that start with http:// or https://"
diff --git a/Android.bp b/Android.bp
index ec8d768..82456b8 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",
@@ -9906,6 +9907,7 @@
     name: "perfetto_src_trace_processor_sorter_sorter",
     srcs: [
         "src/trace_processor/sorter/trace_sorter.cc",
+        "src/trace_processor/sorter/trace_token_buffer.cc",
     ],
 }
 
@@ -9913,8 +9915,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",
     ],
 }
 
@@ -12234,6 +12236,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",
@@ -12445,6 +12448,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",
diff --git a/BUILD b/BUILD
index 9ab91e9..3d746e3 100644
--- a/BUILD
+++ b/BUILD
@@ -1823,8 +1823,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",
     ],
 )
 
@@ -1986,6 +1986,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",
@@ -4581,6 +4590,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",
@@ -4731,6 +4741,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",
@@ -4936,6 +4947,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/include/perfetto/trace_processor/ref_counted.h b/include/perfetto/trace_processor/ref_counted.h
index 02996f0..d753151 100644
--- a/include/perfetto/trace_processor/ref_counted.h
+++ b/include/perfetto/trace_processor/ref_counted.h
@@ -92,6 +92,7 @@
   // 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;
@@ -100,6 +101,7 @@
   // 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;
@@ -136,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/internal/basic_types.h b/include/perfetto/tracing/internal/basic_types.h
index 81f5353..48d46a7 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 a direct index in the TracingMuxer::producer_backends_ vector.
 // Backends are only added and never removed.
 using TracingBackendId = size_t;
 
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/tracing.h b/include/perfetto/tracing/tracing.h
index 73496d4..dbc00e1 100644
--- a/include/perfetto/tracing/tracing.h
+++ b/include/perfetto/tracing/tracing.h
@@ -124,6 +124,10 @@
   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;
 
@@ -143,18 +147,24 @@
   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_,
+                    system_producer_backend_factory_,
+                    system_consumer_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.system_producer_backend_factory_,
+                    other.system_consumer_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();
 };
 
@@ -182,12 +192,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);
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/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/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/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 f85db0b..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"
@@ -219,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/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/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..6abf849 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..aa1d121 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,81 @@
       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 AllocId of this tokenized object corresponding to this event.
+    BumpAllocator::AllocId alloc_id;
+
+    // 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 uint8_t instead and cast to the enum type.
+    uint8_t event_type : kMaxTypeBits;
+
+    // Out-of-band data used to intepret data in the TraceTokenBuffer.
+    // Exists outside of TraceTokenBuffer because we have a bunch of free
+    // bits here and we want to save as memory as much as possible.
+    uint32_t token_buffer_oob : TraceTokenBuffer::Id::kOutOfBandBits;
 
     // 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 ts < evt.ts || (ts == evt.ts && alloc_id < evt.alloc_id);
     }
   };
-
-  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.alloc_id = id.alloc_id;
+        event.event_type = static_cast<uint8_t>(type);
+        event.token_buffer_oob = id.out_of_band;
+        events_.emplace_back(std::move(event));
+      }
 
       // Events are often seen in order.
       if (PERFETTO_LIKELY(ts >= max_ts_)) {
@@ -290,20 +279,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 +302,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 +313,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, event.token_buffer_oob};
   }
 
-  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..01b666a
--- /dev/null
+++ b/src/trace_processor/sorter/trace_token_buffer.cc
@@ -0,0 +1,278 @@
+/*
+ * 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 += 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));
+
+  // Add the "optional" fields of TrackEventData based on whether or not they
+  // are non-null.
+  uint8_t* ptr = static_cast<uint8_t*>(allocator_.GetPointer(alloc_id));
+  ptr = AppendToPtr(ptr, desc);
+  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]);
+  }
+
+  // Store the packet size in the "out of band" bits.
+  uint32_t packet_size = static_cast<uint32_t>(tpd.packet.size());
+  PERFETTO_CHECK(packet_size <= protozero::proto_utils::kMaxMessageLength);
+  return Id{alloc_id, packet_size};
+}
+
+template <>
+TrackEventData TraceTokenBuffer::Extract<TrackEventData>(Id id) {
+  uint8_t* ptr = static_cast<uint8_t*>(allocator_.GetPointer(id.alloc_id));
+  TrackEventDataDescriptor desc =
+      ExtractFromPtr<TrackEventDataDescriptor>(&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,
+                    id.out_of_band);
+  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() {
+  uint32_t erased = allocator_.EraseFrontFreeChunks();
+  interned_blobs_.erase_front(erased);
+  interned_seqs_.erase_front(erased);
+  PERFETTO_DCHECK(interned_blobs_.size() == interned_seqs_.size());
+}
+
+BumpAllocator::AllocId TraceTokenBuffer::AllocAndResizeInternedVectors(
+    uint32_t size) {
+  uint32_t erased = allocator_.erased_front_chunks_count();
+  BumpAllocator::AllocId alloc_id = allocator_.Alloc(size);
+  uint32_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.
+  size_t chunks_added = allocator_chunks_size - interned_blobs_.size();
+  PERFETTO_DCHECK(chunks_added <= 1);
+  PERFETTO_DCHECK(interned_blobs_.size() == interned_seqs_.size());
+  for (uint32_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) {
+  uint32_t interned_index =
+      alloc_id.chunk_index - allocator_.erased_front_chunks_count();
+  PERFETTO_DCHECK(interned_index < interned_blobs_.size());
+  PERFETTO_DCHECK(interned_index < interned_seqs_.size());
+  PERFETTO_DCHECK(interned_blobs_.size() == interned_seqs_.size());
+  return 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..47eec13
--- /dev/null
+++ b/src/trace_processor/sorter/trace_token_buffer.h
@@ -0,0 +1,157 @@
+/*
+ * 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 number of bits available for store data "out-of-band" (i.e. by
+    // bitpacking in free bits available in another data structure). The value
+    // of this is set based on the available bits in
+    // TraceSorter::TimestampedDescriptor.
+    static constexpr uint32_t kOutOfBandBits = 28;
+    static constexpr uint32_t kMaxOutOfBandValue = (1u << kOutOfBandBits) - 1;
+
+    // The allocation id of the object in the buffer.
+    BumpAllocator::AllocId alloc_id;
+
+    // "Out-of-band" data for this object. This allows squeezing as much memory
+    // as possible by using free bits in other data structures to store parts
+    // of tokens.
+    // This size of this field (i.e. kOutOfBandBits) is chosen to match the
+    // available bits in TraceSorter: there is a static_assert in
+    // TraceSorter::TimestampedDescriptor.
+    uint32_t out_of_band : kOutOfBandBits;
+  };
+  static_assert(protozero::proto_utils::kMaxMessageLength <=
+                    Id::kMaxOutOfBandValue,
+                "Packet length must fit in out-of-band field");
+
+  // 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, 0};
+  }
+  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 = uint32_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..6689dc5
--- /dev/null
+++ b/src/trace_processor/sorter/trace_token_buffer_unittest.cc
@@ -0,0 +1,172 @@
+/*
+ * 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));
+  ASSERT_EQ(id.out_of_band, 1024u);
+
+  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));
+  ASSERT_EQ(id.out_of_band, 1234u);
+
+  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/storage/stats.h b/src/trace_processor/storage/stats.h
index 430b1ab..9472b33 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -26,220 +26,223 @@
 // 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(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,  kError,    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/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 38cd239..ce68793 100644
--- a/src/tracing/internal/tracing_muxer_impl.cc
+++ b/src/tracing/internal/tracing_muxer_impl.cc
@@ -353,12 +353,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;
@@ -862,20 +858,20 @@
   supports_multiple_data_source_instances_ =
       args.supports_multiple_data_source_instances;
 
-  auto add_backend = [this, &args](TracingBackend* backend, BackendType type) {
+  auto add_producer_backend = [this, &args](TracingProducerBackend* 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();
+    TracingBackendId backend_id = producer_backends_.size();
+    producer_backends_.emplace_back();
+    RegisteredProducerBackend& rb = producer_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();
@@ -888,19 +884,39 @@
     rb.producer->Initialize(rb.backend->ConnectProducer(rb.producer_conn_args));
   };
 
+  auto add_consumer_backend = [this](TracingConsumerBackend* backend,
+                                     BackendType type) {
+    if (!backend) {
+      return;
+    }
+    consumer_backends_.emplace_back();
+    RegisteredConsumerBackend& rb = consumer_backends_.back();
+    rb.backend = backend;
+    rb.type = type;
+  };
+
   if (args.backends & kSystemBackend) {
-    PERFETTO_CHECK(args.system_backend_factory_);
-    add_backend(args.system_backend_factory_(), kSystemBackend);
+    PERFETTO_CHECK(args.system_producer_backend_factory_);
+    add_producer_backend(args.system_producer_backend_factory_(),
+                         kSystemBackend);
+    if (args.enable_system_consumer) {
+      PERFETTO_CHECK(args.system_consumer_backend_factory_);
+      add_consumer_backend(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);
+    auto* b = args.in_process_backend_factory_();
+    add_producer_backend(b, kInProcessBackend);
+    add_consumer_backend(b, kInProcessBackend);
   }
 
   if (args.backends & kCustomBackend) {
     PERFETTO_CHECK(args.custom_backend);
-    add_backend(args.custom_backend, kCustomBackend);
+    add_producer_backend(args.custom_backend, kCustomBackend);
+    add_consumer_backend(args.custom_backend, kCustomBackend);
   }
 
   if (args.backends & ~(kSystemBackend | kInProcessBackend | kCustomBackend)) {
@@ -910,8 +926,10 @@
   // 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);
+  add_producer_backend(internal::TracingBackendFake::GetInstance(),
+                       BackendType::kUnspecifiedBackend);
+  add_consumer_backend(internal::TracingBackendFake::GetInstance(),
+                       BackendType::kUnspecifiedBackend);
 }
 
 // Can be called from any thread (but not concurrently).
@@ -1019,7 +1037,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 +1189,7 @@
     internal_state->muxer_id_for_testing = muxer_id_for_testing_;
 
     if (startup_session_id) {
-      RegisteredBackend& backend = backends_[backend_id];
+      RegisteredProducerBackend& backend = producer_backends_[backend_id];
       uint16_t& last_reservation =
           backend.producer->last_startup_target_buffer_reservation_;
       if (last_reservation == std::numeric_limits<uint16_t>::max()) {
@@ -1257,7 +1275,7 @@
       ds.internal_state->startup_target_buffer_reservation.load(
           std::memory_order_relaxed);
   if (startup_reservation) {
-    RegisteredBackend& backend = backends_[backend_id];
+    RegisteredProducerBackend& backend = producer_backends_[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(),
@@ -1424,10 +1442,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 = producer_backends_[backend_id];
+  ProducerImpl* producer = producer_backends_[backend_id].producer.get();
   if (!producer)
     return;
 
@@ -1517,9 +1535,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);
@@ -1539,7 +1557,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;
@@ -1610,7 +1628,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;
@@ -1754,7 +1772,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
@@ -1847,7 +1865,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()
@@ -1858,7 +1876,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()
@@ -1872,27 +1890,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;
@@ -1902,7 +1924,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;
     };
@@ -1918,7 +1940,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,
@@ -1961,7 +1983,7 @@
     TracingBackendId backend_id,
     DataSourceInstanceID instance_id) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  auto& backend = backends_[backend_id];
+  auto& backend = producer_backends_[backend_id];
   for (const auto& rds : data_sources_) {
     DataSourceStaticState* static_state = rds.static_state;
     for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
@@ -1997,7 +2019,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 =
+      producer_backends_[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
@@ -2048,26 +2071,20 @@
 
   // Capturing |this| is fine because the TracingMuxer is a leaky singleton.
   task_runner_->PostTask([this, requested_backend_type, session_id] {
-    for (RegisteredBackend& backend : backends_) {
+    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",
@@ -2083,10 +2100,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;
@@ -2095,7 +2113,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)
@@ -2131,15 +2149,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 "
@@ -2259,7 +2277,7 @@
     BackendType backend_type) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
 
-  for (RegisteredBackend& backend : backends_) {
+  for (RegisteredProducerBackend& backend : producer_backends_) {
     if (backend_type != backend.type)
       continue;
 
@@ -2370,21 +2388,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_) {
@@ -2433,7 +2454,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..5bec783 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"
@@ -272,10 +273,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 +298,6 @@
 
     TracingMuxerImpl* muxer_;
     BackendType const backend_type_;
-    TracingBackendId const backend_id_;
     TracingSessionGlobalID const session_id_;
     bool connected_ = false;
 
@@ -425,22 +422,25 @@
     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,
@@ -448,6 +448,8 @@
   explicit TracingMuxerImpl(const TracingInitArgs&);
   void Initialize(const TracingInitArgs& args);
   ConsumerImpl* FindConsumer(TracingSessionGlobalID session_id);
+  std::pair<ConsumerImpl*, RegisteredConsumerBackend*> FindConsumerAndBackend(
+      TracingSessionGlobalID session_id);
   void InitializeConsumer(TracingSessionGlobalID session_id);
   void OnConsumerDisconnected(ConsumerImpl* consumer);
   void OnProducerDisconnected(ProducerImpl* producer);
@@ -493,7 +495,8 @@
   // 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_;
+  std::vector<RegisteredProducerBackend> producer_backends_;
+  std::vector<RegisteredConsumerBackend> consumer_backends_;
   std::vector<RegisteredInterceptor> interceptors_;
   TracingPolicy* policy_ = nullptr;
 
@@ -512,7 +515,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/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index f307488..f9f7b6f 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -4557,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"});
diff --git a/src/tracing/test/api_test_support.cc b/src/tracing/test/api_test_support.cc
index 064f259..f07cb84 100644
--- a/src/tracing/test/api_test_support.cc
+++ b/src/tracing/test/api_test_support.cc
@@ -168,20 +168,31 @@
 
 // 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/tracing.cc b/src/tracing/tracing.cc
index 030f49c..0bd6c13 100644
--- a/src/tracing/tracing.cc
+++ b/src/tracing/tracing.cc
@@ -56,7 +56,11 @@
     base::SetLogMessageCallback(args.log_message_callback);
   }
 
-  if (args.use_monotonic_raw_clock) {
+  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);
   }
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/traced_perf_test_cts.cc b/test/cts/traced_perf_test_cts.cc
index aaa2178..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,6 +22,7 @@
 
 #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"
@@ -137,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,
@@ -232,7 +237,8 @@
 
   // Construct config.
   TraceConfig trace_config;
-  trace_config.add_buffers()->set_size_kb(20 * 1024);
+  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());
@@ -246,16 +252,18 @@
   ds_config->set_name("linux.process_stats");
   ds_config->set_process_stats_config_raw(ps_config.SerializeAsString());
 
-  // profile traced_probes
+  // capture callstacks of traced_probes descheduling
   protos::gen::PerfEventConfig perf_config;
   auto* timebase = perf_config.mutable_timebase();
-  timebase->set_frequency(100);
+  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.
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/include_index.py b/test/trace_processor/diff_tests/include_index.py
index 06500d8..6f9e525 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -57,6 +57,7 @@
 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
@@ -107,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(),
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/ui/src/controller/selection_controller.ts b/ui/src/controller/selection_controller.ts
index 4b4ee82..653bad6 100644
--- a/ui/src/controller/selection_controller.ts
+++ b/ui/src/controller/selection_controller.ts
@@ -22,7 +22,6 @@
   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 {
@@ -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/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/frontend/chrome_slice_panel.ts b/ui/src/frontend/chrome_slice_panel.ts
index 5bf3b40..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);
diff --git a/ui/src/frontend/details_panel.ts b/ui/src/frontend/details_panel.ts
index 9b7e617..d195b7e 100644
--- a/ui/src/frontend/details_panel.ts
+++ b/ui/src/frontend/details_panel.ts
@@ -39,7 +39,7 @@
 import {PivotTableRedux} from './pivot_table_redux';
 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;
       }
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/popup_menu.ts b/ui/src/frontend/popup_menu.ts
index 863ddd1..e4c9190 100644
--- a/ui/src/frontend/popup_menu.ts
+++ b/ui/src/frontend/popup_menu.ts
@@ -23,6 +23,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;
diff --git a/ui/src/frontend/slice_details_panel.ts b/ui/src/frontend/slice_details_panel.ts
index 23e54d2..134b5c6 100644
--- a/ui/src/frontend/slice_details_panel.ts
+++ b/ui/src/frontend/slice_details_panel.ts
@@ -74,7 +74,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)) {
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..ff6e200
--- /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 {
+      where?: 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.where && c.where.length > 0) {
+    result.push(`WHERE ${c.where.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..d8e35ba
--- /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({
+    where: ['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({
+    where: ['id != 1'],
+    orderBy: [{fieldName: 'ts'}],
+    limit: 1,
+  }))).toEqual('WHERE id != 1 ORDER BY ts LIMIT 1');
+});
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..4513c55
--- /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, {
+    where: [`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/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);
+}