Commit 96de0e2f50d for nodejs

commit 96de0e2f50d5289eaaeade9cc9e5d7e01a850774
Author: Node.js GitHub Bot <github-bot@iojs.org>
Date:   Mon Nov 2 19:53:29 2026 -0500

    deps: update perfetto to 59.1

    PR-URL: https://github.com/nodejs/node/pull/67171
    Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
    Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>

diff --git a/deps/perfetto/LICENSE b/deps/perfetto/LICENSE
index 681d40008ee..2db252fd570 100644
--- a/deps/perfetto/LICENSE
+++ b/deps/perfetto/LICENSE
@@ -226,7 +226,7 @@ Files: src/trace_processor/perfetto_sql/stdlib/chromium/*, protos/third_party/ch

 ------------------

-Files: src/trace_processor/perfetto_sql/syntaqlite/syntaqlite_perfetto.{c, h}
+Files: src/perfetto_sql/syntaqlite/syntaqlite_perfetto.{c, h}

    Copyright 2025 The syntaqlite Authors. All rights reserved.

diff --git a/deps/perfetto/VERSION b/deps/perfetto/VERSION
index 5132e635a5f..2246a13a3da 100644
--- a/deps/perfetto/VERSION
+++ b/deps/perfetto/VERSION
@@ -1 +1 @@
-58.2
+59.1
diff --git a/deps/perfetto/sdk/LICENSE b/deps/perfetto/sdk/LICENSE
new file mode 100644
index 00000000000..2db252fd570
--- /dev/null
+++ b/deps/perfetto/sdk/LICENSE
@@ -0,0 +1,285 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+------------------
+
+Files: * except those files noted below
+
+   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.
+
+   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.
+
+
+------------------
+
+Files: src/trace_processor/perfetto_sql/stdlib/chromium/*, protos/third_party/chromium/*, test/trace_processor/diff_tests/stdlib/chrome/*
+
+   Copyright 2015 The Chromium Authors
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+      * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+      * Neither the name of Google LLC nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+------------------
+
+Files: src/perfetto_sql/syntaqlite/syntaqlite_perfetto.{c, h}
+
+   Copyright 2025 The syntaqlite Authors. All rights reserved.
+
+   Machine-generated amalgamation of syntaqlite runtime + Perfetto dialect
+   sources (https://github.com/LalitMaganti/syntaqlite). Portions derive
+   from SQLite's public-domain `parse.y` grammar.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   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.
+
+
+------------------
+
+Files: src/trace_processor/perfetto_sql/preprocessor/preprocessor_grammar.{c, h}
+
+The author disclaims copyright to this source code. In place of a legal notice, here is a blessing:
+
+May you do good and not evil.
+May you find forgiveness for yourself and forgive others.
+May you share freely, never taking more than you give.
+
+
+------------------
+
+Files: src/base/intrusive_tree.cc
+
+$OpenBSD: tree.h,v 1.31 2023/03/08 04:43:09 guenther Exp $
+
+Copyright 2002 Niels Provos <provos@citi.umich.edu>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/deps/perfetto/sdk/perfetto.cc b/deps/perfetto/sdk/perfetto.cc
index 739d4832204..3aa0cd8e36b 100644
--- a/deps/perfetto/sdk/perfetto.cc
+++ b/deps/perfetto/sdk/perfetto.cc
@@ -122,6 +122,14 @@ void AfterMaybeBlockingSyscall() {}

 #include <stdint.h>

+// The __attribute__((constructor)) below is intentional: this function runs at
+// load time on purpose to verify the CPU supports the extensions the binary is
+// built with. -Wglobal-constructors (enabled by -Weverything) would otherwise
+// flag it.
+#if defined(__clang__)
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+
 // gen_amalgamated expanded: #include "perfetto/base/build_config.h"
 // gen_amalgamated expanded: #include "perfetto/base/export.h"

@@ -150,7 +158,13 @@ uint32_t GetXCR0EAX() {

 // If we are building with -msse4 check that the CPU actually supports it.
 // This file must be kept in sync with gn/standalone/BUILD.gn.
-void PERFETTO_EXPORT_COMPONENT __attribute__((constructor))
+//
+// Priority 101 is the earliest non-reserved constructor priority. With the
+// default (65535) this would run after protobuf's descriptor registration
+// (priority 102), which already executes BMI2 instructions and SIGILLs on
+// older CPUs before this check can print its message. See
+// https://github.com/google/perfetto/issues/7504.
+void PERFETTO_EXPORT_COMPONENT __attribute__((constructor(101)))
 CheckCpuOptimizations() {
   uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
   PERFETTO_GETCPUID(eax, ebx, ecx, edx, 1, 0);
@@ -221,6 +235,7 @@ CheckCpuOptimizations() {
 #include <cstdint>
 #include <optional>
 #include <string>
+#include <vector>

 // gen_amalgamated expanded: #include "perfetto/base/build_config.h"

@@ -258,6 +273,7 @@ struct SystemInfo {
   std::string android_storage_model;
   std::string android_ram_model;
   std::string android_serial_console;
+  std::vector<std::string> device_tree_compatibles;
 };

 // Returns the device's utsname information.
@@ -277,9 +293,8 @@ std::string GetPerfettoMachineName();
 }  // namespace perfetto

 #endif  // INCLUDE_PERFETTO_EXT_BASE_ANDROID_UTILS_H_
-// gen_amalgamated begin header: include/perfetto/ext/base/string_utils.h
-// gen_amalgamated begin header: include/perfetto/ext/base/string_view.h
-// gen_amalgamated begin header: include/perfetto/ext/base/fnv_hash.h
+// gen_amalgamated begin header: include/perfetto/ext/base/file_utils.h
+// gen_amalgamated begin header: include/perfetto/base/status.h
 /*
  * Copyright (C) 2019 The Android Open Source Project
  *
@@ -296,135 +311,110 @@ std::string GetPerfettoMachineName();
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_FNV_HASH_H_
-#define INCLUDE_PERFETTO_EXT_BASE_FNV_HASH_H_
+#ifndef INCLUDE_PERFETTO_BASE_STATUS_H_
+#define INCLUDE_PERFETTO_BASE_STATUS_H_

-#include <stddef.h>
-#include <stdint.h>
+#include <optional>
+#include <string>
 #include <string_view>
-#include <type_traits>
-#include <utility>
+#include <vector>

-namespace perfetto::base {
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/export.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"

-// A helper class which computes a 64-bit hash of the input data.
-// The algorithm used is FNV-1a as it is fast and easy to implement and has
-// relatively few collisions.
-// WARNING: This hash function should not be used for any cryptographic purpose.
-class FnvHasher {
- public:
-  // Creates an empty hash object
-  constexpr FnvHasher() = default;
+namespace perfetto {
+namespace base {

-  // Hashes a numeric value.
-  template <
-      typename T,
-      typename std::enable_if<std::is_arithmetic<T>::value, bool>::type = true>
-  void Update(T data) {
-    Update(reinterpret_cast<const char*>(&data), sizeof(data));
+// Represents either the success or the failure message of a function.
+// This can used as the return type of functions which would usually return an
+// bool for success or int for errno but also wants to add some string context
+// (ususally for logging).
+//
+// Similar to absl::Status, an optional "payload" can also be included with more
+// context about the error. This allows passing additional metadata about the
+// error (e.g. location of errors, potential mitigations etc).
+class PERFETTO_EXPORT_COMPONENT Status {
+ public:
+  Status() : ok_(true) {}
+  explicit Status(std::string msg) : ok_(false), message_(std::move(msg)) {
+    PERFETTO_CHECK(!message_.empty());
   }

-  constexpr void Update(char c) { return Update(&c, 1); }
-
-  // Using the loop instead of "Update(str, strlen(str))" to avoid looping twice
-  constexpr void Update(const char* str) {
-    for (const auto* p = str; *p; ++p)
-      Update(*p);
-  }
+  // Copy operations.
+  Status(const Status&) = default;
+  Status& operator=(const Status&) = default;

-  // Hashes a byte array.
-  constexpr void Update(const char* data, size_t size) {
-    for (size_t i = 0; i < size; i++) {
-      result_ ^= static_cast<uint8_t>(data[i]);
-      // Note: Arithmetic overflow of unsigned integers is well defined in C++
-      // standard unlike signed integers.
-      // https://stackoverflow.com/a/41280273
-      result_ *= kFnv1a64Prime;
-    }
-  }
+  // Move operations. The moved-from state is valid but unspecified.
+  Status(Status&&) noexcept = default;
+  Status& operator=(Status&&) = default;

-  // Allow hashing anything that has `data` and `size` and has the kHashable
-  // trait (e.g., base::StringView).
-  template <typename T, typename = std::enable_if_t<T::kHashable>>
-  constexpr void Update(const T& t) {
-    if constexpr (std::is_member_function_pointer_v<decltype(&T::data)>) {
-      Update(t.data(), t.size());
-    } else {
-      Update(t.data, t.size);
-    }
-  }
+  bool ok() const { return ok_; }

-  constexpr void Update(std::string_view s) { Update(s.data(), s.size()); }
+  // When ok() is false this returns the error message. Returns the empty string
+  // otherwise.
+  const std::string& message() const { return message_; }
+  const char* c_message() const { return message_.c_str(); }

-  constexpr uint64_t digest() const { return result_; }
+  //////////////////////////////////////////////////////////////////////////////
+  // Payload Management APIs
+  //////////////////////////////////////////////////////////////////////////////

-  // Usage:
-  // uint64_t hashed_value =
-  //    FnvHasher::Combine(33, false, "ABC", 458L, 3u, 'x');
-  template <typename... Ts>
-  static constexpr uint64_t Combine(Ts&&... args) {
-    FnvHasher hasher;
-    hasher.UpdateAll(std::forward<Ts>(args)...);
-    return hasher.digest();
-  }
+  // Payloads can be attached to error statuses to provide additional context.
+  //
+  // Payloads are (key, value) pairs, where the key is a string acting as a
+  // unique "type URL" and the value is an opaque string. The "type URL" should
+  // be unique, follow the format of a URL and, ideally, documentation on how to
+  // interpret its associated data should be available.
+  //
+  // To attach a payload to a status object, call `Status::SetPayload()`.
+  // Similarly, to extract the payload from a status, call
+  // `Status::GetPayload()`.
+  //
+  // Note: the payload APIs are only meaningful to call when the status is an
+  // error. Otherwise, all methods are noops.

-  // Creates a hasher with `args` already hashed.
+  // Gets the payload for the given |type_url| if one exists.
   //
-  // Usage:
-  // FnvHasher partial = FnvHasher::CreatePartial(33, false, "ABC", 458L);
-  template <typename... Ts>
-  static constexpr FnvHasher CreatePartial(Ts&&... args) {
-    FnvHasher hasher;
-    hasher.UpdateAll(std::forward<Ts>(args)...);
-    return hasher;
-  }
+  // Will always return std::nullopt if |ok()|.
+  std::optional<std::string_view> GetPayload(std::string_view type_url) const;

-  // `hasher.UpdateAll(33, false, "ABC")` is shorthand for:
-  // `hasher.Update(33); hasher.Update(false); hasher.Update("ABC");`
-  constexpr void UpdateAll() {}
+  // Sets the payload for the given key. The key should
+  //
+  // Will always do nothing if |ok()|.
+  void SetPayload(std::string_view type_url, std::string value);

-  template <typename T, typename... Ts>
-  constexpr void UpdateAll(T&& arg, Ts&&... args) {
-    Update(arg);
-    UpdateAll(std::forward<Ts>(args)...);
-  }
+  // Erases the payload for the given string and returns true if the payload
+  // existed and was erased.
+  //
+  // Will always do nothing if |ok()|.
+  bool ErasePayload(std::string_view type_url);

  private:
-  static constexpr uint64_t kFnv1a64OffsetBasis = 0xcbf29ce484222325;
-  static constexpr uint64_t kFnv1a64Prime = 0x100000001b3;
+  struct Payload {
+    std::string type_url;
+    std::string payload;
+  };

-  uint64_t result_ = kFnv1a64OffsetBasis;
+  bool ok_ = false;
+  std::string message_;
+  std::vector<Payload> payloads_;
 };

-// base::FnvHash uses base::FnvHasher for integer values and falls back to
-// std::hash for other types. This is needed as std::hash for integers is just
-// the identity function and Perfetto uses open-addressing hash table, which are
-// very sensitive to hash quality and are known to degrade in performance
-// when using std::hash.
-template <typename T>
-struct FnvHash {
-  // Version for ints, using base::FnvHasher.
-  template <typename U = T>
-  auto operator()(const U& x) ->
-      typename std::enable_if<std::is_arithmetic<U>::value, size_t>::type
-      const {
-    return FnvHasher::Combine(x);
-  }
+// Returns a status object which represents the Ok status.
+inline Status OkStatus() {
+  return Status();
+}

-  // Version for non-ints, falling back to std::hash.
-  template <typename U = T>
-  auto operator()(const U& x) ->
-      typename std::enable_if<!std::is_arithmetic<U>::value, size_t>::type
-      const {
-    return std::hash<U>()(x);
-  }
-};
+Status ErrStatus(const char* format, ...) PERFETTO_PRINTF_FORMAT(1, 2);

-}  // namespace perfetto::base
+}  // namespace base
+}  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_FNV_HASH_H_
+#endif  // INCLUDE_PERFETTO_BASE_STATUS_H_
+// gen_amalgamated begin header: include/perfetto/ext/base/scoped_file.h
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -439,203 +429,189 @@ struct FnvHash {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_H_
-#define INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_SCOPED_FILE_H_
+#define INCLUDE_PERFETTO_EXT_BASE_SCOPED_FILE_H_

-#include <string.h>
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#include <stdio.h>
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <dirent.h>  // For DIR* / opendir().
+#endif

-#include <algorithm>
 #include <string>
-#include <vector>

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/export.h"
 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/fnv_hash.h"
+// gen_amalgamated expanded: #include "perfetto/base/platform_handle.h"

 namespace perfetto {
 namespace base {

-// A string-like object that refers to a non-owned piece of memory.
-// Strings are internally NOT null terminated.
-class StringView {
+namespace internal {
+// Used for the most common cases of ScopedResource where there is only one
+// invalid value.
+template <typename T, T InvalidValue>
+struct DefaultValidityChecker {
+  static bool IsValid(T t) { return t != InvalidValue; }
+};
+}  // namespace internal
+
+// RAII classes for auto-releasing fds and dirs.
+// if T is a pointer type, InvalidValue must be nullptr. Doing otherwise
+// causes weird unexpected behaviors (See https://godbolt.org/z/5nGMW4).
+template <typename T,
+          int (*CloseFunction)(T),
+          T InvalidValue,
+          bool CheckClose = true,
+          class Checker = internal::DefaultValidityChecker<T, InvalidValue>>
+class ScopedResource {
  public:
-  // Allow hashing with base::FnvHash.
-  static constexpr bool kHashable = true;
-  static constexpr size_t npos = static_cast<size_t>(-1);
+  using ValidityChecker = Checker;
+  static constexpr T kInvalid = InvalidValue;

-  StringView() : data_(nullptr), size_(0) {}
-  StringView(const StringView&) = default;
-  StringView& operator=(const StringView&) = default;
-  StringView(const char* data, size_t size) : data_(data), size_(size) {
-    PERFETTO_DCHECK(size == 0 || data != nullptr);
+  explicit ScopedResource(T t = InvalidValue) : t_(t) {}
+  ScopedResource(ScopedResource&& other) noexcept : t_(other.t_) {
+    other.t_ = InvalidValue;
   }
-
-  // Allow implicit conversion from any class that has a |data| and |size| field
-  // and has the kConvertibleToStringView trait (e.g., protozero::ConstChars).
-  template <typename T, typename = std::enable_if<T::kConvertibleToStringView>>
-  StringView(const T& x) : StringView(x.data, x.size) {
-    PERFETTO_DCHECK(x.size == 0 || x.data != nullptr);
+  ScopedResource& operator=(ScopedResource&& other) {
+    reset(other.t_);
+    other.t_ = InvalidValue;
+    return *this;
   }
-
-  // Creates a StringView from a null-terminated C string.
-  // Deliberately not "explicit".
-  StringView(const char* cstr) : data_(cstr), size_(strlen(cstr)) {
-    PERFETTO_DCHECK(cstr != nullptr);
+  T get() const { return t_; }
+  T operator*() const { return t_; }
+  explicit operator bool() const { return Checker::IsValid(t_); }
+  void reset(T r = InvalidValue) {
+    if (Checker::IsValid(t_)) {
+      int res = CloseFunction(t_);
+      if (CheckClose)
+        PERFETTO_CHECK(res == 0);
+    }
+    t_ = r;
+  }
+  T release() {
+    T t = t_;
+    t_ = InvalidValue;
+    return t;
   }
+  ~ScopedResource() { reset(InvalidValue); }

-  StringView(std::string_view str) : data_(str.data()), size_(str.size()) {}
+ private:
+  ScopedResource(const ScopedResource&) = delete;
+  ScopedResource& operator=(const ScopedResource&) = delete;
+  T t_;
+};

-  // This instead has to be explicit, as creating a StringView out of a
-  // std::string can be subtle.
-  explicit StringView(const std::string& str)
-      : data_(str.data()), size_(str.size()) {}
+// Declared in file_utils.h. Forward declared to avoid #include cycles.
+int PERFETTO_EXPORT_COMPONENT CloseFile(int fd);

-  bool empty() const { return size_ == 0; }
-  size_t size() const { return size_; }
-  const char* data() const { return data_; }
-  const char* begin() const { return data_; }
-  const char* end() const { return data_ + size_; }
+// Use this for file resources obtained via open() and similar APIs.
+using ScopedFile = ScopedResource<int, CloseFile, -1>;
+using ScopedFstream = ScopedResource<FILE*, fclose, nullptr>;

-  char at(size_t pos) const {
-    PERFETTO_DCHECK(pos < size_);
-    return data_[pos];
-  }
+// Use this for resources that are HANDLE on Windows. See comments in
+// platform_handle.h
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+using ScopedPlatformHandle = ScopedResource<PlatformHandle,
+                                            ClosePlatformHandle,
+                                            /*InvalidValue=*/nullptr,
+                                            /*CheckClose=*/true,
+                                            PlatformHandleChecker>;
+#else
+// On non-windows systems we alias ScopedPlatformHandle to ScopedFile because
+// they are really the same. This is to allow assignments between the two in
+// Linux-specific code paths that predate ScopedPlatformHandle.
+static_assert(std::is_same<int, PlatformHandle>::value, "");
+using ScopedPlatformHandle = ScopedFile;

-  size_t find(char c, size_t start_pos = 0) const {
-    for (size_t i = start_pos; i < size_; ++i) {
-      if (data_[i] == c)
-        return i;
-    }
-    return npos;
-  }
+// DIR* does not exist on Windows.
+using ScopedDir = ScopedResource<DIR*, closedir, nullptr>;
+#endif

-  size_t find(const StringView& str, size_t start_pos = 0) const {
-    if (start_pos > size())
-      return npos;
-    auto it = std::search(begin() + start_pos, end(), str.begin(), str.end());
-    size_t pos = static_cast<size_t>(it - begin());
-    return pos + str.size() <= size() ? pos : npos;
-  }
+}  // namespace base
+}  // namespace perfetto

-  size_t find(const char* str, size_t start_pos = 0) const {
-    return find(StringView(str), start_pos);
-  }
+#endif  // INCLUDE_PERFETTO_EXT_BASE_SCOPED_FILE_H_
+// gen_amalgamated begin header: include/perfetto/ext/base/sys_types.h
+/*
+ * 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.
+ */

-  size_t rfind(char c) const {
-    for (size_t i = size_; i > 0; --i) {
-      if (data_[i - 1] == c)
-        return i - 1;
-    }
-    return npos;
-  }
+#ifndef INCLUDE_PERFETTO_EXT_BASE_SYS_TYPES_H_
+#define INCLUDE_PERFETTO_EXT_BASE_SYS_TYPES_H_

-  StringView substr(size_t pos, size_t count = npos) const {
-    if (pos >= size_)
-      return StringView("", 0);
-    size_t rcount = std::min(count, size_ - pos);
-    return StringView(data_ + pos, rcount);
-  }
+// This headers deals with sys types commonly used in the codebase that are
+// missing on Windows.

-  bool CaseInsensitiveEq(const StringView& other) const {
-    if (size() != other.size())
-      return false;
-    if (size() == 0)
-      return true;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    return _strnicmp(data(), other.data(), size()) == 0;
-#else
-    return strncasecmp(data(), other.data(), size()) == 0;
-#endif
-  }
+#include <sys/types.h>  // IWYU pragma: export
+#include <cstdint>

-  bool CaseInsensitiveOneOf(const std::vector<StringView>& others) const {
-    for (const StringView& other : others) {
-      if (CaseInsensitiveEq(other)) {
-        return true;
-      }
-    }
-    return false;
-  }
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-  bool StartsWith(const StringView& other) const {
-    if (other.size() == 0)
-      return true;
-    if (size() == 0)
-      return false;
-    if (other.size() > size())
-      return false;
-    return memcmp(data(), other.data(), other.size()) == 0;
-  }
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)

-  bool EndsWith(const StringView& other) const {
-    if (other.size() == 0)
-      return true;
-    if (size() == 0)
-      return false;
-    if (other.size() > size())
-      return false;
-    size_t off = size() - other.size();
-    return memcmp(data() + off, other.data(), other.size()) == 0;
-  }
+#ifdef __MINGW32__

-  std::string ToStdString() const {
-    return size_ == 0 ? "" : std::string(data_, size_);
-  }
+using uid_t = int;
+using gid_t = int;

-  uint64_t Hash() const {
-    base::FnvHasher hasher;
-    hasher.Update(data_, size_);
-    return hasher.digest();
-  }
+#else

- private:
-  const char* data_ = nullptr;
-  size_t size_ = 0;
-};
+#if !PERFETTO_BUILDFLAG(PERFETTO_COMPILER_GCC)
+// MinGW has these. clang-cl and MSVC, which use just the Windows SDK, don't.
+using uid_t = int;
+using gid_t = int;
+using pid_t = int;
+#endif  // !GCC

-inline bool operator==(const StringView& x, const StringView& y) {
-  if (x.size() != y.size())
-    return false;
-  if (x.size() == 0)
-    return true;
-  return memcmp(x.data(), y.data(), x.size()) == 0;
-}
+using ssize_t = intptr_t;

-inline bool operator!=(const StringView& x, const StringView& y) {
-  return !(x == y);
-}
+#endif  // __MINGW32__

-inline bool operator<(const StringView& x, const StringView& y) {
-  auto size = std::min(x.size(), y.size());
-  if (size == 0)
-    return x.size() < y.size();
-  int result = memcmp(x.data(), y.data(), size);
-  return result < 0 || (result == 0 && x.size() < y.size());
-}
+#endif  // OS_WIN

-inline bool operator>=(const StringView& x, const StringView& y) {
-  return !(x < y);
-}
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+// From libcutils' android_filesystem_config.h
+#ifndef AID_ROOT
+#define AID_ROOT 0
+#endif
+#ifndef AID_STATSD
+#define AID_STATSD 1066
+#endif
+#ifndef AID_SHELL
+#define AID_SHELL 2000
+#endif
+#endif

-inline bool operator>(const StringView& x, const StringView& y) {
-  return y < x;
-}
+namespace perfetto {
+namespace base {

-inline bool operator<=(const StringView& x, const StringView& y) {
-  return !(y < x);
-}
+// The machine ID used in the tracing core.
+using MachineID = uint32_t;
+// The default value reserved for the host trace.
+constexpr MachineID kDefaultMachineID = 0;
+
+constexpr uid_t kInvalidUid = static_cast<uid_t>(-1);
+constexpr pid_t kInvalidPid = static_cast<pid_t>(-1);

 }  // namespace base
 }  // namespace perfetto

-template <>
-struct std::hash<::perfetto::base::StringView> {
-  size_t operator()(const ::perfetto::base::StringView& sv) const {
-    return static_cast<size_t>(sv.Hash());
-  }
-};
-
-#endif  // INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_H_
+#endif  // INCLUDE_PERFETTO_EXT_BASE_SYS_TYPES_H_
 /*
  * Copyright (C) 2018 The Android Open Source Project
  *
@@ -652,311 +628,245 @@ struct std::hash<::perfetto::base::StringView> {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_STRING_UTILS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_STRING_UTILS_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_

-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
+#include <fcntl.h>  // For mode_t & O_RDONLY/RDWR. Exists also on Windows.
+#include <stddef.h>
+#include <stdint.h>

-#include <charconv>
-#include <cinttypes>
+#include <functional>
+#include <memory>
 #include <optional>
 #include <string>
-#include <string_view>
-#include <system_error>
 #include <vector>

-// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
-
-namespace perfetto {
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/export.h"
+// gen_amalgamated expanded: #include "perfetto/base/status.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/sys_types.h"
+
+namespace perfetto {
 namespace base {

-inline char Lowercase(char c) {
-  return ('A' <= c && c <= 'Z') ? static_cast<char>(c - ('A' - 'a')) : c;
-}
+class TaskRunner;

-inline char Uppercase(char c) {
-  return ('a' <= c && c <= 'z') ? static_cast<char>(c + ('A' - 'a')) : c;
-}
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+using FileOpenMode = int;
+inline constexpr char kDevNull[] = "NUL";
+inline constexpr char kFopenReadFlag[] = "r";
+#else
+using FileOpenMode = mode_t;
+inline constexpr char kDevNull[] = "/dev/null";
+inline constexpr char kFopenReadFlag[] = "re";
+#endif

-// Returns true if the character is an ASCII whitespace character.
-// This is a locale-independent version that only considers ASCII whitespace:
-// space (0x20), tab (0x09), newline (0x0A), vertical tab (0x0B),
-// form feed (0x0C), and carriage return (0x0D).
-inline bool IsSpace(char c) {
-  return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' ||
-         c == '\r';
-}
+constexpr FileOpenMode kFileModeInvalid = static_cast<FileOpenMode>(-1);

-inline std::optional<uint32_t> CStringToUInt32(const char* s, int base = 10) {
-  char* endptr = nullptr;
-  auto value = static_cast<uint32_t>(strtoul(s, &endptr, base));
-  return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
-}
+// Cross-platform variant of ReadFileDescriptor() that takes a PlatformHandle.
+// On Windows normalizes ERROR_BROKEN_PIPE to EOF so behavior matches POSIX.
+bool ReadPlatformHandle(PlatformHandle, std::string* out);

-inline std::optional<int32_t> CStringToInt32(const char* s, int base = 10) {
-  char* endptr = nullptr;
-  auto value = static_cast<int32_t>(strtol(s, &endptr, base));
-  return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
-}
+// Reads from |fd|, appending what is currently available into |*out|.
+// Returns:
+//  True: EOF reached (all writers of |fd| have closed their end).
+//  False: read error. On a non-blocking |fd| this includes EAGAIN (no data
+//    currently available but writers are still alive); callers can check
+//    IsAgain(errno) and retry on the next readability notification.
+bool ReadFileDescriptor(int fd, std::string* out);

-// Note: it saturates to 7fffffffffffffff if parsing a hex number >= 0x8000...
-inline std::optional<int64_t> CStringToInt64(const char* s, int base = 10) {
-  char* endptr = nullptr;
-  auto value = static_cast<int64_t>(strtoll(s, &endptr, base));
-  return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
-}
+// Convenience wrapper around ReadFileDescriptor() that takes a FILE*.
+bool ReadFileStream(FILE* f, std::string* out);

-inline std::optional<uint64_t> CStringToUInt64(const char* s, int base = 10) {
-  char* endptr = nullptr;
-  auto value = static_cast<uint64_t>(strtoull(s, &endptr, base));
-  return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
-}
+// Opens |path| read-only and reads its contents into |*out|.
+// Returns false if the file cannot be opened.
+bool ReadFile(const std::string& path, std::string* out);

-double StrToD(const char* nptr, char** endptr);
+// A wrapper around read(2). It deals with Linux vs Windows includes. It also
+// deals with handling EINTR. Has the same semantics of UNIX's read(2).
+ssize_t Read(int fd, void* dst, size_t dst_size);

-inline std::optional<double> CStringToDouble(const char* s) {
-  char* endptr = nullptr;
-  double value = StrToD(s, &endptr);
-  std::optional<double> result(std::nullopt);
-  if (*s != '\0' && *endptr == '\0')
-    result = value;
-  return result;
-}
+// Call write until all data is written or an error is detected.
+//
+// man 2 write:
+//   If a write() is interrupted by a signal handler before any bytes are
+//   written, then the call fails with the error EINTR; if it is
+//   interrupted after at least one byte has been written, the call
+//   succeeds, and returns the number of bytes written.
+ssize_t WriteAll(int fd, const void* buf, size_t count);

-inline std::optional<uint32_t> StringToUInt32(const std::string& s,
-                                              int base = 10) {
-  return CStringToUInt32(s.c_str(), base);
-}
+// Copies all data from |fd_in| to |fd_out|. Saves the offset of |fd_in|,
+// rewinds it to the beginning, copies the content, and restores the offset.
+// |fd_in| can't be a pipe, socket of FIFO.
+base::Status CopyFileContents(int fd_in, int fd_out);

-inline std::optional<int32_t> StringToInt32(const std::string& s,
-                                            int base = 10) {
-  return CStringToInt32(s.c_str(), base);
-}
+ssize_t WriteAllHandle(PlatformHandle, const void* buf, size_t count);

-inline std::optional<uint64_t> StringToUInt64(const std::string& s,
-                                              int base = 10) {
-  return CStringToUInt64(s.c_str(), base);
-}
+ScopedFile OpenFile(const std::string& path,
+                    int flags,
+                    FileOpenMode = kFileModeInvalid);
+ScopedFstream OpenFstream(const std::string& path, const std::string& mode);

-inline std::optional<int64_t> StringToInt64(const std::string& s,
-                                            int base = 10) {
-  return CStringToInt64(s.c_str(), base);
-}
+// This is an alias for close(). It's to avoid leaking windows.h in headers.
+// Exported because ScopedFile is used in the /include/ext API by Chromium
+// component builds.
+int PERFETTO_EXPORT_COMPONENT CloseFile(int fd);

-inline std::optional<double> StringToDouble(const std::string& s) {
-  return CStringToDouble(s.c_str());
-}
+bool FlushFile(int fd);

-template <typename T>
-inline std::optional<T> StringViewToNumber(const base::StringView& sv,
-                                           int base = 10) {
-  // std::from_chars() does not regonize the leading '+' character and only
-  // recognizes '-' so remove the '+' if it exists to avoid errors and match
-  // the behavior of the other string conversion utilities above.
-  size_t start_offset = !sv.empty() && sv.at(0) == '+' ? 1 : 0;
-  T value;
-  auto result =
-      std::from_chars(sv.begin() + start_offset, sv.end(), value, base);
-  if (result.ec == std::errc() && result.ptr == sv.end()) {
-    return value;
-  } else {
-    return std::nullopt;
-  }
-}
+// Duplicates an open descriptor. The duplicate is not inherited across exec.
+ScopedFile DupFile(int fd);

-inline std::optional<uint32_t> StringViewToUInt32(const base::StringView& sv,
-                                                  int base = 10) {
-  // std::from_chars() does not recognize the leading '-' character for
-  // unsigned conversions, but strtol does. To Mimic the behavior of strtol,
-  // attempt a signed converion if we see a leading '-', and then cast the
-  // result back to unsigned.
-  if (sv.size() > 0 && sv.at(0) == '-') {
-    return static_cast<std::optional<uint32_t> >(
-        StringViewToNumber<int32_t>(sv, base));
-  } else {
-    return StringViewToNumber<uint32_t>(sv, base);
-  }
-}
+// These queries follow symbolic links. False also means a path could not be
+// inspected. IsSameFile compares file identities, so it detects hard links.
+bool IsRegularFile(const std::string& path);
+bool IsSameFile(const std::string& first, const std::string& second);

-inline std::optional<int32_t> StringViewToInt32(const base::StringView& sv,
-                                                int base = 10) {
-  return StringViewToNumber<int32_t>(sv, base);
-}
+// Moves the file offset to |offset| bytes from the beginning of the file.
+// Returns false if |offset| cannot be represented by the platform or the seek
+// fails.
+bool SeekFile(int fd, uint64_t offset);

-inline std::optional<uint64_t> StringViewToUInt64(const base::StringView& sv,
-                                                  int base = 10) {
-  // std::from_chars() does not recognize the leading '-' character for
-  // unsigned conversions, but strtol does. To Mimic the behavior of strtol,
-  // attempt a signed converion if we see a leading '-', and then cast the
-  // result back to unsigned.
-  if (sv.size() > 0 && sv.at(0) == '-') {
-    return static_cast<std::optional<uint64_t> >(
-        StringViewToNumber<int64_t>(sv, base));
-  } else {
-    return StringViewToNumber<uint64_t>(sv, base);
-  }
-}
+// Changes the size of an open file to |size| bytes.
+// Returns false if |size| cannot be represented by the platform or truncation
+// fails.
+bool TruncateFile(int fd, uint64_t size);

-inline std::optional<int64_t> StringViewToInt64(const base::StringView& sv,
-                                                int base = 10) {
-  return StringViewToNumber<int64_t>(sv, base);
-}
+// Returns true if mkdir succeeds, false if it fails (see errno in that case).
+// `mode` is the permission bits for the new directory; it is ignored on
+// Windows.
+bool Mkdir(const std::string& path, uint32_t mode = 0755);

-// TODO: As of Clang 19.0 std::from_chars is unimplemented for type double
-// despite being part of C++17 standard, and already being supported by GCC and
-// MSVC. Enable this once we have double support in Clang.
-// inline std::optional<double> StringViewToDouble(const base::StringView& sv) {
-//   return StringViewToNumber<double>(sv);
-// }
+// Calls rmdir() on UNIX, _rmdir() on Windows.
+bool Rmdir(const std::string& path);

-bool StartsWith(const std::string& str, const std::string& prefix);
-bool EndsWith(const std::string& str, const std::string& suffix);
-bool StartsWithAny(const std::string& str,
-                   const std::vector<std::string>& prefixes);
-bool Contains(const std::string& haystack, const std::string& needle);
-bool Contains(const std::string& haystack, char needle);
-bool Contains(const std::vector<std::string>& haystack,
-              const std::string& needle);
-size_t Find(const StringView& needle, const StringView& haystack);
-bool CaseInsensitiveEqual(const std::string& first, const std::string& second);
-std::string Join(const std::vector<std::string>& parts,
-                 const std::string& delim);
-std::vector<std::string> SplitString(const std::string& text,
-                                     const std::string& delimiter);
-std::string StripPrefix(const std::string& str, const std::string& prefix);
-std::string StripSuffix(const std::string& str, const std::string& suffix);
-std::string_view TrimWhitespace(std::string_view str);
-std::string TrimWhitespace(const std::string& str);
-std::string_view TrimWhitespace(const char* str);
-std::string ToLower(const std::string& str);
-std::string ToUpper(const std::string& str);
-std::string StripChars(const std::string& str,
-                       const std::string& chars,
-                       char replacement);
-std::string ToHex(const char* data, size_t size);
-inline std::string ToHex(const std::string& s) {
-  return ToHex(s.c_str(), s.size());
-}
-std::string IntToHexString(uint32_t number);
-std::string Uint64ToHexString(uint64_t number);
-std::string Uint64ToHexStringNoPrefix(uint64_t number);
-std::string ReplaceAll(std::string str,
-                       const std::string& to_replace,
-                       const std::string& replacement);
+// Removes a file: unlink() on UNIX, _unlink() on Windows. Takes a const char*
+// and is async-signal-safe on POSIX, so it's callable from a signal handler.
+bool Unlink(const char* path);

-// Checks if all characters in the input string view `str` are ASCII.
-//
-// If so, the function returns true and `output` is not modified.
-// If `str` contains non-ASCII characters, the function returns false,
-// removes invalid UTF-8 characters from `str`, and stores the result in
-// `output`.
-bool CheckAsciiAndRemoveInvalidUTF8(base::StringView str, std::string& output);
+// Wrapper around access(path, F_OK).
+bool FileExists(const std::string& path);

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-bool WideToUTF8(const std::wstring& source, std::string& output);
-bool UTF8ToWide(const std::string& source, std::wstring& output);
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+// Returns true if the path exists and is a directory.
+bool DirectoryExists(const std::string& path);

-// A BSD-style strlcpy without the return value.
-// Copies at most |dst_size|-1 characters. Unlike strncpy, it always \0
-// terminates |dst|, as long as |dst_size| is not 0.
-// Unlike strncpy and like strlcpy it does not zero-pad the rest of |dst|.
-// Returns nothing. The BSD strlcpy returns the size of |src|, which might
-// be > |dst_size|. Anecdotal experience suggests people assume the return value
-// is the number of bytes written in |dst|. That assumption can lead to
-// dangerous bugs.
-// In order to avoid being subtly uncompliant with strlcpy AND avoid misuse,
-// the choice here is to return nothing.
-inline void StringCopy(char* dst, const char* src, size_t dst_size) {
-  for (size_t i = 0; i < dst_size; ++i) {
-    if ((dst[i] = src[i]) == '\0') {
-      return;  // We hit and copied the null terminator.
-    }
-  }
+// Gets the extension for a filename. If the file has two extensions, returns
+// only the last one (foo.pb.gz => .gz). Returns empty string if there is no
+// extension.
+std::string GetFileExtension(const std::string& filename);

-  // We were left off at dst_size. We over copied 1 byte. Null terminate.
-  if (PERFETTO_LIKELY(dst_size > 0))
-    dst[dst_size - 1] = 0;
-}
+// Returns the basename component of a path (the final component after the last
+// directory separator). Behaves like man 2 basename, but works with both '/'
+// and '\' separators for cross-platform compatibility.
+// Examples:
+//   Basename("/usr/bin/ls") => "ls"
+//   Basename("/usr/bin/") => "bin"
+//   Basename("/") => "/"
+//   Basename("foo") => "foo"
+//   Basename("") => "."
+//   Basename("C:\\Windows\\System32") => "System32"
+std::string Basename(const std::string& path);

-// Like snprintf() but returns the number of chars *actually* written (without
-// counting the null terminator) NOT "the number of chars which would have been
-// written to the final string if enough  space had been available".
-// This should be used in almost all cases when the caller uses the return value
-// of snprintf(). If the return value is not used, there is no benefit in using
-// this wrapper, as this just calls snprintf() and mangles the return value.
-// It always null-terminates |dst| (even in case of errors), unless
-// |dst_size| == 0.
+// Returns the directory component of a path (everything up to but not
+// including the final component). Behaves like man 2 dirname, but works with
+// both '/' and '\' separators for cross-platform compatibility.
 // Examples:
-//   SprintfTrunc(x, 4, "123whatever"): returns 3 and writes "123\0".
-//   SprintfTrunc(x, 4, "123"): returns 3 and writes "123\0".
-//   SprintfTrunc(x, 3, "123"): returns 2 and writes "12\0".
-//   SprintfTrunc(x, 2, "123"): returns 1 and writes "1\0".
-//   SprintfTrunc(x, 1, "123"): returns 0 and writes "\0".
-//   SprintfTrunc(x, 0, "123"): returns 0 and writes nothing.
-// NOTE: This means that the caller has no way to tell when truncation happens
-//   vs the edge case of *just* fitting in the buffer.
-size_t SprintfTrunc(char* dst, size_t dst_size, const char* fmt, ...)
-    PERFETTO_PRINTF_FORMAT(3, 4);
+//   Dirname("/usr/bin/ls") => "/usr/bin"
+//   Dirname("/usr/bin") => "/usr"
+//   Dirname("/") => "/"
+//   Dirname("foo") => "."
+//   Dirname("") => "."
+//   Dirname("C:\\Windows\\System32") => "C:\\Windows"
+std::string Dirname(const std::string& path);

-// Line number starts from 1
-struct LineWithOffset {
-  base::StringView line;
-  uint32_t line_offset;
-  uint32_t line_num;
-};
+// Returns the length of the leading root component of a path: "/" on UNIX,
+// "C:\" or "\\" (UNC) on Windows. Returns 0 if the path is relative. Like
+// Basename() and Dirname(), both '/' and '\' are recognized on all platforms.
+// Stripping the returned prefix turns the path into a relative one.
+// Examples:
+//   PathRootPrefixLength("/usr/bin/ls") => 1
+//   PathRootPrefixLength("//usr/bin") => 2
+//   PathRootPrefixLength("C:\\Windows") => 3
+//   PathRootPrefixLength("C:foo") => 0 (relative to the current dir of drive
+//   C:) PathRootPrefixLength("foo/bar") => 0
+size_t PathRootPrefixLength(const std::string& path);

-// For given string and offset Pfinds a line with character for
-// which offset points, what number is this line (starts from 1), and the offset
-// inside this line. returns std::nullopt if the offset points to
-// line break character or exceeds string length.
-std::optional<LineWithOffset> FindLineWithOffset(base::StringView str,
-                                                 uint32_t offset);
+// Returns true if the path starts with a root component. See
+// PathRootPrefixLength().
+bool IsAbsolutePath(const std::string& path);

-// A helper class to facilitate construction and usage of write-once stack
-// strings.
-// Example usage:
-//   StackString<32> x("format %d %s", 42, string_arg);
-//   TakeString(x.c_str() | x.string_view() | x.ToStdString());
-// Rather than char x[32] + sprintf.
-// Advantages:
-// - Avoids useless zero-fills caused by people doing `char buf[32] {}` (mainly
-//   by fearing unknown snprintf failure modes).
-// - Makes the code more robust in case of snprintf truncations (len() and
-//  string_view() will return the truncated length, unlike snprintf).
-template <size_t N>
-class StackString {
+// Puts the path to all files under |dir_path| in |output|, recursively walking
+// subdirectories. File paths are relative to |dir_path|. Only files are
+// included, not directories. Path separator is always '/', even on windows (not
+// '\').
+base::Status ListFilesRecursive(const std::string& dir_path,
+                                std::vector<std::string>& output);
+
+// Lists immediate subdirectories in |dir_path| (non-recursive). Directory names
+// are relative to |dir_path| and do not include the path separator. Returns
+// only directories, not files. Works on both Unix and Windows.
+base::Status ListDirectories(const std::string& dir_path,
+                             std::vector<std::string>& output);
+
+// Sets |path|'s owner group to |group_name| and permission mode bits to
+// |mode_bits|.
+base::Status SetFilePermissions(const std::string& path,
+                                const std::string& group_name,
+                                const std::string& mode_bits);
+
+// Returns the size of the file located at |path|, or nullopt in case of error.
+std::optional<uint64_t> GetFileSize(const std::string& path);
+
+// Returns the size of the open file |fd|, or nullopt in case of error.
+std::optional<uint64_t> GetFileSize(PlatformHandle fd);
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+// On Windows PlatformHandle is a HANDLE, not a file descriptor. This overload
+// accepts the CRT file descriptors returned by base::OpenFile(). On other
+// platforms PlatformHandle is an int, so the overload above already covers it.
+std::optional<uint64_t> GetFileSize(int fd);
+#endif
+
+// This class uses inotify (on Linux/Android) to watch for the creation of
+// files in the filesystem. When the specified file is created, it triggers a
+// callback function.
+// Destroying the returned unique_ptr will automatically unregister the watch.
+//
+// Note: This only works with filesystem paths (not abstract sockets or other
+// special file types).
+// It's only supported on Linux and Android, it's a no-op (returns nullptr) on
+// other platforms.
+//
+// Usage:
+//   auto watch = LinuxFileWatch::WatchFileCreation(
+//       task_runner, "/tmp/my_file", []() {
+//         // Called when /tmp/my_file is created
+//       });
+class LinuxFileWatch {
  public:
-  explicit PERFETTO_PRINTF_FORMAT(/* 1=this */ 2, 3)
-      StackString(const char* fmt, ...) {
-    buf_[0] = '\0';
-    va_list args;
-    va_start(args, fmt);
-    int res = vsnprintf(buf_, sizeof(buf_), fmt, args);
-    va_end(args);
-    buf_[sizeof(buf_) - 1] = '\0';
-    len_ = res < 0 ? 0 : std::min(static_cast<size_t>(res), sizeof(buf_) - 1);
-  }
+  // Creates a watcher for file creation. Returns nullptr if the path is not a
+  // valid filesystem path or if the platform doesn't support inotify. The
+  // callback will be invoked on the provided TaskRunner when the file is
+  // created.
+  static std::unique_ptr<LinuxFileWatch> WatchFileCreation(
+      TaskRunner*,
+      const char* path,
+      std::function<void()> callback);

-  StringView string_view() const { return StringView(buf_, len_); }
-  std::string ToStdString() const { return std::string(buf_, len_); }
-  const char* c_str() const { return buf_; }
-  size_t len() const { return len_; }
-  char* mutable_data() { return buf_; }
+  virtual ~LinuxFileWatch();

- private:
-  char buf_[N];
-  size_t len_ = 0;  // Does not include the \0.
+ protected:
+  LinuxFileWatch() = default;
 };

 }  // namespace base
 }  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_STRING_UTILS_H_
+#endif  // INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_
+// gen_amalgamated begin header: include/perfetto/ext/base/string_splitter.h
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -971,185 +881,239 @@ class StackString {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/android_utils.h"
-
-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_STRING_SPLITTER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_STRING_SPLITTER_H_

 #include <string>

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#include <sys/system_properties.h>
-#endif
-
-// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+namespace perfetto {
+namespace base {

-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) &&  \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL) && \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
-#include <sys/utsname.h>
-#include <unistd.h>
-#endif
+// C++ version of strtok(). Splits a string without making copies or any heap
+// allocations. Destructs the original string passed in input.
+// Supports the special case of using \0 as a delimiter.
+// The token returned in output are valid as long as the input string is valid.
+class StringSplitter {
+ public:
+  // Whether an empty string (two delimiters side-to-side) is a valid token.
+  enum class EmptyTokenMode {
+    DISALLOW_EMPTY_TOKENS,
+    ALLOW_EMPTY_TOKENS,

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#include <sys/sysinfo.h>
-#endif
+    DEFAULT = DISALLOW_EMPTY_TOKENS,
+  };

-namespace perfetto {
-namespace base {
+  // Can take ownership of the string if passed via std::move(), e.g.:
+  // StringSplitter(std::move(str), '\n');
+  StringSplitter(std::string,
+                 char delimiter,
+                 EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  // Splits a C-string. The input string will be forcefully null-terminated (so
+  // str[size - 1] should be == '\0' or the last char will be truncated).
+  StringSplitter(char* str,
+                 size_t size,
+                 char delimiter,
+                 EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);

-std::string GetAndroidProp(const char* name) {
-  std::string ret;
-#if __ANDROID_API__ >= 26
-  const prop_info* pi = __system_property_find(name);
-  if (!pi) {
-    return ret;
-  }
-  __system_property_read_callback(
-      pi,
-      [](void* dst_void, const char*, const char* value, uint32_t) {
-        std::string& dst = *static_cast<std::string*>(dst_void);
-        dst = value;
-      },
-      &ret);
-#else  // __ANDROID_API__ < 26
-  char value_buf[PROP_VALUE_MAX];
-  int len = __system_property_get(name, value_buf);
-  if (len > 0 && static_cast<size_t>(len) < sizeof(value_buf)) {
-    ret = std::string(value_buf, static_cast<size_t>(len));
-  }
-#endif
-  return ret;
-}
+  // Splits the current token from an outer StringSplitter instance. This is to
+  // chain splitters as follows:
+  // for (base::StringSplitter lines(x, '\n'); ss.Next();)
+  //   for (base::StringSplitter words(&lines, ' '); words.Next();)
+  StringSplitter(StringSplitter*,
+                 char delimiter,
+                 EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);

-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  // Returns true if a token is found (in which case it will be stored in
+  // cur_token()), false if no more tokens are found.
+  bool Next();

-Utsname GetUtsname() {
-  Utsname utsname_info;
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) &&  \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL) && \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
-  struct utsname uname_info;
-  if (uname(&uname_info) == 0) {
-    utsname_info.sysname = uname_info.sysname;
-    utsname_info.version = uname_info.version;
-    utsname_info.machine = uname_info.machine;
-    utsname_info.release = uname_info.release;
-  } else {
-    PERFETTO_ELOG("Unable to read Utsname information");
-  }
-#endif  // !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  return utsname_info;
-}
+  // Returns the next token if found (in which case it will be stored in
+  // cur_token()), nullptr if no more tokens are found.
+  char* NextToken() { return Next() ? cur_token() : nullptr; }

-SystemInfo GetSystemInfo() {
-  SystemInfo info;
+  // Returns the current token iff last call to Next() returned true. In this
+  // case it guarantees that the returned string is always null terminated.
+  // In all other cases (before the 1st call to Next() and after Next() returns
+  // false) returns nullptr.
+  char* cur_token() { return cur_; }

-  info.timezone_off_mins = GetTimezoneOffsetMins();
+  // Returns the length of the current token (excluding the null terminator).
+  size_t cur_token_size() const { return cur_size_; }

-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) &&  \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL) && \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
+  // Return the untokenized remainder of the input string that occurs after the
+  // current token.
+  char* remainder() { return next_; }

-  info.utsname_info = GetUtsname();
-  info.page_size = static_cast<uint32_t>(sysconf(_SC_PAGESIZE));
-  info.num_cpus = static_cast<uint32_t>(sysconf(_SC_NPROCESSORS_CONF));
+  // Returns the size of the untokenized input
+  size_t remainder_size() { return static_cast<size_t>(end_ - next_); }

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  // Use the Linux-specific sysinfo() system call on Linux and Android.
-  // https://man7.org/linux/man-pages/man2/sysinfo.2.html
-  struct sysinfo sys_info;
-  if (sysinfo(&sys_info) == 0) {
-    info.system_ram_bytes =
-        static_cast<uint64_t>(sys_info.totalram) * sys_info.mem_unit;
-  }
-#else
-  // POSIX Fallback (macOS, BSD, etc.): Use sysconf() to get physical pages.
-  long pages = sysconf(_SC_PHYS_PAGES);
-  if (pages > 0 && info.page_size.has_value()) {
-    info.system_ram_bytes = static_cast<uint64_t>(pages) * (*info.page_size);
-  }
-#endif
-#endif  // !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  info.android_build_fingerprint = GetAndroidProp("ro.build.fingerprint");
-  if (info.android_build_fingerprint.empty()) {
-    PERFETTO_ELOG("Unable to read ro.build.fingerprint");
+ private:
+  StringSplitter(const StringSplitter&) = delete;
+  StringSplitter& operator=(const StringSplitter&) = delete;
+  void Initialize(char* str, size_t size);
+
+  std::string str_;
+  char* cur_;
+  size_t cur_size_;
+  char* next_;
+  char* end_;  // STL-style, points one past the last char.
+  const char delimiter_;
+  const EmptyTokenMode empty_token_mode_;
+};
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_STRING_SPLITTER_H_
+// gen_amalgamated begin header: include/perfetto/ext/base/string_utils.h
+// gen_amalgamated begin header: include/perfetto/ext/base/string_view.h
+// gen_amalgamated begin header: include/perfetto/ext/base/fnv_hash.h
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_FNV_HASH_H_
+#define INCLUDE_PERFETTO_EXT_BASE_FNV_HASH_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+
+namespace perfetto::base {
+
+// A helper class which computes a 64-bit hash of the input data.
+// The algorithm used is FNV-1a as it is fast and easy to implement and has
+// relatively few collisions.
+// WARNING: This hash function should not be used for any cryptographic purpose.
+class FnvHasher {
+ public:
+  // Creates an empty hash object
+  constexpr FnvHasher() = default;
+
+  // Hashes a numeric value.
+  template <
+      typename T,
+      typename std::enable_if<std::is_arithmetic<T>::value, bool>::type = true>
+  void Update(T data) {
+    Update(reinterpret_cast<const char*>(&data), sizeof(data));
   }

-  info.android_device_manufacturer = GetAndroidProp("ro.product.manufacturer");
-  if (info.android_device_manufacturer.empty()) {
-    PERFETTO_ELOG("Unable to read ro.product.manufacturer");
+  constexpr void Update(char c) { return Update(&c, 1); }
+
+  // Using the loop instead of "Update(str, strlen(str))" to avoid looping twice
+  constexpr void Update(const char* str) {
+    for (const auto* p = str; *p; ++p)
+      Update(*p);
   }

-  std::string sdk_str_value = GetAndroidProp("ro.build.version.sdk");
-  info.android_sdk_version = StringToUInt64(sdk_str_value);
-  if (!info.android_sdk_version.has_value()) {
-    PERFETTO_ELOG("Unable to read ro.build.version.sdk");
+  // Hashes a byte array.
+  constexpr void Update(const char* data, size_t size) {
+    for (size_t i = 0; i < size; i++) {
+      result_ ^= static_cast<uint8_t>(data[i]);
+      // Note: Arithmetic overflow of unsigned integers is well defined in C++
+      // standard unlike signed integers.
+      // https://stackoverflow.com/a/41280273
+      result_ *= kFnv1a64Prime;
+    }
   }

-  info.android_soc_model = GetAndroidProp("ro.soc.model");
-  if (info.android_soc_model.empty()) {
-    PERFETTO_ELOG("Unable to read ro.soc.model");
+  // Allow hashing anything that has `data` and `size` and has the kHashable
+  // trait (e.g., base::StringView).
+  template <typename T, typename = std::enable_if_t<T::kHashable>>
+  constexpr void Update(const T& t) {
+    if constexpr (std::is_member_function_pointer_v<decltype(&T::data)>) {
+      Update(t.data(), t.size());
+    } else {
+      Update(t.data, t.size);
+    }
   }

-  // guest_soc model is not always present
-  info.android_guest_soc_model = GetAndroidProp("ro.boot.guest_soc.model");
+  constexpr void Update(std::string_view s) { Update(s.data(), s.size()); }

-  info.android_hardware_revision = GetAndroidProp("ro.boot.hardware.revision");
-  if (info.android_hardware_revision.empty()) {
-    PERFETTO_ELOG("Unable to read ro.boot.hardware.revision");
-  }
+  constexpr uint64_t digest() const { return result_; }

-  info.android_storage_model = GetAndroidProp("ro.boot.hardware.ufs");
-  if (info.android_storage_model.empty()) {
-    PERFETTO_ELOG("Unable to read ro.boot.hardware.ufs");
+  // Usage:
+  // uint64_t hashed_value =
+  //    FnvHasher::Combine(33, false, "ABC", 458L, 3u, 'x');
+  template <typename... Ts>
+  static constexpr uint64_t Combine(Ts&&... args) {
+    FnvHasher hasher;
+    hasher.UpdateAll(std::forward<Ts>(args)...);
+    return hasher.digest();
   }

-  info.android_ram_model = GetAndroidProp("ro.boot.hardware.ddr");
-  if (info.android_ram_model.empty()) {
-    PERFETTO_ELOG("Unable to read ro.boot.hardware.ddr");
+  // Creates a hasher with `args` already hashed.
+  //
+  // Usage:
+  // FnvHasher partial = FnvHasher::CreatePartial(33, false, "ABC", 458L);
+  template <typename... Ts>
+  static constexpr FnvHasher CreatePartial(Ts&&... args) {
+    FnvHasher hasher;
+    hasher.UpdateAll(std::forward<Ts>(args)...);
+    return hasher;
   }

-  info.android_serial_console = GetAndroidProp("init.svc.console");
-  if (info.android_serial_console.empty()) {
-    PERFETTO_ELOG("Unable to read init.svc.console");
+  // `hasher.UpdateAll(33, false, "ABC")` is shorthand for:
+  // `hasher.Update(33); hasher.Update(false); hasher.Update("ABC");`
+  constexpr void UpdateAll() {}
+
+  template <typename T, typename... Ts>
+  constexpr void UpdateAll(T&& arg, Ts&&... args) {
+    Update(arg);
+    UpdateAll(std::forward<Ts>(args)...);
   }
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)

-  return info;
-}
+ private:
+  static constexpr uint64_t kFnv1a64OffsetBasis = 0xcbf29ce484222325;
+  static constexpr uint64_t kFnv1a64Prime = 0x100000001b3;

-std::string GetPerfettoMachineName() {
-  const char* env_name = getenv("PERFETTO_MACHINE_NAME");
-  if (env_name) {
-    return env_name;
+  uint64_t result_ = kFnv1a64OffsetBasis;
+};
+
+// base::FnvHash uses base::FnvHasher for integer values and falls back to
+// std::hash for other types. This is needed as std::hash for integers is just
+// the identity function and Perfetto uses open-addressing hash table, which are
+// very sensitive to hash quality and are known to degrade in performance
+// when using std::hash.
+template <typename T>
+struct FnvHash {
+  // Version for ints, using base::FnvHasher.
+  template <typename U = T>
+  auto operator()(const U& x) ->
+      typename std::enable_if<std::is_arithmetic<U>::value, size_t>::type
+      const {
+    return FnvHasher::Combine(x);
   }
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  std::string name = base::GetAndroidProp("traced.machine_name");
-  if (name.empty()) {
-    name = GetUtsname().sysname;
+
+  // Version for non-ints, falling back to std::hash.
+  template <typename U = T>
+  auto operator()(const U& x) ->
+      typename std::enable_if<!std::is_arithmetic<U>::value, size_t>::type
+      const {
+    return std::hash<U>()(x);
   }
-  return name;
-#else
-  return GetUtsname().sysname;
-#endif
-}
+};

-}  // namespace base
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/base64.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/base64.h
-// gen_amalgamated begin header: include/perfetto/ext/base/utils.h
-// gen_amalgamated begin header: include/perfetto/ext/base/sys_types.h
+}  // namespace perfetto::base
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_FNV_HASH_H_
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -1164,73 +1128,205 @@ std::string GetPerfettoMachineName() {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_SYS_TYPES_H_
-#define INCLUDE_PERFETTO_EXT_BASE_SYS_TYPES_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_H_
+#define INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_H_

-// This headers deals with sys types commonly used in the codebase that are
-// missing on Windows.
+#include <string.h>

-#include <sys/types.h>  // IWYU pragma: export
-#include <cstdint>
+#include <algorithm>
+#include <string>
+#include <vector>

 // gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/fnv_hash.h"

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+namespace perfetto {
+namespace base {

-#ifdef __MINGW32__
+// A string-like object that refers to a non-owned piece of memory.
+// Strings are internally NOT null terminated.
+class StringView {
+ public:
+  // Allow hashing with base::FnvHash.
+  static constexpr bool kHashable = true;
+  static constexpr size_t npos = static_cast<size_t>(-1);

-using uid_t = int;
-using gid_t = int;
+  StringView() : data_(nullptr), size_(0) {}
+  StringView(const StringView&) = default;
+  StringView& operator=(const StringView&) = default;
+  StringView(const char* data, size_t size) : data_(data), size_(size) {
+    PERFETTO_DCHECK(size == 0 || data != nullptr);
+  }

-#else
+  // Allow implicit conversion from any class that has a |data| and |size| field
+  // and has the kConvertibleToStringView trait (e.g., protozero::ConstChars).
+  template <typename T, typename = std::enable_if<T::kConvertibleToStringView>>
+  StringView(const T& x) : StringView(x.data, x.size) {
+    PERFETTO_DCHECK(x.size == 0 || x.data != nullptr);
+  }

-#if !PERFETTO_BUILDFLAG(PERFETTO_COMPILER_GCC)
-// MinGW has these. clang-cl and MSVC, which use just the Windows SDK, don't.
-using uid_t = int;
-using gid_t = int;
-using pid_t = int;
-#endif  // !GCC
+  // Creates a StringView from a null-terminated C string.
+  // Deliberately not "explicit".
+  StringView(const char* cstr) : data_(cstr), size_(strlen(cstr)) {
+    PERFETTO_DCHECK(cstr != nullptr);
+  }

-#if defined(_WIN64)
-using ssize_t = int64_t;
-#else
-using ssize_t = long;
-#endif  // _WIN64
+  StringView(std::string_view str) : data_(str.data()), size_(str.size()) {}

-#endif  // __MINGW32__
+  // This instead has to be explicit, as creating a StringView out of a
+  // std::string can be subtle.
+  explicit StringView(const std::string& str)
+      : data_(str.data()), size_(str.size()) {}

-#endif  // OS_WIN
+  bool empty() const { return size_ == 0; }
+  size_t size() const { return size_; }
+  const char* data() const { return data_; }
+  const char* begin() const { return data_; }
+  const char* end() const { return data_ + size_; }

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-// From libcutils' android_filesystem_config.h
-#ifndef AID_ROOT
-#define AID_ROOT 0
-#endif
-#ifndef AID_STATSD
-#define AID_STATSD 1066
-#endif
-#ifndef AID_SHELL
-#define AID_SHELL 2000
-#endif
+  char at(size_t pos) const {
+    PERFETTO_DCHECK(pos < size_);
+    return data_[pos];
+  }
+
+  size_t find(char c, size_t start_pos = 0) const {
+    for (size_t i = start_pos; i < size_; ++i) {
+      if (data_[i] == c)
+        return i;
+    }
+    return npos;
+  }
+
+  size_t find(const StringView& str, size_t start_pos = 0) const {
+    if (start_pos > size())
+      return npos;
+    auto it = std::search(begin() + start_pos, end(), str.begin(), str.end());
+    size_t pos = static_cast<size_t>(it - begin());
+    return pos + str.size() <= size() ? pos : npos;
+  }
+
+  size_t find(const char* str, size_t start_pos = 0) const {
+    return find(StringView(str), start_pos);
+  }
+
+  size_t rfind(char c) const {
+    for (size_t i = size_; i > 0; --i) {
+      if (data_[i - 1] == c)
+        return i - 1;
+    }
+    return npos;
+  }
+
+  StringView substr(size_t pos, size_t count = npos) const {
+    if (pos >= size_)
+      return StringView("", 0);
+    size_t rcount = std::min(count, size_ - pos);
+    return StringView(data_ + pos, rcount);
+  }
+
+  bool CaseInsensitiveEq(const StringView& other) const {
+    if (size() != other.size())
+      return false;
+    if (size() == 0)
+      return true;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    return _strnicmp(data(), other.data(), size()) == 0;
+#else
+    return strncasecmp(data(), other.data(), size()) == 0;
 #endif
+  }

-namespace perfetto {
-namespace base {
+  bool CaseInsensitiveOneOf(const std::vector<StringView>& others) const {
+    for (const StringView& other : others) {
+      if (CaseInsensitiveEq(other)) {
+        return true;
+      }
+    }
+    return false;
+  }

-// The machine ID used in the tracing core.
-using MachineID = uint32_t;
-// The default value reserved for the host trace.
-constexpr MachineID kDefaultMachineID = 0;
+  bool StartsWith(const StringView& other) const {
+    if (other.size() == 0)
+      return true;
+    if (size() == 0)
+      return false;
+    if (other.size() > size())
+      return false;
+    return memcmp(data(), other.data(), other.size()) == 0;
+  }

-constexpr uid_t kInvalidUid = static_cast<uid_t>(-1);
-constexpr pid_t kInvalidPid = static_cast<pid_t>(-1);
+  bool EndsWith(const StringView& other) const {
+    if (other.size() == 0)
+      return true;
+    if (size() == 0)
+      return false;
+    if (other.size() > size())
+      return false;
+    size_t off = size() - other.size();
+    return memcmp(data() + off, other.data(), other.size()) == 0;
+  }
+
+  std::string ToStdString() const {
+    return size_ == 0 ? "" : std::string(data_, size_);
+  }
+
+  uint64_t Hash() const {
+    base::FnvHasher hasher;
+    hasher.Update(data_, size_);
+    return hasher.digest();
+  }
+
+ private:
+  const char* data_ = nullptr;
+  size_t size_ = 0;
+};
+
+inline bool operator==(const StringView& x, const StringView& y) {
+  if (x.size() != y.size())
+    return false;
+  if (x.size() == 0)
+    return true;
+  return memcmp(x.data(), y.data(), x.size()) == 0;
+}
+
+inline bool operator!=(const StringView& x, const StringView& y) {
+  return !(x == y);
+}
+
+inline bool operator<(const StringView& x, const StringView& y) {
+  auto size = std::min(x.size(), y.size());
+  if (size == 0)
+    return x.size() < y.size();
+  int result = memcmp(x.data(), y.data(), size);
+  return result < 0 || (result == 0 && x.size() < y.size());
+}
+
+inline bool operator>=(const StringView& x, const StringView& y) {
+  return !(x < y);
+}
+
+inline bool operator>(const StringView& x, const StringView& y) {
+  return y < x;
+}
+
+inline bool operator<=(const StringView& x, const StringView& y) {
+  return !(y < x);
+}

 }  // namespace base
 }  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_SYS_TYPES_H_
+template <>
+struct std::hash<::perfetto::base::StringView> {
+  size_t operator()(const ::perfetto::base::StringView& sv) const {
+    return static_cast<size_t>(sv.Hash());
+  }
+};
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_H_
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -1245,275 +1341,312 @@ constexpr pid_t kInvalidPid = static_cast<pid_t>(-1);
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_UTILS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_UTILS_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_STRING_UTILS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_STRING_UTILS_H_

-#include <errno.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
+#include <stdarg.h>
 #include <stdlib.h>
+#include <string.h>

-#include <atomic>
-#include <functional>
-#include <limits>
-#include <memory>
+#include <charconv>
+#include <cinttypes>
+#include <optional>
 #include <string>
+#include <string_view>
+#include <system_error>
+#include <vector>

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/sys_types.h"
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-// Even if Windows has errno.h, the all syscall-restart behavior does not apply.
-// Trying to handle EINTR can cause more harm than good if errno is left stale.
-// Chromium does the same.
-#define PERFETTO_EINTR(x) (x)
-#else
-#define PERFETTO_EINTR(x)                                   \
-  ([&] {                                                    \
-    decltype(x) eintr_wrapper_result;                       \
-    do {                                                    \
-      eintr_wrapper_result = (x);                           \
-    } while (eintr_wrapper_result == -1 && errno == EINTR); \
-    return eintr_wrapper_result;                            \
-  }())
-#endif
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"

 namespace perfetto {
 namespace base {

-namespace internal {
-extern std::atomic<uint32_t> g_cached_page_size;
-uint32_t GetSysPageSizeSlowpath();
-}  // namespace internal
+inline char Lowercase(char c) {
+  return ('A' <= c && c <= 'Z') ? static_cast<char>(c - ('A' - 'a')) : c;
+}

-// Returns the system's page size. Use this when dealing with mmap, madvise and
-// similar mm-related syscalls.
-// This function might be called in hot paths. Avoid calling getpagesize() all
-// the times, in many implementations getpagesize() calls sysconf() which is
-// not cheap.
-inline uint32_t GetSysPageSize() {
-  const uint32_t page_size =
-      internal::g_cached_page_size.load(std::memory_order_relaxed);
-  return page_size != 0 ? page_size : internal::GetSysPageSizeSlowpath();
+inline char Uppercase(char c) {
+  return ('a' <= c && c <= 'z') ? static_cast<char>(c + ('A' - 'a')) : c;
 }

-template <typename T, size_t TSize>
-constexpr size_t ArraySize(const T (&)[TSize]) {
-  return TSize;
+// Returns true if the character is an ASCII whitespace character.
+// This is a locale-independent version that only considers ASCII whitespace:
+// space (0x20), tab (0x09), newline (0x0A), vertical tab (0x0B),
+// form feed (0x0C), and carriage return (0x0D).
+inline bool IsSpace(char c) {
+  return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' ||
+         c == '\r';
 }

-// Adds `a` and `b`, clamping to INT64_MIN / INT64_MAX on overflow instead of
-// wrapping (which is UB and can flip the sign of the result).
-inline int64_t SaturatingAdd(int64_t a, int64_t b) {
-  constexpr int64_t kMax = std::numeric_limits<int64_t>::max();
-  constexpr int64_t kMin = std::numeric_limits<int64_t>::min();
-#if defined(__clang__) || defined(__GNUC__)
-  int64_t result;
-  if (PERFETTO_UNLIKELY(__builtin_add_overflow(a, b, &result)))
-    return a < 0 ? kMin : kMax;
-  return result;
-#else
-  if (b > 0 && a > kMax - b)
-    return kMax;
-  if (b < 0 && a < kMin - b)
-    return kMin;
-  return a + b;
-#endif
+inline std::optional<uint32_t> CStringToUInt32(const char* s, int base = 10) {
+  char* endptr = nullptr;
+  auto value = static_cast<uint32_t>(strtoul(s, &endptr, base));
+  return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
 }

-// Multiplies `a` by `b`, clamping to INT64_MIN / INT64_MAX on overflow instead
-// of wrapping (which is UB and can flip the sign of the result).
-inline int64_t SaturatingMultiply(int64_t a, int64_t b) {
-  constexpr int64_t kMax = std::numeric_limits<int64_t>::max();
-  constexpr int64_t kMin = std::numeric_limits<int64_t>::min();
-#if defined(__clang__) || defined(__GNUC__)
-  int64_t result;
-  if (PERFETTO_UNLIKELY(__builtin_mul_overflow(a, b, &result))) {
-    // On overflow the true product's sign is the XOR of the operands' signs.
-    // Neither operand can be zero here (0 never overflows).
-    return ((a < 0) == (b < 0)) ? kMax : kMin;
-  }
+inline std::optional<int32_t> CStringToInt32(const char* s, int base = 10) {
+  char* endptr = nullptr;
+  auto value = static_cast<int32_t>(strtol(s, &endptr, base));
+  return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
+}
+
+// Note: it saturates to 7fffffffffffffff if parsing a hex number >= 0x8000...
+inline std::optional<int64_t> CStringToInt64(const char* s, int base = 10) {
+  char* endptr = nullptr;
+  auto value = static_cast<int64_t>(strtoll(s, &endptr, base));
+  return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
+}
+
+inline std::optional<uint64_t> CStringToUInt64(const char* s, int base = 10) {
+  char* endptr = nullptr;
+  auto value = static_cast<uint64_t>(strtoull(s, &endptr, base));
+  return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
+}
+
+double StrToD(const char* nptr, char** endptr);
+
+inline std::optional<double> CStringToDouble(const char* s) {
+  char* endptr = nullptr;
+  double value = StrToD(s, &endptr);
+  std::optional<double> result(std::nullopt);
+  if (*s != '\0' && *endptr == '\0')
+    result = value;
   return result;
-#else
-  // Portable fallback (e.g. MSVC cl.exe): detect overflow *before* multiplying
-  // by dividing a limit by one operand, which never itself overflows.
-  if (a == 0 || b == 0)
-    return 0;
-  if ((a < 0) == (b < 0)) {  // Same sign: product is positive.
-    if (a > 0 ? (a > kMax / b) : (a < kMax / b))
-      return kMax;
-  } else {  // Opposite signs: product is negative.
-    if (a > 0 ? (b < kMin / a) : (a < kMin / b))
-      return kMin;
-  }
-  return a * b;
-#endif
 }

-// Function object which invokes 'free' on its parameter, which must be
-// a pointer. Can be used to store malloc-allocated pointers in std::unique_ptr:
-//
-// std::unique_ptr<int, base::FreeDeleter> foo_ptr(
-//     static_cast<int*>(malloc(sizeof(int))));
-struct FreeDeleter {
-  inline void operator()(void* ptr) const { free(ptr); }
-};
+inline std::optional<uint32_t> StringToUInt32(const std::string& s,
+                                              int base = 10) {
+  return CStringToUInt32(s.c_str(), base);
+}

-template <typename T>
-constexpr T AssumeLittleEndian(T value) {
-#if !PERFETTO_IS_LITTLE_ENDIAN()
-  static_assert(false, "Unimplemented on big-endian archs");
-#endif
-  return value;
+inline std::optional<int32_t> StringToInt32(const std::string& s,
+                                            int base = 10) {
+  return CStringToInt32(s.c_str(), base);
 }

-// Round up |size| to a multiple of |alignment| (must be a power of two).
-inline constexpr size_t AlignUp(size_t size, size_t alignment) {
-  return (size + alignment - 1) & ~(alignment - 1);
+inline std::optional<uint64_t> StringToUInt64(const std::string& s,
+                                              int base = 10) {
+  return CStringToUInt64(s.c_str(), base);
 }

-// Round down |size| to a multiple of |alignment| (must be a power of two).
-inline constexpr size_t AlignDown(size_t size, size_t alignment) {
-  return size & ~(alignment - 1);
+inline std::optional<int64_t> StringToInt64(const std::string& s,
+                                            int base = 10) {
+  return CStringToInt64(s.c_str(), base);
 }

-template <typename T>
-inline constexpr bool IsPowerOfTwo(T x) {
-  static_assert(std::is_unsigned_v<T> && std::is_integral_v<T>,
-                "T must be an unsigned integer");
-  return x != 0 && (x & (x - 1)) == 0;
+inline std::optional<double> StringToDouble(const std::string& s) {
+  return CStringToDouble(s.c_str());
 }

-// Returns the smallest power of two greater than or equal to |x|. Returns zero
-// for zero or when the result is not representable by T.
 template <typename T>
-inline constexpr T RoundUpToPowerOfTwo(T x) {
-  static_assert(std::is_unsigned_v<T> && std::is_integral_v<T>,
-                "T must be an unsigned integer");
-  if (x == 0) {
-    return 0;
-  }
-  --x;
-  for (size_t shift = 1; shift < sizeof(T) * 8; shift *= 2) {
-    x |= x >> shift;
+inline std::optional<T> StringViewToNumber(const base::StringView& sv,
+                                           int base = 10) {
+  // std::from_chars() does not regonize the leading '+' character and only
+  // recognizes '-' so remove the '+' if it exists to avoid errors and match
+  // the behavior of the other string conversion utilities above.
+  size_t start_offset = !sv.empty() && sv.at(0) == '+' ? 1 : 0;
+  T value;
+  auto result =
+      std::from_chars(sv.begin() + start_offset, sv.end(), value, base);
+  if (result.ec == std::errc() && result.ptr == sv.end()) {
+    return value;
+  } else {
+    return std::nullopt;
   }
-  return ++x;
 }

-// TODO(primiano): clean this up and move all existing usages to the constexpr
-// version above.
-template <size_t alignment>
-constexpr size_t AlignUp(size_t size) {
-  static_assert(IsPowerOfTwo(alignment), "alignment must be a pow2");
-  return AlignUp(size, alignment);
+inline std::optional<uint32_t> StringViewToUInt32(const base::StringView& sv,
+                                                  int base = 10) {
+  // std::from_chars() does not recognize the leading '-' character for
+  // unsigned conversions, but strtol does. To Mimic the behavior of strtol,
+  // attempt a signed converion if we see a leading '-', and then cast the
+  // result back to unsigned.
+  if (sv.size() > 0 && sv.at(0) == '-') {
+    return static_cast<std::optional<uint32_t> >(
+        StringViewToNumber<int32_t>(sv, base));
+  } else {
+    return StringViewToNumber<uint32_t>(sv, base);
+  }
 }

-inline bool IsAgain(int err) {
-  return err == EAGAIN || err == EWOULDBLOCK;
+inline std::optional<int32_t> StringViewToInt32(const base::StringView& sv,
+                                                int base = 10) {
+  return StringViewToNumber<int32_t>(sv, base);
 }

-// setenv(2)-equivalent. Deals with Windows vs Posix discrepancies.
-void SetEnv(const std::string& key, const std::string& value);
-
-// unsetenv(2)-equivalent. Deals with Windows vs Posix discrepancies.
-void UnsetEnv(const std::string& key);
-
-// Returns true if |fd| is connected to an interactive terminal (TTY). Deals
-// with Windows vs Posix discrepancies (isatty() vs _isatty()).
-bool IsTty(int fd);
+inline std::optional<uint64_t> StringViewToUInt64(const base::StringView& sv,
+                                                  int base = 10) {
+  // std::from_chars() does not recognize the leading '-' character for
+  // unsigned conversions, but strtol does. To Mimic the behavior of strtol,
+  // attempt a signed converion if we see a leading '-', and then cast the
+  // result back to unsigned.
+  if (sv.size() > 0 && sv.at(0) == '-') {
+    return static_cast<std::optional<uint64_t> >(
+        StringViewToNumber<int64_t>(sv, base));
+  } else {
+    return StringViewToNumber<uint64_t>(sv, base);
+  }
+}

-// Convenience overload for C stdio streams (e.g. stdin/stdout/stderr).
-bool IsTty(FILE* stream);
+inline std::optional<int64_t> StringViewToInt64(const base::StringView& sv,
+                                                int base = 10) {
+  return StringViewToNumber<int64_t>(sv, base);
+}

-// Calls mallopt(M_PURGE, 0) on Android. Does nothing on other platforms.
-// This forces the allocator to release freed memory. This is used to work
-// around various Scudo inefficiencies. See b/170217718.
-void MaybeReleaseAllocatorMemToOS();
+// TODO: As of Clang 19.0 std::from_chars is unimplemented for type double
+// despite being part of C++17 standard, and already being supported by GCC and
+// MSVC. Enable this once we have double support in Clang.
+// inline std::optional<double> StringViewToDouble(const base::StringView& sv) {
+//   return StringViewToNumber<double>(sv);
+// }

-// geteuid() on POSIX OSes, returns 0 on Windows (See comment in utils.cc).
-uid_t GetCurrentUserId();
+bool StartsWith(const std::string& str, const std::string& prefix);
+bool EndsWith(const std::string& str, const std::string& suffix);
+bool StartsWithAny(const std::string& str,
+                   const std::vector<std::string>& prefixes);
+bool Contains(const std::string& haystack, const std::string& needle);
+bool Contains(const std::string& haystack, char needle);
+bool Contains(const std::vector<std::string>& haystack,
+              const std::string& needle);
+size_t Find(const StringView& needle, const StringView& haystack);
+bool CaseInsensitiveEqual(std::string_view first, std::string_view second);
+std::string Join(const std::vector<std::string>& parts,
+                 const std::string& delim);
+std::vector<std::string> SplitString(const std::string& text,
+                                     const std::string& delimiter);
+std::string StripPrefix(const std::string& str, const std::string& prefix);
+std::string StripSuffix(const std::string& str, const std::string& suffix);
+std::string_view TrimWhitespace(std::string_view str);
+std::string TrimWhitespace(const std::string& str);
+std::string_view TrimWhitespace(const char* str);
+std::string ToLower(const std::string& str);
+std::string ToUpper(const std::string& str);
+std::string StripChars(const std::string& str,
+                       const std::string& chars,
+                       char replacement);
+std::string ToHex(const char* data, size_t size);
+inline std::string ToHex(const std::string& s) {
+  return ToHex(s.c_str(), s.size());
+}
+std::string IntToHexString(uint32_t number);
+std::string Uint64ToHexString(uint64_t number);
+std::string Uint64ToHexStringNoPrefix(uint64_t number);
+std::string ReplaceAll(std::string str,
+                       const std::string& to_replace,
+                       const std::string& replacement);

-// Forks the process.
-// Parent: calls |parent_cb| with the child's PID and exits with its return
-//         value. The callback owns any startup output (e.g. printing the PID).
-// Child: redirects stdio onto /dev/null, chdirs into / and returns.
-void Daemonize(std::function<int(pid_t)> parent_cb);
+// Checks if all characters in the input string view `str` are ASCII.
+//
+// If so, the function returns true and `output` is not modified.
+// If `str` contains non-ASCII characters, the function returns false,
+// removes invalid UTF-8 characters from `str`, and stores the result in
+// `output`.
+bool CheckAsciiAndRemoveInvalidUTF8(base::StringView str, std::string& output);

-// Returns the path of the current executable, e.g. /foo/bar/exe.
-std::string GetCurExecutablePath();
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+bool WideToUTF8(const std::wstring& source, std::string& output);
+bool UTF8ToWide(const std::string& source, std::wstring& output);
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)

-// Returns the directory where the current executable lives in, e.g. /foo/bar.
-// This is independent of cwd().
-std::string GetCurExecutableDir();
+// A BSD-style strlcpy without the return value.
+// Copies at most |dst_size|-1 characters. Unlike strncpy, it always \0
+// terminates |dst|, as long as |dst_size| is not 0.
+// Unlike strncpy and like strlcpy it does not zero-pad the rest of |dst|.
+// Returns nothing. The BSD strlcpy returns the size of |src|, which might
+// be > |dst_size|. Anecdotal experience suggests people assume the return value
+// is the number of bytes written in |dst|. That assumption can lead to
+// dangerous bugs.
+// In order to avoid being subtly uncompliant with strlcpy AND avoid misuse,
+// the choice here is to return nothing.
+inline void StringCopy(char* dst, const char* src, size_t dst_size) {
+  for (size_t i = 0; i < dst_size; ++i) {
+    if ((dst[i] = src[i]) == '\0') {
+      return;  // We hit and copied the null terminator.
+    }
+  }

-// Memory returned by AlignedAlloc() must be freed via AlignedFree() not just
-// free. It makes a difference on Windows where _aligned_malloc() and
-// _aligned_free() must be paired.
-// Prefer using the AlignedAllocTyped() below which takes care of the pairing.
-void* AlignedAlloc(size_t alignment, size_t size);
-void AlignedFree(void*);
+  // We were left off at dst_size. We over copied 1 byte. Null terminate.
+  if (PERFETTO_LIKELY(dst_size > 0))
+    dst[dst_size - 1] = 0;
+}

-// Detects Sync-mode MTE (currently being tested in some Android builds).
-// This is known to use extra memory for the stack history buffer.
-bool IsSyncMemoryTaggingEnabled();
+// Like snprintf() but returns the number of chars *actually* written (without
+// counting the null terminator) NOT "the number of chars which would have been
+// written to the final string if enough  space had been available".
+// This should be used in almost all cases when the caller uses the return value
+// of snprintf(). If the return value is not used, there is no benefit in using
+// this wrapper, as this just calls snprintf() and mangles the return value.
+// It always null-terminates |dst| (even in case of errors), unless
+// |dst_size| == 0.
+// Examples:
+//   SprintfTrunc(x, 4, "123whatever"): returns 3 and writes "123\0".
+//   SprintfTrunc(x, 4, "123"): returns 3 and writes "123\0".
+//   SprintfTrunc(x, 3, "123"): returns 2 and writes "12\0".
+//   SprintfTrunc(x, 2, "123"): returns 1 and writes "1\0".
+//   SprintfTrunc(x, 1, "123"): returns 0 and writes "\0".
+//   SprintfTrunc(x, 0, "123"): returns 0 and writes nothing.
+// NOTE: This means that the caller has no way to tell when truncation happens
+//   vs the edge case of *just* fitting in the buffer.
+size_t SprintfTrunc(char* dst, size_t dst_size, const char* fmt, ...)
+    PERFETTO_PRINTF_FORMAT(3, 4);

-// A RAII version of the above, which takes care of pairing Aligned{Alloc,Free}.
-template <typename T>
-struct AlignedDeleter {
-  inline void operator()(T* ptr) const { AlignedFree(ptr); }
+// Line number starts from 1
+struct LineWithOffset {
+  base::StringView line;
+  uint32_t line_offset;
+  uint32_t line_num;
 };

-// The remove_extent<T> here and below is to allow defining unique_ptr<T[]>.
-// As per https://en.cppreference.com/w/cpp/memory/unique_ptr the Deleter takes
-// always a T*, not a T[]*.
-template <typename T>
-using AlignedUniquePtr =
-    std::unique_ptr<T, AlignedDeleter<typename std::remove_extent<T>::type>>;
-
-template <typename T>
-AlignedUniquePtr<T> AlignedAllocTyped(size_t n_membs) {
-  using TU = typename std::remove_extent<T>::type;
-  return AlignedUniquePtr<T>(
-      static_cast<TU*>(AlignedAlloc(alignof(TU), sizeof(TU) * n_membs)));
-}
+// For given string and offset Pfinds a line with character for
+// which offset points, what number is this line (starts from 1), and the offset
+// inside this line. returns std::nullopt if the offset points to
+// line break character or exceeds string length.
+std::optional<LineWithOffset> FindLineWithOffset(base::StringView str,
+                                                 uint32_t offset);

-// A RAII wrapper to invoke a function when leaving a function/scope.
-template <typename Func>
-class OnScopeExitWrapper {
+// A helper class to facilitate construction and usage of write-once stack
+// strings.
+// Example usage:
+//   StackString<32> x("format %d %s", 42, string_arg);
+//   TakeString(x.c_str() | x.string_view() | x.ToStdString());
+// Rather than char x[32] + sprintf.
+// Advantages:
+// - Avoids useless zero-fills caused by people doing `char buf[32] {}` (mainly
+//   by fearing unknown snprintf failure modes).
+// - Makes the code more robust in case of snprintf truncations (len() and
+//  string_view() will return the truncated length, unlike snprintf).
+template <size_t N>
+class StackString {
  public:
-  explicit OnScopeExitWrapper(Func f) : f_(std::move(f)), active_(true) {}
-  OnScopeExitWrapper(OnScopeExitWrapper&& other) noexcept
-      : f_(std::move(other.f_)), active_(other.active_) {
-    other.active_ = false;
+  explicit PERFETTO_PRINTF_FORMAT(/* 1=this */ 2, 3)
+      StackString(const char* fmt, ...) {
+    buf_[0] = '\0';
+    va_list args;
+    va_start(args, fmt);
+    int res = vsnprintf(buf_, sizeof(buf_), fmt, args);
+    va_end(args);
+    buf_[sizeof(buf_) - 1] = '\0';
+    len_ = res < 0 ? 0 : std::min(static_cast<size_t>(res), sizeof(buf_) - 1);
   }
-  ~OnScopeExitWrapper() {
-    if (active_)
-      f_();
+
+  StringView string_view() const { return StringView(buf_, len_); }
+  std::string_view ToStdStringView() const {
+    return std::string_view(buf_, len_);
   }
+  std::string ToStdString() const { return std::string(buf_, len_); }
+  const char* c_str() const { return buf_; }
+  size_t len() const { return len_; }
+  char* mutable_data() { return buf_; }

  private:
-  Func f_;
-  bool active_;
+  char buf_[N];
+  size_t len_ = 0;  // Does not include the \0.
 };

-template <typename Func>
-PERFETTO_WARN_UNUSED_RESULT OnScopeExitWrapper<Func> OnScopeExit(Func f) {
-  return OnScopeExitWrapper<Func>(std::move(f));
-}
-
-// Returns a xxd-style hex dump (hex + ascii chars) of the input data.
-std::string HexDump(const void* data, size_t len, size_t bytes_per_line = 16);
-inline std::string HexDump(const std::string& data,
-                           size_t bytes_per_line = 16) {
-  return HexDump(data.data(), data.size(), bytes_per_line);
-}
-
 }  // namespace base
 }  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_UTILS_H_
+#endif  // INCLUDE_PERFETTO_EXT_BASE_STRING_UTILS_H_
 /*
  * Copyright (C) 2021 The Android Open Source Project
  *
@@ -1530,213 +1663,200 @@ inline std::string HexDump(const std::string& data,
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
-#define INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
+// gen_amalgamated expanded: #include "perfetto/ext/base/android_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_splitter.h"
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-#include <optional>
 #include <string>

-// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"  // For ssize_t.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/system_properties.h>
+#endif
+
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) &&  \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL) && \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
+#include <sys/utsname.h>
+#include <unistd.h>
+#endif
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/sysinfo.h>
+#endif

 namespace perfetto {
 namespace base {

-// Returns the length of the destination string (included '=' padding).
-// Does NOT include the size of the string null terminator.
-inline size_t Base64EncSize(size_t src_size) {
-  return (src_size + 2) / 3 * 4;
-}
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)

-// Returns the upper bound on the length of the destination buffer.
-// The actual decoded length might be <= the number returned here.
-inline size_t Base64DecSize(size_t src_size) {
-  return (src_size + 3) / 4 * 3;
+std::string GetAndroidProp(const char* name) {
+  std::string ret;
+#if __ANDROID_API__ >= 26
+  const prop_info* pi = __system_property_find(name);
+  if (!pi) {
+    return ret;
+  }
+  __system_property_read_callback(
+      pi,
+      [](void* dst_void, const char*, const char* value, uint32_t) {
+        std::string& dst = *static_cast<std::string*>(dst_void);
+        dst = value;
+      },
+      &ret);
+#else  // __ANDROID_API__ < 26
+  char value_buf[PROP_VALUE_MAX];
+  int len = __system_property_get(name, value_buf);
+  if (len > 0 && static_cast<size_t>(len) < sizeof(value_buf)) {
+    ret = std::string(value_buf, static_cast<size_t>(len));
+  }
+#endif
+  return ret;
 }

-// Does NOT null-terminate |dst|.
-ssize_t Base64Encode(const void* src,
-                     size_t src_size,
-                     char* dst,
-                     size_t dst_size);
-
-std::string Base64Encode(const void* src, size_t src_size);
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)

-inline std::string Base64Encode(StringView sv) {
-  return Base64Encode(sv.data(), sv.size());
+Utsname GetUtsname() {
+  Utsname utsname_info;
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) &&  \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL) && \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
+  struct utsname uname_info;
+  if (uname(&uname_info) == 0) {
+    utsname_info.sysname = uname_info.sysname;
+    utsname_info.version = uname_info.version;
+    utsname_info.machine = uname_info.machine;
+    utsname_info.release = uname_info.release;
+  } else {
+    PERFETTO_ELOG("Unable to read Utsname information");
+  }
+#endif  // !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  return utsname_info;
 }

-// Returns -1 in case of failure.
-ssize_t Base64Decode(const char* src,
-                     size_t src_size,
-                     uint8_t* dst,
-                     size_t dst_size);
+SystemInfo GetSystemInfo() {
+  SystemInfo info;

-std::optional<std::string> Base64Decode(const char* src, size_t src_size);
-
-inline std::optional<std::string> Base64Decode(StringView sv) {
-  return Base64Decode(sv.data(), sv.size());
-}
-
-}  // namespace base
-}  // namespace perfetto
-
-#endif  // INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
-/*
- * Copyright (C) 2021 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.
- */
-
-// gen_amalgamated expanded: #include "perfetto/ext/base/base64.h"
-
-namespace perfetto {
-namespace base {
-
-namespace {
+  info.timezone_off_mins = GetTimezoneOffsetMins();

-constexpr char kPadding = '=';
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) &&  \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL) && \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)

-constexpr char kEncTable[] =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-static_assert(sizeof(kEncTable) == (1u << 6) + sizeof('\0'), "Bad table size");
+  info.utsname_info = GetUtsname();
+  info.page_size = static_cast<uint32_t>(sysconf(_SC_PAGESIZE));
+  info.num_cpus = static_cast<uint32_t>(sysconf(_SC_NPROCESSORS_CONF));

-// Maps an ASCII character to its 6-bit value. It only contains translations
-// from '+' to 'z'. Supports the standard (+/) and URL-safe (-_) alphabets.
-constexpr uint8_t kX = 0xff;  // Value used for invalid characters
-constexpr uint8_t kDecTable[] = {
-    62, kX, 62, kX, 63, 52, 53, 54, 55, 56,  // 00 - 09
-    57, 58, 59, 60, 61, kX, kX, kX, 0,  kX,  // 10 - 19
-    kX, kX, 0,  1,  2,  3,  4,  5,  6,  7,   // 20 - 29
-    8,  9,  10, 11, 12, 13, 14, 15, 16, 17,  // 30 - 39
-    18, 19, 20, 21, 22, 23, 24, 25, kX, kX,  // 40 - 49
-    kX, kX, 63, kX, 26, 27, 28, 29, 30, 31,  // 50 - 59
-    32, 33, 34, 35, 36, 37, 38, 39, 40, 41,  // 60 - 69
-    42, 43, 44, 45, 46, 47, 48, 49, 50, 51,  // 70 - 79
-};
-constexpr char kMinDecChar = '+';
-constexpr char kMaxDecChar = 'z';
-static_assert(kMaxDecChar - kMinDecChar <= sizeof(kDecTable), "Bad table size");
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  // Use the Linux-specific sysinfo() system call on Linux and Android.
+  // https://man7.org/linux/man-pages/man2/sysinfo.2.html
+  struct sysinfo sys_info;
+  if (sysinfo(&sys_info) == 0) {
+    info.system_ram_bytes =
+        static_cast<uint64_t>(sys_info.totalram) * sys_info.mem_unit;
+  }
+#else
+  // POSIX Fallback (macOS, BSD, etc.): Use sysconf() to get physical pages.
+  long pages = sysconf(_SC_PHYS_PAGES);
+  if (pages > 0 && info.page_size.has_value()) {
+    info.system_ram_bytes = static_cast<uint64_t>(pages) * (*info.page_size);
+  }
+#endif

-inline uint8_t DecodeChar(char c) {
-  if (c < kMinDecChar || c > kMaxDecChar)
-    return kX;
-  return kDecTable[c - kMinDecChar];
-}
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX)
+  // Extract the device compatible strings from the device tree (if available)
+  const std::string path = "/sys/firmware/devicetree/base/compatible";
+  std::string device_compatible_data;
+  if (ReadFile(path, &device_compatible_data)) {
+    for (base::StringSplitter ss(std::move(device_compatible_data), '\0');
+         ss.Next();) {
+      info.device_tree_compatibles.emplace_back(ss.cur_token(),
+                                                ss.cur_token_size());
+      PERFETTO_DLOG("Found linux device compatible: %s", ss.cur_token());
+    }
+  } else {
+    PERFETTO_DLOG("No linux device compatible found in path: %s", path.c_str());
+  }
+#endif
+#endif  // !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)

-}  // namespace
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  info.android_build_fingerprint = GetAndroidProp("ro.build.fingerprint");
+  if (info.android_build_fingerprint.empty()) {
+    PERFETTO_ELOG("Unable to read ro.build.fingerprint");
+  }

-ssize_t Base64Encode(const void* src,
-                     size_t src_size,
-                     char* dst,
-                     size_t dst_size) {
-  const size_t padded_dst_size = Base64EncSize(src_size);
-  if (dst_size < padded_dst_size)
-    return -1;  // Not enough space in output.
+  info.android_device_manufacturer = GetAndroidProp("ro.product.manufacturer");
+  if (info.android_device_manufacturer.empty()) {
+    PERFETTO_ELOG("Unable to read ro.product.manufacturer");
+  }

-  const uint8_t* rd = static_cast<const uint8_t*>(src);
-  const uint8_t* const end = rd + src_size;
-  size_t wr_size = 0;
-  while (rd < end) {
-    uint8_t s[3]{};
-    s[0] = *(rd++);
-    dst[wr_size++] = kEncTable[s[0] >> 2];
+  std::string sdk_str_value = GetAndroidProp("ro.build.version.sdk");
+  info.android_sdk_version = StringToUInt64(sdk_str_value);
+  if (!info.android_sdk_version.has_value()) {
+    PERFETTO_ELOG("Unable to read ro.build.version.sdk");
+  }

-    uint8_t carry0 = static_cast<uint8_t>((s[0] & 0x03) << 4);
-    if (PERFETTO_LIKELY(rd < end)) {
-      s[1] = *(rd++);
-      dst[wr_size++] = kEncTable[carry0 | (s[1] >> 4)];
-    } else {
-      dst[wr_size++] = kEncTable[carry0];
-      dst[wr_size++] = kPadding;
-      dst[wr_size++] = kPadding;
-      break;
-    }
+  info.android_soc_model = GetAndroidProp("ro.soc.model");
+  if (info.android_soc_model.empty()) {
+    PERFETTO_ELOG("Unable to read ro.soc.model");
+  }

-    uint8_t carry1 = static_cast<uint8_t>((s[1] & 0x0f) << 2);
-    if (PERFETTO_LIKELY(rd < end)) {
-      s[2] = *(rd++);
-      dst[wr_size++] = kEncTable[carry1 | (s[2] >> 6)];
-    } else {
-      dst[wr_size++] = kEncTable[carry1];
-      dst[wr_size++] = kPadding;
-      break;
-    }
+  // guest_soc model is not always present
+  info.android_guest_soc_model = GetAndroidProp("ro.boot.guest_soc.model");

-    dst[wr_size++] = kEncTable[s[2] & 0x3f];
+  info.android_hardware_revision = GetAndroidProp("ro.boot.hardware.revision");
+  if (info.android_hardware_revision.empty()) {
+    PERFETTO_ELOG("Unable to read ro.boot.hardware.revision");
   }
-  PERFETTO_DCHECK(wr_size == padded_dst_size);
-  return static_cast<ssize_t>(padded_dst_size);
-}
-
-std::string Base64Encode(const void* src, size_t src_size) {
-  std::string dst;
-  dst.resize(Base64EncSize(src_size));
-  auto res = Base64Encode(src, src_size, &dst[0], dst.size());
-  PERFETTO_CHECK(res == static_cast<ssize_t>(dst.size()));
-  return dst;
-}

-ssize_t Base64Decode(const char* src,
-                     size_t src_size,
-                     uint8_t* dst,
-                     size_t dst_size) {
-  const size_t min_dst_size = Base64DecSize(src_size);
-  if (dst_size < min_dst_size)
-    return -1;
+  info.android_storage_model = GetAndroidProp("ro.boot.hardware.ufs");
+  if (info.android_storage_model.empty()) {
+    PERFETTO_ELOG("Unable to read ro.boot.hardware.ufs");
+  }

-  const char* rd = src;
-  const char* const end = src + src_size;
-  size_t wr_size = 0;
+  info.android_ram_model = GetAndroidProp("ro.boot.hardware.ddr");
+  if (info.android_ram_model.empty()) {
+    PERFETTO_ELOG("Unable to read ro.boot.hardware.ddr");
+  }

-  char s[4]{};
-  while (rd < end) {
-    uint8_t d[4];
-    for (uint32_t j = 0; j < 4; j++) {
-      // Padding is only feasible for the last 2 chars of each group of 4.
-      s[j] = rd < end ? *(rd++) : (j < 2 ? '\0' : kPadding);
-      d[j] = DecodeChar(s[j]);
-      if (d[j] == kX)
-        return -1;  // Invalid input char.
-    }
-    dst[wr_size] = static_cast<uint8_t>((d[0] << 2) | (d[1] >> 4));
-    dst[wr_size + 1] = static_cast<uint8_t>((d[1] << 4) | (d[2] >> 2));
-    dst[wr_size + 2] = static_cast<uint8_t>((d[2] << 6) | (d[3]));
-    wr_size += 3;
+  info.android_serial_console = GetAndroidProp("init.svc.console");
+  if (info.android_serial_console.empty()) {
+    PERFETTO_ELOG("Unable to read init.svc.console");
   }
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)

-  PERFETTO_CHECK(wr_size <= dst_size);
-  wr_size -= (s[3] == kPadding ? 1 : 0) + (s[2] == kPadding ? 1 : 0);
-  return static_cast<ssize_t>(wr_size);
+  return info;
 }

-std::optional<std::string> Base64Decode(const char* src, size_t src_size) {
-  std::string dst;
-  dst.resize(Base64DecSize(src_size));
-  auto res = Base64Decode(src, src_size, reinterpret_cast<uint8_t*>(&dst[0]),
-                          dst.size());
-  if (res < 0)
-    return std::nullopt;  // Decoding error.
-
-  PERFETTO_CHECK(res <= static_cast<ssize_t>(dst.size()));
-  dst.resize(static_cast<size_t>(res));
-  return std::make_optional(dst);
+std::string GetPerfettoMachineName() {
+  const char* env_name = getenv("PERFETTO_MACHINE_NAME");
+  if (env_name) {
+    return env_name;
+  }
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  std::string name = base::GetAndroidProp("traced.machine_name");
+  if (name.empty()) {
+    name = GetUtsname().sysname;
+  }
+  return name;
+#else
+  return GetUtsname().sysname;
+#endif
 }

 }  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/base/cpu_info.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/cpu_info.h
+// gen_amalgamated begin source: src/base/atomic_file.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/atomic_file.h
 /*
  * Copyright (C) 2026 The Android Open Source Project
  *
@@ -1753,42 +1873,45 @@ std::optional<std::string> Base64Decode(const char* src, size_t src_size) {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_H_
-#define INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_ATOMIC_FILE_H_
+#define INCLUDE_PERFETTO_EXT_BASE_ATOMIC_FILE_H_

-#include <stdint.h>
-#include <optional>
 #include <string>
-#include <vector>
-
-namespace perfetto {
-namespace base {
-
-struct CpuInfo {
-  std::string processor;
-  uint32_t cpu_index = 0;
-  std::optional<uint32_t> implementer;
-  std::optional<uint32_t> architecture;
-  std::optional<uint32_t> variant;
-  std::optional<uint32_t> part;
-  std::optional<uint32_t> revision;
-  uint64_t features = 0;
-  char arm_cpuid[32] = {};
-};
+// gen_amalgamated expanded: #include "perfetto/base/status.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"

-// Parses the contents of the input string into per-CPU entries.
-std::vector<CpuInfo> ParseCpuInfo(std::string proc_cpu_info);
+namespace perfetto::base {

-// Reads /proc/cpuinfo and parses it into per-CPU entries.
-std::vector<CpuInfo> ReadCpuInfo();
+// Writes a sibling temporary file and atomically replaces the destination on
+// Commit(). Until then an existing destination is untouched. Does not follow
+// destination symlinks. An uncommitted temporary file is deleted on
+// destruction.
+class AtomicFile {
+ public:
+  explicit AtomicFile(const std::string& destination);
+  ~AtomicFile();
+  AtomicFile(const AtomicFile&) = delete;
+  AtomicFile& operator=(const AtomicFile&) = delete;
+
+  Status Open();
+  ScopedFile DuplicateFD() const;
+  // Final operation: close all DuplicateFD() handles before committing.
+  // On failure, destruction still attempts to remove the temporary file.
+  Status Commit() &&;
+  const std::string& temp_path() const { return temp_path_; }

-}  // namespace base
-}  // namespace perfetto
+ private:
+  const std::string destination_;
+  const std::string temp_path_;
+  ScopedFile fd_;
+  bool owns_temp_file_ = false;
+};

-#endif  // INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_H_
-// gen_amalgamated begin header: include/perfetto/ext/base/cpu_info_features_allowlist.h
+}  // namespace perfetto::base
+#endif  // INCLUDE_PERFETTO_EXT_BASE_ATOMIC_FILE_H_
+// gen_amalgamated begin header: include/perfetto/ext/base/uuid.h
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -1803,29 +1926,75 @@ std::vector<CpuInfo> ReadCpuInfo();
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_FEATURES_ALLOWLIST_H_
-#define INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_FEATURES_ALLOWLIST_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_UUID_H_
+#define INCLUDE_PERFETTO_EXT_BASE_UUID_H_
+
+#include <string.h>
+#include <array>
+#include <cstdint>
+#include <string>
+
+// gen_amalgamated expanded: #include "perfetto/base/endian.h"
+// gen_amalgamated expanded: #include "perfetto/base/export.h"

 namespace perfetto {
 namespace base {

-// APPEND ONLY. DO NOT EVER REMOVE ENTRIES FROM THIS ARRAY OR REORDER.
-// This array is used both by traced_probes and trace_processor to index the
-// cpuinfo flags. Changing the order will break trace_processor compatibility
-// with old traces.
-constexpr const char* kCpuInfoFeatures[] = {
-    "mte",   // DO NOT REMOVE/REODER.
-    "mte3",  // DO NOT REMOVE/REODER.
+class PERFETTO_EXPORT_COMPONENT Uuid {
+ public:
+  explicit Uuid(const std::string& s);
+  explicit Uuid(int64_t lsb, int64_t msb);
+  Uuid();
+
+  std::array<uint8_t, 16>* data() { return &data_; }
+  const std::array<uint8_t, 16>* data() const { return &data_; }
+
+  bool operator==(const Uuid& other) const { return data_ == other.data_; }
+
+  bool operator!=(const Uuid& other) const { return !(*this == other); }
+
+  explicit operator bool() const { return *this != Uuid(); }
+
+  int64_t msb() const {
+    uint64_t result;
+    memcpy(&result, data_.data() + 8, 8);
+    return static_cast<int64_t>(LE64ToHost(result));
+  }
+
+  int64_t lsb() const {
+    uint64_t result;
+    memcpy(&result, data_.data(), 8);
+    return static_cast<int64_t>(LE64ToHost(result));
+  }
+
+  void set_lsb_msb(int64_t lsb, int64_t msb) {
+    set_lsb(lsb);
+    set_msb(msb);
+  }
+  void set_msb(int64_t msb) {
+    uint64_t le = HostToLE64(static_cast<uint64_t>(msb));
+    memcpy(data_.data() + 8, &le, 8);
+  }
+  void set_lsb(int64_t lsb) {
+    uint64_t le = HostToLE64(static_cast<uint64_t>(lsb));
+    memcpy(data_.data(), &le, 8);
+  }
+
+  std::string ToString() const;
+  std::string ToPrettyString() const;
+
+ private:
+  std::array<uint8_t, 16> data_{};
 };

+Uuid Uuidv4();
+
 }  // namespace base
 }  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_FEATURES_ALLOWLIST_H_
-// gen_amalgamated begin header: include/perfetto/ext/base/file_utils.h
-// gen_amalgamated begin header: include/perfetto/base/status.h
+#endif  // INCLUDE_PERFETTO_EXT_BASE_UUID_H_
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -1840,108 +2009,108 @@ constexpr const char* kCpuInfoFeatures[] = {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_BASE_STATUS_H_
-#define INCLUDE_PERFETTO_BASE_STATUS_H_
+// gen_amalgamated expanded: #include "perfetto/ext/base/atomic_file.h"

-#include <optional>
-#include <string>
-#include <string_view>
-#include <vector>
+#include <cerrno>
+#include <cstdio>
+#include <cstring>

+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
 // gen_amalgamated expanded: #include "perfetto/base/compiler.h"
-// gen_amalgamated expanded: #include "perfetto/base/export.h"
 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/uuid.h"

-namespace perfetto {
-namespace base {
-
-// Represents either the success or the failure message of a function.
-// This can used as the return type of functions which would usually return an
-// bool for success or int for errno but also wants to add some string context
-// (ususally for logging).
-//
-// Similar to absl::Status, an optional "payload" can also be included with more
-// context about the error. This allows passing additional metadata about the
-// error (e.g. location of errors, potential mitigations etc).
-class PERFETTO_EXPORT_COMPONENT Status {
- public:
-  Status() : ok_(true) {}
-  explicit Status(std::string msg) : ok_(false), message_(std::move(msg)) {
-    PERFETTO_CHECK(!message_.empty());
-  }
-
-  // Copy operations.
-  Status(const Status&) = default;
-  Status& operator=(const Status&) = default;
-
-  // Move operations. The moved-from state is valid but unspecified.
-  Status(Status&&) noexcept = default;
-  Status& operator=(Status&&) = default;
-
-  bool ok() const { return ok_; }
-
-  // When ok() is false this returns the error message. Returns the empty string
-  // otherwise.
-  const std::string& message() const { return message_; }
-  const char* c_message() const { return message_.c_str(); }
-
-  //////////////////////////////////////////////////////////////////////////////
-  // Payload Management APIs
-  //////////////////////////////////////////////////////////////////////////////
-
-  // Payloads can be attached to error statuses to provide additional context.
-  //
-  // Payloads are (key, value) pairs, where the key is a string acting as a
-  // unique "type URL" and the value is an opaque string. The "type URL" should
-  // be unique, follow the format of a URL and, ideally, documentation on how to
-  // interpret its associated data should be available.
-  //
-  // To attach a payload to a status object, call `Status::SetPayload()`.
-  // Similarly, to extract the payload from a status, call
-  // `Status::GetPayload()`.
-  //
-  // Note: the payload APIs are only meaningful to call when the status is an
-  // error. Otherwise, all methods are noops.
-
-  // Gets the payload for the given |type_url| if one exists.
-  //
-  // Will always return std::nullopt if |ok()|.
-  std::optional<std::string_view> GetPayload(std::string_view type_url) const;
-
-  // Sets the payload for the given key. The key should
-  //
-  // Will always do nothing if |ok()|.
-  void SetPayload(std::string_view type_url, std::string value);
-
-  // Erases the payload for the given string and returns true if the payload
-  // existed and was erased.
-  //
-  // Will always do nothing if |ok()|.
-  bool ErasePayload(std::string_view type_url);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <io.h>
+#include <windows.h>
+#else
+#include <sys/stat.h>
+#include <unistd.h>
+#endif

- private:
-  struct Payload {
-    std::string type_url;
-    std::string payload;
-  };
+namespace perfetto::base {

-  bool ok_ = false;
-  std::string message_;
-  std::vector<Payload> payloads_;
-};
+AtomicFile::AtomicFile(const std::string& destination)
+    : destination_(destination),
+      temp_path_(destination + ".tmp." + Uuidv4().ToPrettyString()) {}

-// Returns a status object which represents the Ok status.
-inline Status OkStatus() {
-  return Status();
+AtomicFile::~AtomicFile() {
+  fd_.reset();
+  if (owns_temp_file_)
+    Unlink(temp_path_.c_str());
 }

-Status ErrStatus(const char* format, ...) PERFETTO_PRINTF_FORMAT(1, 2);
+base::Status AtomicFile::Open() {
+  PERFETTO_CHECK(!fd_ && !owns_temp_file_);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  DWORD attributes = GetFileAttributesA(destination_.c_str());
+  if (attributes != INVALID_FILE_ATTRIBUTES &&
+      (attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)))
+    return base::ErrStatus(
+        "output must be a regular file, not a directory or link");
+  HANDLE handle =
+      CreateFileA(temp_path_.c_str(), GENERIC_READ | GENERIC_WRITE,
+                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                  nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
+  if (handle == INVALID_HANDLE_VALUE)
+    return base::ErrStatus(
+        "cannot create temporary output beside '%s' (Windows error %lu)",
+        destination_.c_str(), GetLastError());
+  owns_temp_file_ = true;
+  fd_.reset(_open_osfhandle(reinterpret_cast<intptr_t>(handle), _O_BINARY));
+  if (!fd_)
+    CloseHandle(handle);
+#else
+  struct stat output{};
+  bool exists = lstat(destination_.c_str(), &output) == 0;
+  PERFETTO_MSAN_UNPOISON(&output, sizeof(output));
+  if (exists && !S_ISREG(output.st_mode))
+    return base::ErrStatus(
+        "output must be a regular file, not a directory or link: '%s'. Specify "
+        "the target path directly.",
+        destination_.c_str());
+  fd_ = base::OpenFile(temp_path_, O_CREAT | O_EXCL | O_RDWR, 0644);
+  owns_temp_file_ = static_cast<bool>(fd_);
+  if (fd_ && exists && fchmod(*fd_, output.st_mode & 0777) != 0)
+    return base::ErrStatus("cannot preserve output permissions: %s",
+                           strerror(errno));
+#endif
+  if (!fd_)
+    return base::ErrStatus(
+        "cannot create temporary output beside '%s': %s. Check that the parent "
+        "directory exists and is writable.",
+        destination_.c_str(), strerror(errno));
+  return base::OkStatus();
+}

-}  // namespace base
-}  // namespace perfetto
+base::ScopedFile AtomicFile::DuplicateFD() const {
+  PERFETTO_CHECK(fd_);
+  return DupFile(*fd_);
+}

-#endif  // INCLUDE_PERFETTO_BASE_STATUS_H_
-// gen_amalgamated begin header: include/perfetto/ext/base/scoped_file.h
+base::Status AtomicFile::Commit() && {
+  PERFETTO_CHECK(fd_);
+  if (!base::FlushFile(*fd_))
+    return base::ErrStatus("failed to flush output: %s", strerror(errno));
+  fd_.reset();
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  if (!MoveFileExA(temp_path_.c_str(), destination_.c_str(),
+                   MOVEFILE_REPLACE_EXISTING))
+    return base::ErrStatus("failed to publish '%s' (Windows error %lu)",
+                           destination_.c_str(), GetLastError());
+#else
+  if (rename(temp_path_.c_str(), destination_.c_str()) != 0)
+    return base::ErrStatus("failed to publish '%s': %s", destination_.c_str(),
+                           strerror(errno));
+#endif
+  owns_temp_file_ = false;
+  return base::OkStatus();
+}
+}  // namespace perfetto::base
+// gen_amalgamated begin source: src/base/base64.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/base64.h
+// gen_amalgamated begin header: include/perfetto/ext/base/utils.h
 /*
  * Copyright (C) 2017 The Android Open Source Project
  *
@@ -1958,358 +2127,346 @@ Status ErrStatus(const char* format, ...) PERFETTO_PRINTF_FORMAT(1, 2);
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_SCOPED_FILE_H_
-#define INCLUDE_PERFETTO_EXT_BASE_SCOPED_FILE_H_
-
-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_UTILS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_UTILS_H_

+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>

-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <dirent.h>  // For DIR* / opendir().
-#endif
-
+#include <atomic>
+#include <functional>
+#include <limits>
+#include <memory>
 #include <string>

-// gen_amalgamated expanded: #include "perfetto/base/export.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/base/platform_handle.h"
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/sys_types.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+// Even if Windows has errno.h, the all syscall-restart behavior does not apply.
+// Trying to handle EINTR can cause more harm than good if errno is left stale.
+// Chromium does the same.
+#define PERFETTO_EINTR(x) (x)
+#else
+#define PERFETTO_EINTR(x)                                   \
+  ([&] {                                                    \
+    decltype(x) eintr_wrapper_result;                       \
+    do {                                                    \
+      eintr_wrapper_result = (x);                           \
+    } while (eintr_wrapper_result == -1 && errno == EINTR); \
+    return eintr_wrapper_result;                            \
+  }())
+#endif

 namespace perfetto {
 namespace base {

 namespace internal {
-// Used for the most common cases of ScopedResource where there is only one
-// invalid value.
-template <typename T, T InvalidValue>
-struct DefaultValidityChecker {
-  static bool IsValid(T t) { return t != InvalidValue; }
-};
+extern std::atomic<uint32_t> g_cached_page_size;
+uint32_t GetSysPageSizeSlowpath();
 }  // namespace internal

-// RAII classes for auto-releasing fds and dirs.
-// if T is a pointer type, InvalidValue must be nullptr. Doing otherwise
-// causes weird unexpected behaviors (See https://godbolt.org/z/5nGMW4).
-template <typename T,
-          int (*CloseFunction)(T),
-          T InvalidValue,
-          bool CheckClose = true,
-          class Checker = internal::DefaultValidityChecker<T, InvalidValue>>
-class ScopedResource {
- public:
-  using ValidityChecker = Checker;
-  static constexpr T kInvalid = InvalidValue;
-
-  explicit ScopedResource(T t = InvalidValue) : t_(t) {}
-  ScopedResource(ScopedResource&& other) noexcept : t_(other.t_) {
-    other.t_ = InvalidValue;
-  }
-  ScopedResource& operator=(ScopedResource&& other) {
-    reset(other.t_);
-    other.t_ = InvalidValue;
-    return *this;
-  }
-  T get() const { return t_; }
-  T operator*() const { return t_; }
-  explicit operator bool() const { return Checker::IsValid(t_); }
-  void reset(T r = InvalidValue) {
-    if (Checker::IsValid(t_)) {
-      int res = CloseFunction(t_);
-      if (CheckClose)
-        PERFETTO_CHECK(res == 0);
-    }
-    t_ = r;
-  }
-  T release() {
-    T t = t_;
-    t_ = InvalidValue;
-    return t;
-  }
-  ~ScopedResource() { reset(InvalidValue); }
-
- private:
-  ScopedResource(const ScopedResource&) = delete;
-  ScopedResource& operator=(const ScopedResource&) = delete;
-  T t_;
-};
-
-// Declared in file_utils.h. Forward declared to avoid #include cycles.
-int PERFETTO_EXPORT_COMPONENT CloseFile(int fd);
+// Returns the system's page size. Use this when dealing with mmap, madvise and
+// similar mm-related syscalls.
+// This function might be called in hot paths. Avoid calling getpagesize() all
+// the times, in many implementations getpagesize() calls sysconf() which is
+// not cheap.
+inline uint32_t GetSysPageSize() {
+  const uint32_t page_size =
+      internal::g_cached_page_size.load(std::memory_order_relaxed);
+  return page_size != 0 ? page_size : internal::GetSysPageSizeSlowpath();
+}

-// Use this for file resources obtained via open() and similar APIs.
-using ScopedFile = ScopedResource<int, CloseFile, -1>;
-using ScopedFstream = ScopedResource<FILE*, fclose, nullptr>;
+template <typename T, size_t TSize>
+constexpr size_t ArraySize(const T (&)[TSize]) {
+  return TSize;
+}

-// Use this for resources that are HANDLE on Windows. See comments in
-// platform_handle.h
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-using ScopedPlatformHandle = ScopedResource<PlatformHandle,
-                                            ClosePlatformHandle,
-                                            /*InvalidValue=*/nullptr,
-                                            /*CheckClose=*/true,
-                                            PlatformHandleChecker>;
+// Adds `a` and `b` into `*result`, returning false on overflow. The value of
+// `*result` is unspecified when false is returned.
+inline bool CheckedAdd(int64_t a, int64_t b, int64_t* result) {
+#if defined(__clang__) || defined(__GNUC__)
+  return !__builtin_add_overflow(a, b, result);
 #else
-// On non-windows systems we alias ScopedPlatformHandle to ScopedFile because
-// they are really the same. This is to allow assignments between the two in
-// Linux-specific code paths that predate ScopedPlatformHandle.
-static_assert(std::is_same<int, PlatformHandle>::value, "");
-using ScopedPlatformHandle = ScopedFile;
-
-// DIR* does not exist on Windows.
-using ScopedDir = ScopedResource<DIR*, closedir, nullptr>;
+  constexpr int64_t kMax = std::numeric_limits<int64_t>::max();
+  constexpr int64_t kMin = std::numeric_limits<int64_t>::min();
+  if ((b > 0 && a > kMax - b) || (b < 0 && a < kMin - b))
+    return false;
+  *result = a + b;
+  return true;
 #endif
+}

-}  // namespace base
-}  // namespace perfetto
-
-#endif  // INCLUDE_PERFETTO_EXT_BASE_SCOPED_FILE_H_
-/*
- * 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.
- */
-
-#ifndef INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_
-
-#include <fcntl.h>  // For mode_t & O_RDONLY/RDWR. Exists also on Windows.
-#include <stddef.h>
-#include <stdint.h>
-
-#include <functional>
-#include <memory>
-#include <optional>
-#include <string>
-#include <vector>
-
-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/base/export.h"
-// gen_amalgamated expanded: #include "perfetto/base/status.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/sys_types.h"
-
-namespace perfetto {
-namespace base {
-
-class TaskRunner;
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-using FileOpenMode = int;
-inline constexpr char kDevNull[] = "NUL";
-inline constexpr char kFopenReadFlag[] = "r";
+// Subtracts `b` from `a` into `*result`, returning false on overflow. The
+// value of `*result` is unspecified when false is returned.
+inline bool CheckedSub(int64_t a, int64_t b, int64_t* result) {
+#if defined(__clang__) || defined(__GNUC__)
+  return !__builtin_sub_overflow(a, b, result);
 #else
-using FileOpenMode = mode_t;
-inline constexpr char kDevNull[] = "/dev/null";
-inline constexpr char kFopenReadFlag[] = "re";
+  constexpr int64_t kMax = std::numeric_limits<int64_t>::max();
+  constexpr int64_t kMin = std::numeric_limits<int64_t>::min();
+  if ((b < 0 && a > kMax + b) || (b > 0 && a < kMin + b))
+    return false;
+  *result = a - b;
+  return true;
 #endif
+}

-constexpr FileOpenMode kFileModeInvalid = static_cast<FileOpenMode>(-1);
-
-// Cross-platform variant of ReadFileDescriptor() that takes a PlatformHandle.
-// On Windows normalizes ERROR_BROKEN_PIPE to EOF so behavior matches POSIX.
-bool ReadPlatformHandle(PlatformHandle, std::string* out);
-
-// Reads from |fd|, appending what is currently available into |*out|.
-// Returns:
-//  True: EOF reached (all writers of |fd| have closed their end).
-//  False: read error. On a non-blocking |fd| this includes EAGAIN (no data
-//    currently available but writers are still alive); callers can check
-//    IsAgain(errno) and retry on the next readability notification.
-bool ReadFileDescriptor(int fd, std::string* out);
+// Returns whether `value` is positive or negative zero. Compares the bit
+// pattern so it can be used in translation units compiled with -Wfloat-equal.
+inline bool IsZero(double value) {
+  uint64_t bits;
+  memcpy(&bits, &value, sizeof(bits));
+  return (bits << 1) == 0;
+}
+inline bool IsZero(int64_t value) {
+  return value == 0;
+}

-// Convenience wrapper around ReadFileDescriptor() that takes a FILE*.
-bool ReadFileStream(FILE* f, std::string* out);
+// Returns the bits of `value` with -0.0 turned into 0.0 and every NaN into
+// the same NaN, so equal values, and all NaNs, have the same bits.
+inline uint64_t CanonicalFloatBits(double value) {
+  uint64_t bits;
+  memcpy(&bits, &value, sizeof(bits));
+  if (PERFETTO_UNLIKELY((bits << 1) == 0)) {
+    return 0;
+  }
+  if (PERFETTO_UNLIKELY((bits << 1) > (uint64_t{0x7FF} << 53))) {
+    double nan = std::numeric_limits<double>::quiet_NaN();
+    memcpy(&bits, &nan, sizeof(bits));
+  }
+  return bits;
+}
+inline uint32_t CanonicalFloatBits(float value) {
+  uint32_t bits;
+  memcpy(&bits, &value, sizeof(bits));
+  if (PERFETTO_UNLIKELY((bits << 1) == 0)) {
+    return 0;
+  }
+  if (PERFETTO_UNLIKELY((bits << 1) > (uint32_t{0xFF} << 24))) {
+    float nan = std::numeric_limits<float>::quiet_NaN();
+    memcpy(&bits, &nan, sizeof(bits));
+  }
+  return bits;
+}

-// Opens |path| read-only and reads its contents into |*out|.
-// Returns false if the file cannot be opened.
-bool ReadFile(const std::string& path, std::string* out);
+// Adds `a` and `b`, clamping to INT64_MIN / INT64_MAX on overflow instead of
+// wrapping (which is UB and can flip the sign of the result).
+inline int64_t SaturatingAdd(int64_t a, int64_t b) {
+  constexpr int64_t kMax = std::numeric_limits<int64_t>::max();
+  constexpr int64_t kMin = std::numeric_limits<int64_t>::min();
+#if defined(__clang__) || defined(__GNUC__)
+  int64_t result;
+  if (PERFETTO_UNLIKELY(__builtin_add_overflow(a, b, &result)))
+    return a < 0 ? kMin : kMax;
+  return result;
+#else
+  if (b > 0 && a > kMax - b)
+    return kMax;
+  if (b < 0 && a < kMin - b)
+    return kMin;
+  return a + b;
+#endif
+}

-// A wrapper around read(2). It deals with Linux vs Windows includes. It also
-// deals with handling EINTR. Has the same semantics of UNIX's read(2).
-ssize_t Read(int fd, void* dst, size_t dst_size);
+// Multiplies `a` by `b`, clamping to INT64_MIN / INT64_MAX on overflow instead
+// of wrapping (which is UB and can flip the sign of the result).
+inline int64_t SaturatingMultiply(int64_t a, int64_t b) {
+  constexpr int64_t kMax = std::numeric_limits<int64_t>::max();
+  constexpr int64_t kMin = std::numeric_limits<int64_t>::min();
+#if defined(__clang__) || defined(__GNUC__)
+  int64_t result;
+  if (PERFETTO_UNLIKELY(__builtin_mul_overflow(a, b, &result))) {
+    // On overflow the true product's sign is the XOR of the operands' signs.
+    // Neither operand can be zero here (0 never overflows).
+    return ((a < 0) == (b < 0)) ? kMax : kMin;
+  }
+  return result;
+#else
+  // Portable fallback (e.g. MSVC cl.exe): detect overflow *before* multiplying
+  // by dividing a limit by one operand, which never itself overflows.
+  if (a == 0 || b == 0)
+    return 0;
+  if ((a < 0) == (b < 0)) {  // Same sign: product is positive.
+    if (a > 0 ? (a > kMax / b) : (a < kMax / b))
+      return kMax;
+  } else {  // Opposite signs: product is negative.
+    if (a > 0 ? (b < kMin / a) : (a < kMin / b))
+      return kMin;
+  }
+  return a * b;
+#endif
+}

-// Call write until all data is written or an error is detected.
+// Function object which invokes 'free' on its parameter, which must be
+// a pointer. Can be used to store malloc-allocated pointers in std::unique_ptr:
 //
-// man 2 write:
-//   If a write() is interrupted by a signal handler before any bytes are
-//   written, then the call fails with the error EINTR; if it is
-//   interrupted after at least one byte has been written, the call
-//   succeeds, and returns the number of bytes written.
-ssize_t WriteAll(int fd, const void* buf, size_t count);
-
-// Copies all data from |fd_in| to |fd_out|. Saves the offset of |fd_in|,
-// rewinds it to the beginning, copies the content, and restores the offset.
-// |fd_in| can't be a pipe, socket of FIFO.
-base::Status CopyFileContents(int fd_in, int fd_out);
-
-ssize_t WriteAllHandle(PlatformHandle, const void* buf, size_t count);
+// std::unique_ptr<int, base::FreeDeleter> foo_ptr(
+//     static_cast<int*>(malloc(sizeof(int))));
+struct FreeDeleter {
+  inline void operator()(void* ptr) const { free(ptr); }
+};

-ScopedFile OpenFile(const std::string& path,
-                    int flags,
-                    FileOpenMode = kFileModeInvalid);
-ScopedFstream OpenFstream(const std::string& path, const std::string& mode);
+template <typename T>
+constexpr T AssumeLittleEndian(T value) {
+#if !PERFETTO_IS_LITTLE_ENDIAN()
+  static_assert(false, "Unimplemented on big-endian archs");
+#endif
+  return value;
+}

-// This is an alias for close(). It's to avoid leaking windows.h in headers.
-// Exported because ScopedFile is used in the /include/ext API by Chromium
-// component builds.
-int PERFETTO_EXPORT_COMPONENT CloseFile(int fd);
+// Round up |size| to a multiple of |alignment| (must be a power of two).
+inline constexpr size_t AlignUp(size_t size, size_t alignment) {
+  return (size + alignment - 1) & ~(alignment - 1);
+}

-bool FlushFile(int fd);
+// Round down |size| to a multiple of |alignment| (must be a power of two).
+inline constexpr size_t AlignDown(size_t size, size_t alignment) {
+  return size & ~(alignment - 1);
+}

-// Moves the file offset to |offset| bytes from the beginning of the file.
-// Returns false if |offset| cannot be represented by the platform or the seek
-// fails.
-bool SeekFile(int fd, uint64_t offset);
+template <typename T>
+inline constexpr bool IsPowerOfTwo(T x) {
+  static_assert(std::is_unsigned_v<T> && std::is_integral_v<T>,
+                "T must be an unsigned integer");
+  return x != 0 && (x & (x - 1)) == 0;
+}

-// Changes the size of an open file to |size| bytes.
-// Returns false if |size| cannot be represented by the platform or truncation
-// fails.
-bool TruncateFile(int fd, uint64_t size);
+// Returns the smallest power of two greater than or equal to |x|. Returns zero
+// for zero or when the result is not representable by T.
+template <typename T>
+inline constexpr T RoundUpToPowerOfTwo(T x) {
+  static_assert(std::is_unsigned_v<T> && std::is_integral_v<T>,
+                "T must be an unsigned integer");
+  if (x == 0) {
+    return 0;
+  }
+  --x;
+  for (size_t shift = 1; shift < sizeof(T) * 8; shift *= 2) {
+    x |= x >> shift;
+  }
+  return ++x;
+}

-// Returns true if mkdir succeeds, false if it fails (see errno in that case).
-// `mode` is the permission bits for the new directory; it is ignored on
-// Windows.
-bool Mkdir(const std::string& path, uint32_t mode = 0755);
+// TODO(primiano): clean this up and move all existing usages to the constexpr
+// version above.
+template <size_t alignment>
+constexpr size_t AlignUp(size_t size) {
+  static_assert(IsPowerOfTwo(alignment), "alignment must be a pow2");
+  return AlignUp(size, alignment);
+}

-// Calls rmdir() on UNIX, _rmdir() on Windows.
-bool Rmdir(const std::string& path);
+inline bool IsAgain(int err) {
+  return err == EAGAIN || err == EWOULDBLOCK;
+}

-// Removes a file: unlink() on UNIX, _unlink() on Windows. Takes a const char*
-// and is async-signal-safe on POSIX, so it's callable from a signal handler.
-bool Unlink(const char* path);
+// setenv(2)-equivalent. Deals with Windows vs Posix discrepancies.
+void SetEnv(const std::string& key, const std::string& value);

-// Wrapper around access(path, F_OK).
-bool FileExists(const std::string& path);
+// unsetenv(2)-equivalent. Deals with Windows vs Posix discrepancies.
+void UnsetEnv(const std::string& key);

-// Returns true if the path exists and is a directory.
-bool DirectoryExists(const std::string& path);
+// Returns true if |fd| is connected to an interactive terminal (TTY). Deals
+// with Windows vs Posix discrepancies (isatty() vs _isatty()).
+bool IsTty(int fd);

-// Gets the extension for a filename. If the file has two extensions, returns
-// only the last one (foo.pb.gz => .gz). Returns empty string if there is no
-// extension.
-std::string GetFileExtension(const std::string& filename);
+// Convenience overload for C stdio streams (e.g. stdin/stdout/stderr).
+bool IsTty(FILE* stream);

-// Returns the basename component of a path (the final component after the last
-// directory separator). Behaves like man 2 basename, but works with both '/'
-// and '\' separators for cross-platform compatibility.
-// Examples:
-//   Basename("/usr/bin/ls") => "ls"
-//   Basename("/usr/bin/") => "bin"
-//   Basename("/") => "/"
-//   Basename("foo") => "foo"
-//   Basename("") => "."
-//   Basename("C:\\Windows\\System32") => "System32"
-std::string Basename(const std::string& path);
+// Calls mallopt(M_PURGE, 0) on Android. Does nothing on other platforms.
+// This forces the allocator to release freed memory. This is used to work
+// around various Scudo inefficiencies. See b/170217718.
+void MaybeReleaseAllocatorMemToOS();

-// Returns the directory component of a path (everything up to but not
-// including the final component). Behaves like man 2 dirname, but works with
-// both '/' and '\' separators for cross-platform compatibility.
-// Examples:
-//   Dirname("/usr/bin/ls") => "/usr/bin"
-//   Dirname("/usr/bin") => "/usr"
-//   Dirname("/") => "/"
-//   Dirname("foo") => "."
-//   Dirname("") => "."
-//   Dirname("C:\\Windows\\System32") => "C:\\Windows"
-std::string Dirname(const std::string& path);
+// geteuid() on POSIX OSes, returns 0 on Windows (See comment in utils.cc).
+uid_t GetCurrentUserId();

-// Returns the length of the leading root component of a path: "/" on UNIX,
-// "C:\" or "\\" (UNC) on Windows. Returns 0 if the path is relative. Like
-// Basename() and Dirname(), both '/' and '\' are recognized on all platforms.
-// Stripping the returned prefix turns the path into a relative one.
-// Examples:
-//   PathRootPrefixLength("/usr/bin/ls") => 1
-//   PathRootPrefixLength("//usr/bin") => 2
-//   PathRootPrefixLength("C:\\Windows") => 3
-//   PathRootPrefixLength("C:foo") => 0 (relative to the current dir of drive
-//   C:) PathRootPrefixLength("foo/bar") => 0
-size_t PathRootPrefixLength(const std::string& path);
+// Forks the process.
+// Parent: calls |parent_cb| with the child's PID and exits with its return
+//         value. The callback owns any startup output (e.g. printing the PID).
+// Child: redirects stdio onto /dev/null, chdirs into / and returns.
+void Daemonize(std::function<int(pid_t)> parent_cb);

-// Returns true if the path starts with a root component. See
-// PathRootPrefixLength().
-bool IsAbsolutePath(const std::string& path);
+// Returns the path of the current executable, e.g. /foo/bar/exe.
+std::string GetCurExecutablePath();

-// Puts the path to all files under |dir_path| in |output|, recursively walking
-// subdirectories. File paths are relative to |dir_path|. Only files are
-// included, not directories. Path separator is always '/', even on windows (not
-// '\').
-base::Status ListFilesRecursive(const std::string& dir_path,
-                                std::vector<std::string>& output);
+// Returns the directory where the current executable lives in, e.g. /foo/bar.
+// This is independent of cwd().
+std::string GetCurExecutableDir();

-// Lists immediate subdirectories in |dir_path| (non-recursive). Directory names
-// are relative to |dir_path| and do not include the path separator. Returns
-// only directories, not files. Works on both Unix and Windows.
-base::Status ListDirectories(const std::string& dir_path,
-                             std::vector<std::string>& output);
+// Memory returned by AlignedAlloc() must be freed via AlignedFree() not just
+// free. It makes a difference on Windows where _aligned_malloc() and
+// _aligned_free() must be paired.
+// Prefer using the AlignedAllocTyped() below which takes care of the pairing.
+void* AlignedAlloc(size_t alignment, size_t size);
+void AlignedFree(void*);

-// Sets |path|'s owner group to |group_name| and permission mode bits to
-// |mode_bits|.
-base::Status SetFilePermissions(const std::string& path,
-                                const std::string& group_name,
-                                const std::string& mode_bits);
+// Detects Sync-mode MTE (currently being tested in some Android builds).
+// This is known to use extra memory for the stack history buffer.
+bool IsSyncMemoryTaggingEnabled();

-// Returns the size of the file located at |path|, or nullopt in case of error.
-std::optional<uint64_t> GetFileSize(const std::string& path);
+// A RAII version of the above, which takes care of pairing Aligned{Alloc,Free}.
+template <typename T>
+struct AlignedDeleter {
+  inline void operator()(T* ptr) const { AlignedFree(ptr); }
+};

-// Returns the size of the open file |fd|, or nullopt in case of error.
-std::optional<uint64_t> GetFileSize(PlatformHandle fd);
+// The remove_extent<T> here and below is to allow defining unique_ptr<T[]>.
+// As per https://en.cppreference.com/w/cpp/memory/unique_ptr the Deleter takes
+// always a T*, not a T[]*.
+template <typename T>
+using AlignedUniquePtr =
+    std::unique_ptr<T, AlignedDeleter<typename std::remove_extent<T>::type>>;

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-// On Windows PlatformHandle is a HANDLE, not a file descriptor. This overload
-// accepts the CRT file descriptors returned by base::OpenFile(). On other
-// platforms PlatformHandle is an int, so the overload above already covers it.
-std::optional<uint64_t> GetFileSize(int fd);
-#endif
+template <typename T>
+AlignedUniquePtr<T> AlignedAllocTyped(size_t n_membs) {
+  using TU = typename std::remove_extent<T>::type;
+  return AlignedUniquePtr<T>(
+      static_cast<TU*>(AlignedAlloc(alignof(TU), sizeof(TU) * n_membs)));
+}

-// This class uses inotify (on Linux/Android) to watch for the creation of
-// files in the filesystem. When the specified file is created, it triggers a
-// callback function.
-// Destroying the returned unique_ptr will automatically unregister the watch.
-//
-// Note: This only works with filesystem paths (not abstract sockets or other
-// special file types).
-// It's only supported on Linux and Android, it's a no-op (returns nullptr) on
-// other platforms.
-//
-// Usage:
-//   auto watch = LinuxFileWatch::WatchFileCreation(
-//       task_runner, "/tmp/my_file", []() {
-//         // Called when /tmp/my_file is created
-//       });
-class LinuxFileWatch {
+// A RAII wrapper to invoke a function when leaving a function/scope.
+template <typename Func>
+class OnScopeExitWrapper {
  public:
-  // Creates a watcher for file creation. Returns nullptr if the path is not a
-  // valid filesystem path or if the platform doesn't support inotify. The
-  // callback will be invoked on the provided TaskRunner when the file is
-  // created.
-  static std::unique_ptr<LinuxFileWatch> WatchFileCreation(
-      TaskRunner*,
-      const char* path,
-      std::function<void()> callback);
-
-  virtual ~LinuxFileWatch();
+  explicit OnScopeExitWrapper(Func f) : f_(std::move(f)), active_(true) {}
+  OnScopeExitWrapper(OnScopeExitWrapper&& other) noexcept
+      : f_(std::move(other.f_)), active_(other.active_) {
+    other.active_ = false;
+  }
+  ~OnScopeExitWrapper() {
+    if (active_)
+      f_();
+  }

- protected:
-  LinuxFileWatch() = default;
+ private:
+  Func f_;
+  bool active_;
 };

+template <typename Func>
+PERFETTO_WARN_UNUSED_RESULT OnScopeExitWrapper<Func> OnScopeExit(Func f) {
+  return OnScopeExitWrapper<Func>(std::move(f));
+}
+
+// Returns a xxd-style hex dump (hex + ascii chars) of the input data.
+std::string HexDump(const void* data, size_t len, size_t bytes_per_line = 16);
+inline std::string HexDump(const std::string& data,
+                           size_t bytes_per_line = 16) {
+  return HexDump(data.data(), data.size(), bytes_per_line);
+}
+
 }  // namespace base
 }  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_
-// gen_amalgamated begin header: include/perfetto/ext/base/string_splitter.h
+#endif  // INCLUDE_PERFETTO_EXT_BASE_UTILS_H_
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -2324,91 +2481,298 @@ class LinuxFileWatch {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_STRING_SPLITTER_H_
-#define INCLUDE_PERFETTO_EXT_BASE_STRING_SPLITTER_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
+#define INCLUDE_PERFETTO_EXT_BASE_BASE64_H_

+#include <optional>
 #include <string>

+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"  // For ssize_t.
+
 namespace perfetto {
 namespace base {

-// C++ version of strtok(). Splits a string without making copies or any heap
-// allocations. Destructs the original string passed in input.
-// Supports the special case of using \0 as a delimiter.
-// The token returned in output are valid as long as the input string is valid.
-class StringSplitter {
- public:
-  // Whether an empty string (two delimiters side-to-side) is a valid token.
-  enum class EmptyTokenMode {
-    DISALLOW_EMPTY_TOKENS,
-    ALLOW_EMPTY_TOKENS,
+// Returns the length of the destination string (included '=' padding).
+// Does NOT include the size of the string null terminator.
+inline size_t Base64EncSize(size_t src_size) {
+  return (src_size + 2) / 3 * 4;
+}

-    DEFAULT = DISALLOW_EMPTY_TOKENS,
-  };
+// Returns the upper bound on the length of the destination buffer.
+// The actual decoded length might be <= the number returned here.
+inline size_t Base64DecSize(size_t src_size) {
+  return (src_size + 3) / 4 * 3;
+}

-  // Can take ownership of the string if passed via std::move(), e.g.:
-  // StringSplitter(std::move(str), '\n');
-  StringSplitter(std::string,
-                 char delimiter,
-                 EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);
+// Does NOT null-terminate |dst|.
+ssize_t Base64Encode(const void* src,
+                     size_t src_size,
+                     char* dst,
+                     size_t dst_size);

-  // Splits a C-string. The input string will be forcefully null-terminated (so
-  // str[size - 1] should be == '\0' or the last char will be truncated).
-  StringSplitter(char* str,
-                 size_t size,
-                 char delimiter,
-                 EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);
+std::string Base64Encode(const void* src, size_t src_size);

-  // Splits the current token from an outer StringSplitter instance. This is to
-  // chain splitters as follows:
-  // for (base::StringSplitter lines(x, '\n'); ss.Next();)
-  //   for (base::StringSplitter words(&lines, ' '); words.Next();)
-  StringSplitter(StringSplitter*,
-                 char delimiter,
-                 EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);
+inline std::string Base64Encode(StringView sv) {
+  return Base64Encode(sv.data(), sv.size());
+}

-  // Returns true if a token is found (in which case it will be stored in
-  // cur_token()), false if no more tokens are found.
-  bool Next();
+// Returns -1 in case of failure.
+ssize_t Base64Decode(const char* src,
+                     size_t src_size,
+                     uint8_t* dst,
+                     size_t dst_size);

-  // Returns the next token if found (in which case it will be stored in
-  // cur_token()), nullptr if no more tokens are found.
-  char* NextToken() { return Next() ? cur_token() : nullptr; }
+std::optional<std::string> Base64Decode(const char* src, size_t src_size);

-  // Returns the current token iff last call to Next() returned true. In this
-  // case it guarantees that the returned string is always null terminated.
-  // In all other cases (before the 1st call to Next() and after Next() returns
-  // false) returns nullptr.
-  char* cur_token() { return cur_; }
+inline std::optional<std::string> Base64Decode(StringView sv) {
+  return Base64Decode(sv.data(), sv.size());
+}

-  // Returns the length of the current token (excluding the null terminator).
-  size_t cur_token_size() const { return cur_size_; }
+}  // namespace base
+}  // namespace perfetto

-  // Return the untokenized remainder of the input string that occurs after the
-  // current token.
-  char* remainder() { return next_; }
+#endif  // INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
+/*
+ * Copyright (C) 2021 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.
+ */

-  // Returns the size of the untokenized input
-  size_t remainder_size() { return static_cast<size_t>(end_ - next_); }
+// gen_amalgamated expanded: #include "perfetto/ext/base/base64.h"

- private:
-  StringSplitter(const StringSplitter&) = delete;
-  StringSplitter& operator=(const StringSplitter&) = delete;
-  void Initialize(char* str, size_t size);
+namespace perfetto {
+namespace base {

-  std::string str_;
-  char* cur_;
-  size_t cur_size_;
-  char* next_;
-  char* end_;  // STL-style, points one past the last char.
-  const char delimiter_;
-  const EmptyTokenMode empty_token_mode_;
+namespace {
+
+constexpr char kPadding = '=';
+
+constexpr char kEncTable[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static_assert(sizeof(kEncTable) == (1u << 6) + sizeof('\0'), "Bad table size");
+
+// Maps an ASCII character to its 6-bit value. It only contains translations
+// from '+' to 'z'. Supports the standard (+/) and URL-safe (-_) alphabets.
+constexpr uint8_t kX = 0xff;  // Value used for invalid characters
+constexpr uint8_t kDecTable[] = {
+    62, kX, 62, kX, 63, 52, 53, 54, 55, 56,  // 00 - 09
+    57, 58, 59, 60, 61, kX, kX, kX, 0,  kX,  // 10 - 19
+    kX, kX, 0,  1,  2,  3,  4,  5,  6,  7,   // 20 - 29
+    8,  9,  10, 11, 12, 13, 14, 15, 16, 17,  // 30 - 39
+    18, 19, 20, 21, 22, 23, 24, 25, kX, kX,  // 40 - 49
+    kX, kX, 63, kX, 26, 27, 28, 29, 30, 31,  // 50 - 59
+    32, 33, 34, 35, 36, 37, 38, 39, 40, 41,  // 60 - 69
+    42, 43, 44, 45, 46, 47, 48, 49, 50, 51,  // 70 - 79
+};
+constexpr char kMinDecChar = '+';
+constexpr char kMaxDecChar = 'z';
+static_assert(kMaxDecChar - kMinDecChar <= sizeof(kDecTable), "Bad table size");
+
+inline uint8_t DecodeChar(char c) {
+  if (c < kMinDecChar || c > kMaxDecChar)
+    return kX;
+  return kDecTable[c - kMinDecChar];
+}
+
+}  // namespace
+
+ssize_t Base64Encode(const void* src,
+                     size_t src_size,
+                     char* dst,
+                     size_t dst_size) {
+  const size_t padded_dst_size = Base64EncSize(src_size);
+  if (dst_size < padded_dst_size)
+    return -1;  // Not enough space in output.
+
+  const uint8_t* rd = static_cast<const uint8_t*>(src);
+  const uint8_t* const end = rd + src_size;
+  size_t wr_size = 0;
+  while (rd < end) {
+    uint8_t s[3]{};
+    s[0] = *(rd++);
+    dst[wr_size++] = kEncTable[s[0] >> 2];
+
+    uint8_t carry0 = static_cast<uint8_t>((s[0] & 0x03) << 4);
+    if (PERFETTO_LIKELY(rd < end)) {
+      s[1] = *(rd++);
+      dst[wr_size++] = kEncTable[carry0 | (s[1] >> 4)];
+    } else {
+      dst[wr_size++] = kEncTable[carry0];
+      dst[wr_size++] = kPadding;
+      dst[wr_size++] = kPadding;
+      break;
+    }
+
+    uint8_t carry1 = static_cast<uint8_t>((s[1] & 0x0f) << 2);
+    if (PERFETTO_LIKELY(rd < end)) {
+      s[2] = *(rd++);
+      dst[wr_size++] = kEncTable[carry1 | (s[2] >> 6)];
+    } else {
+      dst[wr_size++] = kEncTable[carry1];
+      dst[wr_size++] = kPadding;
+      break;
+    }
+
+    dst[wr_size++] = kEncTable[s[2] & 0x3f];
+  }
+  PERFETTO_DCHECK(wr_size == padded_dst_size);
+  return static_cast<ssize_t>(padded_dst_size);
+}
+
+std::string Base64Encode(const void* src, size_t src_size) {
+  std::string dst;
+  dst.resize(Base64EncSize(src_size));
+  auto res = Base64Encode(src, src_size, &dst[0], dst.size());
+  PERFETTO_CHECK(res == static_cast<ssize_t>(dst.size()));
+  return dst;
+}
+
+ssize_t Base64Decode(const char* src,
+                     size_t src_size,
+                     uint8_t* dst,
+                     size_t dst_size) {
+  const size_t min_dst_size = Base64DecSize(src_size);
+  if (dst_size < min_dst_size)
+    return -1;
+
+  const char* rd = src;
+  const char* const end = src + src_size;
+  size_t wr_size = 0;
+
+  char s[4]{};
+  while (rd < end) {
+    uint8_t d[4];
+    for (uint32_t j = 0; j < 4; j++) {
+      // Padding is only feasible for the last 2 chars of each group of 4.
+      s[j] = rd < end ? *(rd++) : (j < 2 ? '\0' : kPadding);
+      d[j] = DecodeChar(s[j]);
+      if (d[j] == kX)
+        return -1;  // Invalid input char.
+    }
+    dst[wr_size] = static_cast<uint8_t>((d[0] << 2) | (d[1] >> 4));
+    dst[wr_size + 1] = static_cast<uint8_t>((d[1] << 4) | (d[2] >> 2));
+    dst[wr_size + 2] = static_cast<uint8_t>((d[2] << 6) | (d[3]));
+    wr_size += 3;
+  }
+
+  PERFETTO_CHECK(wr_size <= dst_size);
+  wr_size -= (s[3] == kPadding ? 1 : 0) + (s[2] == kPadding ? 1 : 0);
+  return static_cast<ssize_t>(wr_size);
+}
+
+std::optional<std::string> Base64Decode(const char* src, size_t src_size) {
+  std::string dst;
+  dst.resize(Base64DecSize(src_size));
+  auto res = Base64Decode(src, src_size, reinterpret_cast<uint8_t*>(&dst[0]),
+                          dst.size());
+  if (res < 0)
+    return std::nullopt;  // Decoding error.
+
+  PERFETTO_CHECK(res <= static_cast<ssize_t>(dst.size()));
+  dst.resize(static_cast<size_t>(res));
+  return std::make_optional(dst);
+}
+
+}  // namespace base
+}  // namespace perfetto
+// gen_amalgamated begin source: src/base/cpu_info.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/cpu_info.h
+/*
+ * Copyright (C) 2026 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_H_
+#define INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_H_
+
+#include <stdint.h>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace perfetto {
+namespace base {
+
+struct CpuInfo {
+  std::string processor;
+  uint32_t cpu_index = 0;
+  std::optional<uint32_t> implementer;
+  std::optional<uint32_t> architecture;
+  std::optional<uint32_t> variant;
+  std::optional<uint32_t> part;
+  std::optional<uint32_t> revision;
+  uint64_t features = 0;
+  char arm_cpuid[32] = {};
 };

+// Parses the contents of the input string into per-CPU entries.
+std::vector<CpuInfo> ParseCpuInfo(std::string proc_cpu_info);
+
+// Reads /proc/cpuinfo and parses it into per-CPU entries.
+std::vector<CpuInfo> ReadCpuInfo();
+
 }  // namespace base
 }  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_STRING_SPLITTER_H_
+#endif  // INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_H_
+// gen_amalgamated begin header: include/perfetto/ext/base/cpu_info_features_allowlist.h
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_FEATURES_ALLOWLIST_H_
+#define INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_FEATURES_ALLOWLIST_H_
+
+namespace perfetto {
+namespace base {
+
+// APPEND ONLY. DO NOT EVER REMOVE ENTRIES FROM THIS ARRAY OR REORDER.
+// This array is used both by traced_probes and trace_processor to index the
+// cpuinfo flags. Changing the order will break trace_processor compatibility
+// with old traces.
+constexpr const char* kCpuInfoFeatures[] = {
+    "mte",   // DO NOT REMOVE/REODER.
+    "mte3",  // DO NOT REMOVE/REODER.
+};
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_CPU_INFO_FEATURES_ALLOWLIST_H_
 /*
  * Copyright (C) 2026 The Android Open Source Project
  *
@@ -3991,6 +4355,51 @@ bool FlushFile(int fd) {
 #endif
 }

+ScopedFile DupFile(int fd) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  ScopedFile duplicate(_dup(fd));
+  if (duplicate && !SetHandleInformation(
+                       reinterpret_cast<HANDLE>(_get_osfhandle(*duplicate)),
+                       HANDLE_FLAG_INHERIT, 0))
+    return ScopedFile();
+  return duplicate;
+#else
+  return ScopedFile(fcntl(fd, F_DUPFD_CLOEXEC, 0));
+#endif
+}
+
+bool IsRegularFile(const std::string& path) {
+  struct stat st{};
+  if (stat(path.c_str(), &st) != 0)
+    return false;
+  PERFETTO_MSAN_UNPOISON(&st, sizeof(st));
+  return (st.st_mode & S_IFMT) == S_IFREG;
+}
+
+bool IsSameFile(const std::string& first, const std::string& second) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  auto identity = [](const std::string& path,
+                     BY_HANDLE_FILE_INFORMATION* info) {
+    ScopedPlatformHandle file(CreateFileA(
+        path.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+        nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
+    return file && GetFileInformationByHandle(*file, info);
+  };
+  BY_HANDLE_FILE_INFORMATION a{}, b{};
+  return identity(first, &a) && identity(second, &b) &&
+         a.dwVolumeSerialNumber == b.dwVolumeSerialNumber &&
+         a.nFileIndexHigh == b.nFileIndexHigh &&
+         a.nFileIndexLow == b.nFileIndexLow;
+#else
+  struct stat a{}, b{};
+  if (stat(first.c_str(), &a) != 0 || stat(second.c_str(), &b) != 0)
+    return false;
+  PERFETTO_MSAN_UNPOISON(&a, sizeof(a));
+  PERFETTO_MSAN_UNPOISON(&b, sizeof(b));
+  return a.st_dev == b.st_dev && a.st_ino == b.st_ino;
+#endif
+}
+
 bool SeekFile(int fd, uint64_t offset) {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
   if (fd < 0) {
@@ -4082,15 +4491,6 @@ ScopedFile OpenFile(const std::string& path, int flags, FileOpenMode mode) {

 ScopedFstream OpenFstream(const std::string& path, const std::string& mode) {
   ScopedFstream file;
-  // On Windows fopen interprets filename using the ANSI or OEM codepage but
-  // sqlite3_value_text returns a UTF-8 string. To make sure we interpret the
-  // filename correctly we use _wfopen and a UTF-16 string on windows.
-  //
-  // On Windows fopen also open files in the text mode by default, but we want
-  // to open them in the binary mode, to avoid silly EOL translations (and to be
-  // consistent with base::OpenFile). So we check the mode first and append 'b'
-  // mode only when it makes sense.
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
   std::string s_mode(mode);
   // Windows supports non-standard mode extension that sets encoding in text
   // mode. If you need to open a FILE* in text mode, use the fopen API directly.
@@ -4100,13 +4500,22 @@ ScopedFstream OpenFstream(const std::string& path, const std::string& mode) {
   if (!is_binary_mode)
     s_mode += 'b';

+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // On Windows fopen interprets filename using the ANSI or OEM codepage but
+  // sqlite3_value_text returns a UTF-8 string. To make sure we interpret the
+  // filename correctly we use _wfopen and a UTF-16 string on windows.
+  //
+  // On Windows fopen also open files in the text mode by default, but we want
+  // to open them in the binary mode, to avoid silly EOL translations (and to be
+  // consistent with base::OpenFile). So we check the mode first and append 'b'
+  // mode only when it makes sense.
   auto w_path = ToUtf16(path);
   auto w_mode = ToUtf16(s_mode);
   if (w_path && w_mode) {
     file.reset(_wfopen(w_path->c_str(), w_mode->c_str()));
   }
 #else
-  file.reset(fopen(path.c_str(), mode.c_str()));
+  file.reset(fopen(path.c_str(), s_mode.c_str()));
 #endif
   return file;
 }
@@ -4569,10 +4978,11 @@ LinuxFileWatch::~LinuxFileWatch() = default;

 }  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/base/getopt_compat.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/getopt_compat.h
+// gen_amalgamated begin source: src/base/flat_hash_map.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/flat_hash_map.h
+// gen_amalgamated begin header: include/perfetto/ext/base/bits.h
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -4587,63 +4997,95 @@ LinuxFileWatch::~LinuxFileWatch() = default;
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_GETOPT_COMPAT_H_
-#define INCLUDE_PERFETTO_EXT_BASE_GETOPT_COMPAT_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_BITS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_BITS_H_

-#include <cstddef>  // For std::nullptr_t
+#include <cstddef>
+#include <cstdint>

-// No translation units other than base/getopt.h and getopt_compat_unittest.cc
-// should directly include this file. Use base/getopt.h instead.
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
+#include <immintrin.h>
+#endif

 namespace perfetto {
 namespace base {
-namespace getopt_compat {

-// A tiny getopt() replacement for Windows, which doesn't have <getopt.h>.
-// This implementation is based on the subset of features that we use in the
-// Perfetto codebase. It doesn't even try to deal with the full surface of GNU's
-// getopt().
-// Limitations:
-// - getopt_long_only() is not supported.
-// - optional_argument is not supported. That is extremely subtle and caused us
-//   problems in the past with GNU's getopt.
-// - It does not reorder non-option arguments. It behaves like MacOS getopt, or
-//   GNU's when POSIXLY_CORRECT=1.
-// - Doesn't expose optopt or opterr.
-// - option.flag and longindex are not supported and must be nullptr.
+inline PERFETTO_ALWAYS_INLINE uint32_t CountLeadZeros32(uint32_t value) {
+#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
+  return static_cast<uint32_t>(_lzcnt_u32(value));
+#elif defined(__GNUC__) || defined(__clang__)
+  return value ? static_cast<uint32_t>(__builtin_clz(value)) : 32u;
+#else
+  unsigned long out;
+  return _BitScanReverse(&out, value) ? 31 - out : 32u;
+#endif
+}

-enum {
-  no_argument = 0,
-  required_argument = 1,
-};
+inline PERFETTO_ALWAYS_INLINE uint32_t CountLeadZeros64(uint64_t value) {
+#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
+  return static_cast<uint32_t>(_lzcnt_u64(value));
+#elif defined(__GNUC__) || defined(__clang__)
+  return value ? static_cast<uint32_t>(__builtin_clzll(value)) : 64u;
+#else
+  unsigned long out;
+  return _BitScanReverse64(&out, value) ? 63 - out : 64u;
+#endif
+}

-struct option {
-  const char* name;
-  int has_arg;
-  std::nullptr_t flag;  // Only nullptr is supported.
-  int val;
-};
+template <typename T>
+uint32_t CountLeadZeros(T value) {
+  if constexpr (sizeof(T) == 8)
+    return CountLeadZeros64(static_cast<uint64_t>(value));
+  return CountLeadZeros32(static_cast<uint32_t>(value));
+}

-extern char* optarg;
-extern int optind;
-extern int optopt;
-extern int opterr;
+inline PERFETTO_ALWAYS_INLINE uint32_t CountTrailZeros64(uint64_t value) {
+#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
+  return static_cast<uint32_t>(_tzcnt_u64(value));
+#elif defined(__GNUC__) || defined(__clang__)
+  return value ? static_cast<uint32_t>(__builtin_ctzll(value)) : 64u;
+#else
+  unsigned long out;
+  return _BitScanForward64(&out, value) ? static_cast<uint32_t>(out) : 64u;
+#endif
+}

-int getopt_long(int argc,
-                char** argv,
-                const char* shortopts,
-                const option* longopts,
-                std::nullptr_t /*longindex is not supported*/);
+inline PERFETTO_ALWAYS_INLINE uint32_t CountTrailZeros32(uint32_t value) {
+#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
+  return _tzcnt_u32(value);
+#elif defined(__GNUC__) || defined(__clang__)
+  return value ? static_cast<uint32_t>(__builtin_ctz(value)) : 32u;
+#else
+  unsigned long out;
+  return _BitScanForward(&out, value) ? out : 32u;
+#endif
+}

-int getopt(int argc, char** argv, const char* shortopts);
+template <typename T>
+inline PERFETTO_ALWAYS_INLINE uint32_t CountTrailZeros(T value) {
+  if (sizeof(T) == 8) {
+    return CountTrailZeros64(static_cast<uint64_t>(value));
+  }
+  return CountTrailZeros32(static_cast<uint32_t>(value));
+}
+
+template <typename T>
+constexpr bool AllBitsSet(T v) {
+  return v == static_cast<T>(-1);
+}

-}  // namespace getopt_compat
 }  // namespace base
 }  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_GETOPT_COMPAT_H_
+#endif  // INCLUDE_PERFETTO_EXT_BASE_BITS_H_
+// gen_amalgamated begin header: include/perfetto/ext/base/flat_hash_map_v1.h
+// gen_amalgamated begin header: include/perfetto/ext/base/murmur_hash.h
+// gen_amalgamated begin header: include/perfetto/ext/base/hash.h
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -4658,289 +5100,90 @@ int getopt(int argc, char** argv, const char* shortopts);
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/getopt_compat.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <vector>
-
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_HASH_H_
+#define INCLUDE_PERFETTO_EXT_BASE_HASH_H_

-namespace perfetto {
-namespace base {
-namespace getopt_compat {
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <tuple>
+#include <utility>

-char* optarg = nullptr;
-int optind = 0;
-int optopt = 0;
-int opterr = 1;
+namespace perfetto::base {

-namespace {
+// ============================================================================
+// Absl-style hash customization point
+// ============================================================================
+//
+// To make a type hashable with Perfetto hash functions, define a friend
+// function template:
+//
+//   template <typename H>
+//   friend H PerfettoHashValue(H h, const MyType& value) {
+//     return H::Combine(std::move(h), value.field1, value.field2);
+//   }
+//
+// This function will be found via ADL (Argument Dependent Lookup) when hashing
+// your type. No forward declaration is needed - ADL finds it in your type's
+// namespace.

-char* nextchar = nullptr;
+// ============================================================================
+// Built-in PerfettoHashValue implementations for common standard library
+// types. These allow standard library types to work seamlessly with the
+// absl-style hash API without requiring users to define their own
+// implementations.
+// ============================================================================

-const option* LookupLongOpt(const std::vector<option>& opts,
-                            const char* name,
-                            size_t len) {
-  for (const option& opt : opts) {
-    if (strncmp(opt.name, name, len) == 0 && strlen(opt.name) == len)
-      return &opt;
+// Hash function for std::optional - hashes the value if present, or a sentinel
+// if not.
+template <typename H, typename T>
+H PerfettoHashValue(H h, const std::optional<T>& value) {
+  if (value.has_value()) {
+    return H::Combine(H::Combine(std::move(h), true), *value);
   }
-  return nullptr;
+  return H::Combine(std::move(h), false, 0);
 }

-const option* LookupShortOpt(const std::vector<option>& opts, char c) {
-  for (const option& opt : opts) {
-    if (!*opt.name && opt.val == c)
-      return &opt;
-  }
-  return nullptr;
+// Hash function for std::pair - combines hashes of both elements.
+template <typename H, typename T1, typename T2>
+H PerfettoHashValue(H h, const std::pair<T1, T2>& value) {
+  return H::Combine(std::move(h), value.first, value.second);
 }

-// Returns true if |token| is an option-bearing argv element whose argument
-// is supplied by the next argv element (rather than embedded or absent).
-// Used by the GNU-style permutation logic in getopt_long().
-bool TokenConsumesNextArg(const char* token, const std::vector<option>& opts) {
-  if (token[0] != '-' || token[1] == '\0')
-    return false;
-  if (token[1] == '-') {
-    // Long option: "--name" needs a separate arg if it's required_argument
-    // and there is no embedded "=value".
-    if (token[2] == '\0')
-      return false;  // "--" alone.
-    if (strchr(token + 2, '=') != nullptr)
-      return false;
-    size_t len = strlen(token + 2);
-    const option* opt = LookupLongOpt(opts, token + 2, len);
-    return opt && opt->has_arg == required_argument;
-  }
-  // Short option chain: "-abc" / "-aXYZ". Walk until we find an option that
-  // requires an argument; if it is the last character of the token, the next
-  // argv element is the argument. Anything after it would be the embedded arg.
-  for (size_t i = 1; token[i] != '\0'; ++i) {
-    const option* opt = LookupShortOpt(opts, token[i]);
-    if (!opt)
-      return false;
-    if (opt->has_arg == required_argument)
-      return token[i + 1] == '\0';
-  }
-  return false;
+// Hash function for std::tuple - combines hashes of all elements.
+template <typename H, typename... Ts>
+H PerfettoHashValue(H h, const std::tuple<Ts...>& value) {
+  return std::apply(
+      [&h](const auto&... elements) {
+        return H::Combine(std::move(h), elements...);
+      },
+      value);
 }

-bool ParseOpts(const char* shortopts,
-               const option* longopts,
-               std::vector<option>* res) {
-  // Parse long options first.
-  for (const option* lopt = longopts; lopt && lopt->name; lopt++) {
-    PERFETTO_CHECK(lopt->flag == nullptr);
-    PERFETTO_CHECK(lopt->has_arg == no_argument ||
-                   lopt->has_arg == required_argument);
-    res->emplace_back(*lopt);
-  }
-
-  // Merge short options.
-  for (const char* sopt = shortopts; sopt && *sopt;) {
-    const size_t idx = static_cast<size_t>(sopt - shortopts);
-    char c = *sopt++;
-    bool valid = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
-                 (c >= '0' && c <= '9');
-    if (!valid) {
-      fprintf(stderr,
-              "Error parsing shortopts. Unexpected char '%c' at offset %zu\n",
-              c, idx);
-      return false;
-    }
-    res->emplace_back();
-    option& opt = res->back();
-    opt.name = "";
-    opt.val = c;
-    opt.has_arg = no_argument;
-    if (*sopt == ':') {
-      opt.has_arg = required_argument;
-      ++sopt;
-    }
-  }
-  return true;
+// Hash function for pointers - hashes the pointer value as an integer.
+template <typename H, typename T>
+H PerfettoHashValue(H h, const std::unique_ptr<T>& ptr) {
+  return H::Combine(std::move(h), ptr.get());
 }
-
-}  // namespace
-
-int getopt_long(int argc,
-                char** argv,
-                const char* shortopts,
-                const option* longopts,
-                std::nullptr_t /*longind*/) {
-  std::vector<option> opts;
-  optarg = nullptr;
-
-  if (optind == 0)
-    optind = 1;
-
-  if (optind >= argc)
-    return -1;
-
-  if (!ParseOpts(shortopts, longopts, &opts))
-    return '?';
-
-  // GNU-style permutation: if we're at a non-option, move the next option
-  // (and its separate argument, if any) into position |optind|, shifting any
-  // intervening non-options to the right. This matches the default behavior
-  // of GNU getopt(3) so positional args can be freely interleaved with flags.
-  // We only permute when |nextchar| is null (i.e. we're not in the middle of
-  // a "-abc" short-option chain).
-  if (!nextchar) {
-    int scan = optind;
-    while (scan < argc) {
-      const char* s = argv[scan];
-      // Stop at the next option-bearing token ("-x", "--long", or "--").
-      if (s[0] == '-' && s[1] != '\0')
-        break;
-      ++scan;
-    }
-    // No permutation if there's nothing before the next option, or if the
-    // next token is "--" (the explicit end-of-options marker).
-    if (scan < argc && scan > optind && strcmp(argv[scan], "--") != 0) {
-      bool takes_next = TokenConsumesNextArg(argv[scan], opts);
-      int count = (takes_next && scan + 1 < argc) ? 2 : 1;
-      char* opt_token = argv[scan];
-      char* opt_arg = count == 2 ? argv[scan + 1] : nullptr;
-      // Shift argv[optind..scan) right by |count| to make room.
-      for (int k = scan - 1; k >= optind; --k)
-        argv[k + count] = argv[k];
-      argv[optind] = opt_token;
-      if (count == 2)
-        argv[optind + 1] = opt_arg;
-    }
-  }
-
-  char* arg = argv[optind];
-  optopt = 0;
-
-  if (!nextchar) {
-    // If |nextchar| is null we are NOT in the middle of a short option and we
-    // should parse the next argv.
-    if (strncmp(arg, "--", 2) == 0 && strlen(arg) > 2) {
-      // A --long option.
-      arg += 2;
-      char* sep = strchr(arg, '=');
-      optind++;
-
-      size_t len = sep ? static_cast<size_t>(sep - arg) : strlen(arg);
-      const option* opt = LookupLongOpt(opts, arg, len);
-
-      if (!opt) {
-        if (opterr)
-          fprintf(stderr, "unrecognized option '--%s'\n", arg);
-        return '?';
-      }
-
-      optopt = opt->val;
-      if (opt->has_arg == no_argument) {
-        if (sep) {
-          fprintf(stderr, "option '--%s' doesn't allow an argument\n", arg);
-          return '?';
-        } else {
-          return opt->val;
-        }
-      } else if (opt->has_arg == required_argument) {
-        if (sep) {
-          optarg = sep + 1;
-          return opt->val;
-        } else if (optind >= argc) {
-          if (opterr)
-            fprintf(stderr, "option '--%s' requires an argument\n", arg);
-          return '?';
-        } else {
-          optarg = argv[optind++];
-          return opt->val;
-        }
-      }
-      // has_arg must be either |no_argument| or |required_argument|. We
-      // shoulnd't get here unless the check in ParseOpts() has a bug.
-      PERFETTO_CHECK(false);
-    }  // if (arg ~= "--*").
-
-    if (strlen(arg) > 1 && arg[0] == '-' && arg[1] != '-') {
-      // A sequence of short options. Parsing logic continues below.
-      nextchar = &arg[1];
-    }
-  }  // if(!nextchar)
-
-  if (nextchar) {
-    // At this point either:
-    // 1. This is the first char of a sequence of short options, and we fell
-    //    through here from the lines above.
-    // 2. This is the N (>1) char of a sequence of short options, and we got
-    //    here from a new getopt() call to getopt().
-    const char cur_char = *nextchar;
-    PERFETTO_CHECK(cur_char != '\0');
-
-    // Advance the option char in any case, before we start reasoning on them.
-    // if we got to the end of the "-abc" sequence, increment optind so the next
-    // getopt() call resumes from the next argv argument.
-    if (*(++nextchar) == '\0') {
-      nextchar = nullptr;
-      ++optind;
-    }
-
-    const option* opt = LookupShortOpt(opts, cur_char);
-    optopt = cur_char;
-    if (!opt) {
-      if (opterr)
-        fprintf(stderr, "invalid option -- '%c'\n", cur_char);
-      return '?';
-    }
-    if (opt->has_arg == no_argument) {
-      return cur_char;
-    } else if (opt->has_arg == required_argument) {
-      // This is a subtle getopt behavior. Say you call `tar -fx`, there are
-      // two cases:
-      // 1. If 'f' is no_argument then 'x' (and anything else after) is
-      //    interpreted as an independent argument (like `tar -f -x`).
-      // 2. If 'f' is required_argument, than everything else after the 'f'
-      //    is interpreted as the option argument (like `tar -f x`)
-      if (!nextchar) {
-        // Case 1.
-        if (optind >= argc) {
-          if (opterr)
-            fprintf(stderr, "option requires an argument -- '%c'\n", cur_char);
-          return '?';
-        } else {
-          optarg = argv[optind++];
-          return cur_char;
-        }
-      } else {
-        // Case 2.
-        optarg = nextchar;
-        nextchar = nullptr;
-        optind++;
-        return cur_char;
-      }
-    }
-    PERFETTO_CHECK(false);
-  }  // if (nextchar)
-
-  // If we get here, we found the first non-option argument. Stop here.
-
-  if (strcmp(arg, "--") == 0)
-    optind++;
-
-  return -1;
+template <typename H, typename T>
+H PerfettoHashValue(H h, const std::shared_ptr<T>& ptr) {
+  return H::Combine(std::move(h), ptr.get());
 }

-int getopt(int argc, char** argv, const char* shortopts) {
-  return getopt_long(argc, argv, shortopts, nullptr, nullptr);
-}
+// This is for using already-hashed key into std::unordered_map and avoid the
+// cost of re-hashing. Example:
+// unordered_map<uint64_t, Value, AlreadyHashed> my_map.
+template <typename T>
+struct AlreadyHashed {
+  size_t operator()(const T& x) const { return static_cast<size_t>(x); }
+};

-}  // namespace getopt_compat
-}  // namespace base
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/intrusive_list.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/intrusive_list.h
+}  // namespace perfetto::base
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_HASH_H_
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -4955,209 +5198,340 @@ int getopt(int argc, char** argv, const char* shortopts) {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_INTRUSIVE_LIST_H_
-#define INCLUDE_PERFETTO_EXT_BASE_INTRUSIVE_LIST_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_MURMUR_HASH_H_
+#define INCLUDE_PERFETTO_EXT_BASE_MURMUR_HASH_H_

 #include <cstddef>
 #include <cstdint>
+#include <cstring>
+#include <limits>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <utility>

-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/hash.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+// gen_amalgamated expanded: #include "perfetto/public/compiler.h"

-// An intrusive (doubly linked) list implementation.
-// Unlike std::list<>, the entries being inserted into the list need to
-// explicitly declare an IntrusiveListNode structure (one for each list they are
-// part of). The user must specify a Traits struct for each list the entry is
-// part of. The traits struct defines how to get to the IntrusiveListNode from
-// the outer object.
-//
-// Usage example:
-// class Person {
-//  public:
-//   struct ListTraits {
-//     static constexpr size_t node_offset() { return offsetof(Person, node); }
-//   };
-//   std::string name;
-//   IntrusiveListNode node{};
-// }
+// This file provides an implementation of the 64-bit MurmurHash2 algorithm,
+// also known as MurmurHash64A. This algorithm, created by Austin Appleby, is a
+// fast, non-cryptographic hash function with excellent distribution properties,
+// making it ideal for use in hash tables.
 //
-// IntrusiveList<Person, Person::ListTraits> list;
-// Person person;
-// list.PushBack(person);
-// ...
+// The file also includes related hashing utilities:
+// - A standalone `fmix64` finalizer from MurmurHash3, used for hashing
+//   individual numeric types.
+// - A hash combiner for creating a single hash from a sequence of values.
+//
+// NOTE: This implementation is NOT cryptographically secure. It must not be
+// used for security-sensitive applications like password storage or digital
+// signatures, as it is not designed to be resistant to malicious attacks.

 namespace perfetto::base {

-namespace internal {
-
-struct ListNode {
-  ListNode* prev;
-  ListNode* next;
-};
-
-// IntrusiveList's Base class to factor out type-independent code (avoid binary
-// bloat)
-class ListOps {
- public:
-  void PushFront(ListNode* node);
-  void PopFront();
-  void Erase(ListNode* node);
-
-  ListNode* front_{nullptr};
-  size_t size_{0};
-};
-
-}  // namespace internal
-
-using IntrusiveListNode = internal::ListNode;
-
-// T is the class that has one or more IntrusiveListNode as fields.
-// Traits defines getter and offset between node and T.
-// Traits is separate to allow the same T to be part of different lists (which
-// necessitate a different Traits, at very least for the offset).
-template <typename T, typename ListTraits>
-class IntrusiveList : private internal::ListOps {
- public:
-  class Iterator {
-   public:
-    Iterator() = default;
-    explicit Iterator(IntrusiveListNode* node) : node_(node) {}
-    ~Iterator() = default;
-    Iterator(const Iterator&) = default;
-    Iterator& operator=(const Iterator&) = default;
-    Iterator(Iterator&&) noexcept = default;
-    Iterator& operator=(Iterator&&) noexcept = default;
+namespace murmur_internal {

-    bool operator==(const Iterator& other) const {
-      return node_ == other.node_;
-    }
-    bool operator!=(const Iterator& other) const { return !(*this == other); }
-    T* operator->() { return const_cast<T*>(entryof(node_)); }
-    T& operator*() {
-      PERFETTO_DCHECK(node_);
-      return *operator->();
-    }
-    explicit operator bool() const { return node_ != nullptr; }
+// Finalizes an intermediate hash value using the `fmix64` routine from
+// MurmurHash3.
+//
+// This function's purpose is to thoroughly mix the bits of the hash state to
+// ensure the final result is well-distributed, which is critical for avoiding
+// collisions in hash tables.
+//
+// Args:
+//   h: The intermediate hash value to be finalized.
+//
+// Returns:
+//   The final, well-mixed 64-bit hash value.
+inline uint64_t MurmurHashMix(uint64_t h) {
+  h ^= h >> 33;
+  h *= 0xff51afd7ed558ccdULL;
+  h ^= h >> 33;
+  h *= 0xc4ceb9fe1a85ec53ULL;
+  h ^= h >> 33;
+  return h;
+}

-    Iterator& operator++() {
-      PERFETTO_DCHECK(node_);
-      node_ = node_->next;
-      return *this;
-    }
+// Computes a 64-bit hash for a block of memory using the MurmurHash64A
+// algorithm.
+//
+// The process involves four main steps:
+// 1. Initialization: The hash state is seeded with a value derived from the
+//    input length.
+// 2. Main Loop: Data is processed in 8-byte chunks, with each chunk being
+//    mixed into the hash state.
+// 3. Tail Processing: The final 1-7 bytes of data are handled.
+// 4. Finalization: The hash state is passed through a final mixing sequence to
+//    ensure good bit distribution.
+//
+// Args:
+//   input: A pointer to the data to be hashed.
+//   len:   The length of the data in bytes.
+//
+// Returns:
+//   The 64-bit MurmurHash64A hash of the input data.
+inline uint64_t MurmurHashBytes(const void* input, size_t len) {
+  // This implementation follows the canonical MurmurHash64A algorithm.
+  // The constants `kMulConstant` (m) and the shift value `47` (r) are from
+  // the original specification.
+  // The seed is inspired by the one used in DuckDB.
+  static constexpr uint64_t kSeed = 0xe17a1465U;
+  static constexpr uint64_t kMulConstant = 0xc6a4a7935bd1e995;
+  static constexpr int kShift = 47;

-   private:
-    IntrusiveListNode* node_{nullptr};
-  };
+  uint64_t h = kSeed ^ (len * kMulConstant);
+  const auto* data = static_cast<const uint8_t*>(input);
+  const size_t num_blocks = len / 8;

-  using value_type = T;
-  using const_pointer = const T*;
+  // Process 8-byte (64-bit) chunks
+  for (size_t i = 0; i < num_blocks; ++i) {
+    uint64_t k;
+    memcpy(&k, data, sizeof(k));
+    data += sizeof(k);  // Advance the pointer by 8 bytes

-  void PushFront(T& entry) { internal::ListOps::PushFront(nodeof(&entry)); }
+    k *= kMulConstant;
+    k ^= k >> kShift;
+    k *= kMulConstant;

-  void PopFront() { internal::ListOps::PopFront(); }
+    h ^= k;
+    h *= kMulConstant;
+  }

-  T& front() {
-    PERFETTO_DCHECK(front_);
-    return const_cast<T&>(*entryof(front_));
+  // Process the remaining 1 to 7 bytes
+  // The 'byte_ptr' now points to the beginning of the tail.
+  switch (len & 7) {
+    case 7:
+      h ^= static_cast<uint64_t>(data[6]) << 48;
+      [[fallthrough]];
+    case 6:
+      h ^= static_cast<uint64_t>(data[5]) << 40;
+      [[fallthrough]];
+    case 5:
+      h ^= static_cast<uint64_t>(data[4]) << 32;
+      [[fallthrough]];
+    case 4:
+      h ^= static_cast<uint64_t>(data[3]) << 24;
+      [[fallthrough]];
+    case 3:
+      h ^= static_cast<uint64_t>(data[2]) << 16;
+      [[fallthrough]];
+    case 2:
+      h ^= static_cast<uint64_t>(data[1]) << 8;
+      [[fallthrough]];
+    case 1:
+      h ^= static_cast<uint64_t>(data[0]);
+      h *= kMulConstant;
   }

-  void Erase(T& entry) { internal::ListOps::Erase(nodeof(&entry)); }
+  // Final mixing stage
+  h ^= h >> kShift;
+  h *= kMulConstant;
+  h ^= h >> kShift;

-  bool empty() const { return size_ == 0; }
+  return h;
+}

-  size_t size() const { return size_; }
+// Computes a 64-bit hash for a single built-in value without any combination.
+// This is the core primitive used by both MurmurHashValue and
+// MurmurHashCombiner::CombineOne for built-in types.
+//
+// NOTE: This function intentionally has no else branch for non-builtin types,
+// which will cause a compile error if called with an unsupported type. Callers
+// should check if the type is supported before calling this function.
+template <typename T>
+auto MurmurHashBuiltinValue(const T& value) {
+  if constexpr (std::is_enum_v<T>) {
+    return murmur_internal::MurmurHashMix(
+        static_cast<uint64_t>(static_cast<std::underlying_type_t<T>>(value)));
+  } else if constexpr (std::is_integral_v<T>) {
+    return murmur_internal::MurmurHashMix(static_cast<uint64_t>(value));
+  } else if constexpr (std::is_same_v<T, double>) {
+    return murmur_internal::MurmurHashMix(base::CanonicalFloatBits(value));
+  } else if constexpr (std::is_same_v<T, float>) {
+    return murmur_internal::MurmurHashMix(base::CanonicalFloatBits(value));
+  } else if constexpr (std::is_same_v<T, std::string> ||
+                       std::is_same_v<T, std::string_view> ||
+                       std::is_same_v<T, base::StringView>) {
+    return murmur_internal::MurmurHashBytes(value.data(), value.size());
+  } else if constexpr (std::is_same_v<T, const char*>) {
+    std::string_view view(value);
+    return murmur_internal::MurmurHashBytes(view.data(), view.size());
+  } else if constexpr (std::is_pointer_v<T>) {
+    return murmur_internal::MurmurHashMix(
+        static_cast<uint64_t>(reinterpret_cast<uintptr_t>(value)));
+  } else {
+    struct InvalidBuiltin {};
+    return InvalidBuiltin{};
+  }
+}

-  Iterator begin() { return Iterator{front_}; }
+// Helper to check if a type has a built-in MurmurHash implementation.
+template <typename T>
+constexpr bool HasMurmurHashBuiltinValue() {
+  return std::is_same_v<decltype(MurmurHashBuiltinValue(std::declval<T>())),
+                        uint64_t>;
+}

-  Iterator end() { return Iterator{nullptr}; }
+// Helper to check if a type is string-like (i.e. string, c-string or string
+// views).
+template <typename T>
+constexpr bool IsStringLike() {
+  return std::is_same_v<T, std::string> ||
+         std::is_same_v<T, std::string_view> ||
+         std::is_same_v<T, base::StringView> || std::is_same_v<T, const char*>;
+}

- private:
-  static constexpr size_t kNodeOffset = ListTraits::node_offset();
+// Helper to check if heterogeneous lookup is allowed between T and U: only
+// between string-like types. Other types (e.g. integers) are converted to T by
+// the caller instead, as converting them is cheap, and hashing them as is
+// could differ (e.g. a negative int hashed for a uint32_t key).
+template <typename T, typename U>
+constexpr bool AllowsHeterogeneousLookup() {
+  return IsStringLike<T>() && IsStringLike<U>();
+}

-  static constexpr IntrusiveListNode* nodeof(T* entry) {
-    return reinterpret_cast<IntrusiveListNode*>(
-        reinterpret_cast<uintptr_t>(entry) + kNodeOffset);
-  }
+// MurmurHash<T> is transparent (i.e. supports heterogeneous lookups) only for
+// string-like T.
+template <typename T, bool = IsStringLike<T>()>
+struct MurmurHashTransparency {};

-  static constexpr const T* entryof(IntrusiveListNode* node) {
-    return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(node) -
-                                kNodeOffset);
-  }
+template <typename T>
+struct MurmurHashTransparency<T, true> {
+  using is_transparent = void;
 };

-}  // namespace perfetto::base
-
-#endif  // INCLUDE_PERFETTO_EXT_BASE_INTRUSIVE_LIST_H_
-/*
- * Copyright (C) 2025 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.
- */
-
-// gen_amalgamated expanded: #include "perfetto/ext/base/intrusive_list.h"
+// Helper to detect pointers in Combine(...).
+// Hashing pointers directly is prohibited as it often indicates a misuse
+// (e.g., passing ptr and len instead of a single std::string_view).
+template <typename... Args>
+constexpr bool HasPointerV = (std::is_pointer_v<Args> || ...);

-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+}  // namespace murmur_internal

-namespace perfetto::base::internal {
+// ============================================================================
+// MurmurHashCombiner - the core hasher state object
+// ============================================================================
+//
+// A helper class to create a 64-bit MurmurHash from a series of
+// structured fields.
+//
+// This class supports both the absl-style hasher API and a direct
+// member Combine() method.
+//
+// Absl-style API (for custom types with PerfettoHashValue):
+//   template <typename H>
+//   friend H PerfettoHashValue(H h, const MyType& value) {
+//     return H::Combine(std::move(h), value.field1, value.field2);
+//   }
+//
+// Direct API (for simple hash combining):
+//   MurmurHashCombiner combiner;
+//   combiner.Combine(field1, field2, ...);
+//   return combiner.digest();
+//
+// IMPORTANT: This is NOT a true streaming hash. It is an order-dependent
+// combiner. It does not guarantee that hashing two concatenated chunks of data
+// will produce the same result as hashing them separately in sequence.
+class MurmurHashCombiner {
+ public:
+  MurmurHashCombiner() = default;

-void ListOps::PushFront(IntrusiveListNode* node) {
-  node->prev = nullptr;
-  node->next = front_;
+  // Static Combine - returns a new hasher with the combined state.
+  // This is used by the absl-style PerfettoHashValue API.
+  template <typename... Args>
+  static MurmurHashCombiner Combine(MurmurHashCombiner h, const Args&... args) {
+    h.Combine(args...);
+    return h;
+  }

-  if (front_) {
-    front_->prev = node;
+  // Member Combine - combines values into this hasher's state.
+  // This is a convenient API for directly combining multiple values.
+  // The combination is order-dependent.
+  template <typename... Args>
+  void Combine(const Args&... args) {
+    static_assert(!murmur_internal::HasPointerV<Args...>,
+                  "MurmurHashCombiner::Combine() does not support pointers. "
+                  "If you want to hash the contents of a memory range, use a "
+                  "single hashable object (e.g., std::string_view). If you "
+                  "want to hash a pointer address, cast it to uintptr_t.");
+    // Uses a C++17 fold expression with CombineOne for each argument.
+    (CombineOne(args), ...);
   }

-  front_ = node;
-  ++size_;
-}
+  // Returns the digest (i.e. current state of the combiner).
+  uint64_t digest() const { return hash_; }

-void ListOps::PopFront() {
-  PERFETTO_DCHECK(front_);
-  front_ = front_->next;
+ private:
+  // Combines a single value into the hasher state.
+  template <typename T>
+  void CombineOne(const T& value) {
+    if constexpr (murmur_internal::HasMurmurHashBuiltinValue<T>()) {
+      Update(murmur_internal::MurmurHashBuiltinValue(value));
+    } else {
+      // For custom types, use ADL to find the PerfettoHashValue function.
+      // This will cause a compile error with a clear message if the function
+      // is not defined.
+      hash_ = PerfettoHashValue(std::move(*this), value).digest();
+    }
+  }

-  if (front_) {
-    front_->prev = nullptr;
+  // Low-level update with a pre-computed hash value. This uses a fast,
+  // order-dependent combination step inspired by the `hash_combine` function
+  // in the Boost C++ libraries.
+  void Update(uint64_t piece_hash) {
+    hash_ ^= piece_hash + 0x9e3779b9 + (hash_ << 6) + (hash_ >> 2);
   }

-  PERFETTO_DCHECK(size_ > 0);
-  --size_;
-}
+  static constexpr uint64_t kSeed = 0xe17a1465U;
+  uint64_t hash_ = kSeed;
+};

-void ListOps::Erase(IntrusiveListNode* node) {
-  auto* prev = node->prev;
-  auto* next = node->next;
+// Simple wrapper function around MurmurHashCombiner to improve clarity in
+// callsites to not have to instantiate the class, call Combine() then digest().
+template <typename... Args>
+uint64_t MurmurHashCombine(const Args&... value) {
+  return MurmurHashCombiner::Combine(MurmurHashCombiner{}, value...).digest();
+}

-  if (node == front_) {
-    front_ = next;
+// Simple wrapper function to compute a hash value for a single value.
+// This is the primitive hash operation that MurmurHash<T> delegates to.
+//
+// For built-in types (integers, floats, strings), this uses a fast path that
+// avoids the overhead of the MurmurHashCombiner. For custom types, it delegates
+// to MurmurHashCombiner which will use ADL to find the PerfettoHashValue.
+template <typename T>
+uint64_t MurmurHashValue(const T& value) {
+  if constexpr (murmur_internal::HasMurmurHashBuiltinValue<T>()) {
+    return murmur_internal::MurmurHashBuiltinValue(value);
+  } else {
+    return MurmurHashCombine(value);
   }
+}

-  if (prev) {
-    prev->next = next;
-  }
+// std::hash<T> drop-in class which uses MurmurHashValue as the primitive.
+// All specializations consistently delegate to MurmurHashValue.
+template <typename T>
+struct MurmurHash : murmur_internal::MurmurHashTransparency<T> {
+  uint64_t operator()(const T& value) const { return MurmurHashValue(value); }

-  if (next) {
-    next->prev = prev;
+  // Heterogeneous lookup support, between string-like types only.
+  template <typename U>
+  auto operator()(const U& value) const -> std::enable_if_t<
+      murmur_internal::AllowsHeterogeneousLookup<T, std::decay_t<const U>>(),
+      uint64_t> {
+    // Forwarding callers retain string literals as arrays. Hash them as
+    // C strings instead of converting them to an owning std::string.
+    return MurmurHashValue<std::decay_t<const U>>(value);
   }
+};

-  PERFETTO_DCHECK(size_ > 0);
-  --size_;
-}
+}  // namespace perfetto::base

-}  // namespace perfetto::base::internal
-// gen_amalgamated begin source: src/base/intrusive_tree.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/intrusive_tree.h
+#endif  // INCLUDE_PERFETTO_EXT_BASE_MURMUR_HASH_H_
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -5172,239 +5546,456 @@ void ListOps::Erase(IntrusiveListNode* node) {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_INTRUSIVE_TREE_H_
-#define INCLUDE_PERFETTO_EXT_BASE_INTRUSIVE_TREE_H_
-
-#include <cstddef>
-#include <cstdint>
-#include <functional>
+#ifndef INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_V1_H_
+#define INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_V1_H_

 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/murmur_hash.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+// gen_amalgamated expanded: #include "perfetto/public/compiler.h"

-// An intrusive tree implementation, inspired from BSD kernel's tree.h
-// Unlike std::set<>, the nodes being inserted into the tree need to explicitly
-// declare a RBNode structure (one for each tree they are part of).
-// The user must specify a TreeTraits for each tree the struct is part of.
-// The traits struct defines the type of the key and how to get to the node
-// entry from the outer object.
-// Usage example:
-// class Person {
-//  public:
-//   struct Traits {
-//     using KeyType = std::string;
-//     static const KeyType& GetKey(const Person& p) { return p->unique_id; }
-//     static constexpr size_t NodeOffset() { return offsetof(Person, node); }
-//   };
-//   std::string unique_id;
-//   std::string name;
-//   std::string surname;
-//   IntrusiveTreeNode node{};
-// }
-//  IntrusiveTree<Person, Person::Traits> tree;
-//  tree.insert(&person1);
-//  ...
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <limits>
+#include <memory>
+#include <type_traits>
+#include <utility>

 namespace perfetto::base {

-namespace internal {
+// An open-addressing hashmap implementation.
+// Pointers are not stable, neither for keys nor values.
+// Has similar performances of a RobinHood hash (without the complications)
+// and 2x an unordered map.
+// Doc: http://go/perfetto-hashtables .
+//
+// When used to implement a string pool in TraceProcessor, the performance
+// characteristics obtained by replaying the set of strings seeen in a 4GB trace
+// (226M strings, 1M unique) are the following (see flat_hash_map_benchmark.cc):
+// This(Linear+AppendOnly)    879,383,676 ns    258.013M insertions/s
+// This(LinearProbe):         909,206,047 ns    249.546M insertions/s
+// This(QuadraticProbe):    1,083,844,388 ns    209.363M insertions/s
+// std::unordered_map:      6,203,351,870 ns    36.5811M insertions/s
+// tsl::robin_map:            931,403,397 ns    243.622M insertions/s
+// absl::flat_hash_map:       998,013,459 ns    227.379M insertions/s
+// FollyF14FastMap:         1,181,480,602 ns    192.074M insertions/s
+//
+// The structs below define the probing algorithm used to probe slots upon a
+// collision. They are guaranteed to visit all slots as our table size is always
+// a power of two (see https://en.wikipedia.org/wiki/Quadratic_probing).

-enum RBColor : uint8_t {
-  BLACK = 0,
-  RED = 1,
+// Linear probing can be faster if the hashing is well distributed and the load
+// is not high. For TraceProcessor's StringPool this is the fastest. It can
+// degenerate badly if the hashing doesn't spread (e.g., if using directly pids
+// as keys, with a no-op hashing function).
+struct LinearProbe {
+  static inline size_t Calc(size_t key_hash, size_t step, size_t capacity) {
+    return (key_hash + step) & (capacity - 1);  // Linear probe
+  }
 };

-struct RBNode {
-  RBNode* left = nullptr;
-  RBNode* right = nullptr;
-  RBNode* parent = nullptr;
-  RBColor color = RBColor::BLACK;
+// Generates the sequence: 0, 3, 10, 21, 36, 55, ...
+// Can be a bit (~5%) slower than LinearProbe because it's less cache hot, but
+// avoids degenerating badly if the hash function is bad and causes clusters.
+// A good default choice unless benchmarks prove otherwise.
+struct QuadraticProbe {
+  static inline size_t Calc(size_t key_hash, size_t step, size_t capacity) {
+    return (key_hash + 2 * step * step + step) & (capacity - 1);
+  }
 };

-void RBInsertColor(RBNode** root, RBNode* elm);
-void RBRemove(RBNode** root, RBNode* elm);
+// Tends to perform in the middle between linear and quadratic.
+// It's a bit more cache-effective than the QuadraticProbe but can create more
+// clustering if the hash function doesn't spread well.
+// Generates the sequence: 0, 1, 3, 6, 10, 15, 21, ...
+struct QuadraticHalfProbe {
+  static inline size_t Calc(size_t key_hash, size_t step, size_t capacity) {
+    return (key_hash + (step * step + step) / 2) & (capacity - 1);
+  }
+};

-// Returns nullptr after reaching the last leaf (the max element).
-const RBNode* RBNext(const RBNode* node);
+// Non-templated base class to hold helpers for FlatHashMap.
+struct FlatHashMapV1Base {
+ public:
+  // Helper to detect if a hasher has is_transparent defined.
+  template <typename, typename = void>
+  struct HasIsTransparent : std::false_type {};

-// KeyCompare tries first to use the CompareKey function in Traits, if present.
-// The signature of that function is int(const KeyType&, const KeyType&).
-// If the comparator function doesn't exist, falls backk on std::less<KeyType>.
-template <
-    class Traits,
-    class = std::enable_if_t<std::is_function_v<typename Traits::CompareKey>,
-                             void> >
-int KeyCompare(const typename Traits::KeyType& k1,
-               const typename Traits::KeyType& k2) {
-  return Traits::CompareKey(k1, k2);
-}
-
-// SFINAE fallback on std::less<KeyType>
-template <class Traits>
-int KeyCompare(const typename Traits::KeyType& k1,
-               const typename Traits::KeyType& k2) {
-  std::less<typename Traits::KeyType> less_cmp;
-  return less_cmp(k1, k2) ? -1 : (less_cmp(k2, k1) ? 1 : 0);
-}
-
-}  // namespace internal
+  template <typename H>
+  struct HasIsTransparent<H, std::void_t<typename H::is_transparent>>
+      : std::true_type {};

-using IntrusiveTreeNode = internal::RBNode;
+  // Helper to check if a lookup key type K is allowed.
+  // Returns true if:
+  // 1. K can be implicitly converted to Key, OR
+  // 2. Hasher has is_transparent AND Hasher is invocable with K AND Key and K
+  // are equality comparable
+  template <typename K, typename Key, typename Hasher>
+  static constexpr bool IsLookupKeyAllowed() {
+    if constexpr (HasIsTransparent<Hasher>::value) {
+      return std::is_invocable_v<Hasher, const K&> &&
+             std::is_same_v<decltype(std::declval<const Key&>() ==
+                                     std::declval<const K&>()),
+                            bool>;
+    } else if constexpr (std::is_convertible_v<K, Key>) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+};

-// T is the class that has one or more IntrusiveTreeNode as fiels.
-// Traits defines the key type, getter and offset between node and T.
-// Traits is separate to allow the same T to be part of different trees (which
-// necessitate a different Traits, at very least for the offset).
-template <typename T, typename Traits>
-class IntrusiveTree {
+template <typename Key,
+          typename Value,
+          typename Hasher = base::MurmurHash<Key>,
+          typename Probe = QuadraticProbe,
+          bool AppendOnly = false>
+class FlatHashMapV1 : protected FlatHashMapV1Base {
  public:
-  using Key = typename Traits::KeyType;
-
   class Iterator {
    public:
-    Iterator() = default;
-    explicit Iterator(const internal::RBNode* node) : node_(node) {}
+    explicit Iterator(const FlatHashMapV1* map) : map_(map) {
+      FindNextNonFree();
+    }
     ~Iterator() = default;
     Iterator(const Iterator&) = default;
     Iterator& operator=(const Iterator&) = default;
     Iterator(Iterator&&) noexcept = default;
     Iterator& operator=(Iterator&&) noexcept = default;

-    bool operator==(const Iterator& o) const { return node_ == o.node_; }
-    bool operator!=(const Iterator& o) const { return !(*this == o); }
-    const T* operator->() const { return entryof(node_); }
-    const T& operator*() const {
-      PERFETTO_DCHECK(node_ != nullptr);
-      return *operator->();
-    }
-    T* operator->() { return const_cast<T*>(entryof(node_)); }
-    T& operator*() {
-      PERFETTO_DCHECK(node_ != nullptr);
-      return *operator->();
-    }
-    explicit operator bool() const { return node_ != nullptr; }
+    Key& key() { return map_->keys_[idx_]; }
+    Value& value() { return map_->values_[idx_]; }
+    const Key& key() const { return map_->keys_[idx_]; }
+    const Value& value() const { return map_->values_[idx_]; }

+    explicit operator bool() const { return idx_ != kEnd; }
     Iterator& operator++() {
-      node_ = internal::RBNext(node_);
+      PERFETTO_DCHECK(idx_ < map_->capacity_);
+      ++idx_;
+      FindNextNonFree();
       return *this;
     }

    private:
-    const internal::RBNode* node_ = nullptr;
+    static constexpr size_t kEnd = std::numeric_limits<size_t>::max();
+
+    void FindNextNonFree() {
+      const auto& tags = map_->tags_;
+      for (; idx_ < map_->capacity_; idx_++) {
+        if (tags[idx_] != kFreeSlot && (AppendOnly || tags[idx_] != kTombstone))
+          return;
+      }
+      idx_ = kEnd;
+    }
+
+    const FlatHashMapV1* map_ = nullptr;
+    size_t idx_ = 0;
   };  // Iterator
+  static constexpr int kDefaultLoadLimitPct = 75;
+  explicit FlatHashMapV1(size_t initial_capacity = 0,
+                         int load_limit_pct = kDefaultLoadLimitPct)
+      : load_limit_percent_(load_limit_pct) {
+    if (initial_capacity > 0)
+      Reset(initial_capacity, true);
+  }

-  using value_type = T;
-  using const_pointer = const T*;
-  using const_iterator = Iterator;
+  // We are calling Clear() so that the destructors for the inserted entries are
+  // called (unless they are trivial, in which case it will be a no-op).
+  ~FlatHashMapV1() { Clear(); }

-  std::pair<Iterator, bool> Insert(T& entry) {
-    // The insertion preamble is inlined because it's few instructions and
-    // out-lining it would require std::function indirections for getting the
-    // key and the comparator.
-    int comp = 0;
-    internal::RBNode* tmp = root_;
-    internal::RBNode* parent = nullptr;
-    internal::RBNode* const entry_node = nodeof(&entry);
-    while (tmp) {
-      parent = tmp;
-      comp = key_compare(entry_node, parent);
-      if (comp < 0) {
-        tmp = tmp->left;
-      } else if (comp > 0) {
-        tmp = tmp->right;
-      } else {
-        return {Iterator(tmp), false};  // The key exists already.
+  FlatHashMapV1(FlatHashMapV1&& other) noexcept {
+    tags_ = std::move(other.tags_);
+    keys_ = std::move(other.keys_);
+    values_ = std::move(other.values_);
+    capacity_ = other.capacity_;
+    size_ = other.size_;
+    tombstones_ = other.tombstones_;
+    max_probe_length_ = other.max_probe_length_;
+    load_limit_ = other.load_limit_;
+    load_limit_percent_ = other.load_limit_percent_;
+
+    new (&other) FlatHashMapV1();
+  }
+
+  FlatHashMapV1& operator=(FlatHashMapV1&& other) noexcept {
+    this->~FlatHashMapV1();
+    new (this) FlatHashMapV1(std::move(other));
+    return *this;
+  }
+
+  FlatHashMapV1(const FlatHashMapV1&) = delete;
+  FlatHashMapV1& operator=(const FlatHashMapV1&) = delete;
+
+  std::pair<Value*, bool> Insert(Key key, Value value) {
+    const size_t key_hash = Hasher{}(key);
+    const uint8_t tag = HashToTag(key_hash);
+    static constexpr size_t kSlotNotFound = std::numeric_limits<size_t>::max();
+
+    // This for loop does in reality at most two attempts:
+    // The first iteration either:
+    //  - Early-returns, because the key exists already,
+    //  - Finds an insertion slot and proceeds because the load is < limit.
+    // The second iteration is only hit in the unlikely case of this insertion
+    // bringing the table beyond the target |load_limit_| (or the edge case
+    // of the HT being full, if |load_limit_pct_| = 100).
+    // We cannot simply pre-grow the table before insertion, because we must
+    // guarantee that calling Insert() with a key that already exists doesn't
+    // invalidate iterators.
+    size_t insertion_slot;
+    size_t probe_len;
+    for (;;) {
+      PERFETTO_DCHECK((capacity_ & (capacity_ - 1)) == 0);  // Must be a pow2.
+      insertion_slot = kSlotNotFound;
+      // Start the iteration at the desired slot (key_hash % capacity_)
+      // searching either for a free slot or a tombstone. In the worst case we
+      // might end up scanning the whole array of slots. The Probe functions are
+      // guaranteed to visit all the slots within |capacity_| steps. If we find
+      // a free slot, we can stop the search immediately (a free slot acts as an
+      // "end of chain for entries having the same hash". If we find a
+      // tombstones (a deleted slot) we remember its position, but have to keep
+      // searching until a free slot to make sure we don't insert a duplicate
+      // key.
+      for (probe_len = 0; probe_len < capacity_;) {
+        const size_t idx = Probe::Calc(key_hash, probe_len, capacity_);
+        PERFETTO_DCHECK(idx < capacity_);
+        const uint8_t tag_idx = tags_[idx];
+        ++probe_len;
+        if (tag_idx == kFreeSlot) {
+          // Rationale for "insertion_slot == kSlotNotFound": if we encountered
+          // a tombstone while iterating we should reuse that rather than
+          // taking another slot.
+          if (AppendOnly || insertion_slot == kSlotNotFound)
+            insertion_slot = idx;
+          break;
+        }
+        // We should never encounter tombstones in AppendOnly mode.
+        PERFETTO_DCHECK(!(tag_idx == kTombstone && AppendOnly));
+        if (!AppendOnly && tag_idx == kTombstone) {
+          insertion_slot = idx;
+          continue;
+        }
+        if (tag_idx == tag && keys_[idx] == key) {
+          // The key is already in the map.
+          return std::make_pair(&values_[idx], false);
+        }
+      }  // for (idx)
+
+      // If we got to this point the key does not exist (otherwise we would have
+      // hit the return above) and we are going to insert a new entry.
+      // Before doing so, ensure we stay under the target load limit.
+      if (PERFETTO_UNLIKELY(size_ >= load_limit_)) {
+        MaybeGrowAndRehash(/*grow=*/true);
+        continue;
       }
-    }  // while(tmp)
-    entry_node->left = entry_node->right = nullptr;
-    entry_node->parent = parent;
-    entry_node->color = internal::RBColor::RED;
-    if (parent) {
-      if (comp < 0) {
-        PERFETTO_DCHECK(parent->left == nullptr);
-        parent->left = entry_node;
-      } else {
-        PERFETTO_DCHECK(parent->right == nullptr);
-        parent->right = entry_node;
+      // If there are too many tombstones, it's worth doing a rehash to
+      // clean them up. This is to avoid the case where we have a table full
+      // of tombstones which would cause lookups to be very slow.
+      bool is_many_tombstones = tombstones_ > size_ && size_ > 128;
+      bool is_tombstones_plus_size_too_high = tombstones_ + size_ > load_limit_;
+      if (PERFETTO_UNLIKELY(is_many_tombstones ||
+                            is_tombstones_plus_size_too_high)) {
+        MaybeGrowAndRehash(/*grow=*/false);
+        continue;
       }
-    } else {
-      root_ = entry_node;
+      PERFETTO_DCHECK(insertion_slot != kSlotNotFound);
+      break;
+    }  // for (attempt)
+
+    PERFETTO_CHECK(insertion_slot < capacity_);
+
+    // We found a free slot (or a tombstone). Proceed with the insertion.
+    if (tags_[insertion_slot] == kTombstone) {
+      PERFETTO_DCHECK(tombstones_ > 0);
+      tombstones_--;
     }
-    internal::RBInsertColor(&root_, entry_node);
-    ++size_;
-    return {Iterator(entry_node), true};
+    Value* value_idx = &values_[insertion_slot];
+    new (&keys_[insertion_slot]) Key(std::move(key));
+    new (value_idx) Value(std::move(value));
+    tags_[insertion_slot] = tag;
+    PERFETTO_DCHECK(probe_len > 0 && probe_len <= capacity_);
+    max_probe_length_ = std::max(max_probe_length_, probe_len);
+    size_++;
+
+    return std::make_pair(value_idx, true);
   }

-  Iterator Find(const Key& key) const {
-    internal::RBNode* tmp = root_;
-    while (tmp) {
-      int comp =
-          internal::KeyCompare<Traits>(key, Traits::GetKey(*entryof(tmp)));
-      if (comp < 0) {
-        tmp = tmp->left;
-      } else if (comp > 0) {
-        tmp = tmp->right;
-      } else {
-        return Iterator(tmp);
-      }
-    }
-    return Iterator(nullptr);
+  template <typename K = Key>
+  Value* Find(const K& key) const {
+    const size_t idx = FindInternal(key);
+    if (idx == kNotFound)
+      return nullptr;
+    return &values_[idx];
   }

-  bool Remove(const Key& key) {
-    Iterator it = Find(key);
-    if (!it)
+  template <typename K = Key>
+  bool Erase(const K& key) {
+    if (AppendOnly)
+      PERFETTO_FATAL("Erase() not supported because AppendOnly=true");
+    size_t idx = FindInternal(key);
+    if (idx == kNotFound)
       return false;
-    internal::RBRemove(&root_, nodeof(std::addressof(*it)));
-    --size_;
+    EraseInternal(idx);
     return true;
   }

-  Iterator Remove(T& entry) { return Remove(Iterator(nodeof(&entry))); }
+  void Clear() {
+    // Avoid trivial heap operations on zero-capacity std::move()-d objects.
+    if (PERFETTO_UNLIKELY(capacity_ == 0))
+      return;

-  Iterator Remove(Iterator it) {
-    Iterator next = it;
-    ++next;
-    internal::RBRemove(&root_, nodeof(std::addressof(*it)));
-    --size_;
-    return next;
+    for (size_t i = 0; i < capacity_; ++i) {
+      const uint8_t tag = tags_[i];
+      if (tag != kFreeSlot && (AppendOnly || tag != kTombstone)) {
+        keys_[i].~Key();
+        values_[i].~Value();
+      }
+    }
+    Reset(capacity_, false);
   }

-  size_t Size() const { return size_; }
+  Value& operator[](Key key) {
+    auto it_and_inserted = Insert(std::move(key), Value{});
+    return *it_and_inserted.first;
+  }

-  Iterator begin() const {
-    const internal::RBNode* node = root_;
-    while (node && node->left)
-      node = node->left;
-    return Iterator(node);
+  Iterator GetIterator() { return Iterator(this); }
+  const Iterator GetIterator() const { return Iterator(this); }
+
+  size_t size() const { return size_; }
+  size_t capacity() const { return capacity_; }
+
+  // "protected" here is only for the flat_hash_map_benchmark.cc. Everything
+  // below is by all means private.
+ protected:
+  enum ReservedTags : uint8_t { kFreeSlot = 0, kTombstone = 1 };
+  static constexpr size_t kNotFound = std::numeric_limits<size_t>::max();
+
+  template <typename K = Key>
+  size_t FindInternal(const K& key) const {
+    static_assert(
+        IsLookupKeyAllowed<K, Key, Hasher>(),
+        "Heterogeneous lookup requires Hasher to define is_transparent and "
+        "support hashing the lookup key type. For same-type lookup, Key and K "
+        "must match exactly.");
+    const size_t key_hash = Hasher{}(key);
+    const uint8_t tag = HashToTag(key_hash);
+    PERFETTO_DCHECK((capacity_ & (capacity_ - 1)) == 0);  // Must be a pow2.
+    PERFETTO_DCHECK(max_probe_length_ <= capacity_);
+    for (size_t i = 0; i < max_probe_length_; ++i) {
+      const size_t idx = Probe::Calc(key_hash, i, capacity_);
+      const uint8_t tag_idx = tags_[idx];
+
+      if (tag_idx == kFreeSlot)
+        return kNotFound;
+      // HashToTag() never returns kTombstone, so the tag-check below cannot
+      // possibly match. Also we just want to skip tombstones.
+      if (tag_idx == tag && keys_[idx] == key) {
+        PERFETTO_DCHECK(tag_idx > kTombstone);
+        return idx;
+      }
+    }  // for (idx)
+    return kNotFound;
   }

-  Iterator end() const { return Iterator(nullptr); }
+  void EraseInternal(size_t idx) {
+    PERFETTO_DCHECK(tags_[idx] > kTombstone);
+    PERFETTO_DCHECK(size_ > 0);
+    tags_[idx] = kTombstone;
+    keys_[idx].~Key();
+    values_[idx].~Value();
+    size_--;
+    tombstones_++;
+    PERFETTO_DCHECK(size_ + tombstones_ <= capacity_);
+  }

- private:
-  static constexpr size_t off_ = Traits::NodeOffset();
-  static internal::RBNode* nodeof(T* t) {
-    PERFETTO_DCHECK(t != nullptr);
-    return reinterpret_cast<internal::RBNode*>(reinterpret_cast<uintptr_t>(t) +
-                                               off_);
+  PERFETTO_NO_INLINE void MaybeGrowAndRehash(bool grow) {
+    PERFETTO_DCHECK(size_ <= capacity_);
+    const size_t old_capacity = capacity_;
+
+    // Grow quickly up to 1MB, then chill.
+    const size_t old_size_bytes = old_capacity * (sizeof(Key) + sizeof(Value));
+    const size_t grow_factor = old_size_bytes < (1024u * 1024u) ? 8 : 2;
+    const size_t new_capacity =
+        grow ? std::max(old_capacity * grow_factor, size_t(1024))
+             : old_capacity;
+
+    auto old_tags(std::move(tags_));
+    auto old_keys(std::move(keys_));
+    auto old_values(std::move(values_));
+    size_t old_size = size_;
+
+    // This must be a CHECK (i.e. not just a DCHECK) to prevent UAF attacks on
+    // 32-bit archs that try to double the size of the table until wrapping.
+    PERFETTO_CHECK(new_capacity >= old_capacity);
+    Reset(new_capacity, true);
+
+    size_t new_size = 0;  // Recompute the size.
+    for (size_t i = 0; i < old_capacity; ++i) {
+      const uint8_t old_tag = old_tags[i];
+      if (old_tag != kFreeSlot && old_tag != kTombstone) {
+        Insert(std::move(old_keys[i]), std::move(old_values[i]));
+        old_keys[i].~Key();  // Destroy the old objects.
+        old_values[i].~Value();
+        new_size++;
+      }
+    }
+    PERFETTO_DCHECK(new_size == old_size);
+    PERFETTO_DCHECK(tombstones_ == 0);
+    size_ = new_size;
   }
-  static const T* entryof(const internal::RBNode* n) {
-    PERFETTO_DCHECK(n != nullptr);
-    return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(n) - off_);
+
+  // Doesn't call destructors. Use Clear() for that.
+  PERFETTO_NO_INLINE void Reset(size_t n, bool reallocate) {
+    PERFETTO_DCHECK((n & (n - 1)) == 0);  // Must be a pow2.
+
+    capacity_ = n;
+    max_probe_length_ = 0;
+    size_ = 0;
+    tombstones_ = 0;
+    load_limit_ = n * static_cast<size_t>(load_limit_percent_) / 100;
+    load_limit_ = std::min(load_limit_, n);
+
+    if (reallocate) {
+      tags_.reset(new uint8_t[n]);
+      keys_ = AlignedAllocTyped<Key[]>(n);  // Deliberately not 0-initialized.
+      values_ =
+          AlignedAllocTyped<Value[]>(n);  // Deliberately not 0-initialized.
+    }
+
+    // Only clear the tags if not nullptr.
+    if (tags_) {
+      memset(&tags_[0], 0, n);  // Clear all tags.
+    }
   }
-  static int key_compare(const internal::RBNode* node_a,
-                         const internal::RBNode* node_b) {
-    auto* a = entryof(node_a);
-    auto* b = entryof(node_b);
-    return internal::KeyCompare<Traits>(Traits::GetKey(*a), Traits::GetKey(*b));
+
+  static inline uint8_t HashToTag(size_t full_hash) {
+    uint8_t tag = full_hash >> (sizeof(full_hash) * 8 - 8);
+    // Ensure the hash is always >= 2. We use 0, 1 for kFreeSlot and kTombstone.
+    tag += (tag <= kTombstone) << 1;
+    PERFETTO_DCHECK(tag > kTombstone);
+    return tag;
   }

-  internal::RBNode* root_ = nullptr;
+  size_t capacity_ = 0;
   size_t size_ = 0;
+  size_t tombstones_ = 0;
+  size_t max_probe_length_ = 0;
+  size_t load_limit_ = 0;  // Updated every time |capacity_| changes.
+  int load_limit_percent_ =
+      kDefaultLoadLimitPct;  // Load factor limit in % of |capacity_|.
+
+  // These arrays have always the |capacity_| elements.
+  // Note: AlignedUniquePtr just allocates memory, doesn't invoke any ctor/dtor.
+  std::unique_ptr<uint8_t[]> tags_;
+  AlignedUniquePtr<Key[]> keys_;
+  AlignedUniquePtr<Value[]> values_;
 };

 }  // namespace perfetto::base

-#endif  // INCLUDE_PERFETTO_EXT_BASE_INTRUSIVE_TREE_H_
+#endif  // INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_V1_H_
 /*
  * Copyright (C) 2025 The Android Open Source Project
  *
@@ -5420,1183 +6011,941 @@ class IntrusiveTree {
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-/* Part of this work is inspired by the original OpenBSD's tree.h */
-/* $OpenBSD: tree.h,v 1.31 2023/03/08 04:43:09 guenther Exp $ */
-/*
- * Copyright 2002 Niels Provos <provos@citi.umich.edu>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */

-// gen_amalgamated expanded: #include "perfetto/ext/base/intrusive_tree.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_H_
+#define INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_H_

-namespace perfetto::base::internal {
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/bits.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/flat_hash_map_v1.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/murmur_hash.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+// gen_amalgamated expanded: #include "perfetto/public/compiler.h"

-namespace {
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <functional>
+#include <limits>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <utility>

-void RBSetBlackRed(RBNode* black, RBNode* red) {
-  black->color = RBColor::BLACK;
-  red->color = RBColor::RED;
-}
+#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
+#include <immintrin.h>
+#endif

-void RBRotateLeft(RBNode** root, RBNode* elm, RBNode* tmp) {
-  tmp = elm->right;
-  if ((elm->right = tmp->left)) {
-    tmp->left->parent = elm;
-  }
-  // RB_AUGMENT(elm);
-  if ((tmp->parent = elm->parent)) {
-    if (elm == elm->parent->left)
-      elm->parent->left = tmp;
-    else
-      elm->parent->right = tmp;
-  } else
-    *root = tmp;
-  tmp->left = elm;
-  elm->parent = tmp;
-  // RB_AUGMENT(tmp);
-  // if ((tmp->parent))
-  // RB_AUGMENT(tmp->parent);
-}
+namespace perfetto::base {

-void RBRotateRight(RBNode** root, RBNode* elm, RBNode* tmp) {
-  tmp = elm->left;
-  if ((elm->left = tmp->right)) {
-    tmp->right->parent = elm;
-  }
-  // RB_AUGMENT(elm);
-  if ((tmp->parent = elm->parent)) {
-    if (elm == elm->parent->left)
-      elm->parent->left = tmp;
-    else
-      elm->parent->right = tmp;
-  } else
-    *root = tmp;
-  tmp->right = elm;
-  elm->parent = tmp;
-  // RB_AUGMENT(tmp);
-  // if ((tmp->parent))
-  // RB_AUGMENT(tmp->parent);
-}
+// A Swiss Table-style open-addressing hashmap implementation.
+// Inspired by absl::flat_hash_map, this uses a metadata array of control bytes
+// to enable fast SIMD-accelerated probing.
+//
+// Key design choices:
+// - Control bytes: Each slot has a 1-byte tag (7-bit H2 hash or special marker)
+//   stored in a separate array, enabling fast group-based matching.
+// - SIMD acceleration: On x64, uses SSE instructions to match 16 control bytes
+//   in parallel. Falls back to SWAR (SIMD Within A Register) on other platforms
+//   matching 8 bytes at a time.
+// - Triangular probing: Uses the sequence 0, 16, 48, 96, ... (like Absl) to
+//   probe groups of slots, ensuring good cache behavior.
+// - Pointers are NOT stable: Neither keys nor values have stable addresses
+//   across insertions that trigger rehashing.
+//
+// See also: FlatHashMapV1 in flat_hash_map_v1.h for the older implementation
+// using traditional open-addressing with configurable probing strategies.

-void RBRemoveColor(RBNode** root, RBNode* parent, RBNode* elm) {
-  RBNode* tmp;
-  while ((elm == nullptr || elm->color == RBColor::BLACK) && elm != *root) {
-    if (parent->left == elm) {
-      tmp = parent->right;
-      if (tmp->color == RBColor::RED) {
-        RBSetBlackRed(tmp, parent);
-        RBRotateLeft(root, parent, tmp);
-        tmp = parent->right;
-      }
-      if ((tmp->left == nullptr || tmp->left->color == RBColor::BLACK) &&
-          (tmp->right == nullptr || tmp->right->color == RBColor::BLACK)) {
-        tmp->color = RBColor::RED;
-        elm = parent;
-        parent = elm->parent;
-      } else {
-        if (tmp->right == nullptr || tmp->right->color == RBColor::BLACK) {
-          RBNode* oleft;
-          if ((oleft = tmp->left))
-            oleft->color = RBColor::BLACK;
-          tmp->color = RBColor::RED;
-          RBRotateRight(root, tmp, oleft);
-          tmp = parent->right;
-        }
-        tmp->color = parent->color;
-        parent->color = RBColor::BLACK;
-        if (tmp->right)
-          tmp->right->color = RBColor::BLACK;
-        RBRotateLeft(root, parent, tmp);
-        elm = *root;
-        break;
-      }
-    } else {
-      tmp = parent->left;
-      if (tmp->color == RBColor::RED) {
-        RBSetBlackRed(tmp, parent);
-        RBRotateRight(root, parent, tmp);
-        tmp = parent->left;
-      }
-      if ((tmp->left == nullptr || tmp->left->color == RBColor::BLACK) &&
-          (tmp->right == nullptr || tmp->right->color == RBColor::BLACK)) {
-        tmp->color = RBColor::RED;
-        elm = parent;
-        parent = elm->parent;
-      } else {
-        if (tmp->left == nullptr || tmp->left->color == RBColor::BLACK) {
-          RBNode* oright;
-          if ((oright = tmp->right))
-            oright->color = RBColor::BLACK;
-          tmp->color = RBColor::RED;
-          RBRotateLeft(root, tmp, oright);
-          tmp = parent->left;
-        }
-        tmp->color = parent->color;
-        parent->color = RBColor::BLACK;
-        if (tmp->left)
-          tmp->left->color = RBColor::BLACK;
-        RBRotateRight(root, parent, tmp);
-        elm = *root;
-        break;
-      }
-    }
-  }
-  if (elm)
-    elm->color = RBColor::BLACK;
-}
+// Non-templated base class to hold helpers for FlatHashMapV2.
+namespace flat_hash_map_v2_internal {

-}  // namespace
+// Helper to detect if a hasher has is_transparent defined.
+template <typename, typename = void>
+struct HasIsTransparent : std::false_type {};

-void RBInsertColor(RBNode** root, RBNode* elm) {
-  RBNode *parent, *gparent, *tmp;
-  while ((parent = elm->parent) && parent->color == RBColor::RED) {
-    gparent = parent->parent;
-    if (parent == gparent->left) {
-      tmp = gparent->right;
-      if (tmp && tmp->color == RBColor::RED) {
-        tmp->color = RBColor::BLACK;
-        RBSetBlackRed(parent, gparent);
-        elm = gparent;
-        continue;
-      }
-      if (parent->right == elm) {
-        RBRotateLeft(root, parent, tmp);
-        tmp = parent;
-        parent = elm;
-        elm = tmp;
-      }
-      RBSetBlackRed(parent, gparent);
-      RBRotateRight(root, gparent, tmp);
-    } else {
-      tmp = gparent->left;
-      if (tmp && tmp->color == RBColor::RED) {
-        tmp->color = RBColor::BLACK;
-        RBSetBlackRed(parent, gparent);
-        elm = gparent;
-        continue;
-      }
-      if (parent->left == elm) {
-        RBRotateRight(root, parent, tmp);
-        tmp = parent;
-        parent = elm;
-        elm = tmp;
-      }
-      RBSetBlackRed(parent, gparent);
-      RBRotateLeft(root, gparent, tmp);
-    }
-  }
-  (*root)->color = RBColor::BLACK;
-}
+template <typename H>
+struct HasIsTransparent<H, std::void_t<typename H::is_transparent>>
+    : std::true_type {};

-void RBRemove(RBNode** root, RBNode* elm) {
-  RBNode* child = elm;
-  RBNode* parent = elm;
-  RBNode* old = elm;
-  RBColor color;
+// Equality comparator trait.
+template <typename T>
+struct HashEq : public std::equal_to<T> {};

-  if (elm->left == nullptr)
-    child = elm->right;
-  else if (elm->right == nullptr)
-    child = elm->left;
-  else {
-    RBNode* left;
-    elm = elm->right;
-    while ((left = elm->left))
-      elm = left;
-    child = elm->right;
-    parent = elm->parent;
-    color = elm->color;
-    if (child)
-      child->parent = parent;
-    if (parent) {
-      if (parent->left == elm) {
-        parent->left = child;
-      } else {
-        parent->right = child;
-      }
-      // RB_AUGMENT(parent);
-    } else {
-      *root = child;
-    }
-    if (elm->parent == old)
-      parent = elm;
-    *elm = *old;
-    if (old->parent) {
-      if (old->parent->left == old) {
-        old->parent->left = elm;
-      } else {
-        old->parent->right = elm;
-      }
-      // RB_AUGMENT(old->parent);
-    } else {
-      *root = elm;
-    }
-    old->left->parent = elm;
-    if (old->right)
-      old->right->parent = elm;
-    if (parent) {
-      left = parent;
-      // do {
-      //   RB_AUGMENT(left);
-      // } while ((left = left->parent));
-    }
-    goto color;
+// Specialization for std::string to compare via std::string_view.
+//
+// This exists because after benchmarking, it turns out libc++ has an
+// "optimization" for std::string equality that does something byte by
+// byte comparision for short strings but this is slower than just memcmp.
+//
+// This helps close the gap to absl::flat_hash_map for string keys.
+template <>
+struct HashEq<std::string> {
+  bool operator()(const std::string& a, const std::string& b) const {
+    return std::string_view(a) == std::string_view(b);
   }
-  parent = elm->parent;
-  color = elm->color;
-  if (child)
-    child->parent = parent;
-  if (parent) {
-    if (parent->left == elm)
-      parent->left = child;
-    else
-      parent->right = child;
-    // RB_AUGMENT(parent);
-  } else {
-    *root = child;
+  bool operator()(const std::string& a, const std::string_view& b) const {
+    return std::string_view(a) == b;
   }
-color:
-  if (color == RBColor::BLACK)
-    RBRemoveColor(root, parent, child);
-}
+  bool operator()(const std::string& a, base::StringView b) const {
+    return base::StringView(a) == b;
+  }
+  bool operator()(const std::string& a, const char* b) const {
+    return std::string_view(a) == std::string_view(b);
+  }
+};

-// Returns nullptr after reaching the last leaf (the max element).
-const RBNode* RBNext(const RBNode* node) {
-  if (node->right) {
-    node = node->right;
-    while (node->left)
-      node = node->left;
+// Specialization for double to prevent spurious -Wfloat-equal warnings.
+//
+// Trace processor legitimately has a need to compare with exact equality
+template <>
+struct HashEq<double> {
+  bool operator()(double a, double b) const { return std::equal_to<>()(a, b); }
+};
+
+// Helper to check if a lookup key type K is allowed.
+// Returns true if:
+// 1. Hasher is transparent, can hash K, and Eq can compare Key and K, OR
+// 2. Hasher is not transparent and K can be implicitly converted to Key.
+template <typename K, typename Key, typename Hasher, typename Eq>
+constexpr bool IsLookupKeyAllowed() {
+  if constexpr (HasIsTransparent<Hasher>::value) {
+    return std::is_invocable_v<Hasher, const K&> &&
+           std::is_invocable_v<Eq, const Key&, const K&>;
+  } else if constexpr (std::is_convertible_v<K, Key>) {
+    return true;
   } else {
-    if (node->parent && node == node->parent->left) {
-      node = node->parent;
-    } else {
-      while (node->parent && node == node->parent->right) {
-        node = node->parent;
-      }
-      node = node->parent;
-    }
+    return false;
   }
-  return node;
 }

-}  // namespace perfetto::base::internal
-// gen_amalgamated begin source: src/base/logging.cc
-// gen_amalgamated begin header: src/base/log_ring_buffer.h
-// gen_amalgamated begin header: include/perfetto/ext/base/thread_annotations.h
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Swiss Table control byte encoding:
+// - Empty:   0x80 (10000000) - MSB set, easy to detect with sign bit
+// - Deleted: 0xFE (11111110) - MSB set
+// - Full:    0x00-0x7F - MSB clear, stores 7-bit H2 hash
+static constexpr uint8_t kFreeSlot = 0x80;   // Empty slot
+static constexpr uint8_t kTombstone = 0xFE;  // Deleted slot

-#ifndef INCLUDE_PERFETTO_EXT_BASE_THREAD_ANNOTATIONS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_THREAD_ANNOTATIONS_H_
+// The default load limit percent before growing the table.
+static constexpr int kDefaultLoadLimitPct = 75;

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// Abstraction over a group of control bytes that enables batch operations.
+// On x64, uses SSE to match 16 control bytes in parallel.
+// On other platforms, uses SWAR (SIMD Within A Register) for 8 bytes.
+//
+// Match() returns an iterator over slots whose control byte matches h2.
+// MatchEmpty() returns an iterator over empty slots (kFreeSlot).
+// MatchEmptyOrDeleted() returns an iterator over empty or deleted slots.
+#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
+struct Group {
+ public:
+  // Group size 16 for x64 SSE
+  static constexpr size_t kSize = 16;

-// Windows TSAN doesn't currently support these annotations.
-#if defined(THREAD_SANITIZER) && !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-extern "C" {
-void AnnotateBenignRaceSized(const char* file,
-                             int line,
-                             const volatile void* address,
-                             size_t size,
-                             const char* description);
-}
+  // Iterates over set bits in a match mask. Each set bit indicates a slot
+  // in the group that matched the search criteria. Call Next() to get the
+  // index of each matching slot.
+  struct Iterator {
+   public:
+    PERFETTO_ALWAYS_INLINE explicit Iterator(uint16_t mask) : mask_(mask) {}
+    PERFETTO_ALWAYS_INLINE explicit operator bool() const { return mask_; }

-#define PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(pointer, size, description) \
-  AnnotateBenignRaceSized(__FILE__, __LINE__, pointer, size, description);
-#else  // defined(ADDRESS_SANITIZER)
-#define PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(pointer, size, description)
-#endif  // defined(ADDRESS_SANITIZER)
+    PERFETTO_ALWAYS_INLINE size_t Next() {
+      auto idx = static_cast<size_t>(CountTrailZeros(mask_));
+      mask_ &= static_cast<uint16_t>(mask_ - 1);
+      return idx;
+    }

-#endif  // INCLUDE_PERFETTO_EXT_BASE_THREAD_ANNOTATIONS_H_
-/*
- * Copyright (C) 2021 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.
- */
+   private:
+    uint16_t mask_;
+  };

-#ifndef SRC_BASE_LOG_RING_BUFFER_H_
-#define SRC_BASE_LOG_RING_BUFFER_H_
+  PERFETTO_ALWAYS_INLINE explicit Group(const uint8_t* pos) {
+    ctrl_ = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pos));
+  }

-#include <stddef.h>
-#include <stdio.h>
+  PERFETTO_ALWAYS_INLINE Iterator Match(uint8_t h2) const {
+    auto match = _mm_cmpeq_epi8(ctrl_, _mm_set1_epi8(static_cast<char>(h2)));
+    return Iterator(static_cast<uint16_t>(_mm_movemask_epi8(match)));
+  }

-#include <array>
-#include <atomic>
+  PERFETTO_ALWAYS_INLINE Iterator MatchEmpty() const {
+    return Iterator(
+        static_cast<uint16_t>(_mm_movemask_epi8(_mm_sign_epi8(ctrl_, ctrl_))));
+  }

-// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/thread_annotations.h"
+  PERFETTO_ALWAYS_INLINE Iterator MatchEmptyOrDeleted() const {
+    return Iterator(static_cast<uint16_t>(_mm_movemask_epi8(ctrl_)));
+  }

-namespace perfetto {
-namespace base {
+ private:
+  __m128i ctrl_;
+};
+#else
+// SWAR fallback: processes 8 control bytes at a time using 64-bit arithmetic.
+struct Group {
+ public:
+  // Group size 8 for ARM and other platforms
+  static constexpr size_t kSize = 8;

-// Defined out of line because a static constexpr requires static storage if
-// ODR-used, not worth adding a .cc file just for tests.
-constexpr size_t kLogRingBufEntries = 8;
-constexpr size_t kLogRingBufMsgLen = 256;
+  // Iterates over set bits in a sparse 64-bit mask. Each set MSB indicates
+  // a matching byte in the group. Call Next() to get the index of each
+  // matching slot.
+  struct Iterator {
+   public:
+    PERFETTO_ALWAYS_INLINE explicit Iterator(uint64_t mask) : mask_(mask) {}
+    PERFETTO_ALWAYS_INLINE explicit operator bool() const { return mask_; }
+    PERFETTO_ALWAYS_INLINE size_t Next() {
+      // Count zeros and divide by 8 (shift 3)
+      // 0x80 (Byte 0) -> CTZ 7  -> 7>>3 = 0
+      // 0x8000 (Byte 1) -> CTZ 15 -> 15>>3 = 1
+      size_t idx = static_cast<size_t>(CountTrailZeros(mask_) >> 3);
+      mask_ &= mask_ - 1;  // Clear lowest set bit
+      return idx;
+    }

-// A static non-allocating ring-buffer to hold the most recent log events.
-// This class is really an implementation detail of logging.cc. The only reason
-// why is fully defined in a dedicated header is for allowing unittesting,
-// without leaking extra headers into logging.h (which is a high-fanout header).
-// This is used to report the last logs in a crash report when a CHECK/FATAL
-// is encountered.
-// This class has just an Append() method to insert events into the buffer and
-// a Read() to read the events in FIFO order. Read() is non-destructive.
-//
-// Thread safety considerations:
-// - The Append() method can be called concurrently by several threads, unless
-//   there are > kLogRingBufEntries concurrent threads. Even if that happens,
-//   case some events will contain a mix of strings but the behavior of
-//   further Append() and Read() is still defined.
-// - The Read() method is not thread safe but it's fine in practice. Even if
-//   it's called concurrently with other Append(), it only causes some partial
-//   events to be emitted in output.
-// In both cases, we never rely purely on \0, all operations are size-bound.
-//
-// See logging_unittest.cc for tests.
-class LogRingBuffer {
- public:
-  LogRingBuffer() = default;
-  LogRingBuffer(const LogRingBuffer&) = delete;
-  LogRingBuffer& operator=(const LogRingBuffer&) = delete;
-  LogRingBuffer(LogRingBuffer&&) = delete;
-  LogRingBuffer& operator=(LogRingBuffer&&) = delete;
+   private:
+    uint64_t mask_;
+  };

-  // This takes three arguments because it fits its only caller (logging.cc).
-  // The args are just concatenated together (plus one space before the msg).
-  void Append(StringView tstamp, StringView source, StringView log_msg) {
-    // Reserve atomically a slot in the ring buffer, so any concurrent Append()
-    // won't overlap (unless too many concurrent Append() happen together).
-    // There is no strict synchronization here, |event_slot_| is atomic only for
-    // the sake of avoiding colliding on the same slot but does NOT guarantee
-    // full consistency and integrity of the log messages written in each slot.
-    // A release-store (or acq+rel) won't be enough for full consistency. Two
-    // threads that race on Append() and take the N+1 and N+2 slots could finish
-    // the write in reverse order. So Read() would need to synchronize with
-    // something else (either a per-slot atomic flag or with a second atomic
-    // counter which is incremented after the snprintf). Both options increase
-    // the cost of Append() with no huge benefits (90% of the perfetto services
-    // where we use it is single thread, and the log ring buffer is disabled
-    // on non-standalone builds like the SDK).
-    uint32_t slot = event_slot_.fetch_add(1, std::memory_order_relaxed);
-    slot = slot % kLogRingBufEntries;
+  PERFETTO_ALWAYS_INLINE explicit Group(const uint8_t* pos) {
+    memcpy(&ctrl_, pos, sizeof(ctrl_));
+  }

-    char* const msg = events_[slot];
-    PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(msg, kLogRingBufMsgLen,
-                                        "see comments in log_ring_buffer.h")
-    snprintf(msg, kLogRingBufMsgLen, "%.*s%.*s %.*s",
-             static_cast<int>(tstamp.size()), tstamp.data(),
-             static_cast<int>(source.size()), source.data(),
-             static_cast<int>(log_msg.size()), log_msg.data());
+  PERFETTO_ALWAYS_INLINE Iterator Match(uint8_t h2) const {
+    uint64_t x = ctrl_ ^ (kLsbs * h2);
+    return Iterator((x - kLsbs) & ~x & kMsbs);
   }

-  // Reads back the buffer in FIFO order, up to |len - 1| characters at most
-  // (the -1 is because a NUL terminator is always appended, unless |len| == 0).
-  // The string written in |dst| is guaranteed to be NUL-terminated, even if
-  // |len| < buffer contents length.
-  // Returns the number of bytes written in output, excluding the \0 terminator.
-  size_t Read(char* dst, size_t len) {
-    if (len == 0)
-      return 0;
-    // This is a relaxed-load because we don't need to fully synchronize on the
-    // writing path for the reasons described in the fetch_add() above.
-    const uint32_t event_slot = event_slot_.load(std::memory_order_relaxed);
-    size_t dst_written = 0;
-    for (uint32_t pos = 0; pos < kLogRingBufEntries; ++pos) {
-      const uint32_t slot = (event_slot + pos) % kLogRingBufEntries;
-      const char* src = events_[slot];
-      if (*src == '\0')
-        continue;  // Empty slot. Skip.
-      char* const wptr = dst + dst_written;
-      // |src| might not be null terminated. This can happen if some
-      // thread-race happened. Limit the copy length.
-      const size_t limit = std::min(len - dst_written, kLogRingBufMsgLen);
-      for (size_t i = 0; i < limit; ++i) {
-        const char c = src[i];
-        ++dst_written;
-        if (c == '\0' || i == limit - 1) {
-          wptr[i] = '\n';
-          break;
-        }
-        // Skip non-printable ASCII characters to avoid confusing crash reports.
-        // Note that this deliberately mangles \n. Log messages should not have
-        // a \n in the middle and are NOT \n terminated. The trailing \n between
-        // each line is appended by the if () branch above.
-        const bool is_printable = c >= ' ' && c <= '~';
-        wptr[i] = is_printable ? c : '?';
-      }
-    }
-    // Ensure that the output string is null-terminated.
-    PERFETTO_DCHECK(dst_written <= len);
-    if (dst_written == len) {
-      // In case of truncation we replace the last char with \0. But the return
-      // value is the number of chars without \0, hence the --.
-      dst[--dst_written] = '\0';
-    } else {
-      dst[dst_written] = '\0';
-    }
-    return dst_written;
+  PERFETTO_ALWAYS_INLINE Iterator MatchEmpty() const {
+    // 0x80 check (Empty)
+    return Iterator((ctrl_ & ~(ctrl_ << 6)) & kMsbs);
   }

- private:
-  using EventBuf = char[kLogRingBufMsgLen];
-  EventBuf events_[kLogRingBufEntries]{};
+  PERFETTO_ALWAYS_INLINE Iterator MatchEmptyOrDeleted() const {
+    // 0x80 or 0xFE check (Empty or Deleted)
+    return Iterator((ctrl_ & ~(ctrl_ << 7)) & kMsbs);
+  }

-  static_assert((kLogRingBufEntries & (kLogRingBufEntries - 1)) == 0,
-                "kLogRingBufEntries must be a power of two");
+ private:
+  static constexpr uint64_t kLsbs = 0x0101010101010101ULL;
+  static constexpr uint64_t kMsbs = 0x8080808080808080ULL;

-  // A monotonically increasing counter incremented on each event written.
-  // It determines which of the kLogRingBufEntries indexes in |events_| should
-  // be used next.
-  // It grows >> kLogRingBufEntries, it's supposed to be always used
-  // mod(kLogRingBufEntries). A static_assert in the .cc file ensures that
-  // kLogRingBufEntries is a power of two so wraps are aligned.
-  std::atomic<uint32_t> event_slot_{};
+  uint64_t ctrl_;
 };
+#endif

-}  // namespace base
-}  // namespace perfetto
+// The state and the operations of FlatHashMapV2 which don't depend on the key
+// and value types. Not templated, and the heavier operations are defined in
+// flat_hash_map.cc, so that all maps share one copy of the code.
+class FlatHashMapV2Base {
+ protected:
+  // Tracks growth capacity and whether any deletions have occurred.
+  // Using bitfields like absl's GrowthInfo to avoid manual bit manipulation.
+  struct GrowthInfo {
+    uint64_t growth_left : 63;
+    uint64_t has_tombstones : 1;
+  };

-#endif  // SRC_BASE_LOG_RING_BUFFER_H_
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+  // The table being moved out of while growing.
+  struct OldTable {
+    std::unique_ptr<uint8_t[]> storage;
+    const uint8_t* ctrl;
+    uint8_t* slots;
+    size_t capacity;
+    size_t size;
+  };

-#include <stdarg.h>
-#include <stdio.h>
+  // The number of cloned control bytes after the main control byte array.
+  static constexpr size_t kNumClones = Group::kSize - 1;

-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <unistd.h>  // For isatty()
-#endif
+  explicit FlatHashMapV2Base(int load_limit_pct)
+      : load_limit_percent_(load_limit_pct) {}

-#include <atomic>
-#include <memory>
+  FlatHashMapV2Base(FlatHashMapV2Base&& other) noexcept
+      : storage_(std::move(other.storage_)),
+        capacity_(other.capacity_),
+        size_(other.size_),
+        growth_info_(other.growth_info_),
+        load_limit_percent_(other.load_limit_percent_),
+        ctrl_(other.ctrl_),
+        slots_(other.slots_) {
+    other.capacity_ = 0;
+    other.size_ = 0;
+    other.growth_info_ = {0, 0};
+    other.load_limit_percent_ = kDefaultLoadLimitPct;
+    other.ctrl_ = nullptr;
+    other.slots_ = nullptr;
+  }

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/crash_keys.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
-// gen_amalgamated expanded: #include "src/base/log_ring_buffer.h"
+  // Swiss Table hash splitting (matching absl):
+  // H1 = upper bits for bucket index
+  // H2 = lower 7 bits for tag
+  // This ensures H1 and H2 are independent, avoiding tag collisions within
+  // buckets. The seed XOR prevents clustering when hash values have patterns
+  // (e.g., sequential keys)
+  static constexpr size_t H1(size_t hash) { return (hash >> 7); }
+  static constexpr uint8_t H2(size_t hash) { return hash & 0x7F; }

-#if PERFETTO_ENABLE_LOG_RING_BUFFER() && PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#include <android/set_abort_message.h>
-#endif
+  // Doesn't call destructors.
+  void Reset(size_t n, bool reallocate, size_t slot_size, size_t slot_align);

-namespace perfetto {
-namespace base {
+  // Find first empty OR tombstone slot for insertion.
+  // Called when has_tombstones is set to find an earlier tombstone that can
+  // be reused instead of taking a new empty slot.
+  size_t FindFirstEmptyOrTombstone(size_t key_hash) const;

-namespace {
-const char kReset[] = "\x1b[0m";
-const char kDefault[] = "\x1b[39m";
-const char kDim[] = "\x1b[2m";
-const char kRed[] = "\x1b[31m";
-const char kBoldGreen[] = "\x1b[1m\x1b[32m";
-const char kLightGray[] = "\x1b[90m";
+  PERFETTO_ALWAYS_INLINE size_t
+  FindFirstEmptyOrTombstoneImpl(size_t key_hash) const {
+    const size_t cap_mask = capacity_ - 1;
+    size_t offset = H1(key_hash) & cap_mask;
+    size_t probe_size = 0;
+    while (true) {
+      Group group(ctrl_ + offset);
+      if (auto it = group.MatchEmptyOrDeleted(); PERFETTO_LIKELY(it)) {
+        return (offset + it.Next()) & cap_mask;
+      }
+      probe_size += Group::kSize;
+      offset = (offset + probe_size) & cap_mask;
+    }
+  }

-std::atomic<LogMessageCallback> g_log_callback{};
+  // Swaps in an empty table twice as large and returns the old one, from
+  // which the caller must move the entries before calling FinishGrow().
+  OldTable Grow(size_t slot_size, size_t slot_align);

-#if PERFETTO_BUILDFLAG(PERFETTO_STDERR_CRASH_DUMP)
-// __attribute__((constructor)) causes a static initializer that automagically
-// early runs this function before the main().
-void PERFETTO_EXPORT_COMPONENT __attribute__((constructor))
-InitDebugCrashReporter() {
-  // This function is defined in debug_crash_stack_trace.cc.
-  // The dynamic initializer is in logging.cc because logging.cc is included
-  // in virtually any target that depends on base. Having it in
-  // debug_crash_stack_trace.cc would require figuring out -Wl,whole-archive
-  // which is not worth it.
-  EnableStacktraceOnCrashForDebug();
-}
-#endif
+  void FinishGrow(size_t new_size) {
+    PERFETTO_CHECK(growth_info_.growth_left >= new_size);
+    size_ = new_size;
+    growth_info_.growth_left -= new_size;
+  }

-#if PERFETTO_ENABLE_LOG_RING_BUFFER()
-LogRingBuffer g_log_ring_buffer{};
+  // Set control byte and update clone if needed
+  PERFETTO_ALWAYS_INLINE void SetCtrl(size_t i, uint8_t h) {
+    ctrl_[i] = h;
+    // Update clone if this is one of the first kNumClones entries
+    if (PERFETTO_UNLIKELY(i < kNumClones)) {
+      ctrl_[capacity_ + i] = h;
+    }
+  }

-// This is global to avoid allocating memory or growing too much the stack
-// in MaybeSerializeLastLogsForCrashReporting(), which is called from
-// arbitrary code paths hitting PERFETTO_CHECK()/FATAL().
-char g_crash_buf[kLogRingBufEntries * kLogRingBufMsgLen];
-#endif
+  // Owns the actual memory with the following layout:
+  //
+  // [Control bytes]
+  //   |capacity_| bytes for control bytes.
+  //   kNumClones (15 or 7) bytes for control byte clones (*).
+  //   No alignment required (accessed at arbitrary byte offsets).
+  //
+  // [Padding for Slot alignment]
+  //
+  // [Slots]
+  //   capacity_ * sizeof(Slot): contains key-value pairs.
+  //   Must be aligned to alignof(Slot).
+  //
+  // (*) Control byte clones: The first kNumClones control bytes are duplicated
+  // at the end of the control array. This allows SIMD operations to read a full
+  // group (16 or 8 bytes) starting from any position without bounds checking,
+  // even near the end of the array.
+  std::unique_ptr<uint8_t[]> storage_;

-}  // namespace
+  size_t capacity_ = 0;
+  size_t size_ = 0;

-void SetLogMessageCallback(LogMessageCallback callback) {
-  g_log_callback.store(callback, std::memory_order_relaxed);
-}
+  // Slots remaining + has_deleted flag
+  GrowthInfo growth_info_{0, 0};

-void LogMessage(LogLev level,
-                const char* fname,
-                int line,
-                const char* fmt,
-                ...) {
-  char stack_buf[512];
-  std::unique_ptr<char[]> large_buf;
-  char* log_msg = &stack_buf[0];
-  size_t log_msg_len = 0;
+  // Load factor limit in % of |capacity_|.
+  int load_limit_percent_ = kDefaultLoadLimitPct;

-  // By default use a stack allocated buffer because most log messages are quite
-  // short. In rare cases they can be larger (e.g. --help). In those cases we
-  // pay the cost of allocating the buffer on the heap.
-  for (size_t max_len = sizeof(stack_buf);;) {
-    va_list args;
-    va_start(args, fmt);
-    int res = vsnprintf(log_msg, max_len, fmt, args);
-    va_end(args);
+  // Cached pointers for fast access (like absl::flat_hash_map)
+  // These are updated whenever storage is allocated/reallocated.
+  uint8_t* ctrl_ = nullptr;   // Points to control bytes
+  uint8_t* slots_ = nullptr;  // Points to slot array
+};

-    // If for any reason the print fails, overwrite the message but still print
-    // it. The code below will attach the filename and line, which is still
-    // useful.
-    if (res < 0) {
-      snprintf(log_msg, max_len, "%s", "[printf format error]");
-      break;
-    }
+}  // namespace flat_hash_map_v2_internal

-    // if res == max_len, vsnprintf saturated the input buffer. Retry with a
-    // larger buffer in that case (within reasonable limits).
-    if (res < static_cast<int>(max_len) || max_len >= 128 * 1024) {
-      // In case of truncation vsnprintf returns the len that "would have been
-      // written if the string was longer", not the actual chars written.
-      log_msg_len = std::min(static_cast<size_t>(res), max_len - 1);
-      break;
+// Hash and equality functors which treat ASCII strings case-insensitively.
+// Pass as the Hasher/Eq parameters of FlatHashMapV2 for maps keyed by
+// case-insensitive identifiers (e.g. SQL object names). Transparent, so
+// lookups accept std::string_view or const char* without constructing a
+// std::string key.
+struct CaseInsensitiveHash {
+  using is_transparent = void;
+  uint64_t operator()(std::string_view value) const {
+    MurmurHashCombiner combiner;
+    for (char c : value) {
+      combiner.Combine(Lowercase(c));
     }
-    max_len *= 4;
-    large_buf.reset(new char[max_len]);
-    log_msg = &large_buf[0];
-  }
-
-  LogMessageCallback cb = g_log_callback.load(std::memory_order_relaxed);
-  if (cb) {
-    cb({level, line, fname, log_msg});
-    return;
+    return combiner.digest();
   }
+};

-  const char* color = kDefault;
-  switch (level) {
-    case kLogDebug:
-      color = kDim;
-      break;
-    case kLogInfo:
-      color = kDefault;
-      break;
-    case kLogImportant:
-      color = kBoldGreen;
-      break;
-    case kLogError:
-      color = kRed;
-      break;
+struct CaseInsensitiveEq {
+  using is_transparent = void;
+  bool operator()(std::string_view lhs, std::string_view rhs) const {
+    return CaseInsensitiveEqual(lhs, rhs);
   }
+};

-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) &&  \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_WASM) && \
-    !PERFETTO_BUILDFLAG(PERFETTO_CHROMIUM_BUILD)
-  static const bool use_colors = isatty(STDERR_FILENO);
-#else
-  static const bool use_colors = false;
-#endif
-
-  // Formats file.cc:line as a space-padded fixed width string. If the file name
-  // |fname| is too long, truncate it on the left-hand side.
-  StackString<10> line_str("%d", line);
-
-  // 24 will be the width of the file.cc:line column in the log event.
-  static constexpr size_t kMaxNameAndLine = 24;
-  size_t fname_len = strlen(fname);
-  size_t fname_max = kMaxNameAndLine - line_str.len() - 2;  // 2 = ':' + '\0'.
-  size_t fname_offset = fname_len <= fname_max ? 0 : fname_len - fname_max;
-  StackString<kMaxNameAndLine> file_and_line(
-      "%*s:%s", static_cast<int>(fname_max), &fname[fname_offset],
-      line_str.c_str());
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  // Logcat has already timestamping, don't re-emit it.
-  __android_log_print(int{ANDROID_LOG_DEBUG} + level, "perfetto", "%s %s",
-                      file_and_line.c_str(), log_msg);
-#endif
+template <typename Key,
+          typename Value,
+          typename Hasher = base::MurmurHash<Key>,
+          typename Eq = flat_hash_map_v2_internal::HashEq<Key>>
+class FlatHashMapV2 : private flat_hash_map_v2_internal::FlatHashMapV2Base {
+ private:
+  // Import constants from internal namespace.
+  static constexpr uint8_t kFreeSlot = flat_hash_map_v2_internal::kFreeSlot;
+  static constexpr uint8_t kTombstone = flat_hash_map_v2_internal::kTombstone;
+  static constexpr int kDefaultLoadLimitPct =
+      flat_hash_map_v2_internal::kDefaultLoadLimitPct;

-  // When printing on stderr, print also the timestamp. We don't really care
-  // about the actual time. We just need some reference clock that can be used
-  // to correlated events across different processes (e.g. traced and
-  // traced_probes). The wall time % 1000 is good enough.
-  uint32_t t_ms = static_cast<uint32_t>(GetWallTimeMs().count());
-  uint32_t t_sec = t_ms / 1000;
-  t_ms -= t_sec * 1000;
-  t_sec = t_sec % 1000;
-  StackString<32> timestamp("[%03u.%03u] ", t_sec, t_ms);
+  // Slot structure holds both key and value
+  struct Slot {
+    Key key;
+    Value value;
+  };

-  if (use_colors) {
-    fprintf(stderr, "%s%s%s%s %s%s%s\n", kLightGray, timestamp.c_str(),
-            file_and_line.c_str(), kReset, color, log_msg, kReset);
-  } else {
-    fprintf(stderr, "%s%s %s\n", timestamp.c_str(), file_and_line.c_str(),
-            log_msg);
+  // Whether |K| is a type other than Key which, with a transparent Hasher,
+  // is hashed and compared to Key as is (e.g. a std::string_view looked up in
+  // a map keyed by std::string), sparing the construction of a Key.
+  template <typename K>
+  static constexpr bool IsHeterogeneousKey() {
+    using RawK = std::remove_cv_t<std::remove_reference_t<K>>;
+    if constexpr (std::is_same_v<RawK, Key>) {
+      return false;
+    } else {
+      return flat_hash_map_v2_internal::HasIsTransparent<Hasher>::value &&
+             flat_hash_map_v2_internal::IsLookupKeyAllowed<RawK, Key, Hasher,
+                                                           Eq>();
+    }
   }

-#if PERFETTO_ENABLE_LOG_RING_BUFFER()
-  // Append the message to the ring buffer for crash reporting postmortems.
-  StringView timestamp_sv = timestamp.string_view();
-  StringView file_and_line_sv = file_and_line.string_view();
-  StringView log_msg_sv(log_msg, static_cast<size_t>(log_msg_len));
-  g_log_ring_buffer.Append(timestamp_sv, file_and_line_sv, log_msg_sv);
-#else
-  ignore_result(log_msg_len);
-#endif
-}
+ public:
+  class Iterator {
+   public:
+    explicit Iterator(const uint8_t* ctrl, const uint8_t* ctrl_end, Slot* slot)
+        : ctrl_(ctrl), ctrl_end_(ctrl_end), slot_(slot) {
+      FindNextNonFree();
+    }
+    ~Iterator() = default;
+    Iterator(const Iterator&) = default;
+    Iterator& operator=(const Iterator&) = default;
+    Iterator(Iterator&&) noexcept = default;
+    Iterator& operator=(Iterator&&) noexcept = default;

-#if PERFETTO_ENABLE_LOG_RING_BUFFER()
-void MaybeSerializeLastLogsForCrashReporting() {
-  // Keep this function minimal. This is called from the watchdog thread, often
-  // when the system is thrashing.
+    const Key& key() { return slot_->key; }
+    Value& value() { return slot_->value; }
+    const Key& key() const { return slot_->key; }
+    const Value& value() const { return slot_->value; }

-  // This is racy because two threads could hit a CHECK/FATAL at the same time.
-  // But if that happens we have bigger problems, not worth designing around it.
-  // The behaviour is still defined in the race case (the string attached to
-  // the crash report will contain a mixture of log strings).
-  size_t wr = 0;
-  wr += SerializeCrashKeys(&g_crash_buf[wr], sizeof(g_crash_buf) - wr);
-  wr += g_log_ring_buffer.Read(&g_crash_buf[wr], sizeof(g_crash_buf) - wr);
+    explicit operator bool() const { return ctrl_ != ctrl_end_; }
+    Iterator& operator++() {
+      PERFETTO_DCHECK(ctrl_ != ctrl_end_);
+      ++ctrl_;
+      ++slot_;
+      FindNextNonFree();
+      return *this;
+    }

-  // Read() null-terminates the string properly. This is just to avoid UB when
-  // two threads race on each other (T1 writes a shorter string, T2
-  // overwrites the \0 writing a longer string. T1 continues here before T2
-  // finishes writing the longer string with the \0 -> boom.
-  g_crash_buf[sizeof(g_crash_buf) - 1] = '\0';
+   private:
+    void FindNextNonFree() {
+      for (; ctrl_ != ctrl_end_; ++ctrl_, ++slot_) {
+        const uint8_t cur_ctrl = *ctrl_;
+        if (cur_ctrl != kFreeSlot && cur_ctrl != kTombstone)
+          return;
+      }
+    }
+    const uint8_t* ctrl_ = nullptr;
+    const uint8_t* ctrl_end_ = nullptr;
+    Slot* slot_ = nullptr;
+  };  // Iterator

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  // android_set_abort_message() will cause debuggerd to report the message
-  // in the tombstone and in the crash log in logcat.
-  // NOTE: android_set_abort_message() can be called only once. This should
-  // be called only when we are sure we are about to crash.
-  android_set_abort_message(g_crash_buf);
-#else
-  // Print out the message on stderr on Linux/Mac/Win.
-  fputs("\n-----BEGIN PERFETTO PRE-CRASH LOG-----\n", stderr);
-  fputs(g_crash_buf, stderr);
-  fputs("\n-----END PERFETTO PRE-CRASH LOG-----\n", stderr);
-#endif
-}
-#endif  // PERFETTO_ENABLE_LOG_RING_BUFFER
+  explicit FlatHashMapV2(size_t initial_capacity = 0,
+                         int load_limit_pct = kDefaultLoadLimitPct)
+      : FlatHashMapV2Base(load_limit_pct) {
+    if (initial_capacity > 0) {
+      Reset(initial_capacity, true);
+    }
+  }

-}  // namespace base
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/metatrace.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/metatrace.h
-// gen_amalgamated begin header: include/perfetto/ext/base/metatrace_events.h
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+  // We are calling Clear() so that the destructors for the inserted entries are
+  // called (unless they are trivial, in which case it will be a no-op).
+  ~FlatHashMapV2() { Clear(); }

-#ifndef INCLUDE_PERFETTO_EXT_BASE_METATRACE_EVENTS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_METATRACE_EVENTS_H_
+  FlatHashMapV2(FlatHashMapV2&& other) noexcept
+      : FlatHashMapV2Base(std::move(other)) {}

-#include <stdint.h>
+  FlatHashMapV2& operator=(FlatHashMapV2&& other) noexcept {
+    this->~FlatHashMapV2();
+    new (this) FlatHashMapV2(std::move(other));
+    return *this;
+  }

-namespace perfetto {
-namespace metatrace {
+  FlatHashMapV2(const FlatHashMapV2&) = delete;
+  FlatHashMapV2& operator=(const FlatHashMapV2&) = delete;

-enum Tags : uint32_t {
-  TAG_NONE = 0,
-  TAG_ANY = uint32_t(-1),
-  TAG_FTRACE = 1 << 0,
-  TAG_PROC_POLLERS = 1 << 1,
-  TAG_TRACE_WRITER = 1 << 2,
-  TAG_TRACE_SERVICE = 1 << 3,
-  TAG_PRODUCER = 1 << 4,
-};
+  // Find(), Erase(), Insert() and operator[] take a Key, which the caller
+  // converts to as needed. With a transparent Hasher, they also take any type
+  // it can hash and compare to Key as is (see IsHeterogeneousKey()).

-// The macros below generate matching enums and arrays of string literals.
-// This is to avoid maintaining string maps manually.
+  PERFETTO_ALWAYS_INLINE Value* Find(const Key& key) const {
+    return FindImpl(key);
+  }
+  template <typename K, typename = std::enable_if_t<IsHeterogeneousKey<K>()>>
+  PERFETTO_ALWAYS_INLINE Value* Find(const K& key) const {
+    return FindImpl(key);
+  }

-// clang-format off
+  bool Erase(const Key& key) { return EraseImpl(key); }
+  template <typename K, typename = std::enable_if_t<IsHeterogeneousKey<K>()>>
+  bool Erase(const K& key) {
+    return EraseImpl(key);
+  }

-// DO NOT remove or reshuffle items in this list, only append. The ID of these
-// events are an ABI, the trace processor relies on these to open old traces.
-#define PERFETTO_METATRACE_EVENTS(F) \
-  F(EVENT_ZERO_UNUSED), \
-  F(FTRACE_CPU_READER_READ), /*unused*/ \
-  F(FTRACE_DRAIN_CPUS), /*unused*/ \
-  F(FTRACE_UNBLOCK_READERS), /*unused*/ \
-  F(FTRACE_CPU_READ_NONBLOCK), /*unused*/ \
-  F(FTRACE_CPU_READ_BLOCK), /*unused*/ \
-  F(FTRACE_CPU_SPLICE_NONBLOCK), /*unused*/ \
-  F(FTRACE_CPU_SPLICE_BLOCK), /*unused*/ \
-  F(FTRACE_CPU_WAIT_CMD), /*unused*/ \
-  F(FTRACE_CPU_RUN_CYCLE), /*unused*/ \
-  F(FTRACE_CPU_FLUSH), \
-  F(FTRACE_CPU_BUFFER_WATERMARK), \
-  F(READ_SYS_STATS), \
-  F(PS_WRITE_ALL_PROCESSES), \
-  F(PS_ON_PIDS), \
-  F(PS_ON_RENAME_PIDS), \
-  F(PS_WRITE_ALL_PROCESS_STATS), \
-  F(TRACE_WRITER_COMMIT_STARTUP_WRITER_BATCH), \
-  F(FTRACE_READ_TICK), \
-  F(FTRACE_CPU_READ_CYCLE), \
-  F(FTRACE_CPU_READ_BATCH), \
-  F(KALLSYMS_PARSE), \
-  F(PROFILER_READ_TICK), \
-  F(PROFILER_READ_CPU), \
-  F(PROFILER_UNWIND_TICK), \
-  F(PROFILER_UNWIND_SAMPLE), \
-  F(PROFILER_UNWIND_INITIAL_ATTEMPT), \
-  F(PROFILER_UNWIND_ATTEMPT), \
-  F(PROFILER_MAPS_PARSE), \
-  F(PROFILER_MAPS_REPARSE), \
-  F(PROFILER_UNWIND_CACHE_CLEAR), \
-  F(ANDROID_CPU_PER_UID_TICK), \
-  F(ANDROID_KERNEL_WAKELOCKS_TICK), \
-  F(ANDROID_POWER_TICK), \
-  F(ANDROID_POWER_WRITE_BATTERY_COUNTERS), \
-  F(ANDROID_POWER_WRITE_ENERGY_ESTIMATION_BREAKDOWN), \
-  F(ANDROID_POWER_WRITE_ENTITY_STATE_RESIDENCY), \
-  F(ANDROID_POWER_WRITE_POWER_RAILS_DATA), \
-  F(LINUX_POWER_SYSFS_TICK)
+  PERFETTO_ALWAYS_INLINE std::pair<Value*, bool> Insert(const Key& key,
+                                                        Value value) {
+    return InsertImpl(key, std::move(value));
+  }
+  PERFETTO_ALWAYS_INLINE std::pair<Value*, bool> Insert(Key&& key,
+                                                        Value value) {
+    return InsertImpl(std::move(key), std::move(value));
+  }
+  // Constructs the Key only if |key| is absent.
+  template <typename K, typename = std::enable_if_t<IsHeterogeneousKey<K>()>>
+  PERFETTO_ALWAYS_INLINE std::pair<Value*, bool> Insert(K&& key, Value value) {
+    return InsertImpl(std::forward<K>(key), std::move(value));
+  }

-// Append only, see above.
-//
-// Values that aren't used as counters:
-// * FTRACE_SERVICE_COMMIT_DATA is a bit-packed representation of an event, see
-//   tracing_service_impl.cc for the format.
-// * PROFILER_UNWIND_CURRENT_PID represents the PID that is being unwound.
-// * WATCHDOG_CRASH_REASON represents the base::WatchdogCrashReason enum.
-// * WATCHDOG_CRASH_TIME_BOOT_MS The time in ms since boot when the watchdog
-//   timeout alarm triggered (i.e. when it would have caused a crash, without
-//   counting for the optional handler time).
-// * WATCHDOG_CRASH_ACTUAL_{MONO,BOOT,CPU} are the elapsed seconds (since the
-//   offending fatal timer was created) sampled from CLOCK_MONOTONIC,
-//   CLOCK_BOOTTIME and CLOCK_PROCESS_CPUTIME_ID respectively.
-//
-#define PERFETTO_METATRACE_COUNTERS(F) \
-  F(COUNTER_ZERO_UNUSED),\
-  F(FTRACE_PAGES_DRAINED), \
-  F(PS_PIDS_SCANNED), \
-  F(TRACE_SERVICE_COMMIT_DATA), \
-  F(PROFILER_UNWIND_QUEUE_SZ), \
-  F(PROFILER_UNWIND_CURRENT_PID), \
-  F(WATCHDOG_CRASH_REASON), \
-  F(WATCHDOG_CRASH_TIME_BOOT_MS), \
-  F(WATCHDOG_CRASH_ACTUAL_MONO), \
-  F(WATCHDOG_CRASH_ACTUAL_BOOT), \
-  F(WATCHDOG_CRASH_ACTUAL_CPU), \
-  F(FTRACE_SESSIONS)
+  Value& operator[](const Key& key) { return *Insert(key, Value{}).first; }
+  Value& operator[](Key&& key) {
+    return *Insert(std::move(key), Value{}).first;
+  }
+  template <typename K, typename = std::enable_if_t<IsHeterogeneousKey<K>()>>
+  Value& operator[](K&& key) {
+    return *Insert(std::forward<K>(key), Value{}).first;
+  }

-// clang-format on
+  void Clear() {
+    // Avoid trivial heap operations on zero-capacity std::move()-d objects.
+    if (PERFETTO_UNLIKELY(capacity_ == 0)) {
+      return;
+    }
+    for (size_t i = 0; i < capacity_; ++i) {
+      const uint8_t tag = ctrl_[i];
+      if (tag == kFreeSlot || tag == kTombstone) {
+        continue;
+      }
+      slots()[i].key.~Key();
+      slots()[i].value.~Value();
+    }
+    Reset(capacity_, false);
+  }

-#define PERFETTO_METATRACE_IDENTITY(name) name
-#define PERFETTO_METATRACE_TOSTRING(name) #name
+  Iterator GetIterator() { return Iterator(ctrl_, ctrl_ + capacity_, slots()); }
+  Iterator GetIterator() const {
+    return Iterator(ctrl_, ctrl_ + capacity_, slots());
+  }

-enum Events : uint16_t {
-  PERFETTO_METATRACE_EVENTS(PERFETTO_METATRACE_IDENTITY),
-  EVENTS_MAX
-};
-constexpr char const* kEventNames[] = {
-    PERFETTO_METATRACE_EVENTS(PERFETTO_METATRACE_TOSTRING)};
+  size_t size() const { return size_; }
+  size_t capacity() const { return capacity_; }

-enum Counters : uint16_t {
-  PERFETTO_METATRACE_COUNTERS(PERFETTO_METATRACE_IDENTITY),
-  COUNTERS_MAX
-};
-constexpr char const* kCounterNames[] = {
-    PERFETTO_METATRACE_COUNTERS(PERFETTO_METATRACE_TOSTRING)};
+ private:
+  // Result struct for FindOrPrepareInsert - avoids bit manipulation overhead
+  struct FindResult {
+    uint64_t idx : 63;
+    uint64_t needs_insert : 1;
+  };

-inline void SuppressUnusedVarsInAmalgamatedBuild() {
-  (void)kCounterNames;
-  (void)kEventNames;
-}
+  // Not found sentinel (must fit in 63-bit FindResult.idx)
+  static constexpr size_t kNotFound = std::numeric_limits<size_t>::max() >> 1;

-}  // namespace metatrace
-}  // namespace perfetto
+  using Group = flat_hash_map_v2_internal::Group;

-#endif  // INCLUDE_PERFETTO_EXT_BASE_METATRACE_EVENTS_H_
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef INCLUDE_PERFETTO_EXT_BASE_METATRACE_H_
-#define INCLUDE_PERFETTO_EXT_BASE_METATRACE_H_
-
-#include <array>
-#include <atomic>
-#include <functional>
-#include <string>
-
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/base/thread_utils.h"
-// gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/metatrace_events.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
-
-// A facility to trace execution of the perfetto codebase itself.
-// The meta-tracing framework is organized into three layers:
-//
-// 1. A static ring-buffer in base/ (this file) that supports concurrent writes
-//    and a single reader.
-//    The responsibility of this layer is to store events and counters as
-//    efficiently as possible without re-entering any tracing code.
-//    This is really a static-storage-based ring-buffer based on a POD array.
-//    This layer does NOT deal with serializing the meta-trace buffer.
-//    It posts a task when it's half full and expects something outside of
-//    base/ to drain the ring-buffer and serialize it, eventually writing it
-//    into the trace itself, before it gets 100% full.
-//
-// 2. A class in tracing/core which takes care of serializing the meta-trace
-//    buffer into the trace using a TraceWriter. See metatrace_writer.h .
-//
-// 3. A data source in traced_probes that, when be enabled via the trace config,
-//    injects metatrace events into the trace. See metatrace_data_source.h .
-//
-// The available events and tags are defined in metatrace_events.h .
-
-namespace perfetto {
+  // Searches for a key in the table. This function IGNORES tombstones during
+  // the search - it only stops at empty slots (kFreeSlot) or matching keys.
+  //
+  // Why ignore tombstones? In Swiss Tables, tombstones mark deleted entries but
+  // must be skipped during lookup because the key we're searching for may have
+  // been inserted AFTER the tombstone was created (i.e., the key's probe
+  // sequence may have skipped over that tombstone). Only an empty slot
+  // definitively proves the key doesn't exist.
+  //
+  // Returns FindResult with idx and whether the key needs to be inserted:
+  // - If key is found: {idx, false} where idx is the slot containing the key.
+  // - If key not found and ForInsert=true: {empty_idx, true} where empty_idx
+  //   is the index of the first EMPTY slot encountered.
+  // - If key not found and ForInsert=false: {kNotFound, true}.
+  //
+  // IMPORTANT for insertion (ForInsert=true): The returned empty_idx is NOT
+  // necessarily the best slot to insert into! There may be an earlier tombstone
+  // in the probe sequence that should be reused to avoid wasting slots. When
+  // has_tombstones is set, the caller must make a SECOND pass by calling
+  // FindFirstEmptyOrTombstone() to find the actual insertion slot.
+  template <bool ForInsert, typename K = Key>
+  PERFETTO_ALWAYS_INLINE FindResult
+  FindSlotIgnoringTombstones(const K& key, size_t key_hash, uint8_t h2) const {
+    static_assert(
+        flat_hash_map_v2_internal::IsLookupKeyAllowed<K, Key, Hasher, Eq>(),
+        "Heterogeneous lookup requires Hasher to define is_transparent and "
+        "support hashing the lookup key type, and Eq to compare Key and K. "
+        "Without a transparent Hasher, K must be convertible to Key.");

-namespace base {
-class TaskRunner;
-}  // namespace base
+    if (PERFETTO_UNLIKELY(ctrl_ == nullptr)) {
+      return {kNotFound, true};
+    }

-namespace metatrace {
+    const size_t cap_mask = capacity_ - 1;
+    size_t offset = H1(key_hash) & cap_mask;
+    size_t probe_size = 0;
+    const uint8_t* ctrl = ctrl_;

-// Meta-tracing is organized in "tags" that can be selectively enabled. This is
-// to enable meta-tracing only of one sub-system. This word has one "enabled"
-// bit for each tag. 0 -> meta-tracing off.
-extern std::atomic<uint32_t> g_enabled_tags;
+    // Prefetch control bytes (like Absl's prefetch_heap_block).
+    // Use locality hint 3 (high temporal locality) for better L1 cache usage.
+    __builtin_prefetch(ctrl_ + offset, 0, 3);

-// Time of the Enable() call. Used as a reference for keeping delta timestmaps
-// in Record.
-extern std::atomic<uint64_t> g_enabled_timestamp;
+    while (true) {
+      // Prefetch slots at current probe offset (like Absl).
+      __builtin_prefetch(slots() + offset, 0, 3);

-// Enables meta-tracing for one or more tags. Once enabled it will discard any
-// further Enable() calls and return false until disabled,
-// |read_task| is a closure that will be called enqueued |task_runner| when the
-// meta-tracing ring buffer is half full. The task is expected to read the ring
-// buffer using RingBuffer::GetReadIterator() and serialize the contents onto a
-// file or into the trace itself.
-// Must be called on the |task_runner| passed.
-// |task_runner| must have static lifetime.
-bool Enable(std::function<void()> read_task, base::TaskRunner*, uint32_t tags);
+      Group group(ctrl + offset);

-// Disables meta-tracing.
-// Must be called on the same |task_runner| as Enable().
-void Disable();
+      // Match H2 tags in this group.
+      for (auto it = group.Match(h2); PERFETTO_LIKELY(it);) {
+        // Must mask because offset + it.Next() can exceed capacity when
+        // group straddles the table boundary (using cloned control bytes).
+        size_t idx = (offset + it.Next()) & cap_mask;
+        if (PERFETTO_LIKELY(Eq{}(slots()[idx].key, key))) {
+          return {idx, false};  // Found
+        }
+      }

-inline uint64_t TraceTimeNowNs() {
-  return static_cast<uint64_t>(base::GetBootTimeNs().count());
-}
+      // Check for empty slot (NOT tombstones). If we find an empty slot, the
+      // key cannot exist in the table (empty slots terminate probe chains).
+      if (auto it = group.MatchEmpty(); PERFETTO_LIKELY(it)) {
+        if constexpr (ForInsert) {
+          size_t empty_idx = (offset + it.Next()) & cap_mask;
+          return {empty_idx, true};  // Not found - empty slot for insertion
+        } else {
+          return {kNotFound, true};  // Not found - no need to compute slot
+        }
+      }

-// Returns a relaxed view of whether metatracing is enabled for the given tag.
-// Useful for skipping unnecessary argument computation if metatracing is off.
-inline bool IsEnabled(uint32_t tag) {
-  auto enabled_tags = g_enabled_tags.load(std::memory_order_relaxed);
-  return PERFETTO_UNLIKELY((enabled_tags & tag) != 0);
-}
+      // Triangular probing (like Absl): 0, 16, 48, 96, ...
+      probe_size += Group::kSize;
+      offset = (offset + probe_size) & cap_mask;

-// Holds the data for a metatrace event or counter.
-struct Record {
-  static constexpr uint16_t kTypeMask = 0x8000;
-  static constexpr uint16_t kTypeCounter = 0x8000;
-  static constexpr uint16_t kTypeEvent = 0;
+      // Should never happen with load limit.
+      PERFETTO_DCHECK(probe_size <= capacity_);
+    }
+  }

-  uint64_t timestamp_ns() const {
-    auto base_ns = g_enabled_timestamp.load(std::memory_order_relaxed);
-    PERFETTO_DCHECK(base_ns);
-    return base_ns + ((static_cast<uint64_t>(timestamp_ns_high) << 32) |
-                      timestamp_ns_low);
+  // Only constructing the key and value is inlined: finding the slot is out of
+  // line in FindOrPrepareInsert(), compiled once per lookup key type rather
+  // than per call site or per value category of |key|.
+  template <typename K>
+  PERFETTO_ALWAYS_INLINE std::pair<Value*, bool> InsertImpl(K&& key,
+                                                            Value&& value) {
+    const std::remove_reference_t<K>& lookup_key = key;
+    FindResult res = FindOrPrepareInsert(lookup_key);
+    Slot* slot = &slots()[res.idx];
+    const bool inserted = res.needs_insert;
+    if (inserted) {
+      new (&slot->key) Key(std::forward<K>(key));
+      new (&slot->value) Value(std::move(value));
+    }
+    return {&slot->value, inserted};
   }

-  void set_timestamp(uint64_t ts) {
-    auto t_start = g_enabled_timestamp.load(std::memory_order_relaxed);
-    uint64_t diff = ts - t_start;
-    PERFETTO_DCHECK(diff < (1ull << 48));
-    timestamp_ns_low = static_cast<uint32_t>(diff);
-    timestamp_ns_high = static_cast<uint16_t>(diff >> 32);
+  // Out of line, compiled once per lookup key type.
+  template <typename K>
+  PERFETTO_NO_INLINE Value* FindImpl(const K& key) const {
+    size_t key_hash = Hasher{}(key);
+    uint8_t h2 = H2(key_hash);
+    FindResult res = FindSlotIgnoringTombstones<false>(key, key_hash, h2);
+    if (PERFETTO_UNLIKELY(res.needs_insert)) {
+      return nullptr;
+    }
+    return &slots()[res.idx].value;
   }

-  // We can't just memset() this class because on MSVC std::atomic<> is not
-  // trivially constructible anymore. Also std::atomic<> has a deleted copy
-  // constructor so we cant just do "*this = Record()" either.
-  // See http://bit.ly/339Jlzd .
-  void clear() {
-    this->~Record();
-    new (this) Record();
+  template <typename K>
+  PERFETTO_NO_INLINE bool EraseImpl(const K& key) {
+    size_t key_hash = Hasher{}(key);
+    uint8_t h2 = H2(key_hash);
+    FindResult res = FindSlotIgnoringTombstones<false>(key, key_hash, h2);
+    if (PERFETTO_UNLIKELY(res.needs_insert)) {
+      return false;
+    }
+    PERFETTO_DCHECK(size_ > 0);
+    SetCtrl(res.idx, kTombstone);
+    slots()[res.idx].key.~Key();
+    slots()[res.idx].value.~Value();
+    size_--;
+    growth_info_.has_tombstones = 1;
+    return true;
   }

-  // This field holds the type (counter vs event) in the MSB and event ID (as
-  // defined in metatrace_events.h) in the lowest 15 bits. It is also used also
-  // as a linearization point: this is always written after all the other
-  // fields with a release-store. This is so the reader can determine whether it
-  // can safely process the other event fields after a load-acquire.
-  std::atomic<uint16_t> type_and_id{};
+  // Returns the slot holding |key| if present. Otherwise claims a slot for it,
+  // growing the table if needed, and the caller must construct into it.
+  template <typename K>
+  PERFETTO_NO_INLINE FindResult FindOrPrepareInsert(const K& key) {
+    size_t key_hash = Hasher{}(key);
+    uint8_t h2 = H2(key_hash);
+    FindResult res = FindSlotIgnoringTombstones<true>(key, key_hash, h2);
+    if (PERFETTO_UNLIKELY(!res.needs_insert)) {
+      return res;
+    }
+    if (PERFETTO_UNLIKELY(growth_info_.growth_left == 0)) {
+      GrowAndRehash();
+      // After rehash, table has no tombstones. Find an empty slot directly
+      // instead of recursing (which would prevent inlining).
+      res.idx = FindFirstEmptyOrTombstone(key_hash);
+    }
+    PERFETTO_DCHECK(res.idx != kNotFound);
+    size_t insert_idx = res.idx;
+    bool is_freeslot = true;
+    if (PERFETTO_UNLIKELY(growth_info_.has_tombstones)) {
+      insert_idx = FindFirstEmptyOrTombstone(key_hash);
+      is_freeslot = ctrl_[insert_idx] != kTombstone;
+    }
+    SetCtrl(insert_idx, h2);
+    size_++;
+    if (is_freeslot) {
+      growth_info_.growth_left--;
+    }
+    return {insert_idx, true};
+  }

-  // Timestamp is stored as a 48-bits value diffed against g_enabled_timestamp.
-  // This gives us 78 hours from Enabled().
-  uint16_t timestamp_ns_high = 0;
-  uint32_t timestamp_ns_low = 0;
+  // Allocating the new table is shared by all maps in Grow(): only moving the
+  // entries over, which needs their types, is compiled per map.
+  PERFETTO_NO_INLINE void GrowAndRehash() {
+    OldTable old = Grow(sizeof(Slot), alignof(Slot));
+    Slot* old_slots = reinterpret_cast<Slot*>(old.slots);
+    // Keys are unique and the new table has no tombstones, so each entry goes
+    // straight into the first empty slot of its probe sequence.
+    size_t new_size = 0;
+    for (size_t i = 0; i < old.capacity; ++i) {
+      if (uint8_t t = old.ctrl[i]; t == kFreeSlot || t == kTombstone) {
+        continue;
+      }
+      Slot& old_slot = old_slots[i];
+      size_t key_hash = Hasher{}(old_slot.key);
+      size_t idx = FindFirstEmptyOrTombstoneImpl(key_hash);
+      new (&slots()[idx].key) Key(std::move(old_slot.key));
+      new (&slots()[idx].value) Value(std::move(old_slot.value));
+      SetCtrl(idx, H2(key_hash));
+      old_slot.key.~Key();  // Destroy the old objects.
+      old_slot.value.~Value();
+      new_size++;
+    }
+    PERFETTO_DCHECK(new_size == old.size);
+    FinishGrow(new_size);
+  }

-  uint32_t thread_id = 0;
+  // Doesn't call destructors. Use Clear() for that.
+  void Reset(size_t n, bool reallocate) {
+    FlatHashMapV2Base::Reset(n, reallocate, sizeof(Slot), alignof(Slot));
+  }

-  union {
-    // Only one of the two elements can be zero initialized, clang complains
-    // about "initializing multiple members of union" otherwise.
-    uint32_t duration_ns = 0;  // If type == event.
-    int32_t counter_value;     // If type == counter.
-  };
+  PERFETTO_ALWAYS_INLINE Slot* slots() const {
+    return reinterpret_cast<Slot*>(slots_);
+  }
 };

-// Hold the meta-tracing data into a statically allocated array.
-// This class uses static storage (as opposite to being a singleton) to:
-// - Have the guarantee of always valid storage, so that meta-tracing can be
-//   safely used in any part of the codebase, including base/ itself.
-// - Avoid barriers that thread-safe static locals would require.
-class RingBuffer {
- public:
-  static constexpr size_t kCapacity = 4096;  // 4096 * 16 bytes = 64K.
-
-  // This iterator is not idempotent and will bump the read index in the buffer
-  // at the end of the reads. There can be only one reader at any time.
-  // Usage: for (auto it = RingBuffer::GetReadIterator(); it; ++it) { it->... }
-  class ReadIterator {
-   public:
-    ReadIterator(ReadIterator&& other) {
-      PERFETTO_DCHECK(other.valid_);
-      cur_ = other.cur_;
-      end_ = other.end_;
-      valid_ = other.valid_;
-      other.valid_ = false;
-    }
+// Alias FlatHashMap to FlatHashMapV1 for backward compatibility.
+//
+// TODO(lalitm): Once FlatHashMapV2 is fully tested and verified, switch this
+// to FlatHashMapV2.
+template <typename Key,
+          typename Value,
+          typename Hasher = MurmurHash<Key>,
+          typename Probe = QuadraticProbe,
+          bool AppendOnly = false>
+using FlatHashMap = FlatHashMapV1<Key, Value, Hasher, Probe, AppendOnly>;

-    ~ReadIterator() {
-      if (!valid_)
-        return;
-      PERFETTO_DCHECK(cur_ >= RingBuffer::rd_index_);
-      PERFETTO_DCHECK(cur_ <= RingBuffer::wr_index_);
-      RingBuffer::rd_index_.store(cur_, std::memory_order_release);
-    }
+}  // namespace perfetto::base

-    explicit operator bool() const { return cur_ < end_; }
-    const Record* operator->() const { return RingBuffer::At(cur_); }
-    const Record& operator*() const { return *operator->(); }
+#endif  // INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_H_
+/*
+ * Copyright (C) 2026 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 is for ++it. it++ is deliberately not supported.
-    ReadIterator& operator++() {
-      PERFETTO_DCHECK(cur_ < end_);
-      // Once a record has been read, mark it as free clearing its type_and_id,
-      // so if we encounter it in another read iteration while being written
-      // we know it's not fully written yet.
-      // The memory_order_relaxed below is enough because:
-      // - The reader is single-threaded and doesn't re-read the same records.
-      // - Before starting a read batch, the reader has an acquire barrier on
-      //   |rd_index_|.
-      // - After terminating a read batch, the ~ReadIterator dtor updates the
-      //   |rd_index_| with a release-store.
-      // - Reader and writer are typically kCapacity/2 apart. So unless an
-      //   overrun happens a writer won't reuse a newly released record any time
-      //   soon. If an overrun happens, everything is busted regardless.
-      At(cur_)->type_and_id.store(0, std::memory_order_relaxed);
-      ++cur_;
-      return *this;
-    }
+// gen_amalgamated expanded: #include "perfetto/ext/base/flat_hash_map.h"

-   private:
-    friend class RingBuffer;
-    ReadIterator(uint64_t begin, uint64_t end)
-        : cur_(begin), end_(end), valid_(true) {}
-    ReadIterator& operator=(const ReadIterator&) = delete;
-    ReadIterator(const ReadIterator&) = delete;
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <utility>

-    uint64_t cur_;
-    uint64_t end_;
-    bool valid_;
-  };
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"

-  static Record* At(uint64_t index) {
-    // Doesn't really have to be pow2, but if not the compiler will emit
-    // arithmetic operations to compute the modulo instead of a bitwise AND.
-    static_assert(!(kCapacity & (kCapacity - 1)), "kCapacity must be pow2");
-    PERFETTO_DCHECK(index >= rd_index_);
-    PERFETTO_DCHECK(index <= wr_index_);
-    return &records_[index % kCapacity];
-  }
+namespace perfetto::base::flat_hash_map_v2_internal {

-  // Must be called on the same task runner passed to Enable()
-  static ReadIterator GetReadIterator() {
-    PERFETTO_DCHECK(RingBuffer::IsOnValidTaskRunner());
-    return ReadIterator(rd_index_.load(std::memory_order_acquire),
-                        wr_index_.load(std::memory_order_acquire));
-  }
+void FlatHashMapV2Base::Reset(size_t n,
+                              bool reallocate,
+                              size_t slot_size,
+                              size_t slot_align) {
+  // Must be a pow2.
+  PERFETTO_CHECK((n & (n - 1)) == 0);

-  static Record* AppendNewRecord();
-  static void Reset();
+  // Always ensure at least 128 capacity to avoid too frequent growths.
+  capacity_ = std::max<size_t>(n, 128u);
+  size_ = 0;
+  growth_info_.growth_left =
+      (capacity_ * static_cast<size_t>(load_limit_percent_)) / 100;
+  growth_info_.has_tombstones = 0;

-  static bool has_overruns() {
-    return has_overruns_.load(std::memory_order_acquire);
+  if (reallocate) {
+    // See memory layout comment above |storage_| in flat_hash_map.h.
+    size_t slots_offset = base::AlignUp(capacity_ + kNumClones, slot_align);
+    storage_.reset(new uint8_t[slots_offset + (capacity_ * slot_size)]);
+    ctrl_ = storage_.get();
+    slots_ = storage_.get() + slots_offset;
   }
-
-  // Can temporarily return a value >= kCapacity but is eventually consistent.
-  // This would happen in case of overruns until threads hit the --wr_index_
-  // in AppendNewRecord().
-  static uint64_t GetSizeForTesting() {
-    auto wr_index = wr_index_.load(std::memory_order_relaxed);
-    auto rd_index = rd_index_.load(std::memory_order_relaxed);
-    PERFETTO_DCHECK(wr_index >= rd_index);
-    return wr_index - rd_index;
+  if (ctrl_) {
+    // Initialize all control bytes (including clones) to empty (kFreeSlot)
+    memset(ctrl_, kFreeSlot, capacity_ + kNumClones);
   }
+}

- private:
-  friend class ReadIterator;
+size_t FlatHashMapV2Base::FindFirstEmptyOrTombstone(size_t key_hash) const {
+  return FindFirstEmptyOrTombstoneImpl(key_hash);
+}

-  // Returns true if the caller is on the task runner passed to Enable().
-  // Used only for DCHECKs.
-  static bool IsOnValidTaskRunner();
+FlatHashMapV2Base::OldTable FlatHashMapV2Base::Grow(size_t slot_size,
+                                                    size_t slot_align) {
+  PERFETTO_DCHECK(size_ <= capacity_);
+  OldTable old{std::move(storage_), ctrl_, slots_, capacity_, size_};
+  // This must be a CHECK (i.e. not just a DCHECK) to prevent UAF attacks on
+  // 32-bit archs that try to double the size of the table until wrapping.
+  size_t new_capacity = capacity_ * 2;
+  PERFETTO_CHECK(new_capacity >= capacity_);
+  Reset(new_capacity, true, slot_size, slot_align);
+  return old;
+}

-  static std::array<Record, kCapacity> records_;
-  static std::atomic<bool> read_task_queued_;
-  static std::atomic<uint64_t> wr_index_;
-  static std::atomic<uint64_t> rd_index_;
-  static std::atomic<bool> has_overruns_;
-  static Record bankruptcy_record_;  // Used in case of overruns.
-};
+}  // namespace perfetto::base::flat_hash_map_v2_internal
+// gen_amalgamated begin source: src/base/futex.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/futex.h
+/*
+ * Copyright (C) 2026 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.
+ */

-inline void TraceCounter(uint32_t tag, uint16_t id, int32_t value) {
-  // memory_order_relaxed is okay because the storage has static lifetime.
-  // It is safe to accidentally log an event soon after disabling.
-  auto enabled_tags = g_enabled_tags.load(std::memory_order_relaxed);
-  if (PERFETTO_LIKELY((enabled_tags & tag) == 0))
-    return;
-  Record* record = RingBuffer::AppendNewRecord();
-  record->thread_id = static_cast<uint32_t>(base::GetThreadId());
-  record->set_timestamp(TraceTimeNowNs());
-  record->counter_value = value;
-  record->type_and_id.store(Record::kTypeCounter | id,
-                            std::memory_order_release);
-}
+#ifndef INCLUDE_PERFETTO_EXT_BASE_FUTEX_H_
+#define INCLUDE_PERFETTO_EXT_BASE_FUTEX_H_

-class ScopedEvent {
- public:
-  ScopedEvent(uint32_t tag, uint16_t event_id) {
-    auto enabled_tags = g_enabled_tags.load(std::memory_order_relaxed);
-    if (PERFETTO_LIKELY((enabled_tags & tag) == 0))
-      return;
-    event_id_ = event_id;
-    record_ = RingBuffer::AppendNewRecord();
-    record_->thread_id = static_cast<uint32_t>(base::GetThreadId());
-    record_->set_timestamp(TraceTimeNowNs());
-  }
+#include <stdint.h>

-  ~ScopedEvent() {
-    if (PERFETTO_LIKELY(!record_))
-      return;
-    auto now = TraceTimeNowNs();
-    record_->duration_ns = static_cast<uint32_t>(now - record_->timestamp_ns());
-    record_->type_and_id.store(Record::kTypeEvent | event_id_,
-                               std::memory_order_release);
-  }
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

- private:
-  Record* record_ = nullptr;
-  uint16_t event_id_ = 0;
-  ScopedEvent(const ScopedEvent&) = delete;
-  ScopedEvent& operator=(const ScopedEvent&) = delete;
-};
+// Whether this build implements FutexWait() and FutexWake(). It does not check
+// runtime restrictions, such as a sandbox that blocks the system call.
+// TODO(rsavitski): Add support for other platforms.
+#define PERFETTO_HAS_FUTEX()                            \
+  (PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+   PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID))

-// Boilerplate to derive a unique variable name for the event.
-#define PERFETTO_METATRACE_UID2(a, b) a##b
-#define PERFETTO_METATRACE_UID(x) PERFETTO_METATRACE_UID2(metatrace_, x)
+namespace perfetto::base {

-#define PERFETTO_METATRACE_SCOPED(TAG, ID)                                \
-  ::perfetto::metatrace::ScopedEvent PERFETTO_METATRACE_UID(__COUNTER__)( \
-      ::perfetto::metatrace::TAG, ::perfetto::metatrace::ID)
+// Futex operations wait and wake threads on a 32-bit word in memory. A waiter
+// sleeps if the word matches an expected value. Another thread can update the
+// word and wake waiters to recheck their condition.
+//
+// These operations also work across processes when the word is in shared
+// memory. Each process uses its own address for the same backing storage.
+//
+// Neither operation modifies the word or provides C++ memory ordering. Callers
+// must use atomic accesses and the memory ordering required by their protocol.
+// To avoid lost wakeups, update the word before calling FutexWake().
+// Wakeups are not remembered for subsequent waits.
+//
+// Implemented on Linux and Android. Use PERFETTO_HAS_FUTEX() to select a
+// fallback on other platforms.
+
+enum class FutexWaitResult {
+  // The wait returned after a wake. The wake can be spurious, so the word can
+  // still hold the expected value.
+  kWoken,
+  // The word did not match the expected value when checked before sleeping.
+  kValueMismatch,
+  // The relative timeout expired.
+  kTimedOut,
+  // The wait was interrupted, for example by a signal. It was not retried.
+  kInterrupted,
+  // The operation failed, including on unsupported platforms. May indicate
+  // invalid arguments or runtime restrictions. Do not blindly retry.
+  kError,
+};

-#define PERFETTO_METATRACE_COUNTER(TAG, ID, VALUE)                \
-  ::perfetto::metatrace::TraceCounter(::perfetto::metatrace::TAG, \
-                                      ::perfetto::metatrace::ID,  \
-                                      static_cast<int32_t>(VALUE))
+// Makes one attempt to sleep if |word| contains |expected_value|. The value
+// check and the start of the wait are atomic with respect to FutexWake().
+// Returns kValueMismatch without sleeping if the value does not match.
+//
+// |timeout_ms| is a relative timeout in milliseconds. Zero causes no wait.
+// Scheduling can delay the return beyond the timeout.
+//
+// Recheck the caller's condition after every result other than kError,
+// including kWoken. Interruptions and spurious wakes are not retried. A retry
+// loop with an overall deadline must compute the remaining timeout each time.
+//
+// |word| requirements:
+// - It is 4-byte aligned and stays mapped until the call returns.
+// - Only the kernel reads it. This call makes no C++ load of it.
+// - Every write to it is one atomic store that covers all 32 bits. For
+//   example, a std::atomic<uint32_t> store, or a lock-free
+//   std::atomic<uint64_t> store that contains the word.
+//
+// Returns kError without sleeping on unsupported platforms.
+FutexWaitResult FutexWait(const uint32_t* word,
+                          uint32_t expected_value,
+                          uint32_t timeout_ms);
+
+// Wakes up to |max_waiters| threads waiting on |word|, including waiters in
+// other processes that share the same storage.
+// |max_waiters| must be positive. Use INT_MAX to wake all waiters.
+//
+// |word| has the same storage requirements as FutexWait().
+// Returns the number of waiters woken, or -1 on failure or unsupported
+// platforms.
+int FutexWake(const uint32_t* word, int max_waiters);

-}  // namespace metatrace
-}  // namespace perfetto
+}  // namespace perfetto::base

-#endif  // INCLUDE_PERFETTO_EXT_BASE_METATRACE_H_
+#endif  // INCLUDE_PERFETTO_EXT_BASE_FUTEX_H_
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -6611,136 +6960,93 @@ class ScopedEvent {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/metatrace.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/futex.h"

-// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
-// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
-// gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/thread_annotations.h"
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"

-namespace perfetto {
-namespace metatrace {
+#if PERFETTO_HAS_FUTEX()
+#include <errno.h>
+#include <linux/futex.h>
+#include <linux/types.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#endif

-std::atomic<uint32_t> g_enabled_tags{0};
-std::atomic<uint64_t> g_enabled_timestamp{0};
+namespace perfetto::base {

-// static members
-std::array<Record, RingBuffer::kCapacity> RingBuffer::records_;
-std::atomic<bool> RingBuffer::read_task_queued_;
-std::atomic<uint64_t> RingBuffer::wr_index_;
-std::atomic<uint64_t> RingBuffer::rd_index_;
-std::atomic<bool> RingBuffer::has_overruns_;
-Record RingBuffer::bankruptcy_record_;
+#if PERFETTO_HAS_FUTEX()

-namespace {
+// Use the shared FUTEX_WAIT/FUTEX_WAKE operations so different mappings of the
+// same shared-memory word refer to the same wait queue across processes.

-// std::function<> is not trivially de/constructible. This struct wraps it in a
-// heap-allocated struct to avoid static initializers.
-struct Delegate {
-  static Delegate* GetInstance() {
-    static Delegate* instance = new Delegate();
-    return instance;
+FutexWaitResult FutexWait(const uint32_t* word,
+                          uint32_t expected_value,
+                          uint32_t timeout_ms) {
+  // SYS_futex expects seconds and nanoseconds in two kernel-sized longs.
+  // Use that layout directly because libc's timespec can use a wider time_t.
+  // Even UINT32_MAX milliseconds fits in signed 32-bit seconds.
+  struct KernelTimespec {
+    __kernel_long_t tv_sec;
+    __kernel_long_t tv_nsec;
+  };
+  const KernelTimespec timeout{
+      static_cast<__kernel_long_t>(timeout_ms / 1000),
+      static_cast<__kernel_long_t>((timeout_ms % 1000) * 1000000)};
+  const int ret = static_cast<int>(syscall(
+      SYS_futex, word, FUTEX_WAIT, expected_value, &timeout, nullptr, 0));
+  if (ret == 0)
+    return FutexWaitResult::kWoken;
+
+  switch (errno) {
+    case EAGAIN:
+      return FutexWaitResult::kValueMismatch;
+    case ETIMEDOUT:
+      return FutexWaitResult::kTimedOut;
+    case EINTR:
+      return FutexWaitResult::kInterrupted;
+    default:
+      PERFETTO_DPLOG("futex FUTEX_WAIT failed");
+      return FutexWaitResult::kError;
   }
+}

-  base::TaskRunner* task_runner = nullptr;
-  std::function<void()> read_task;
-};
+int FutexWake(const uint32_t* word, int max_waiters) {
+  PERFETTO_DCHECK(max_waiters > 0);
+  const int ret = static_cast<int>(
+      syscall(SYS_futex, word, FUTEX_WAKE, max_waiters, nullptr, nullptr, 0));
+  if (PERFETTO_UNLIKELY(ret < 0)) {
+    PERFETTO_DPLOG("futex FUTEX_WAKE failed");
+    return -1;
+  }
+  return ret;
+}

-}  // namespace
+#else

-bool Enable(std::function<void()> read_task,
-            base::TaskRunner* task_runner,
-            uint32_t tags) {
-  PERFETTO_DCHECK(read_task);
-  PERFETTO_DCHECK(task_runner->RunsTasksOnCurrentThread());
-  if (g_enabled_tags.load(std::memory_order_acquire))
-    return false;
+FutexWaitResult FutexWait(const uint32_t* word,
+                          uint32_t expected_value,
+                          uint32_t timeout_ms) {
+  ignore_result(word);
+  ignore_result(expected_value);
+  ignore_result(timeout_ms);
+  return FutexWaitResult::kError;
+}

-  Delegate* dg = Delegate::GetInstance();
-  dg->task_runner = task_runner;
-  dg->read_task = std::move(read_task);
-  RingBuffer::Reset();
-  g_enabled_timestamp.store(TraceTimeNowNs(), std::memory_order_relaxed);
-  g_enabled_tags.store(tags, std::memory_order_release);
-  return true;
+int FutexWake(const uint32_t* word, int max_waiters) {
+  ignore_result(word);
+  ignore_result(max_waiters);
+  return -1;
 }

-void Disable() {
-  g_enabled_tags.store(0, std::memory_order_release);
-  Delegate* dg = Delegate::GetInstance();
-  PERFETTO_DCHECK(!dg->task_runner ||
-                  dg->task_runner->RunsTasksOnCurrentThread());
-  dg->task_runner = nullptr;
-  dg->read_task = nullptr;
-}
-
-// static
-void RingBuffer::Reset() {
-  bankruptcy_record_.clear();
-  for (Record& record : records_)
-    record.clear();
-  wr_index_ = 0;
-  rd_index_ = 0;
-  has_overruns_ = false;
-  read_task_queued_ = false;
-}
-
-// static
-Record* RingBuffer::AppendNewRecord() {
-  auto wr_index = wr_index_.fetch_add(1, std::memory_order_acq_rel);
-
-  // rd_index can only monotonically increase, we don't care if we read an
-  // older value, we'll just hit the slow-path a bit earlier if it happens.
-  auto rd_index = rd_index_.load(std::memory_order_relaxed);
-
-  PERFETTO_DCHECK(wr_index >= rd_index);
-  auto size = wr_index - rd_index;
-  if (PERFETTO_LIKELY(size < kCapacity / 2))
-    return At(wr_index);
-
-  // Slow-path: Enqueue the read task and handle overruns.
-  bool expected = false;
-  if (RingBuffer::read_task_queued_.compare_exchange_strong(expected, true)) {
-    Delegate* dg = Delegate::GetInstance();
-    if (dg->task_runner) {
-      dg->task_runner->PostTask([] {
-        // Meta-tracing might have been disabled in the meantime.
-        auto read_task = Delegate::GetInstance()->read_task;
-        if (read_task)
-          read_task();
-        RingBuffer::read_task_queued_ = false;
-      });
-    }
-  }
-
-  if (PERFETTO_LIKELY(size < kCapacity))
-    return At(wr_index);
-
-  has_overruns_.store(true, std::memory_order_release);
-  wr_index_.fetch_sub(1, std::memory_order_acq_rel);
-
-  // In the case of overflows, threads will race writing on the same memory
-  // location and TSan will rightly complain. This is fine though because nobody
-  // will read the bankruptcy record and it's designed to contain garbage.
-  PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(&bankruptcy_record_, sizeof(Record),
-                                      "nothing reads bankruptcy_record_")
-  return &bankruptcy_record_;
-}
-
-// static
-bool RingBuffer::IsOnValidTaskRunner() {
-  auto* task_runner = Delegate::GetInstance()->task_runner;
-  return task_runner && task_runner->RunsTasksOnCurrentThread();
-}
+#endif  // PERFETTO_HAS_FUTEX()

-}  // namespace metatrace
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/paged_memory.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/paged_memory.h
-// gen_amalgamated begin header: include/perfetto/ext/base/container_annotations.h
+}  // namespace perfetto::base
+// gen_amalgamated begin source: src/base/getopt_compat.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/getopt_compat.h
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -6755,51 +7061,63 @@ bool RingBuffer::IsOnValidTaskRunner() {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_CONTAINER_ANNOTATIONS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_CONTAINER_ANNOTATIONS_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_GETOPT_COMPAT_H_
+#define INCLUDE_PERFETTO_EXT_BASE_GETOPT_COMPAT_H_

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+#include <cstddef>  // For std::nullptr_t

-// Windows ASAN doesn't currently support these annotations.
-#if defined(ADDRESS_SANITIZER) && !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
-    !defined(ADDRESS_SANITIZER_WITHOUT_INSTRUMENTATION)
+// No translation units other than base/getopt.h and getopt_compat_unittest.cc
+// should directly include this file. Use base/getopt.h instead.

-#include <sanitizer/common_interface_defs.h>
+namespace perfetto {
+namespace base {
+namespace getopt_compat {

-#define ANNOTATE_NEW_BUFFER(buffer, capacity, new_size)                      \
-  if (buffer) {                                                              \
-    __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
-                                              (buffer) + (capacity),         \
-                                              (buffer) + (new_size));        \
-  }
-#define ANNOTATE_DELETE_BUFFER(buffer, capacity, old_size)                   \
-  if (buffer) {                                                              \
-    __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
-                                              (buffer) + (old_size),         \
-                                              (buffer) + (capacity));        \
-  }
-#define ANNOTATE_CHANGE_SIZE(buffer, capacity, old_size, new_size)           \
-  if (buffer) {                                                              \
-    __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
-                                              (buffer) + (old_size),         \
-                                              (buffer) + (new_size));        \
-  }
-#define ANNOTATE_CHANGE_CAPACITY(buffer, old_capacity, buffer_size, \
-                                 new_capacity)                      \
-  ANNOTATE_DELETE_BUFFER(buffer, old_capacity, buffer_size);        \
-  ANNOTATE_NEW_BUFFER(buffer, new_capacity, buffer_size);
-// Annotations require buffers to begin on an 8-byte boundary.
-#else  // defined(ADDRESS_SANITIZER)
-#define ANNOTATE_NEW_BUFFER(buffer, capacity, new_size)
-#define ANNOTATE_DELETE_BUFFER(buffer, capacity, old_size)
-#define ANNOTATE_CHANGE_SIZE(buffer, capacity, old_size, new_size)
-#define ANNOTATE_CHANGE_CAPACITY(buffer, old_capacity, buffer_size, \
-                                 new_capacity)
-#endif  // defined(ADDRESS_SANITIZER)
+// A tiny getopt() replacement for Windows, which doesn't have <getopt.h>.
+// This implementation is based on the subset of features that we use in the
+// Perfetto codebase. It doesn't even try to deal with the full surface of GNU's
+// getopt().
+// Limitations:
+// - getopt_long_only() is not supported.
+// - optional_argument is not supported. That is extremely subtle and caused us
+//   problems in the past with GNU's getopt.
+// - It does not reorder non-option arguments. It behaves like MacOS getopt, or
+//   GNU's when POSIXLY_CORRECT=1.
+// - Doesn't expose optopt or opterr.
+// - option.flag and longindex are not supported and must be nullptr.

-#endif  // INCLUDE_PERFETTO_EXT_BASE_CONTAINER_ANNOTATIONS_H_
+enum {
+  no_argument = 0,
+  required_argument = 1,
+};
+
+struct option {
+  const char* name;
+  int has_arg;
+  std::nullptr_t flag;  // Only nullptr is supported.
+  int val;
+};
+
+extern char* optarg;
+extern int optind;
+extern int optopt;
+extern int opterr;
+
+int getopt_long(int argc,
+                char** argv,
+                const char* shortopts,
+                const option* longopts,
+                std::nullptr_t /*longindex is not supported*/);
+
+int getopt(int argc, char** argv, const char* shortopts);
+
+}  // namespace getopt_compat
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_GETOPT_COMPAT_H_
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -6814,97 +7132,289 @@ bool RingBuffer::IsOnValidTaskRunner() {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_PAGED_MEMORY_H_
-#define INCLUDE_PERFETTO_EXT_BASE_PAGED_MEMORY_H_
+// gen_amalgamated expanded: #include "perfetto/ext/base/getopt_compat.h"

-#include <cstddef>
-#include <memory>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/container_annotations.h"
+#include <vector>

-// We need to track the committed size on windows and when ASAN is enabled.
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) || defined(ADDRESS_SANITIZER)
-#define TRACK_COMMITTED_SIZE() 1
-#else
-#define TRACK_COMMITTED_SIZE() 0
-#endif
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"

 namespace perfetto {
 namespace base {
+namespace getopt_compat {

-class PagedMemory {
- public:
-  // Initializes an invalid PagedMemory pointing to nullptr.
-  PagedMemory();
+char* optarg = nullptr;
+int optind = 0;
+int optopt = 0;
+int opterr = 1;

-  ~PagedMemory();
+namespace {

-  PagedMemory(PagedMemory&& other) noexcept;
-  PagedMemory& operator=(PagedMemory&& other);
+char* nextchar = nullptr;

-  enum AllocationFlags {
-    // By default, Allocate() crashes if the underlying mmap fails (e.g., if out
-    // of virtual address space). When this flag is provided, an invalid
-    // PagedMemory pointing to nullptr is returned in this case instead.
-    kMayFail = 1 << 0,
+const option* LookupLongOpt(const std::vector<option>& opts,
+                            const char* name,
+                            size_t len) {
+  for (const option& opt : opts) {
+    if (strncmp(opt.name, name, len) == 0 && strlen(opt.name) == len)
+      return &opt;
+  }
+  return nullptr;
+}

-    // By default, Allocate() commits the allocated memory immediately. When
-    // this flag is provided, the memory virtual address space may only be
-    // reserved and the user should call EnsureCommitted() before writing to
-    // memory addresses.
-    kDontCommit = 1 << 1,
-  };
+const option* LookupShortOpt(const std::vector<option>& opts, char c) {
+  for (const option& opt : opts) {
+    if (!*opt.name && opt.val == c)
+      return &opt;
+  }
+  return nullptr;
+}

-  // Allocates |size| bytes using mmap(MAP_ANONYMOUS). The returned memory is
-  // guaranteed to be page-aligned and guaranteed to be zeroed.
-  // For |flags|, see the AllocationFlags enum above.
-  static PagedMemory Allocate(size_t size, int flags = 0);
+// Returns true if |token| is an option-bearing argv element whose argument
+// is supplied by the next argv element (rather than embedded or absent).
+// Used by the GNU-style permutation logic in getopt_long().
+bool TokenConsumesNextArg(const char* token, const std::vector<option>& opts) {
+  if (token[0] != '-' || token[1] == '\0')
+    return false;
+  if (token[1] == '-') {
+    // Long option: "--name" needs a separate arg if it's required_argument
+    // and there is no embedded "=value".
+    if (token[2] == '\0')
+      return false;  // "--" alone.
+    if (strchr(token + 2, '=') != nullptr)
+      return false;
+    size_t len = strlen(token + 2);
+    const option* opt = LookupLongOpt(opts, token + 2, len);
+    return opt && opt->has_arg == required_argument;
+  }
+  // Short option chain: "-abc" / "-aXYZ". Walk until we find an option that
+  // requires an argument; if it is the last character of the token, the next
+  // argv element is the argument. Anything after it would be the embedded arg.
+  for (size_t i = 1; token[i] != '\0'; ++i) {
+    const option* opt = LookupShortOpt(opts, token[i]);
+    if (!opt)
+      return false;
+    if (opt->has_arg == required_argument)
+      return token[i + 1] == '\0';
+  }
+  return false;
+}

-  // Hint to the OS that the memory range is not needed and can be discarded.
-  // The memory remains accessible and its contents may be retained, or they
-  // may be zeroed. This function may be a NOP on some platforms. Returns true
-  // if implemented.
-  bool AdviseDontNeed(void* p, size_t size);
+bool ParseOpts(const char* shortopts,
+               const option* longopts,
+               std::vector<option>* res) {
+  // Parse long options first.
+  for (const option* lopt = longopts; lopt && lopt->name; lopt++) {
+    PERFETTO_CHECK(lopt->flag == nullptr);
+    PERFETTO_CHECK(lopt->has_arg == no_argument ||
+                   lopt->has_arg == required_argument);
+    res->emplace_back(*lopt);
+  }

-  // Ensures that at least the first |committed_size| bytes of the allocated
-  // memory region are committed. The implementation may commit memory in larger
-  // chunks above |committed_size|. Crashes if the memory couldn't be committed.
-#if TRACK_COMMITTED_SIZE()
-  void EnsureCommitted(size_t committed_size);
-#else   // TRACK_COMMITTED_SIZE()
-  void EnsureCommitted(size_t /*committed_size*/) {}
-#endif  // TRACK_COMMITTED_SIZE()
+  // Merge short options.
+  for (const char* sopt = shortopts; sopt && *sopt;) {
+    const size_t idx = static_cast<size_t>(sopt - shortopts);
+    char c = *sopt++;
+    bool valid = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+                 (c >= '0' && c <= '9');
+    if (!valid) {
+      fprintf(stderr,
+              "Error parsing shortopts. Unexpected char '%c' at offset %zu\n",
+              c, idx);
+      return false;
+    }
+    res->emplace_back();
+    option& opt = res->back();
+    opt.name = "";
+    opt.val = c;
+    opt.has_arg = no_argument;
+    if (*sopt == ':') {
+      opt.has_arg = required_argument;
+      ++sopt;
+    }
+  }
+  return true;
+}

-  inline void* Get() const noexcept { return p_; }
-  inline bool IsValid() const noexcept { return !!p_; }
-  inline size_t size() const noexcept { return size_; }
+}  // namespace

- private:
-  PagedMemory(char* p, size_t size);
+int getopt_long(int argc,
+                char** argv,
+                const char* shortopts,
+                const option* longopts,
+                std::nullptr_t /*longind*/) {
+  std::vector<option> opts;
+  optarg = nullptr;

-  PagedMemory(const PagedMemory&) = delete;
-  // Defaulted for implementation of move constructor + assignment.
-  PagedMemory& operator=(const PagedMemory&) = default;
+  if (optind == 0)
+    optind = 1;

-  char* p_ = nullptr;
+  if (optind >= argc)
+    return -1;

-  // The size originally passed to Allocate(). The actual virtual memory
-  // reservation will be larger due to: (i) guard pages; (ii) rounding up to
-  // the system page size.
-  size_t size_ = 0;
+  if (!ParseOpts(shortopts, longopts, &opts))
+    return '?';

-#if TRACK_COMMITTED_SIZE()
-  size_t committed_size_ = 0u;
-#endif  // TRACK_COMMITTED_SIZE()
-};
+  // GNU-style permutation: if we're at a non-option, move the next option
+  // (and its separate argument, if any) into position |optind|, shifting any
+  // intervening non-options to the right. This matches the default behavior
+  // of GNU getopt(3) so positional args can be freely interleaved with flags.
+  // We only permute when |nextchar| is null (i.e. we're not in the middle of
+  // a "-abc" short-option chain).
+  if (!nextchar) {
+    int scan = optind;
+    while (scan < argc) {
+      const char* s = argv[scan];
+      // Stop at the next option-bearing token ("-x", "--long", or "--").
+      if (s[0] == '-' && s[1] != '\0')
+        break;
+      ++scan;
+    }
+    // No permutation if there's nothing before the next option, or if the
+    // next token is "--" (the explicit end-of-options marker).
+    if (scan < argc && scan > optind && strcmp(argv[scan], "--") != 0) {
+      bool takes_next = TokenConsumesNextArg(argv[scan], opts);
+      int count = (takes_next && scan + 1 < argc) ? 2 : 1;
+      char* opt_token = argv[scan];
+      char* opt_arg = count == 2 ? argv[scan + 1] : nullptr;
+      // Shift argv[optind..scan) right by |count| to make room.
+      for (int k = scan - 1; k >= optind; --k)
+        argv[k + count] = argv[k];
+      argv[optind] = opt_token;
+      if (count == 2)
+        argv[optind + 1] = opt_arg;
+    }
+  }
+
+  char* arg = argv[optind];
+  optopt = 0;
+
+  if (!nextchar) {
+    // If |nextchar| is null we are NOT in the middle of a short option and we
+    // should parse the next argv.
+    if (strncmp(arg, "--", 2) == 0 && strlen(arg) > 2) {
+      // A --long option.
+      arg += 2;
+      char* sep = strchr(arg, '=');
+      optind++;
+
+      size_t len = sep ? static_cast<size_t>(sep - arg) : strlen(arg);
+      const option* opt = LookupLongOpt(opts, arg, len);
+
+      if (!opt) {
+        if (opterr)
+          fprintf(stderr, "unrecognized option '--%s'\n", arg);
+        return '?';
+      }
+
+      optopt = opt->val;
+      if (opt->has_arg == no_argument) {
+        if (sep) {
+          fprintf(stderr, "option '--%s' doesn't allow an argument\n", arg);
+          return '?';
+        } else {
+          return opt->val;
+        }
+      } else if (opt->has_arg == required_argument) {
+        if (sep) {
+          optarg = sep + 1;
+          return opt->val;
+        } else if (optind >= argc) {
+          if (opterr)
+            fprintf(stderr, "option '--%s' requires an argument\n", arg);
+          return '?';
+        } else {
+          optarg = argv[optind++];
+          return opt->val;
+        }
+      }
+      // has_arg must be either |no_argument| or |required_argument|. We
+      // shoulnd't get here unless the check in ParseOpts() has a bug.
+      PERFETTO_CHECK(false);
+    }  // if (arg ~= "--*").
+
+    if (strlen(arg) > 1 && arg[0] == '-' && arg[1] != '-') {
+      // A sequence of short options. Parsing logic continues below.
+      nextchar = &arg[1];
+    }
+  }  // if(!nextchar)
+
+  if (nextchar) {
+    // At this point either:
+    // 1. This is the first char of a sequence of short options, and we fell
+    //    through here from the lines above.
+    // 2. This is the N (>1) char of a sequence of short options, and we got
+    //    here from a new getopt() call to getopt().
+    const char cur_char = *nextchar;
+    PERFETTO_CHECK(cur_char != '\0');
+
+    // Advance the option char in any case, before we start reasoning on them.
+    // if we got to the end of the "-abc" sequence, increment optind so the next
+    // getopt() call resumes from the next argv argument.
+    if (*(++nextchar) == '\0') {
+      nextchar = nullptr;
+      ++optind;
+    }
+
+    const option* opt = LookupShortOpt(opts, cur_char);
+    optopt = cur_char;
+    if (!opt) {
+      if (opterr)
+        fprintf(stderr, "invalid option -- '%c'\n", cur_char);
+      return '?';
+    }
+    if (opt->has_arg == no_argument) {
+      return cur_char;
+    } else if (opt->has_arg == required_argument) {
+      // This is a subtle getopt behavior. Say you call `tar -fx`, there are
+      // two cases:
+      // 1. If 'f' is no_argument then 'x' (and anything else after) is
+      //    interpreted as an independent argument (like `tar -f -x`).
+      // 2. If 'f' is required_argument, than everything else after the 'f'
+      //    is interpreted as the option argument (like `tar -f x`)
+      if (!nextchar) {
+        // Case 1.
+        if (optind >= argc) {
+          if (opterr)
+            fprintf(stderr, "option requires an argument -- '%c'\n", cur_char);
+          return '?';
+        } else {
+          optarg = argv[optind++];
+          return cur_char;
+        }
+      } else {
+        // Case 2.
+        optarg = nextchar;
+        nextchar = nullptr;
+        optind++;
+        return cur_char;
+      }
+    }
+    PERFETTO_CHECK(false);
+  }  // if (nextchar)
+
+  // If we get here, we found the first non-option argument. Stop here.
+
+  if (strcmp(arg, "--") == 0)
+    optind++;
+
+  return -1;
+}
+
+int getopt(int argc, char** argv, const char* shortopts) {
+  return getopt_long(argc, argv, shortopts, nullptr, nullptr);
+}

+}  // namespace getopt_compat
 }  // namespace base
 }  // namespace perfetto
-
-#endif  // INCLUDE_PERFETTO_EXT_BASE_PAGED_MEMORY_H_
+// gen_amalgamated begin source: src/base/intrusive_list.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/intrusive_list.h
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -6919,257 +7429,140 @@ class PagedMemory {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/paged_memory.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_INTRUSIVE_LIST_H_
+#define INCLUDE_PERFETTO_EXT_BASE_INTRUSIVE_LIST_H_

-#include <algorithm>
-#include <cmath>
 #include <cstddef>
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <windows.h>
-#else  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <sys/mman.h>
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <cstdint>

 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/container_annotations.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
-
-namespace perfetto {
-namespace base {
-
-namespace {
-
-#if TRACK_COMMITTED_SIZE()
-constexpr size_t kCommitChunkSize = 4 * 1024 * 1024;  // 4MB
-#endif

-size_t RoundUpToSysPageSize(size_t req_size) {
-  const size_t page_size = GetSysPageSize();
-  return (req_size + page_size - 1) & ~(page_size - 1);
-}
-
-size_t GuardSize() {
-  return GetSysPageSize();
-}
-
-}  // namespace
-
-// static
-PagedMemory PagedMemory::Allocate(size_t req_size, int flags) {
-  size_t rounded_up_size = RoundUpToSysPageSize(req_size);
-  PERFETTO_CHECK(rounded_up_size >= req_size);
-  size_t outer_size = rounded_up_size + GuardSize() * 2;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  void* ptr = VirtualAlloc(nullptr, outer_size, MEM_RESERVE, PAGE_NOACCESS);
-  if (!ptr && (flags & kMayFail))
-    return PagedMemory();
-  PERFETTO_CHECK(ptr);
-  char* usable_region = reinterpret_cast<char*>(ptr) + GuardSize();
-#else   // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  void* ptr = mmap(nullptr, outer_size, PROT_READ | PROT_WRITE,
-                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-  if (ptr == MAP_FAILED && (flags & kMayFail))
-    return PagedMemory();
-  PERFETTO_CHECK(ptr && ptr != MAP_FAILED);
-  char* usable_region = reinterpret_cast<char*>(ptr) + GuardSize();
-  int res = mprotect(ptr, GuardSize(), PROT_NONE);
-  res |= mprotect(usable_region + rounded_up_size, GuardSize(), PROT_NONE);
-  PERFETTO_CHECK(res == 0);
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+// An intrusive (doubly linked) list implementation.
+// Unlike std::list<>, the entries being inserted into the list need to
+// explicitly declare an IntrusiveListNode structure (one for each list they are
+// part of). The user must specify a Traits struct for each list the entry is
+// part of. The traits struct defines how to get to the IntrusiveListNode from
+// the outer object.
+//
+// Usage example:
+// class Person {
+//  public:
+//   struct ListTraits {
+//     static constexpr size_t node_offset() { return offsetof(Person, node); }
+//   };
+//   std::string name;
+//   IntrusiveListNode node{};
+// }
+//
+// IntrusiveList<Person, Person::ListTraits> list;
+// Person person;
+// list.PushBack(person);
+// ...

-  auto memory = PagedMemory(usable_region, req_size);
-#if TRACK_COMMITTED_SIZE()
-  size_t initial_commit = req_size;
-  if (flags & kDontCommit)
-    initial_commit = std::min(initial_commit, kCommitChunkSize);
-  memory.EnsureCommitted(initial_commit);
-#endif  // TRACK_COMMITTED_SIZE()
-  return memory;
-}
+namespace perfetto::base {

-PagedMemory::PagedMemory() {}
+namespace internal {

-// clang-format off
-PagedMemory::PagedMemory(char* p, size_t size) : p_(p), size_(size) {
-  ANNOTATE_NEW_BUFFER(p_, size_, committed_size_)
-}
+struct ListNode {
+  ListNode* prev;
+  ListNode* next;
+};

-PagedMemory::PagedMemory(PagedMemory&& other) noexcept {
-  *this = other;
-  other.p_ = nullptr;
-}
-// clang-format on
+// IntrusiveList's Base class to factor out type-independent code (avoid binary
+// bloat)
+class ListOps {
+ public:
+  void PushFront(ListNode* node);
+  void PopFront();
+  void Erase(ListNode* node);

-PagedMemory& PagedMemory::operator=(PagedMemory&& other) {
-  this->~PagedMemory();
-  new (this) PagedMemory(std::move(other));
-  return *this;
-}
+  ListNode* front_{nullptr};
+  size_t size_{0};
+};

-PagedMemory::~PagedMemory() {
-  if (!p_)
-    return;
-  PERFETTO_CHECK(size_);
-  char* start = p_ - GuardSize();
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  BOOL res = VirtualFree(start, 0, MEM_RELEASE);
-  PERFETTO_CHECK(res != 0);
-#else   // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  const size_t outer_size = RoundUpToSysPageSize(size_) + GuardSize() * 2;
-  int res = munmap(start, outer_size);
-  PERFETTO_CHECK(res == 0);
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  ANNOTATE_DELETE_BUFFER(p_, size_, committed_size_)
-}
+}  // namespace internal

-bool PagedMemory::AdviseDontNeed(void* p, size_t size) {
-  PERFETTO_DCHECK(p_);
-  PERFETTO_DCHECK(p >= p_);
-  PERFETTO_DCHECK(static_cast<char*>(p) + size <= p_ + size_);
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) || PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
-  // Discarding pages on Windows has more CPU cost than is justified for the
-  // possible memory savings.
-  return false;
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_QNX)
-  int res = posix_madvise(p, size, POSIX_MADV_DISCARD_NP);
-  PERFETTO_DCHECK(res == 0);
-  return true;
-#else   // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) ||
-        // PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
-  // http://man7.org/linux/man-pages/man2/madvise.2.html
-  int res = madvise(p, size, MADV_DONTNEED);
-  PERFETTO_DCHECK(res == 0);
-  return true;
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) ||
-        // PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
-}
+using IntrusiveListNode = internal::ListNode;

-#if TRACK_COMMITTED_SIZE()
-void PagedMemory::EnsureCommitted(size_t committed_size) {
-  PERFETTO_DCHECK(committed_size <= size_);
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  if (committed_size_ >= committed_size)
-    return;
-  // Rounding up.
-  size_t delta = committed_size - committed_size_;
-  size_t num_additional_chunks =
-      (delta + kCommitChunkSize - 1) / kCommitChunkSize;
-  PERFETTO_DCHECK(num_additional_chunks * kCommitChunkSize >= delta);
-  // Don't commit more than the total size.
-  size_t commit_size = std::min(num_additional_chunks * kCommitChunkSize,
-                                size_ - committed_size_);
-  void* res = VirtualAlloc(p_ + committed_size_, commit_size, MEM_COMMIT,
-                           PAGE_READWRITE);
-  PERFETTO_CHECK(res);
-  ANNOTATE_CHANGE_SIZE(p_, size_, committed_size_,
-                       committed_size_ + commit_size)
-  committed_size_ += commit_size;
-#else   // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  // mmap commits automatically as needed, so we only track here for ASAN.
-  committed_size = std::max(committed_size_, committed_size);
-  ANNOTATE_CHANGE_SIZE(p_, size_, committed_size_, committed_size)
-  committed_size_ = committed_size;
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-}
-#endif  // TRACK_COMMITTED_SIZE()
+// T is the class that has one or more IntrusiveListNode as fields.
+// Traits defines getter and offset between node and T.
+// Traits is separate to allow the same T to be part of different lists (which
+// necessitate a different Traits, at very least for the offset).
+template <typename T, typename ListTraits>
+class IntrusiveList : private internal::ListOps {
+ public:
+  class Iterator {
+   public:
+    Iterator() = default;
+    explicit Iterator(IntrusiveListNode* node) : node_(node) {}
+    ~Iterator() = default;
+    Iterator(const Iterator&) = default;
+    Iterator& operator=(const Iterator&) = default;
+    Iterator(Iterator&&) noexcept = default;
+    Iterator& operator=(Iterator&&) noexcept = default;

-}  // namespace base
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/periodic_task.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/periodic_task.h
-/*
- * Copyright (C) 2021 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.
- */
+    bool operator==(const Iterator& other) const {
+      return node_ == other.node_;
+    }
+    bool operator!=(const Iterator& other) const { return !(*this == other); }
+    T* operator->() { return const_cast<T*>(entryof(node_)); }
+    T& operator*() {
+      PERFETTO_DCHECK(node_);
+      return *operator->();
+    }
+    explicit operator bool() const { return node_ != nullptr; }

-#ifndef INCLUDE_PERFETTO_EXT_BASE_PERIODIC_TASK_H_
-#define INCLUDE_PERFETTO_EXT_BASE_PERIODIC_TASK_H_
+    Iterator& operator++() {
+      PERFETTO_DCHECK(node_);
+      node_ = node_->next;
+      return *this;
+    }

-#include <functional>
+   private:
+    IntrusiveListNode* node_{nullptr};
+  };

-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/weak_ptr.h"
+  using value_type = T;
+  using const_pointer = const T*;

-namespace perfetto {
-namespace base {
+  void PushFront(T& entry) { internal::ListOps::PushFront(nodeof(&entry)); }

-class TaskRunner;
+  void PopFront() { internal::ListOps::PopFront(); }

-// A periodic task utility class. It wraps the logic necessary to do periodic
-// tasks using a TaskRunner, taking care of subtleties like ensuring that
-// outstanding tasks are cancelled after reset/dtor.
-// Tasks are aligned on wall time (unless they are |one_shot|). This is to
-// ensure that when using multiple periodic tasks, they happen at the same time,
-// minimizing context switches.
-// On Linux/Android it also supports suspend-aware mode (via timerfd). On other
-// operating systems it falls back to PostDelayedTask, which is not
-// suspend-aware.
-// TODO(primiano): this should probably become a periodic timer scheduler, so we
-// can use one FD for everything rather than one FD per task. For now we take
-// the hit of a FD-per-task to keep this low-risk.
-// TODO(primiano): consider renaming this class to TimerTask. When |one_shot|
-// is set, the "Periodic" part of the class name becomes a lie.
-class PeriodicTask {
- public:
-  explicit PeriodicTask(base::TaskRunner*);
-  ~PeriodicTask();  // Calls Reset().
+  T& front() {
+    PERFETTO_DCHECK(front_);
+    return const_cast<T&>(*entryof(front_));
+  }

-  struct Args {
-    uint32_t period_ms = 0;
-    std::function<void()> task = nullptr;
-    bool start_first_task_immediately = false;
-    bool use_suspend_aware_timer = false;
-    bool one_shot = false;
-  };
+  void Erase(T& entry) { internal::ListOps::Erase(nodeof(&entry)); }

-  void Start(Args);
+  bool empty() const { return size_ == 0; }

-  // Safe to be called multiple times, even without calling Start():
-  void Reset();
+  size_t size() const { return size_; }

-  // No copy or move. WeakPtr-wrapped pointers to |this| are posted on the
-  // task runner, this class is not easily movable.
-  PeriodicTask(const PeriodicTask&) = delete;
-  PeriodicTask& operator=(const PeriodicTask&) = delete;
-  PeriodicTask(PeriodicTask&&) = delete;
-  PeriodicTask& operator=(PeriodicTask&&) = delete;
+  Iterator begin() { return Iterator{front_}; }

-  base::PlatformHandle timer_fd_for_testing() { return *timer_fd_; }
+  Iterator end() { return Iterator{nullptr}; }

  private:
-  static void RunTaskAndPostNext(base::WeakPtr<PeriodicTask>,
-                                 uint32_t generation);
-  void PostNextTask();
-  void ResetTimerFd();
+  static constexpr size_t kNodeOffset = ListTraits::node_offset();

-  base::TaskRunner* const task_runner_;
-  Args args_;
-  uint32_t generation_ = 0;
-  base::ScopedPlatformHandle timer_fd_;
+  static constexpr IntrusiveListNode* nodeof(T* entry) {
+    return reinterpret_cast<IntrusiveListNode*>(
+        reinterpret_cast<uintptr_t>(entry) + kNodeOffset);
+  }

-  PERFETTO_THREAD_CHECKER(thread_checker_)
-  base::WeakPtrFactory<PeriodicTask> weak_ptr_factory_;  // Keep last.
+  static constexpr const T* entryof(IntrusiveListNode* node) {
+    return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(node) -
+                                kNodeOffset);
+  }
 };

-}  // namespace base
-}  // namespace perfetto
+}  // namespace perfetto::base

-#endif  // INCLUDE_PERFETTO_EXT_BASE_PERIODIC_TASK_H_
+#endif  // INCLUDE_PERFETTO_EXT_BASE_INTRUSIVE_LIST_H_
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -7184,180 +7577,61 @@ class PeriodicTask {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/periodic_task.h"
-
-#include <limits>
+// gen_amalgamated expanded: #include "perfetto/ext/base/intrusive_list.h"

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
-// gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) ||           \
-    (PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && __ANDROID_API__ >= 19)
-#include <sys/timerfd.h>
-#endif
-
-namespace perfetto {
-namespace base {
-
-namespace {
-
-uint32_t GetNextDelayMs(const TimeMillis& now_ms,
-                        const PeriodicTask::Args& args) {
-  if (args.one_shot)
-    return args.period_ms;

-  return args.period_ms -
-         static_cast<uint32_t>(now_ms.count() % args.period_ms);
-}
+namespace perfetto::base::internal {

-ScopedPlatformHandle CreateTimerFd(const PeriodicTask::Args& args) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) ||           \
-    (PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && __ANDROID_API__ >= 19)
-  ScopedPlatformHandle tfd(
-      timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK));
-  uint32_t phase_ms = GetNextDelayMs(GetBootTimeMs(), args);
+void ListOps::PushFront(IntrusiveListNode* node) {
+  node->prev = nullptr;
+  node->next = front_;

-  struct itimerspec its{};
-  // The "1 +" is to make sure that we never pass a zero it_value in the
-  // unlikely case of phase_ms being 0. That would cause the timer to be
-  // considered disarmed by timerfd_settime.
-  its.it_value.tv_sec = static_cast<time_t>(phase_ms / 1000u);
-  its.it_value.tv_nsec = 1 + static_cast<long>((phase_ms % 1000u) * 1000000u);
-  if (args.one_shot) {
-    its.it_interval.tv_sec = 0;
-    its.it_interval.tv_nsec = 0;
-  } else {
-    const uint32_t period_ms = args.period_ms;
-    its.it_interval.tv_sec = static_cast<time_t>(period_ms / 1000u);
-    its.it_interval.tv_nsec = static_cast<long>((period_ms % 1000u) * 1000000u);
+  if (front_) {
+    front_->prev = node;
   }
-  if (timerfd_settime(*tfd, 0, &its, nullptr) < 0)
-    return ScopedPlatformHandle();
-  return tfd;
-#else
-  ignore_result(args);
-  return ScopedPlatformHandle();
-#endif
-}
-
-}  // namespace
-
-PeriodicTask::PeriodicTask(TaskRunner* task_runner)
-    : task_runner_(task_runner), weak_ptr_factory_(this) {}

-PeriodicTask::~PeriodicTask() {
-  Reset();
+  front_ = node;
+  ++size_;
 }

-void PeriodicTask::Start(Args args) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  Reset();
-  if (args.period_ms == 0 || !args.task) {
-    PERFETTO_DCHECK(args.period_ms > 0);
-    PERFETTO_DCHECK(args.task);
-    return;
-  }
-  args_ = std::move(args);
-  if (args_.use_suspend_aware_timer) {
-    timer_fd_ = CreateTimerFd(args_);
-    if (timer_fd_) {
-      auto weak_this = weak_ptr_factory_.GetWeakPtr();
-      task_runner_->AddFileDescriptorWatch(
-          *timer_fd_,
-          std::bind(PeriodicTask::RunTaskAndPostNext, weak_this, generation_));
-    } else {
-      PERFETTO_DPLOG("timerfd not supported, falling back on PostDelayedTask");
-    }
-  }  // if (use_suspend_aware_timer).
+void ListOps::PopFront() {
+  PERFETTO_DCHECK(front_);
+  front_ = front_->next;

-  if (!timer_fd_)
-    PostNextTask();
+  if (front_) {
+    front_->prev = nullptr;
+  }

-  if (args_.start_first_task_immediately)
-    args_.task();
+  PERFETTO_DCHECK(size_ > 0);
+  --size_;
 }

-void PeriodicTask::PostNextTask() {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  PERFETTO_DCHECK(args_.period_ms > 0);
-  PERFETTO_DCHECK(!timer_fd_);
-  uint32_t delay_ms = GetNextDelayMs(GetWallTimeMs(), args_);
-  auto weak_this = weak_ptr_factory_.GetWeakPtr();
-  task_runner_->PostDelayedTask(
-      std::bind(PeriodicTask::RunTaskAndPostNext, weak_this, generation_),
-      delay_ms);
-}
+void ListOps::Erase(IntrusiveListNode* node) {
+  auto* prev = node->prev;
+  auto* next = node->next;

-// static
-// This function can be called in two ways (both from the TaskRunner):
-// 1. When using a timerfd, this task is registered as a FD watch.
-// 2. When using PostDelayedTask, this is the task posted on the TaskRunner.
-void PeriodicTask::RunTaskAndPostNext(WeakPtr<PeriodicTask> thiz,
-                                      uint32_t generation) {
-  if (!thiz || !thiz->args_.task || generation != thiz->generation_)
-    return;  // Destroyed or Reset() in the meanwhile.
-  PERFETTO_DCHECK_THREAD(thiz->thread_checker_);
-  if (thiz->timer_fd_) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    PERFETTO_FATAL("timerfd for periodic tasks unsupported on Windows");
-#else
-    // If we are using a timerfd there is no need to repeatedly call
-    // PostDelayedTask(). The kernel will wakeup the timer fd periodically. We
-    // just need to read() it.
-    uint64_t ignored = 0;
-    errno = 0;
-    auto rsize = Read(*thiz->timer_fd_, &ignored, sizeof(&ignored));
-    if (rsize != sizeof(uint64_t)) {
-      if (errno == EAGAIN)
-        return;  // A spurious wakeup. Rare, but can happen, just ignore.
-      PERFETTO_PLOG("read(timerfd) failed, falling back on PostDelayedTask");
-      thiz->ResetTimerFd();
-    }
-#endif
+  if (node == front_) {
+    front_ = next;
   }

-  // Create a copy of the task to deal with either:
-  // 1. one_shot causing a Reset().
-  // 2. task() invoking internally Reset().
-  // That would cause a reset of the args_.task itself, which would invalidate
-  // the task bind state while we are invoking it.
-  auto task = thiz->args_.task;
-
-  // The repetition of the if() is to deal with the ResetTimerFd() case above.
-  if (thiz->args_.one_shot) {
-    thiz->Reset();
-  } else if (!thiz->timer_fd_) {
-    thiz->PostNextTask();
+  if (prev) {
+    prev->next = next;
   }

-  task();
-}
-
-void PeriodicTask::Reset() {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  ++generation_;
-  args_ = Args();
-  PERFETTO_DCHECK(!args_.task);
-  ResetTimerFd();
-}
+  if (next) {
+    next->prev = prev;
+  }

-void PeriodicTask::ResetTimerFd() {
-  if (!timer_fd_)
-    return;
-  task_runner_->RemoveFileDescriptorWatch(*timer_fd_);
-  timer_fd_.reset();
+  PERFETTO_DCHECK(size_ > 0);
+  --size_;
 }

-}  // namespace base
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/pipe.cc
+}  // namespace perfetto::base::internal
+// gen_amalgamated begin source: src/base/intrusive_tree.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/intrusive_tree.h
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -7372,275 +7646,241 @@ void PeriodicTask::ResetTimerFd() {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/pipe.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_INTRUSIVE_TREE_H_
+#define INCLUDE_PERFETTO_EXT_BASE_INTRUSIVE_TREE_H_

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+#include <cstddef>
+#include <cstdint>
+#include <functional>

-#include <fcntl.h>  // For O_BINARY (Windows) and F_SETxx (UNIX)
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <windows.h>
+// An intrusive tree implementation, inspired from BSD kernel's tree.h
+// Unlike std::set<>, the nodes being inserted into the tree need to explicitly
+// declare a RBNode structure (one for each tree they are part of).
+// The user must specify a TreeTraits for each tree the struct is part of.
+// The traits struct defines the type of the key and how to get to the node
+// entry from the outer object.
+// Usage example:
+// class Person {
+//  public:
+//   struct Traits {
+//     using KeyType = std::string;
+//     static const KeyType& GetKey(const Person& p) { return p->unique_id; }
+//     static constexpr size_t NodeOffset() { return offsetof(Person, node); }
+//   };
+//   std::string unique_id;
+//   std::string name;
+//   std::string surname;
+//   IntrusiveTreeNode node{};
+// }
+//  IntrusiveTree<Person, Person::Traits> tree;
+//  tree.insert(&person1);
+//  ...

-#include <namedpipeapi.h>
-#else
-#include <sys/types.h>
-#include <unistd.h>
-#endif
+namespace perfetto::base {

-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+namespace internal {

-namespace perfetto {
-namespace base {
+enum RBColor : uint8_t {
+  BLACK = 0,
+  RED = 1,
+};

-Pipe::Pipe() = default;
-Pipe::Pipe(Pipe&&) noexcept = default;
-Pipe& Pipe::operator=(Pipe&&) = default;
+struct RBNode {
+  RBNode* left = nullptr;
+  RBNode* right = nullptr;
+  RBNode* parent = nullptr;
+  RBColor color = RBColor::BLACK;
+};

-Pipe Pipe::Create(Flags flags) {
-  PlatformHandle fds[2];
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  PERFETTO_CHECK(::CreatePipe(&fds[0], &fds[1], /*lpPipeAttributes=*/nullptr,
-                              0 /*default size*/));
-#else
-  PERFETTO_CHECK(pipe(fds) == 0);
-  PERFETTO_CHECK(fcntl(fds[0], F_SETFD, FD_CLOEXEC) == 0);
-  PERFETTO_CHECK(fcntl(fds[1], F_SETFD, FD_CLOEXEC) == 0);
-#endif
-  Pipe p;
-  p.rd.reset(fds[0]);
-  p.wr.reset(fds[1]);
+void RBInsertColor(RBNode** root, RBNode* elm);
+void RBRemove(RBNode** root, RBNode* elm);

-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  if (flags == kBothNonBlock || flags == kRdNonBlock) {
-    int cur_flags = fcntl(*p.rd, F_GETFL, 0);
-    PERFETTO_CHECK(cur_flags >= 0);
-    PERFETTO_CHECK(fcntl(*p.rd, F_SETFL, cur_flags | O_NONBLOCK) == 0);
-  }
+// Returns nullptr after reaching the last leaf (the max element).
+const RBNode* RBNext(const RBNode* node);

-  if (flags == kBothNonBlock || flags == kWrNonBlock) {
-    int cur_flags = fcntl(*p.wr, F_GETFL, 0);
-    PERFETTO_CHECK(cur_flags >= 0);
-    PERFETTO_CHECK(fcntl(*p.wr, F_SETFL, cur_flags | O_NONBLOCK) == 0);
-  }
-#else
-  PERFETTO_CHECK(flags == kBothBlock);
-#endif
-  return p;
+// KeyCompare tries first to use the CompareKey function in Traits, if present.
+// The signature of that function is int(const KeyType&, const KeyType&).
+// If the comparator function doesn't exist, falls backk on std::less<KeyType>.
+template <
+    class Traits,
+    class = std::enable_if_t<std::is_function_v<typename Traits::CompareKey>,
+                             void> >
+int KeyCompare(const typename Traits::KeyType& k1,
+               const typename Traits::KeyType& k2) {
+  return Traits::CompareKey(k1, k2);
 }

-}  // namespace base
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/rt_mutex.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/rt_mutex.h
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef INCLUDE_PERFETTO_EXT_BASE_RT_MUTEX_H_
-#define INCLUDE_PERFETTO_EXT_BASE_RT_MUTEX_H_
-
-// This header introduces the RtMutex class for priority-inheritance mutexes.
-// RtMutex is NOT a blanket replacement for std::mutex and should be used only
-// in cases where we know we are expect to be used on a RT thread.
-// In the contended case RtMutex is generally slower than a std::mutex (or any
-// non-RT implementation).
-// Under the hoods this class does the following:
-// - Android: it uses PI futexes.
-// - Linux/MacOS/iOS: it uses pthread_mutex with PTHREAD_PRIO_INHERIT.
-// - Other platforms: falls back on a standard std::mutex. On Windows 11+
-//   std::mutex has effectively PI semantics due to AutoBoost
-//   https://github.com/MicrosoftDocs/win32/commit/a43cb3b5039c5cfc53642bfcea174003a2f1168f
-
-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/base/thread_annotations.h"
-// gen_amalgamated expanded: #include "perfetto/public/compiler.h"
-
-#define _PERFETTO_MUTEX_MODE_STD 0
-#define _PERFETTO_MUTEX_MODE_RT_FUTEX 1
-#define _PERFETTO_MUTEX_MODE_RT_MUTEX 2
-
-// The logic below determines which mutex implementation to use.
-// For Android platform builds, the choice is controlled by aconfig flags.
-// For other builds, it's determined by OS support and GN build arguments.
-//
-// Rationale for platform-specific choices:
-// 1. `RtFutex` is enabled only on Android because it relies on `gettid()` being
-//    a cheap thread-local storage access provided by Bionic. On Linux with
-//    glibc, `gettid()` is a full syscall, making the pthread-based
-//    implementation faster.
-// 2. The pthread-based `RtPosixMutex` is not viable on all Android versions, as
-//    `pthread_mutexattr_setprotocol` was introduced in API level 28. Using
-//    `dlsym` to backport it can lead to deadlocks with the loader lock if
-//    tracing is initialized from a static constructor (see b/443178555).
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
-    PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
-#define _PERFETTO_MUTEX_MODE _PERFETTO_MUTEX_MODE_RT_FUTEX
-#elif PERFETTO_BUILDFLAG(PERFETTO_ENABLE_RT_MUTEX)
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define _PERFETTO_MUTEX_MODE _PERFETTO_MUTEX_MODE_RT_FUTEX
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-#define _PERFETTO_MUTEX_MODE _PERFETTO_MUTEX_MODE_RT_MUTEX
-#endif
-#endif
-
-// If no RT implementation was selected, default to std::mutex.
-#ifndef _PERFETTO_MUTEX_MODE
-#define _PERFETTO_MUTEX_MODE _PERFETTO_MUTEX_MODE_STD
-#endif
-
-// Public macros for conditional compilation based on the selected mutex type.
-#define PERFETTO_HAS_POSIX_RT_MUTEX() \
-  (_PERFETTO_MUTEX_MODE == _PERFETTO_MUTEX_MODE_RT_MUTEX)
-#define PERFETTO_HAS_RT_FUTEX() \
-  (_PERFETTO_MUTEX_MODE == _PERFETTO_MUTEX_MODE_RT_FUTEX)
+// SFINAE fallback on std::less<KeyType>
+template <class Traits>
+int KeyCompare(const typename Traits::KeyType& k1,
+               const typename Traits::KeyType& k2) {
+  std::less<typename Traits::KeyType> less_cmp;
+  return less_cmp(k1, k2) ? -1 : (less_cmp(k2, k1) ? 1 : 0);
+}

-#include <atomic>
-#include <mutex>
-#include <type_traits>
+}  // namespace internal

-#if PERFETTO_HAS_POSIX_RT_MUTEX()
-#include <pthread.h>
-#endif
+using IntrusiveTreeNode = internal::RBNode;

-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <unistd.h>  // For gettid().
-#endif
+// T is the class that has one or more IntrusiveTreeNode as fiels.
+// Traits defines the key type, getter and offset between node and T.
+// Traits is separate to allow the same T to be part of different trees (which
+// necessitate a different Traits, at very least for the offset).
+template <typename T, typename Traits>
+class IntrusiveTree {
+ public:
+  using Key = typename Traits::KeyType;

-namespace perfetto::base {
+  class Iterator {
+   public:
+    Iterator() = default;
+    explicit Iterator(const internal::RBNode* node) : node_(node) {}
+    ~Iterator() = default;
+    Iterator(const Iterator&) = default;
+    Iterator& operator=(const Iterator&) = default;
+    Iterator(Iterator&&) noexcept = default;
+    Iterator& operator=(Iterator&&) noexcept = default;

-namespace internal {
+    bool operator==(const Iterator& o) const { return node_ == o.node_; }
+    bool operator!=(const Iterator& o) const { return !(*this == o); }
+    const T* operator->() const { return entryof(node_); }
+    const T& operator*() const {
+      PERFETTO_DCHECK(node_ != nullptr);
+      return *operator->();
+    }
+    T* operator->() { return const_cast<T*>(entryof(node_)); }
+    T& operator*() {
+      PERFETTO_DCHECK(node_ != nullptr);
+      return *operator->();
+    }
+    explicit operator bool() const { return node_ != nullptr; }

-#if PERFETTO_HAS_RT_FUTEX()
-// A wrapper around PI Futexes. A futex is a wrapper around an atomic integer
-// with an ABI shared with the kernel to handle the slowpath in the cases when
-// the mutex is held, or we find out that there are waiters queued when we
-// unlock. The operating principle is the following:
-// - In the no-contention case, a futex boils down to an atomic
-//   compare-and-exchange, without involving the kernel.
-// - If a lock is contented at acquire time, we have to enter the kernel to
-//   suspend our execution and join a wait chain.
-// - It could still happen that we acquire the mutex via the fastpath (without
-//   involving the kernel) but other waiters might queue up while we hold the
-//   mutex. In that case the kernel will add a bit to the atomic int. That bit
-//   will cause the unlock() compare-and-exchange to fail (because it no longer
-//   matches our tid) which in turn will signal us to do a syscall to notify the
-//   waiters.
-class PERFETTO_LOCKABLE RtFutex {
- public:
-  RtFutex() { PERFETTO_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); }
-  ~RtFutex() { PERFETTO_TSAN_MUTEX_DESTROY(this, __tsan_mutex_not_static); }
+    Iterator& operator++() {
+      node_ = internal::RBNext(node_);
+      return *this;
+    }

-  // Disable copy or move. Copy doesn't make sense. Move isn't feasible because
-  // the pointer to the atomic integer is the handle used by the kernel to setup
-  // the wait chain. A movable futex would require the atomic integer to be heap
-  // allocated, but that would create an indirection layer that is not needed in
-  // most cases. If you really need a movable RtMutex, wrap it in a unique_ptr.
-  RtFutex(const RtFutex&) = delete;
-  RtFutex& operator=(const RtFutex&) = delete;
-  RtFutex(RtFutex&&) = delete;
-  RtFutex& operator=(RtFutex&&) = delete;
+   private:
+    const internal::RBNode* node_ = nullptr;
+  };  // Iterator

-  inline bool TryLockFastpath() noexcept {
-    int expected = 0;
-    return lock_.compare_exchange_strong(expected, ::gettid(),
-                                         std::memory_order_acquire,
-                                         std::memory_order_relaxed);
-  }
+  using value_type = T;
+  using const_pointer = const T*;
+  using const_iterator = Iterator;

-  bool try_lock() noexcept PERFETTO_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
-    PERFETTO_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock);
-    if (PERFETTO_LIKELY(TryLockFastpath()) || TryLockSlowpath()) {
-      PERFETTO_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_try_lock, 0);
-      return true;
+  std::pair<Iterator, bool> Insert(T& entry) {
+    // The insertion preamble is inlined because it's few instructions and
+    // out-lining it would require std::function indirections for getting the
+    // key and the comparator.
+    int comp = 0;
+    internal::RBNode* tmp = root_;
+    internal::RBNode* parent = nullptr;
+    internal::RBNode* const entry_node = nodeof(&entry);
+    while (tmp) {
+      parent = tmp;
+      comp = key_compare(entry_node, parent);
+      if (comp < 0) {
+        tmp = tmp->left;
+      } else if (comp > 0) {
+        tmp = tmp->right;
+      } else {
+        return {Iterator(tmp), false};  // The key exists already.
+      }
+    }  // while(tmp)
+    entry_node->left = entry_node->right = nullptr;
+    entry_node->parent = parent;
+    entry_node->color = internal::RBColor::RED;
+    if (parent) {
+      if (comp < 0) {
+        PERFETTO_DCHECK(parent->left == nullptr);
+        parent->left = entry_node;
+      } else {
+        PERFETTO_DCHECK(parent->right == nullptr);
+        parent->right = entry_node;
+      }
+    } else {
+      root_ = entry_node;
     }
-    PERFETTO_TSAN_MUTEX_POST_LOCK(
-        this, __tsan_mutex_try_lock | __tsan_mutex_try_lock_failed, 0);
-    return false;
+    internal::RBInsertColor(&root_, entry_node);
+    ++size_;
+    return {Iterator(entry_node), true};
   }

-  void lock() PERFETTO_EXCLUSIVE_LOCK_FUNCTION() {
-    PERFETTO_TSAN_MUTEX_PRE_LOCK(this, 0);
-    if (!PERFETTO_LIKELY(TryLockFastpath())) {
-      LockSlowpath();
+  Iterator Find(const Key& key) const {
+    internal::RBNode* tmp = root_;
+    while (tmp) {
+      int comp =
+          internal::KeyCompare<Traits>(key, Traits::GetKey(*entryof(tmp)));
+      if (comp < 0) {
+        tmp = tmp->left;
+      } else if (comp > 0) {
+        tmp = tmp->right;
+      } else {
+        return Iterator(tmp);
+      }
     }
-    PERFETTO_TSAN_MUTEX_POST_LOCK(this, 0, 0);
+    return Iterator(nullptr);
   }

-  void unlock() noexcept PERFETTO_UNLOCK_FUNCTION() {
-    PERFETTO_TSAN_MUTEX_PRE_UNLOCK(this, 0);
-    int expected = ::gettid();
-    // If the current value is our tid, we can unlock without a syscall since
-    // there are no current waiters.
-    if (!PERFETTO_LIKELY(lock_.compare_exchange_strong(
-            expected, 0, std::memory_order_release,
-            std::memory_order_relaxed))) {
-      // The tid doesn't match because the kernel appended the FUTEX_WAITERS
-      // bit. There are waiters, tell the kernel to notify them and unlock.
-      UnlockSlowpath();
-    }
-    PERFETTO_TSAN_MUTEX_POST_UNLOCK(this, 0);
+  bool Remove(const Key& key) {
+    Iterator it = Find(key);
+    if (!it)
+      return false;
+    internal::RBRemove(&root_, nodeof(std::addressof(*it)));
+    --size_;
+    return true;
   }

- private:
-  std::atomic<int> lock_{};
-
-  void LockSlowpath();
-  bool TryLockSlowpath();
-  void UnlockSlowpath();
-};
+  Iterator Remove(T& entry) { return Remove(Iterator(nodeof(&entry))); }

-#endif  // PERFETTO_HAS_RT_FUTEX
+  Iterator Remove(Iterator it) {
+    Iterator next = it;
+    ++next;
+    internal::RBRemove(&root_, nodeof(std::addressof(*it)));
+    --size_;
+    return next;
+  }

-#if PERFETTO_HAS_POSIX_RT_MUTEX()
-class PERFETTO_LOCKABLE RtPosixMutex {
- public:
-  RtPosixMutex() noexcept;
-  ~RtPosixMutex() noexcept;
+  size_t Size() const { return size_; }

-  RtPosixMutex(const RtPosixMutex&) = delete;
-  RtPosixMutex& operator=(const RtPosixMutex&) = delete;
-  RtPosixMutex(RtPosixMutex&&) = delete;
-  RtPosixMutex& operator=(RtPosixMutex&&) = delete;
+  Iterator begin() const {
+    const internal::RBNode* node = root_;
+    while (node && node->left)
+      node = node->left;
+    return Iterator(node);
+  }

-  bool try_lock() noexcept PERFETTO_EXCLUSIVE_TRYLOCK_FUNCTION(true);
-  void lock() noexcept PERFETTO_EXCLUSIVE_LOCK_FUNCTION();
-  void unlock() noexcept PERFETTO_UNLOCK_FUNCTION();
+  Iterator end() const { return Iterator(nullptr); }

  private:
-  pthread_mutex_t mutex_;
-};
-
-#endif  // PERFETTO_HAS_POSIX_RT_MUTEX
-}  // namespace internal
+  static constexpr size_t off_ = Traits::NodeOffset();
+  static internal::RBNode* nodeof(T* t) {
+    PERFETTO_DCHECK(t != nullptr);
+    return reinterpret_cast<internal::RBNode*>(reinterpret_cast<uintptr_t>(t) +
+                                               off_);
+  }
+  static const T* entryof(const internal::RBNode* n) {
+    PERFETTO_DCHECK(n != nullptr);
+    return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(n) - off_);
+  }
+  static int key_compare(const internal::RBNode* node_a,
+                         const internal::RBNode* node_b) {
+    auto* a = entryof(node_a);
+    auto* b = entryof(node_b);
+    return internal::KeyCompare<Traits>(Traits::GetKey(*a), Traits::GetKey(*b));
+  }

-// Select the best real-time mutex implementation for the target platform, or
-// fall back to std::mutex if none is available.
-#if PERFETTO_HAS_RT_FUTEX()
-using MaybeRtMutex = internal::RtFutex;
-#elif PERFETTO_HAS_POSIX_RT_MUTEX()
-using MaybeRtMutex = internal::RtPosixMutex;
-#else
-using MaybeRtMutex = std::mutex;
-#endif
+  internal::RBNode* root_ = nullptr;
+  size_t size_ = 0;
+};

 }  // namespace perfetto::base

-#endif  // INCLUDE_PERFETTO_EXT_BASE_RT_MUTEX_H_
+#endif  // INCLUDE_PERFETTO_EXT_BASE_INTRUSIVE_TREE_H_
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -7654,95 +7894,290 @@ using MaybeRtMutex = std::mutex;
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+/* Part of this work is inspired by the original OpenBSD's tree.h */
+/* $OpenBSD: tree.h,v 1.31 2023/03/08 04:43:09 guenther Exp $ */
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */

-// gen_amalgamated expanded: #include "perfetto/ext/base/rt_mutex.h"
-
-#include <errno.h>
-
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
-
-#if PERFETTO_HAS_RT_FUTEX()
-#include <linux/futex.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-#endif
-
-namespace perfetto::base {
-
-namespace internal {
-
-#if PERFETTO_HAS_RT_FUTEX()
+// gen_amalgamated expanded: #include "perfetto/ext/base/intrusive_tree.h"

-void RtFutex::LockSlowpath() {
-  auto res = PERFETTO_EINTR(
-      syscall(SYS_futex, &lock_, FUTEX_LOCK_PI_PRIVATE, 0, nullptr));
-  PERFETTO_CHECK(res == 0);
-}
+namespace perfetto::base::internal {

-bool RtFutex::TryLockSlowpath() {
-  auto res = PERFETTO_EINTR(
-      syscall(SYS_futex, &lock_, FUTEX_TRYLOCK_PI_PRIVATE, 0, nullptr));
-  if (res == 0)
-    return true;
-  if (errno == EBUSY || errno == EDEADLK)
-    return false;
-  PERFETTO_FATAL("FUTEX_TRYLOCK_PI_PRIVATE failed");
-}
+namespace {

-void RtFutex::UnlockSlowpath() {
-  auto res = PERFETTO_EINTR(
-      syscall(SYS_futex, &lock_, FUTEX_UNLOCK_PI_PRIVATE, 0, nullptr));
-  PERFETTO_CHECK(res == 0);
+void RBSetBlackRed(RBNode* black, RBNode* red) {
+  black->color = RBColor::BLACK;
+  red->color = RBColor::RED;
 }

-#endif  // PERFETTO_HAS_RT_FUTEX
-
-#if PERFETTO_HAS_POSIX_RT_MUTEX()
-
-RtPosixMutex::RtPosixMutex() noexcept {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && __ANDROID_API__ < 28
-  // pthread_mutexattr_setprotocol is only available on API 28.
-#error \
-    "Priority-inheritance RtMutex is not available in this version of Android."
-#endif
-  pthread_mutexattr_t at{};
-  PERFETTO_CHECK(pthread_mutexattr_init(&at) == 0);
-  PERFETTO_CHECK(pthread_mutexattr_setprotocol(&at, PTHREAD_PRIO_INHERIT) == 0);
-  PERFETTO_CHECK(pthread_mutex_init(&mutex_, &at) == 0);
+void RBRotateLeft(RBNode** root, RBNode* elm, RBNode* tmp) {
+  tmp = elm->right;
+  if ((elm->right = tmp->left)) {
+    tmp->left->parent = elm;
+  }
+  // RB_AUGMENT(elm);
+  if ((tmp->parent = elm->parent)) {
+    if (elm == elm->parent->left)
+      elm->parent->left = tmp;
+    else
+      elm->parent->right = tmp;
+  } else
+    *root = tmp;
+  tmp->left = elm;
+  elm->parent = tmp;
+  // RB_AUGMENT(tmp);
+  // if ((tmp->parent))
+  // RB_AUGMENT(tmp->parent);
 }

-RtPosixMutex::~RtPosixMutex() noexcept {
-  pthread_mutex_destroy(&mutex_);
+void RBRotateRight(RBNode** root, RBNode* elm, RBNode* tmp) {
+  tmp = elm->left;
+  if ((elm->left = tmp->right)) {
+    tmp->right->parent = elm;
+  }
+  // RB_AUGMENT(elm);
+  if ((tmp->parent = elm->parent)) {
+    if (elm == elm->parent->left)
+      elm->parent->left = tmp;
+    else
+      elm->parent->right = tmp;
+  } else
+    *root = tmp;
+  tmp->right = elm;
+  elm->parent = tmp;
+  // RB_AUGMENT(tmp);
+  // if ((tmp->parent))
+  // RB_AUGMENT(tmp->parent);
 }

-bool RtPosixMutex::try_lock() noexcept {
-  int res = pthread_mutex_trylock(&mutex_);
-  if (res == 0)
-    return true;
-  // NOTE: Unlike most Linux APIs, pthread_mutex_trylock "returns" the error
-  // code, it does NOT use errno.
-  if (res == EBUSY)
-    return false;
-  PERFETTO_FATAL("pthread_mutex_trylock() failed");
+void RBRemoveColor(RBNode** root, RBNode* parent, RBNode* elm) {
+  RBNode* tmp;
+  while ((elm == nullptr || elm->color == RBColor::BLACK) && elm != *root) {
+    if (parent->left == elm) {
+      tmp = parent->right;
+      if (tmp->color == RBColor::RED) {
+        RBSetBlackRed(tmp, parent);
+        RBRotateLeft(root, parent, tmp);
+        tmp = parent->right;
+      }
+      if ((tmp->left == nullptr || tmp->left->color == RBColor::BLACK) &&
+          (tmp->right == nullptr || tmp->right->color == RBColor::BLACK)) {
+        tmp->color = RBColor::RED;
+        elm = parent;
+        parent = elm->parent;
+      } else {
+        if (tmp->right == nullptr || tmp->right->color == RBColor::BLACK) {
+          RBNode* oleft;
+          if ((oleft = tmp->left))
+            oleft->color = RBColor::BLACK;
+          tmp->color = RBColor::RED;
+          RBRotateRight(root, tmp, oleft);
+          tmp = parent->right;
+        }
+        tmp->color = parent->color;
+        parent->color = RBColor::BLACK;
+        if (tmp->right)
+          tmp->right->color = RBColor::BLACK;
+        RBRotateLeft(root, parent, tmp);
+        elm = *root;
+        break;
+      }
+    } else {
+      tmp = parent->left;
+      if (tmp->color == RBColor::RED) {
+        RBSetBlackRed(tmp, parent);
+        RBRotateRight(root, parent, tmp);
+        tmp = parent->left;
+      }
+      if ((tmp->left == nullptr || tmp->left->color == RBColor::BLACK) &&
+          (tmp->right == nullptr || tmp->right->color == RBColor::BLACK)) {
+        tmp->color = RBColor::RED;
+        elm = parent;
+        parent = elm->parent;
+      } else {
+        if (tmp->left == nullptr || tmp->left->color == RBColor::BLACK) {
+          RBNode* oright;
+          if ((oright = tmp->right))
+            oright->color = RBColor::BLACK;
+          tmp->color = RBColor::RED;
+          RBRotateLeft(root, tmp, oright);
+          tmp = parent->left;
+        }
+        tmp->color = parent->color;
+        parent->color = RBColor::BLACK;
+        if (tmp->left)
+          tmp->left->color = RBColor::BLACK;
+        RBRotateRight(root, parent, tmp);
+        elm = *root;
+        break;
+      }
+    }
+  }
+  if (elm)
+    elm->color = RBColor::BLACK;
 }

-void RtPosixMutex::lock() noexcept {
-  PERFETTO_CHECK(pthread_mutex_lock(&mutex_) == 0);
+}  // namespace
+
+void RBInsertColor(RBNode** root, RBNode* elm) {
+  RBNode *parent, *gparent, *tmp;
+  while ((parent = elm->parent) && parent->color == RBColor::RED) {
+    gparent = parent->parent;
+    if (parent == gparent->left) {
+      tmp = gparent->right;
+      if (tmp && tmp->color == RBColor::RED) {
+        tmp->color = RBColor::BLACK;
+        RBSetBlackRed(parent, gparent);
+        elm = gparent;
+        continue;
+      }
+      if (parent->right == elm) {
+        RBRotateLeft(root, parent, tmp);
+        tmp = parent;
+        parent = elm;
+        elm = tmp;
+      }
+      RBSetBlackRed(parent, gparent);
+      RBRotateRight(root, gparent, tmp);
+    } else {
+      tmp = gparent->left;
+      if (tmp && tmp->color == RBColor::RED) {
+        tmp->color = RBColor::BLACK;
+        RBSetBlackRed(parent, gparent);
+        elm = gparent;
+        continue;
+      }
+      if (parent->left == elm) {
+        RBRotateRight(root, parent, tmp);
+        tmp = parent;
+        parent = elm;
+        elm = tmp;
+      }
+      RBSetBlackRed(parent, gparent);
+      RBRotateLeft(root, gparent, tmp);
+    }
+  }
+  (*root)->color = RBColor::BLACK;
 }

-void RtPosixMutex::unlock() noexcept {
-  PERFETTO_CHECK(pthread_mutex_unlock(&mutex_) == 0);
+void RBRemove(RBNode** root, RBNode* elm) {
+  RBNode* child = elm;
+  RBNode* parent = elm;
+  RBNode* old = elm;
+  RBColor color;
+
+  if (elm->left == nullptr)
+    child = elm->right;
+  else if (elm->right == nullptr)
+    child = elm->left;
+  else {
+    RBNode* left;
+    elm = elm->right;
+    while ((left = elm->left))
+      elm = left;
+    child = elm->right;
+    parent = elm->parent;
+    color = elm->color;
+    if (child)
+      child->parent = parent;
+    if (parent) {
+      if (parent->left == elm) {
+        parent->left = child;
+      } else {
+        parent->right = child;
+      }
+      // RB_AUGMENT(parent);
+    } else {
+      *root = child;
+    }
+    if (elm->parent == old)
+      parent = elm;
+    *elm = *old;
+    if (old->parent) {
+      if (old->parent->left == old) {
+        old->parent->left = elm;
+      } else {
+        old->parent->right = elm;
+      }
+      // RB_AUGMENT(old->parent);
+    } else {
+      *root = elm;
+    }
+    old->left->parent = elm;
+    if (old->right)
+      old->right->parent = elm;
+    if (parent) {
+      left = parent;
+      // do {
+      //   RB_AUGMENT(left);
+      // } while ((left = left->parent));
+    }
+    goto color;
+  }
+  parent = elm->parent;
+  color = elm->color;
+  if (child)
+    child->parent = parent;
+  if (parent) {
+    if (parent->left == elm)
+      parent->left = child;
+    else
+      parent->right = child;
+    // RB_AUGMENT(parent);
+  } else {
+    *root = child;
+  }
+color:
+  if (color == RBColor::BLACK)
+    RBRemoveColor(root, parent, child);
 }

-#endif  // PERFETTO_HAS_POSIX_RT_MUTEX
+// Returns nullptr after reaching the last leaf (the max element).
+const RBNode* RBNext(const RBNode* node) {
+  if (node->right) {
+    node = node->right;
+    while (node->left)
+      node = node->left;
+  } else {
+    if (node->parent && node == node->parent->left) {
+      node = node->parent;
+    } else {
+      while (node->parent && node == node->parent->right) {
+        node = node->parent;
+      }
+      node = node->parent;
+    }
+  }
+  return node;
+}

-}  // namespace internal
-}  // namespace perfetto::base
-// gen_amalgamated begin source: src/base/scoped_mmap.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/scoped_mmap.h
+}  // namespace perfetto::base::internal
+// gen_amalgamated begin source: src/base/logging.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/progress_reporter.h
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -7757,86 +8192,60 @@ void RtPosixMutex::unlock() noexcept {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_SCOPED_MMAP_H_
-#define INCLUDE_PERFETTO_EXT_BASE_SCOPED_MMAP_H_
-
-#include <cstddef>
+#ifndef INCLUDE_PERFETTO_EXT_BASE_PROGRESS_REPORTER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_PROGRESS_REPORTER_H_

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+#include <cstdint>
+#include <mutex>
+#include <string_view>

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#define PERFETTO_HAS_MMAP() 1
-#else
-#define PERFETTO_HAS_MMAP() 0
-#endif
+// gen_amalgamated expanded: #include "perfetto/base/thread_annotations.h"

 namespace perfetto::base {

-// RAII wrapper that holds ownership of an mmap()d area and of a file. Calls
-// unmap() and close() on destruction.
-class ScopedMmap {
- public:
-  // Creates a memory mapping for the first `length` bytes of `file`.
-  static ScopedMmap FromHandle(base::ScopedPlatformHandle file, size_t length);
-
-  ScopedMmap() {}
-  ~ScopedMmap();
-  ScopedMmap(ScopedMmap&& other) noexcept;
-
-  ScopedMmap& operator=(ScopedMmap&& other) noexcept;
-
-  // Returns a pointer to the mapped memory area. Only valid if `IsValid()` is
-  // true.
-  void* data() const { return ptr_; }
+// True if stderr is an interactive terminal (and TERM is not "dumb"). On WASM
+// this is always true: the embedder consumes stderr line by line.
+bool StderrSupportsProgress();

-  // Returns true if this object contains a successfully mapped area.
-  bool IsValid() const { return ptr_ != nullptr; }
+// True if ANSI colors should be emitted on stderr. Nonempty FORCE_COLOR forces
+// them on, otherwise nonempty NO_COLOR forces them off, otherwise they are
+// used when stderr is an interactive terminal.
+bool StderrSupportsColor();

-  // Returns the length of the mapped area.
-  size_t length() const { return length_; }
+// Process-wide, throttled, single-line progress display on stderr. Updates are
+// only shown when StderrSupportsProgress(). Log messages clear the line before
+// printing, so diagnostics never get merged with progress text.
+class ProgressReporter {
+ public:
+  static ProgressReporter& GetInstance();

-  // Unmaps the area and closes the file. Returns false if this held a mmap()d
-  // area and unmapping failed. In any case, after this method, `IsValid()` will
-  // return false.
-  bool reset() noexcept;
+  // Suppresses all progress output. Diagnostics are unaffected.
+  void set_enabled(bool enabled);

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-  // Takes ownership of an mmap()d area that starts at `data`, `size` bytes
-  // long. `data` should not be MAP_FAILED.
-  static ScopedMmap InheritMmappedRange(void* data, size_t size);
-#endif
+  // |message| must be plain ASCII without newlines or terminal escapes.
+  void Update(std::string_view message);
+  void Clear();

  private:
-  ScopedMmap(const ScopedMmap&) = delete;
-  ScopedMmap& operator=(const ScopedMmap&) = delete;
-
-  size_t length_ = 0;
-  void* ptr_ = nullptr;
-  ScopedPlatformHandle file_;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  ScopedPlatformHandle map_;
-#endif
-};
+  ProgressReporter() = default;
+  ~ProgressReporter() = default;
+  ProgressReporter(const ProgressReporter&) = delete;
+  ProgressReporter& operator=(const ProgressReporter&) = delete;

-// Tries to open `file_path` and maps its first `length` bytes in memory.
-ScopedMmap ReadMmapFilePart(const std::string& file_path, size_t length);
+  void ClearLocked() PERFETTO_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

-// Tries to open `file_path` and maps the whole file into memory.
-ScopedMmap ReadMmapWholeFile(const std::string& file_path);
+  std::mutex mutex_;
+  bool enabled_ PERFETTO_GUARDED_BY(mutex_) = true;
+  int64_t last_update_ms_ PERFETTO_GUARDED_BY(mutex_) = 0;
+  size_t visible_width_ PERFETTO_GUARDED_BY(mutex_) = 0;
+};

 }  // namespace perfetto::base
-
-#endif  // INCLUDE_PERFETTO_EXT_BASE_SCOPED_MMAP_H_
+#endif  // INCLUDE_PERFETTO_EXT_BASE_PROGRESS_REPORTER_H_
+// gen_amalgamated begin header: src/base/log_ring_buffer.h
+// gen_amalgamated begin header: include/perfetto/ext/base/thread_annotations.h
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -7851,169 +8260,30 @@ ScopedMmap ReadMmapWholeFile(const std::string& file_path);
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_mmap.h"
-
-#include <utility>
-
-// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-#include <sys/mman.h>
-#include <unistd.h>
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <windows.h>
-#endif
-
-namespace perfetto::base {
-namespace {
-
-ScopedPlatformHandle OpenFileForMmap(const std::string& file_path) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-  return OpenFile(file_path, O_RDONLY);
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  // This does not use base::OpenFile to avoid getting an exclusive lock.
-  //
-  // The share flags mirror the POSIX open(O_RDONLY) above, which has no notion
-  // of share modes. Same flags as LLVM's openNativeFileInternal():
-  // https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/Windows/Path.inc
-  return ScopedPlatformHandle(
-      CreateFileA(file_path.c_str(), GENERIC_READ,
-                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-                  nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
-#else
-  // mmap is not supported. Do not even open the file.
-  base::ignore_result(file_path);
-  return ScopedPlatformHandle();
-#endif
-}
-
-}  // namespace
-
-ScopedMmap::ScopedMmap(ScopedMmap&& other) noexcept {
-  *this = std::move(other);
-}
-
-ScopedMmap& ScopedMmap::operator=(ScopedMmap&& other) noexcept {
-  if (this == &other) {
-    return *this;
-  }
-  reset();
-  std::swap(ptr_, other.ptr_);
-  std::swap(length_, other.length_);
-  std::swap(file_, other.file_);
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  std::swap(map_, other.map_);
-#endif
-  return *this;
-}
-
-ScopedMmap::~ScopedMmap() {
-  reset();
-}
-
-// static
-ScopedMmap ScopedMmap::FromHandle(base::ScopedPlatformHandle file,
-                                  size_t length) {
-  ScopedMmap ret;
-  if (!file) {
-    return ret;
-  }
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-  void* ptr = mmap(nullptr, length, PROT_READ, MAP_PRIVATE, *file, 0);
-  if (ptr != MAP_FAILED) {
-    ret.ptr_ = ptr;
-    ret.length_ = length;
-    ret.file_ = std::move(file);
-  }
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  ScopedPlatformHandle map(
-      CreateFileMapping(*file, nullptr, PAGE_READONLY, 0, 0, nullptr));
-  if (!map) {
-    return ret;
-  }
-  void* ptr = MapViewOfFile(*map, FILE_MAP_READ, 0, 0, length);
-  if (ptr != nullptr) {
-    ret.ptr_ = ptr;
-    ret.length_ = length;
-    ret.file_ = std::move(file);
-    ret.map_ = std::move(map);
-  }
-#else
-  base::ignore_result(length);
-#endif
-  return ret;
-}
-
-bool ScopedMmap::reset() noexcept {
-  bool ret = true;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-  if (ptr_ != nullptr) {
-    ret = munmap(ptr_, length_) == 0;
-  }
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  if (ptr_ != nullptr) {
-    ret = UnmapViewOfFile(ptr_);
-  }
-  map_.reset();
-#endif
-  ptr_ = nullptr;
-  length_ = 0;
-  file_.reset();
-  return ret;
-}
+#ifndef INCLUDE_PERFETTO_EXT_BASE_THREAD_ANNOTATIONS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_THREAD_ANNOTATIONS_H_

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-// static
-ScopedMmap ScopedMmap::InheritMmappedRange(void* data, size_t size) {
-  ScopedMmap ret;
-  ret.ptr_ = data;
-  ret.length_ = size;
-  return ret;
-}
-#endif
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-ScopedMmap ReadMmapFilePart(const std::string& fname, size_t length) {
-  return ScopedMmap::FromHandle(OpenFileForMmap(fname), length);
+// Windows TSAN doesn't currently support these annotations.
+#if defined(THREAD_SANITIZER) && !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+extern "C" {
+void AnnotateBenignRaceSized(const char* file,
+                             int line,
+                             const volatile void* address,
+                             size_t size,
+                             const char* description);
 }

-ScopedMmap ReadMmapWholeFile(const std::string& fname) {
-  ScopedPlatformHandle file = OpenFileForMmap(fname);
-  if (!file) {
-    return ScopedMmap();
-  }
-  std::optional<uint64_t> file_size = GetFileSize(file.get());
-  if (!file_size.has_value()) {
-    return ScopedMmap();
-  }
-  size_t size = static_cast<size_t>(*file_size);
-  if (static_cast<uint64_t>(size) != *file_size) {
-    return ScopedMmap();
-  }
-  return ScopedMmap::FromHandle(std::move(file), size);
-}
+#define PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(pointer, size, description) \
+  AnnotateBenignRaceSized(__FILE__, __LINE__, pointer, size, description);
+#else  // defined(ADDRESS_SANITIZER)
+#define PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(pointer, size, description)
+#endif  // defined(ADDRESS_SANITIZER)

-}  // namespace perfetto::base
-// gen_amalgamated begin source: src/base/scoped_sched_boost.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/scoped_sched_boost.h
-// gen_amalgamated begin header: include/perfetto/ext/base/status_or.h
+#endif  // INCLUDE_PERFETTO_EXT_BASE_THREAD_ANNOTATIONS_H_
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -8028,75 +8298,152 @@ ScopedMmap ReadMmapWholeFile(const std::string& fname) {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_STATUS_OR_H_
-#define INCLUDE_PERFETTO_EXT_BASE_STATUS_OR_H_
+#ifndef SRC_BASE_LOG_RING_BUFFER_H_
+#define SRC_BASE_LOG_RING_BUFFER_H_

-#include <optional>
+#include <stddef.h>
+#include <stdio.h>

-// gen_amalgamated expanded: #include "perfetto/base/status.h"
+#include <array>
+#include <atomic>
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_annotations.h"

 namespace perfetto {
 namespace base {

-// Union of a object of type |T| with a |base::Status|. Useful for cases where
-// a |T| indicates a successful result of an operation and |base::Status|
-// represents an error.
+// Defined out of line because a static constexpr requires static storage if
+// ODR-used, not worth adding a .cc file just for tests.
+constexpr size_t kLogRingBufEntries = 8;
+constexpr size_t kLogRingBufMsgLen = 256;
+
+// A static non-allocating ring-buffer to hold the most recent log events.
+// This class is really an implementation detail of logging.cc. The only reason
+// why is fully defined in a dedicated header is for allowing unittesting,
+// without leaking extra headers into logging.h (which is a high-fanout header).
+// This is used to report the last logs in a crash report when a CHECK/FATAL
+// is encountered.
+// This class has just an Append() method to insert events into the buffer and
+// a Read() to read the events in FIFO order. Read() is non-destructive.
 //
-// This class is modelled closely on absl::Status and should essentially 1:1
-// match it's API.
-template <typename T>
-class StatusOr {
+// Thread safety considerations:
+// - The Append() method can be called concurrently by several threads, unless
+//   there are > kLogRingBufEntries concurrent threads. Even if that happens,
+//   case some events will contain a mix of strings but the behavior of
+//   further Append() and Read() is still defined.
+// - The Read() method is not thread safe but it's fine in practice. Even if
+//   it's called concurrently with other Append(), it only causes some partial
+//   events to be emitted in output.
+// In both cases, we never rely purely on \0, all operations are size-bound.
+//
+// See logging_unittest.cc for tests.
+class LogRingBuffer {
  public:
-  // Matches naming of declarations in similar types e.g. std::optional,
-  // std::variant.
-  using value_type = T;
+  LogRingBuffer() = default;
+  LogRingBuffer(const LogRingBuffer&) = delete;
+  LogRingBuffer& operator=(const LogRingBuffer&) = delete;
+  LogRingBuffer(LogRingBuffer&&) = delete;
+  LogRingBuffer& operator=(LogRingBuffer&&) = delete;

-  // Intentionally implicit to allow idomatic usage (e.g. returning value/status
-  // from base::StatusOr returning function).
-  StatusOr(base::Status status) : StatusOr(std::move(status), std::nullopt) {
-    if (status_.ok()) {
-      // Matches what Abseil's approach towards OkStatus being passed to
-      // absl::StatusOr<T>.
-      PERFETTO_FATAL("base::OkStatus passed to StatusOr: this is not allowed");
-    }
-  }
-  StatusOr(T value) : StatusOr(base::OkStatus(), std::move(value)) {}
-
-  bool ok() const { return status_.ok(); }
-  const base::Status& status() const { return status_; }
+  // This takes three arguments because it fits its only caller (logging.cc).
+  // The args are just concatenated together (plus one space before the msg).
+  void Append(StringView tstamp, StringView source, StringView log_msg) {
+    // Reserve atomically a slot in the ring buffer, so any concurrent Append()
+    // won't overlap (unless too many concurrent Append() happen together).
+    // There is no strict synchronization here, |event_slot_| is atomic only for
+    // the sake of avoiding colliding on the same slot but does NOT guarantee
+    // full consistency and integrity of the log messages written in each slot.
+    // A release-store (or acq+rel) won't be enough for full consistency. Two
+    // threads that race on Append() and take the N+1 and N+2 slots could finish
+    // the write in reverse order. So Read() would need to synchronize with
+    // something else (either a per-slot atomic flag or with a second atomic
+    // counter which is incremented after the snprintf). Both options increase
+    // the cost of Append() with no huge benefits (90% of the perfetto services
+    // where we use it is single thread, and the log ring buffer is disabled
+    // on non-standalone builds like the SDK).
+    uint32_t slot = event_slot_.fetch_add(1, std::memory_order_relaxed);
+    slot = slot % kLogRingBufEntries;

-  T& value() {
-    PERFETTO_DCHECK(status_.ok());
-    return *value_;
+    char* const msg = events_[slot];
+    PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(msg, kLogRingBufMsgLen,
+                                        "see comments in log_ring_buffer.h")
+    snprintf(msg, kLogRingBufMsgLen, "%.*s%.*s %.*s",
+             static_cast<int>(tstamp.size()), tstamp.data(),
+             static_cast<int>(source.size()), source.data(),
+             static_cast<int>(log_msg.size()), log_msg.data());
   }
-  const T& value() const { return *value_; }
-
-  T& operator*() { return value(); }
-  const T& operator*() const { return value(); }

-  T* operator->() { return &value(); }
-  const T* operator->() const { return &value(); }
+  // Reads back the buffer in FIFO order, up to |len - 1| characters at most
+  // (the -1 is because a NUL terminator is always appended, unless |len| == 0).
+  // The string written in |dst| is guaranteed to be NUL-terminated, even if
+  // |len| < buffer contents length.
+  // Returns the number of bytes written in output, excluding the \0 terminator.
+  size_t Read(char* dst, size_t len) {
+    if (len == 0)
+      return 0;
+    // This is a relaxed-load because we don't need to fully synchronize on the
+    // writing path for the reasons described in the fetch_add() above.
+    const uint32_t event_slot = event_slot_.load(std::memory_order_relaxed);
+    size_t dst_written = 0;
+    for (uint32_t pos = 0; pos < kLogRingBufEntries; ++pos) {
+      const uint32_t slot = (event_slot + pos) % kLogRingBufEntries;
+      const char* src = events_[slot];
+      if (*src == '\0')
+        continue;  // Empty slot. Skip.
+      char* const wptr = dst + dst_written;
+      // |src| might not be null terminated. This can happen if some
+      // thread-race happened. Limit the copy length.
+      const size_t limit = std::min(len - dst_written, kLogRingBufMsgLen);
+      for (size_t i = 0; i < limit; ++i) {
+        const char c = src[i];
+        ++dst_written;
+        if (c == '\0' || i == limit - 1) {
+          wptr[i] = '\n';
+          break;
+        }
+        // Skip non-printable ASCII characters to avoid confusing crash reports.
+        // Note that this deliberately mangles \n. Log messages should not have
+        // a \n in the middle and are NOT \n terminated. The trailing \n between
+        // each line is appended by the if () branch above.
+        const bool is_printable = c >= ' ' && c <= '~';
+        wptr[i] = is_printable ? c : '?';
+      }
+    }
+    // Ensure that the output string is null-terminated.
+    PERFETTO_DCHECK(dst_written <= len);
+    if (dst_written == len) {
+      // In case of truncation we replace the last char with \0. But the return
+      // value is the number of chars without \0, hence the --.
+      dst[--dst_written] = '\0';
+    } else {
+      dst[dst_written] = '\0';
+    }
+    return dst_written;
+  }

  private:
-  StatusOr(base::Status status, std::optional<T> value)
-      : status_(std::move(status)), value_(std::move(value)) {
-    PERFETTO_DCHECK(!status_.ok() || value_.has_value());
-  }
+  using EventBuf = char[kLogRingBufMsgLen];
+  EventBuf events_[kLogRingBufEntries]{};

-  base::Status status_;
-  std::optional<T> value_;
-};
+  static_assert((kLogRingBufEntries & (kLogRingBufEntries - 1)) == 0,
+                "kLogRingBufEntries must be a power of two");

-// Deduction guide to make returning StatusOr less verbose.
-template <typename T>
-StatusOr(T) -> StatusOr<T>;
+  // A monotonically increasing counter incremented on each event written.
+  // It determines which of the kLogRingBufEntries indexes in |events_| should
+  // be used next.
+  // It grows >> kLogRingBufEntries, it's supposed to be always used
+  // mod(kLogRingBufEntries). A static_assert in the .cc file ensures that
+  // kLogRingBufEntries is a power of two so wraps are aligned.
+  std::atomic<uint32_t> event_slot_{};
+};

 }  // namespace base
 }  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_STATUS_OR_H_
+#endif  // SRC_BASE_LOG_RING_BUFFER_H_
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -8111,134 +8458,233 @@ StatusOr(T) -> StatusOr<T>;
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_SCOPED_SCHED_BOOST_H_
-#define INCLUDE_PERFETTO_EXT_BASE_SCOPED_SCHED_BOOST_H_
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/progress_reporter.h"

-// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"
+#include <stdarg.h>
+#include <stdio.h>

-namespace perfetto::base {
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <unistd.h>  // For isatty()
+#endif

-// kSchedOther: it's the default policy (e.g., CFS on Linux). Range: 0-20.
-//              prio is interpreted as -(nice), i.e., 1 is slightly higher prio
-//              than default 0, 20 is the highest priority.
-//              Note that this is the opposite semantic of the cmdline nice, and
-//              is done for consistency with kSchedFifo, so higher
-//              number == higher prio.
-// kSchedFifo: real-time priority. Range: 1-99. 1 is the lowest priority, 99 the
-//             highest.
-struct SchedPolicyAndPrio {
-  enum class Policy {
-    kSchedOther,
-    kSchedFifo
-  };  // Order matters for operator<().
+#include <atomic>
+#include <memory>

-  bool operator<(const SchedPolicyAndPrio& other) const {
-    return std::tie(policy, prio) < std::tie(other.policy, other.prio);
-  }
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/crash_keys.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
+// gen_amalgamated expanded: #include "src/base/log_ring_buffer.h"

-  bool operator==(const SchedPolicyAndPrio& other) const {
-    return policy == other.policy && prio == other.prio;
-  }
+#if PERFETTO_ENABLE_LOG_RING_BUFFER() && PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <android/set_abort_message.h>
+#endif

-  bool operator!=(const SchedPolicyAndPrio& other) const {
-    return !(*this == other);
-  }
+// The __attribute__((constructor)) below is intentional, it sets the crash
+// reporter globally for Perfetto. It's only enabled on debug builds so should
+// be pretty safe.
+#if defined(__clang__)
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif

-  Policy policy = Policy::kSchedOther;
-  uint32_t prio = 0;
-};
+namespace perfetto {
+namespace base {

-// Used to mock Linux scheduling policy and parameters API in tests.
-// In production code calls the Linux API, the class is stateless.
-class SchedOsHooks {
- public:
-  struct SchedOsConfig {
-    int policy;
-    int rt_prio;
-    int nice;
-  };
+namespace {
+const char kReset[] = "\x1b[0m";
+const char kDefault[] = "\x1b[39m";
+const char kDim[] = "\x1b[2m";
+const char kRed[] = "\x1b[31m";
+const char kBoldGreen[] = "\x1b[1m\x1b[32m";
+const char kLightGray[] = "\x1b[90m";

-  virtual ~SchedOsHooks() = default;
-  static SchedOsHooks* GetInstance();
-  // virtual for testing
-  virtual Status SetSchedConfig(const SchedOsConfig& arg);
-  // virtual for testing
-  virtual StatusOr<SchedOsConfig> GetCurrentSchedConfig() const;
-};
+std::atomic<LogMessageCallback> g_log_callback{};

-// RAII helper to temporarily boost the scheduler priority of the current
-// thread. The priority is reverted to the original value when the object goes
-// out of scope.
-// It is supported only on Linux/Android, fails on other platforms.
-class ScopedSchedBoost {
- public:
-  static StatusOr<ScopedSchedBoost> Boost(SchedPolicyAndPrio);
-  ScopedSchedBoost(ScopedSchedBoost&&) noexcept;
-  ScopedSchedBoost& operator=(ScopedSchedBoost&&) noexcept;
-  ~ScopedSchedBoost();
+#if PERFETTO_BUILDFLAG(PERFETTO_STDERR_CRASH_DUMP)
+// __attribute__((constructor)) causes a static initializer that automagically
+// early runs this function before the main().
+void PERFETTO_EXPORT_COMPONENT __attribute__((constructor))
+InitDebugCrashReporter() {
+  // This function is defined in debug_crash_stack_trace.cc.
+  // The dynamic initializer is in logging.cc because logging.cc is included
+  // in virtually any target that depends on base. Having it in
+  // debug_crash_stack_trace.cc would require figuring out -Wl,whole-archive
+  // which is not worth it.
+  EnableStacktraceOnCrashForDebug();
+}
+#endif

-  // No copy constructors.
-  ScopedSchedBoost(const ScopedSchedBoost&) = delete;
-  ScopedSchedBoost& operator=(const ScopedSchedBoost&) = delete;
+#if PERFETTO_ENABLE_LOG_RING_BUFFER()
+LogRingBuffer g_log_ring_buffer{};

-  static void ResetForTesting(SchedOsHooks*);
+// This is global to avoid allocating memory or growing too much the stack
+// in MaybeSerializeLastLogsForCrashReporting(), which is called from
+// arbitrary code paths hitting PERFETTO_CHECK()/FATAL().
+char g_crash_buf[kLogRingBufEntries * kLogRingBufMsgLen];
+#endif

- private:
-  explicit ScopedSchedBoost(SchedPolicyAndPrio p) : policy_and_prio_(p) {}
+}  // namespace

-  std::optional<SchedPolicyAndPrio> policy_and_prio_;
-  ThreadChecker thread_checker_;
-};
-}  // namespace perfetto::base
+void SetLogMessageCallback(LogMessageCallback callback) {
+  g_log_callback.store(callback, std::memory_order_relaxed);
+}

-#endif  // INCLUDE_PERFETTO_EXT_BASE_SCOPED_SCHED_BOOST_H_
-// gen_amalgamated begin header: include/perfetto/ext/base/status_macros.h
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+void LogMessage(LogLev level,
+                const char* fname,
+                int line,
+                const char* fmt,
+                ...) {
+  char stack_buf[512];
+  std::unique_ptr<char[]> large_buf;
+  char* log_msg = &stack_buf[0];
+  size_t log_msg_len = 0;

-#ifndef INCLUDE_PERFETTO_EXT_BASE_STATUS_MACROS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_STATUS_MACROS_H_
+  // By default use a stack allocated buffer because most log messages are quite
+  // short. In rare cases they can be larger (e.g. --help). In those cases we
+  // pay the cost of allocating the buffer on the heap.
+  for (size_t max_len = sizeof(stack_buf);;) {
+    va_list args;
+    va_start(args, fmt);
+    int res = vsnprintf(log_msg, max_len, fmt, args);
+    va_end(args);

-// gen_amalgamated expanded: #include "perfetto/base/status.h"
+    // If for any reason the print fails, overwrite the message but still print
+    // it. The code below will attach the filename and line, which is still
+    // useful.
+    if (res < 0) {
+      snprintf(log_msg, max_len, "%s", "[printf format error]");
+      break;
+    }

-// Evaluates |expr|, which should return a base::Status. If the status is an
-// error status, returns the status from the current function.
-#define RETURN_IF_ERROR(expr)                                       \
-  do {                                                              \
-    ::perfetto::base::Status status_macro_internal_status = (expr); \
-    if (!status_macro_internal_status.ok())                         \
-      return status_macro_internal_status;                          \
-  } while (0)
+    // if res == max_len, vsnprintf saturated the input buffer. Retry with a
+    // larger buffer in that case (within reasonable limits).
+    if (res < static_cast<int>(max_len) || max_len >= 128 * 1024) {
+      // In case of truncation vsnprintf returns the len that "would have been
+      // written if the string was longer", not the actual chars written.
+      log_msg_len = std::min(static_cast<size_t>(res), max_len - 1);
+      break;
+    }
+    max_len *= 4;
+    large_buf.reset(new char[max_len]);
+    log_msg = &large_buf[0];
+  }

-#define PERFETTO_INTERNAL_CONCAT_IMPL(x, y) x##y
-#define PERFETTO_INTERNAL_MACRO_CONCAT(x, y) PERFETTO_INTERNAL_CONCAT_IMPL(x, y)
+  ProgressReporter::GetInstance().Clear();
+  LogMessageCallback cb = g_log_callback.load(std::memory_order_relaxed);
+  if (cb) {
+    cb({level, line, fname, log_msg});
+    return;
+  }

-// Evalues |rhs| which should return a base::StatusOr<T> and assigns this
-// to |lhs|. If the status is an error status, returns the status from the
-// current function.
-#define ASSIGN_OR_RETURN(lhs, rhs)                                   \
-  PERFETTO_INTERNAL_MACRO_CONCAT(auto status_or, __LINE__) = rhs;    \
-  RETURN_IF_ERROR(                                                   \
-      PERFETTO_INTERNAL_MACRO_CONCAT(status_or, __LINE__).status()); \
-  lhs = std::move(PERFETTO_INTERNAL_MACRO_CONCAT(status_or, __LINE__).value())
+  const char* color = kDefault;
+  switch (level) {
+    case kLogDebug:
+      color = kDim;
+      break;
+    case kLogInfo:
+      color = kDefault;
+      break;
+    case kLogImportant:
+      color = kBoldGreen;
+      break;
+    case kLogError:
+      color = kRed;
+      break;
+  }

-#endif  // INCLUDE_PERFETTO_EXT_BASE_STATUS_MACROS_H_
+  const bool use_colors = StderrSupportsColor();
+
+  // Formats file.cc:line as a space-padded fixed width string. If the file name
+  // |fname| is too long, truncate it on the left-hand side.
+  StackString<10> line_str("%d", line);
+
+  // 24 will be the width of the file.cc:line column in the log event.
+  static constexpr size_t kMaxNameAndLine = 24;
+  size_t fname_len = strlen(fname);
+  size_t fname_max = kMaxNameAndLine - line_str.len() - 2;  // 2 = ':' + '\0'.
+  size_t fname_offset = fname_len <= fname_max ? 0 : fname_len - fname_max;
+  StackString<kMaxNameAndLine> file_and_line(
+      "%*s:%s", static_cast<int>(fname_max), &fname[fname_offset],
+      line_str.c_str());
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  // Logcat has already timestamping, don't re-emit it.
+  __android_log_print(int{ANDROID_LOG_DEBUG} + level, "perfetto", "%s %s",
+                      file_and_line.c_str(), log_msg);
+#endif
+
+  // When printing on stderr, print also the timestamp. We don't really care
+  // about the actual time. We just need some reference clock that can be used
+  // to correlated events across different processes (e.g. traced and
+  // traced_probes). The wall time % 1000 is good enough.
+  uint32_t t_ms = static_cast<uint32_t>(GetWallTimeMs().count());
+  uint32_t t_sec = t_ms / 1000;
+  t_ms -= t_sec * 1000;
+  t_sec = t_sec % 1000;
+  StackString<32> timestamp("[%03u.%03u] ", t_sec, t_ms);
+
+  if (use_colors) {
+    fprintf(stderr, "%s%s%s%s %s%s%s\n", kLightGray, timestamp.c_str(),
+            file_and_line.c_str(), kReset, color, log_msg, kReset);
+  } else {
+    fprintf(stderr, "%s%s %s\n", timestamp.c_str(), file_and_line.c_str(),
+            log_msg);
+  }
+
+#if PERFETTO_ENABLE_LOG_RING_BUFFER()
+  // Append the message to the ring buffer for crash reporting postmortems.
+  StringView timestamp_sv = timestamp.string_view();
+  StringView file_and_line_sv = file_and_line.string_view();
+  StringView log_msg_sv(log_msg, static_cast<size_t>(log_msg_len));
+  g_log_ring_buffer.Append(timestamp_sv, file_and_line_sv, log_msg_sv);
+#else
+  ignore_result(log_msg_len);
+#endif
+}
+
+#if PERFETTO_ENABLE_LOG_RING_BUFFER()
+void MaybeSerializeLastLogsForCrashReporting() {
+  // Keep this function minimal. This is called from the watchdog thread, often
+  // when the system is thrashing.
+
+  // This is racy because two threads could hit a CHECK/FATAL at the same time.
+  // But if that happens we have bigger problems, not worth designing around it.
+  // The behaviour is still defined in the race case (the string attached to
+  // the crash report will contain a mixture of log strings).
+  size_t wr = 0;
+  wr += SerializeCrashKeys(&g_crash_buf[wr], sizeof(g_crash_buf) - wr);
+  wr += g_log_ring_buffer.Read(&g_crash_buf[wr], sizeof(g_crash_buf) - wr);
+
+  // Read() null-terminates the string properly. This is just to avoid UB when
+  // two threads race on each other (T1 writes a shorter string, T2
+  // overwrites the \0 writing a longer string. T1 continues here before T2
+  // finishes writing the longer string with the \0 -> boom.
+  g_crash_buf[sizeof(g_crash_buf) - 1] = '\0';
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  // android_set_abort_message() will cause debuggerd to report the message
+  // in the tombstone and in the crash log in logcat.
+  // NOTE: android_set_abort_message() can be called only once. This should
+  // be called only when we are sure we are about to crash.
+  android_set_abort_message(g_crash_buf);
+#else
+  // Print out the message on stderr on Linux/Mac/Win.
+  fputs("\n-----BEGIN PERFETTO PRE-CRASH LOG-----\n", stderr);
+  fputs(g_crash_buf, stderr);
+  fputs("\n-----END PERFETTO PRE-CRASH LOG-----\n", stderr);
+#endif
+}
+#endif  // PERFETTO_ENABLE_LOG_RING_BUFFER
+
+}  // namespace base
+}  // namespace perfetto
+// gen_amalgamated begin source: src/base/metatrace.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/metatrace.h
+// gen_amalgamated begin header: include/perfetto/ext/base/metatrace_events.h
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -8253,249 +8699,132 @@ class ScopedSchedBoost {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_sched_boost.h"
-
-// gen_amalgamated expanded: #include "perfetto/ext/base/status_macros.h"
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#include <sched.h>         // for 'SCHED_' macros and 'sched_' functions
-#include <sys/resource.h>  // for 'setpriority', 'getpriority', 'PRIO_PROCESS'
-#include <sys/types.h>     // for 'pid_t'
-
-#include <algorithm>
-#include <vector>
-
-#endif  // PERFETTO_OS_LINUX || PERFETTO_OS_ANDROID
-
-namespace perfetto::base {
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-
-namespace {
-constexpr pid_t kCurrentPid = 0;
-
-class ThreadMgr {
- public:
-  // In this class we manage the boost state for the whole process.
-  // If we want to use it for each single thread, this needs to become
-  // 'GetInstanceForThread()'.
-  static ThreadMgr* GetInstance();
-
-  explicit ThreadMgr(SchedOsHooks*);
-
-  Status Add(SchedPolicyAndPrio);
-  void Remove(SchedPolicyAndPrio);
-  Status RecalcAndUpdatePrio();
+#ifndef INCLUDE_PERFETTO_EXT_BASE_METATRACE_EVENTS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_METATRACE_EVENTS_H_

-  ThreadMgr(const ThreadMgr&) = delete;
-  ThreadMgr& operator=(const ThreadMgr&) = delete;
-  ThreadMgr(ThreadMgr&&) = delete;
-  ThreadMgr& operator=(ThreadMgr&&) = delete;
+#include <stdint.h>

-  void ResetForTesting(SchedOsHooks*);
+namespace perfetto {
+namespace metatrace {

- private:
-  SchedOsHooks* os_hooks_;
-  SchedOsHooks::SchedOsConfig initial_config_{};
-  std::vector<SchedPolicyAndPrio> prios_;
+enum Tags : uint32_t {
+  TAG_NONE = 0,
+  TAG_ANY = uint32_t(-1),
+  TAG_FTRACE = 1 << 0,
+  TAG_PROC_POLLERS = 1 << 1,
+  TAG_TRACE_WRITER = 1 << 2,
+  TAG_TRACE_SERVICE = 1 << 3,
+  TAG_PRODUCER = 1 << 4,
 };

-ThreadMgr* ThreadMgr::GetInstance() {
-  static auto* instance = new ThreadMgr(SchedOsHooks::GetInstance());
-  return instance;
-}
-ThreadMgr::ThreadMgr(SchedOsHooks* os_hooks) : os_hooks_(os_hooks) {
-  auto res = os_hooks_->GetCurrentSchedConfig();
-  if (!res.ok()) {
-    // Should never fail: even without CAP_SYS_NICE we can always get our own
-    // policy and prio. If something goes very wrong, log an error and use
-    // SCHED_OTHER as initial config.
-    PERFETTO_DFATAL_OR_ELOG("Failed to get default sched config: %s",
-                            res.status().c_message());
-    initial_config_ = SchedOsHooks::SchedOsConfig{SCHED_OTHER, 0, 0};
-  } else {
-    initial_config_ = res.value();
-  }
-}
+// The macros below generate matching enums and arrays of string literals.
+// This is to avoid maintaining string maps manually.

-Status ThreadMgr::Add(SchedPolicyAndPrio spp) {
-  prios_.push_back(spp);
-  return RecalcAndUpdatePrio();
-}
+// clang-format off

-void ThreadMgr::Remove(SchedPolicyAndPrio spp) {
-  auto it = std::find(prios_.begin(), prios_.end(), spp);
-  if (it != prios_.end()) {
-    prios_.erase(it);
-  }
-  // It is possible that we previously added the wrongly configured policy,
-  // that wasn't the max policy. In that case the policy will be validated now,
-  // in 'RecalcAndUpdatePrio'.
-  // This for loop makes us ignore all wrongly configured policies and fallback
-  // to the first correct (or the initial one).
-  for (;;) {
-    if (auto res = RecalcAndUpdatePrio(); !res.ok()) {
-      PERFETTO_ELOG("%s", res.c_message());
-    } else {
-      break;
-    }
-  }
-}
-
-Status ThreadMgr::RecalcAndUpdatePrio() {
-  if (prios_.empty()) {
-    return os_hooks_->SetSchedConfig(initial_config_);
-  }
-  // TODO(ktimofeev): Check previously set prio to skip unnecessary syscall?
-  auto max_prio = std::max_element(prios_.begin(), prios_.end());
-  SchedOsHooks::SchedOsConfig os_config{};
-  switch (max_prio->policy) {
-    case SchedPolicyAndPrio::Policy::kSchedOther:
-      os_config = SchedOsHooks::SchedOsConfig{
-          SCHED_OTHER, 0, -1 * static_cast<int>(max_prio->prio)};
-      break;
-    case SchedPolicyAndPrio::Policy::kSchedFifo:
-      os_config = SchedOsHooks::SchedOsConfig{
-          SCHED_FIFO, static_cast<int>(max_prio->prio), 0};
-      break;
-  }
-  Status res = os_hooks_->SetSchedConfig(os_config);
-  if (!res.ok()) {
-    prios_.erase(max_prio);
-    return res;
-  }
-  return OkStatus();
-}
-
-void ThreadMgr::ResetForTesting(SchedOsHooks* os_hooks) {
-  os_hooks_ = os_hooks;
-  initial_config_ = os_hooks_->GetCurrentSchedConfig().value();
-  prios_.clear();
-}
-
-}  // namespace
-
-SchedOsHooks* SchedOsHooks::GetInstance() {
-  static auto* instance = new SchedOsHooks();
-  return instance;
-}
-
-Status SchedOsHooks::SetSchedConfig(const SchedOsConfig& arg) {
-  sched_param param{};
-  param.sched_priority = arg.rt_prio;
-  int ret = sched_setscheduler(kCurrentPid, arg.policy, &param);
-  if (ret == -1) {
-    return ErrStatus("sched_setscheduler(%d, %d) failed (errno: %d, %s)",
-                     arg.policy, arg.rt_prio, errno, strerror(errno));
-  }
-  // For the real-time policies the 'arg.rt_prio' value is >= 1.
-  // The userspace policies (SCHED_OTHER, SCHED_BATCH, SCHED_IDLE) use the nice
-  // value.
-  if (arg.rt_prio == 0) {
-    ret = setpriority(PRIO_PROCESS, kCurrentPid, arg.nice);
-    if (ret == -1) {
-      return ErrStatus("setpriority(%d) failed (errno: %d, %s)", arg.nice,
-                       errno, strerror(errno));
-    }
-  }
-  return OkStatus();
-}
-
-StatusOr<SchedOsHooks::SchedOsConfig> SchedOsHooks::GetCurrentSchedConfig()
-    const {
-  int policy = sched_getscheduler(kCurrentPid);
-  if (policy == -1) {
-    return ErrStatus("sched_getscheduler failed (errno: %d, %s)", errno,
-                     strerror(errno));
-  }
-  sched_param param{};
-  if (sched_getparam(kCurrentPid, &param) == -1) {
-    return ErrStatus("sched_getparam failed (errno: %d, %s)", errno,
-                     strerror(errno));
-  }
-  int nice = 0;
-  // For the real-time policies the 'param.sched_priority' value is >= 1.
-  // The userspace policies (SCHED_OTHER, SCHED_BATCH, SCHED_IDLE) use the nice
-  // value.
-  if (param.sched_priority == 0) {
-    errno = 0;
-    nice = getpriority(PRIO_PROCESS, kCurrentPid);
-    if (nice == -1 && errno != 0) {
-      return ErrStatus("getpriority failed (errno: %d, %s)", errno,
-                       strerror(errno));
-    }
-  }
-  return SchedOsConfig{policy, param.sched_priority, nice};
-}
-
-// static
-StatusOr<ScopedSchedBoost> ScopedSchedBoost::Boost(SchedPolicyAndPrio spp) {
-  auto res = ThreadMgr::GetInstance()->Add(spp);
-  RETURN_IF_ERROR(res);
-  return ScopedSchedBoost(spp);
-}
-
-ScopedSchedBoost::ScopedSchedBoost(ScopedSchedBoost&& other) noexcept {
-  this->policy_and_prio_ = other.policy_and_prio_;
-  other.policy_and_prio_ = std::nullopt;
-}
-
-ScopedSchedBoost& ScopedSchedBoost::operator=(
-    ScopedSchedBoost&& other) noexcept {
-  if (this != &other) {
-    this->~ScopedSchedBoost();
-    new (this) ScopedSchedBoost(std::move(other));
-  }
-  return *this;
-}
-
-ScopedSchedBoost::~ScopedSchedBoost() {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  if (!policy_and_prio_.has_value())
-    return;
-  ThreadMgr::GetInstance()->Remove(*policy_and_prio_);
-  policy_and_prio_ = std::nullopt;
-}
+// DO NOT remove or reshuffle items in this list, only append. The ID of these
+// events are an ABI, the trace processor relies on these to open old traces.
+#define PERFETTO_METATRACE_EVENTS(F) \
+  F(EVENT_ZERO_UNUSED), \
+  F(FTRACE_CPU_READER_READ), /*unused*/ \
+  F(FTRACE_DRAIN_CPUS), /*unused*/ \
+  F(FTRACE_UNBLOCK_READERS), /*unused*/ \
+  F(FTRACE_CPU_READ_NONBLOCK), /*unused*/ \
+  F(FTRACE_CPU_READ_BLOCK), /*unused*/ \
+  F(FTRACE_CPU_SPLICE_NONBLOCK), /*unused*/ \
+  F(FTRACE_CPU_SPLICE_BLOCK), /*unused*/ \
+  F(FTRACE_CPU_WAIT_CMD), /*unused*/ \
+  F(FTRACE_CPU_RUN_CYCLE), /*unused*/ \
+  F(FTRACE_CPU_FLUSH), \
+  F(FTRACE_CPU_BUFFER_WATERMARK), \
+  F(READ_SYS_STATS), \
+  F(PS_WRITE_ALL_PROCESSES), \
+  F(PS_ON_PIDS), \
+  F(PS_ON_RENAME_PIDS), \
+  F(PS_WRITE_ALL_PROCESS_STATS), \
+  F(TRACE_WRITER_COMMIT_STARTUP_WRITER_BATCH), \
+  F(FTRACE_READ_TICK), \
+  F(FTRACE_CPU_READ_CYCLE), \
+  F(FTRACE_CPU_READ_BATCH), \
+  F(KALLSYMS_PARSE), \
+  F(PROFILER_READ_TICK), \
+  F(PROFILER_READ_CPU), \
+  F(PROFILER_UNWIND_TICK), \
+  F(PROFILER_UNWIND_SAMPLE), \
+  F(PROFILER_UNWIND_INITIAL_ATTEMPT), \
+  F(PROFILER_UNWIND_ATTEMPT), \
+  F(PROFILER_MAPS_PARSE), \
+  F(PROFILER_MAPS_REPARSE), \
+  F(PROFILER_UNWIND_CACHE_CLEAR), \
+  F(ANDROID_CPU_PER_UID_TICK), \
+  F(ANDROID_KERNEL_WAKELOCKS_TICK), \
+  F(ANDROID_POWER_TICK), \
+  F(ANDROID_POWER_WRITE_BATTERY_COUNTERS), \
+  F(ANDROID_POWER_WRITE_ENERGY_ESTIMATION_BREAKDOWN), \
+  F(ANDROID_POWER_WRITE_ENTITY_STATE_RESIDENCY), \
+  F(ANDROID_POWER_WRITE_POWER_RAILS_DATA), \
+  F(LINUX_POWER_SYSFS_TICK), \
+  F(LINUX_SMAPS_ENQUEUE), \
+  F(LINUX_SMAPS_SERIALIZE)

-// static
-void ScopedSchedBoost::ResetForTesting(SchedOsHooks* os_manager) {
-  ThreadMgr::GetInstance()->ResetForTesting(os_manager);
-}
+// Append only, see above.
+//
+// Values that aren't used as counters:
+// * FTRACE_SERVICE_COMMIT_DATA is a bit-packed representation of an event, see
+//   tracing_service_impl.cc for the format.
+// * PROFILER_UNWIND_CURRENT_PID represents the PID that is being unwound.
+// * WATCHDOG_CRASH_REASON represents the base::WatchdogCrashReason enum.
+// * WATCHDOG_CRASH_TIME_BOOT_MS The time in ms since boot when the watchdog
+//   timeout alarm triggered (i.e. when it would have caused a crash, without
+//   counting for the optional handler time).
+// * WATCHDOG_CRASH_ACTUAL_{MONO,BOOT,CPU} are the elapsed seconds (since the
+//   offending fatal timer was created) sampled from CLOCK_MONOTONIC,
+//   CLOCK_BOOTTIME and CLOCK_PROCESS_CPUTIME_ID respectively.
+//
+#define PERFETTO_METATRACE_COUNTERS(F) \
+  F(COUNTER_ZERO_UNUSED),\
+  F(FTRACE_PAGES_DRAINED), \
+  F(PS_PIDS_SCANNED), \
+  F(TRACE_SERVICE_COMMIT_DATA), \
+  F(PROFILER_UNWIND_QUEUE_SZ), \
+  F(PROFILER_UNWIND_CURRENT_PID), \
+  F(WATCHDOG_CRASH_REASON), \
+  F(WATCHDOG_CRASH_TIME_BOOT_MS), \
+  F(WATCHDOG_CRASH_ACTUAL_MONO), \
+  F(WATCHDOG_CRASH_ACTUAL_BOOT), \
+  F(WATCHDOG_CRASH_ACTUAL_CPU), \
+  F(FTRACE_SESSIONS)

-#else
+// clang-format on

-// static
-SchedOsHooks* SchedOsHooks::GetInstance() {
-  return nullptr;
-}
+#define PERFETTO_METATRACE_IDENTITY(name) name
+#define PERFETTO_METATRACE_TOSTRING(name) #name

-Status SchedOsHooks::SetSchedConfig(const SchedOsHooks::SchedOsConfig&) {
-  return ErrStatus("SchedOsHooks is supported only on Linux/Android");
-}
+enum Events : uint16_t {
+  PERFETTO_METATRACE_EVENTS(PERFETTO_METATRACE_IDENTITY),
+  EVENTS_MAX
+};
+constexpr char const* kEventNames[] = {
+    PERFETTO_METATRACE_EVENTS(PERFETTO_METATRACE_TOSTRING)};

-StatusOr<SchedOsHooks::SchedOsConfig> SchedOsHooks::GetCurrentSchedConfig()
-    const {
-  return ErrStatus("SchedOsHooks is supported only on Linux/Android");
-}
+enum Counters : uint16_t {
+  PERFETTO_METATRACE_COUNTERS(PERFETTO_METATRACE_IDENTITY),
+  COUNTERS_MAX
+};
+constexpr char const* kCounterNames[] = {
+    PERFETTO_METATRACE_COUNTERS(PERFETTO_METATRACE_TOSTRING)};

-// static
-StatusOr<ScopedSchedBoost> ScopedSchedBoost::Boost(SchedPolicyAndPrio) {
-  return ErrStatus("ScopedSchedBoost is supported only on Linux/Android");
+inline void SuppressUnusedVarsInAmalgamatedBuild() {
+  (void)kCounterNames;
+  (void)kEventNames;
 }

-ScopedSchedBoost::ScopedSchedBoost(ScopedSchedBoost&&) noexcept = default;
-ScopedSchedBoost& ScopedSchedBoost::operator=(ScopedSchedBoost&&) noexcept =
-    default;
-ScopedSchedBoost::~ScopedSchedBoost() = default;
-
-#endif  // PERFETTO_OS_LINUX || PERFETTO_OS_ANDROID
+}  // namespace metatrace
+}  // namespace perfetto

-}  // namespace perfetto::base
-// gen_amalgamated begin source: src/base/status.cc
+#endif  // INCLUDE_PERFETTO_EXT_BASE_METATRACE_EVENTS_H_
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -8510,190 +8839,308 @@ ScopedSchedBoost::~ScopedSchedBoost() = default;
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/base/status.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_METATRACE_H_
+#define INCLUDE_PERFETTO_EXT_BASE_METATRACE_H_

-#include <algorithm>
-#include <cstdarg>
-#include <cstdio>
+#include <array>
+#include <atomic>
+#include <functional>
 #include <string>
-#include <utility>

-namespace perfetto::base {
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/thread_utils.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/metatrace_events.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"

-Status ErrStatus(const char* format, ...) {
-  std::string buf;
-  buf.resize(1024);
-  for (;;) {
-    va_list ap;
-    va_start(ap, format);
-    int N = vsnprintf(buf.data(), buf.size() - 1, format, ap);
-    va_end(ap);
+// A facility to trace execution of the perfetto codebase itself.
+// The meta-tracing framework is organized into three layers:
+//
+// 1. A static ring-buffer in base/ (this file) that supports concurrent writes
+//    and a single reader.
+//    The responsibility of this layer is to store events and counters as
+//    efficiently as possible without re-entering any tracing code.
+//    This is really a static-storage-based ring-buffer based on a POD array.
+//    This layer does NOT deal with serializing the meta-trace buffer.
+//    It posts a task when it's half full and expects something outside of
+//    base/ to drain the ring-buffer and serialize it, eventually writing it
+//    into the trace itself, before it gets 100% full.
+//
+// 2. A class in tracing/core which takes care of serializing the meta-trace
+//    buffer into the trace using a TraceWriter. See metatrace_writer.h .
+//
+// 3. A data source in traced_probes that, when be enabled via the trace config,
+//    injects metatrace events into the trace. See metatrace_data_source.h .
+//
+// The available events and tags are defined in metatrace_events.h .

-    if (N <= 0) {
-      buf = "[printf format error]";
-      break;
-    }
+namespace perfetto {

-    auto sN = static_cast<size_t>(N);
-    if (sN > buf.size() - 1) {
-      // Indicates that the string was truncated and sN is the "number of
-      // non-null bytes which would be needed to fit the result". This is the
-      // C99 standard behaviour in the case of truncation. In that case, resize
-      // the buffer to match the returned value (with + 1 for the null
-      // terminator) and try again.
-      buf.resize(sN + 1);
-      continue;
-    }
-    if (sN == buf.size() - 1) {
-      // Indicates that the string was likely truncated and sN is just the
-      // number of bytes written into the string. This is the behaviour of
-      // non-standard compilers (MSVC) etc. In that case, just double the
-      // storage and try again.
-      buf.resize(sN * 2);
-      continue;
-    }
+namespace base {
+class TaskRunner;
+}  // namespace base

-    // Otherwise, indicates the string was written successfully: we need to
-    // resize to match the number of non-null bytes and return.
-    buf.resize(sN);
-    break;
-  }
-  return Status(std::move(buf));
+namespace metatrace {
+
+// Meta-tracing is organized in "tags" that can be selectively enabled. This is
+// to enable meta-tracing only of one sub-system. This word has one "enabled"
+// bit for each tag. 0 -> meta-tracing off.
+extern std::atomic<uint32_t> g_enabled_tags;
+
+// Time of the Enable() call. Used as a reference for keeping delta timestmaps
+// in Record.
+extern std::atomic<uint64_t> g_enabled_timestamp;
+
+// Enables meta-tracing for one or more tags. Once enabled it will discard any
+// further Enable() calls and return false until disabled,
+// |read_task| is a closure that will be called enqueued |task_runner| when the
+// meta-tracing ring buffer is half full. The task is expected to read the ring
+// buffer using RingBuffer::GetReadIterator() and serialize the contents onto a
+// file or into the trace itself.
+// Must be called on the |task_runner| passed.
+// |task_runner| must have static lifetime.
+bool Enable(std::function<void()> read_task, base::TaskRunner*, uint32_t tags);
+
+// Disables meta-tracing.
+// Must be called on the same |task_runner| as Enable().
+void Disable();
+
+inline uint64_t TraceTimeNowNs() {
+  return static_cast<uint64_t>(base::GetBootTimeNs().count());
 }

-std::optional<std::string_view> Status::GetPayload(
-    std::string_view type_url) const {
-  if (ok()) {
-    return std::nullopt;
-  }
-  for (const auto& kv : payloads_) {
-    if (kv.type_url == type_url) {
-      return kv.payload;
-    }
-  }
-  return std::nullopt;
+// Returns a relaxed view of whether metatracing is enabled for the given tag.
+// Useful for skipping unnecessary argument computation if metatracing is off.
+inline bool IsEnabled(uint32_t tag) {
+  auto enabled_tags = g_enabled_tags.load(std::memory_order_relaxed);
+  return PERFETTO_UNLIKELY((enabled_tags & tag) != 0);
 }

-void Status::SetPayload(std::string_view type_url, std::string value) {
-  if (ok()) {
-    return;
-  }
-  for (auto& kv : payloads_) {
-    if (kv.type_url == type_url) {
-      kv.payload = value;
-      return;
-    }
+// Holds the data for a metatrace event or counter.
+struct Record {
+  static constexpr uint16_t kTypeMask = 0x8000;
+  static constexpr uint16_t kTypeCounter = 0x8000;
+  static constexpr uint16_t kTypeEvent = 0;
+
+  uint64_t timestamp_ns() const {
+    auto base_ns = g_enabled_timestamp.load(std::memory_order_relaxed);
+    PERFETTO_DCHECK(base_ns);
+    return base_ns + ((static_cast<uint64_t>(timestamp_ns_high) << 32) |
+                      timestamp_ns_low);
   }
-  payloads_.push_back(Payload{std::string(type_url), std::move(value)});
-}

-bool Status::ErasePayload(std::string_view type_url) {
-  if (ok()) {
-    return false;
+  void set_timestamp(uint64_t ts) {
+    auto t_start = g_enabled_timestamp.load(std::memory_order_relaxed);
+    uint64_t diff = ts - t_start;
+    PERFETTO_DCHECK(diff < (1ull << 48));
+    timestamp_ns_low = static_cast<uint32_t>(diff);
+    timestamp_ns_high = static_cast<uint16_t>(diff >> 32);
   }
-  auto it = std::remove_if(
-      payloads_.begin(), payloads_.end(),
-      [type_url](const Payload& p) { return p.type_url == type_url; });
-  bool erased = it != payloads_.end();
-  payloads_.erase(it, payloads_.end());
-  return erased;
-}

-}  // namespace perfetto::base
-// gen_amalgamated begin source: src/base/string_splitter.cc
-/*
- * 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.
- */
+  // We can't just memset() this class because on MSVC std::atomic<> is not
+  // trivially constructible anymore. Also std::atomic<> has a deleted copy
+  // constructor so we cant just do "*this = Record()" either.
+  // See http://bit.ly/339Jlzd .
+  void clear() {
+    this->~Record();
+    new (this) Record();
+  }

-// gen_amalgamated expanded: #include "perfetto/ext/base/string_splitter.h"
+  // This field holds the type (counter vs event) in the MSB and event ID (as
+  // defined in metatrace_events.h) in the lowest 15 bits. It is also used also
+  // as a linearization point: this is always written after all the other
+  // fields with a release-store. This is so the reader can determine whether it
+  // can safely process the other event fields after a load-acquire.
+  std::atomic<uint16_t> type_and_id{};

-#include <utility>
+  // Timestamp is stored as a 48-bits value diffed against g_enabled_timestamp.
+  // This gives us 78 hours from Enabled().
+  uint16_t timestamp_ns_high = 0;
+  uint32_t timestamp_ns_low = 0;

-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+  uint32_t thread_id = 0;

-namespace perfetto {
-namespace base {
+  union {
+    // Only one of the two elements can be zero initialized, clang complains
+    // about "initializing multiple members of union" otherwise.
+    uint32_t duration_ns = 0;  // If type == event.
+    int32_t counter_value;     // If type == counter.
+  };
+};

-StringSplitter::StringSplitter(std::string str,
-                               char delimiter,
-                               EmptyTokenMode empty_token_mode)
-    : str_(std::move(str)),
-      delimiter_(delimiter),
-      empty_token_mode_(empty_token_mode) {
-  // It's legal to access str[str.size()] in C++11 (it always returns \0),
-  // hence the +1 (which becomes just size() after the -1 in Initialize()).
-  Initialize(&str_[0], str_.size() + 1);
-}
+// Hold the meta-tracing data into a statically allocated array.
+// This class uses static storage (as opposite to being a singleton) to:
+// - Have the guarantee of always valid storage, so that meta-tracing can be
+//   safely used in any part of the codebase, including base/ itself.
+// - Avoid barriers that thread-safe static locals would require.
+class RingBuffer {
+ public:
+  static constexpr size_t kCapacity = 4096;  // 4096 * 16 bytes = 64K.

-StringSplitter::StringSplitter(char* str,
-                               size_t size,
-                               char delimiter,
-                               EmptyTokenMode empty_token_mode)
-    : delimiter_(delimiter), empty_token_mode_(empty_token_mode) {
-  Initialize(str, size);
-}
+  // This iterator is not idempotent and will bump the read index in the buffer
+  // at the end of the reads. There can be only one reader at any time.
+  // Usage: for (auto it = RingBuffer::GetReadIterator(); it; ++it) { it->... }
+  class ReadIterator {
+   public:
+    ReadIterator(ReadIterator&& other) {
+      PERFETTO_DCHECK(other.valid_);
+      cur_ = other.cur_;
+      end_ = other.end_;
+      valid_ = other.valid_;
+      other.valid_ = false;
+    }

-StringSplitter::StringSplitter(StringSplitter* outer,
-                               char delimiter,
-                               EmptyTokenMode empty_token_mode)
-    : delimiter_(delimiter), empty_token_mode_(empty_token_mode) {
-  Initialize(outer->cur_token(), outer->cur_token_size() + 1);
-}
+    ~ReadIterator() {
+      if (!valid_)
+        return;
+      PERFETTO_DCHECK(cur_ >= RingBuffer::rd_index_);
+      PERFETTO_DCHECK(cur_ <= RingBuffer::wr_index_);
+      RingBuffer::rd_index_.store(cur_, std::memory_order_release);
+    }

-void StringSplitter::Initialize(char* str, size_t size) {
-  PERFETTO_DCHECK(!size || str);
-  next_ = str;
-  end_ = str + size;
-  cur_ = nullptr;
-  cur_size_ = 0;
-  if (size)
-    next_[size - 1] = '\0';
-}
+    explicit operator bool() const { return cur_ < end_; }
+    const Record* operator->() const { return RingBuffer::At(cur_); }
+    const Record& operator*() const { return *operator->(); }

-bool StringSplitter::Next() {
-  for (; next_ < end_; next_++) {
-    if (*next_ == delimiter_ &&
-        empty_token_mode_ == EmptyTokenMode::DISALLOW_EMPTY_TOKENS) {
-      // If empty tokens are disallowed, find fist non-delimiter character.
-      continue;
-    }
-    cur_ = next_;
-    for (;; next_++) {
-      if (*next_ == delimiter_) {
-        cur_size_ = static_cast<size_t>(next_ - cur_);
-        *(next_++) = '\0';
-        break;
-      }
-      if (*next_ == '\0') {
-        cur_size_ = static_cast<size_t>(next_ - cur_);
-        next_ = end_;
-        break;
-      }
+    // This is for ++it. it++ is deliberately not supported.
+    ReadIterator& operator++() {
+      PERFETTO_DCHECK(cur_ < end_);
+      // Once a record has been read, mark it as free clearing its type_and_id,
+      // so if we encounter it in another read iteration while being written
+      // we know it's not fully written yet.
+      // The memory_order_relaxed below is enough because:
+      // - The reader is single-threaded and doesn't re-read the same records.
+      // - Before starting a read batch, the reader has an acquire barrier on
+      //   |rd_index_|.
+      // - After terminating a read batch, the ~ReadIterator dtor updates the
+      //   |rd_index_| with a release-store.
+      // - Reader and writer are typically kCapacity/2 apart. So unless an
+      //   overrun happens a writer won't reuse a newly released record any time
+      //   soon. If an overrun happens, everything is busted regardless.
+      At(cur_)->type_and_id.store(0, std::memory_order_relaxed);
+      ++cur_;
+      return *this;
     }
-    if (*cur_ || empty_token_mode_ == EmptyTokenMode::ALLOW_EMPTY_TOKENS)
-      return true;
-    break;
+
+   private:
+    friend class RingBuffer;
+    ReadIterator(uint64_t begin, uint64_t end)
+        : cur_(begin), end_(end), valid_(true) {}
+    ReadIterator& operator=(const ReadIterator&) = delete;
+    ReadIterator(const ReadIterator&) = delete;
+
+    uint64_t cur_;
+    uint64_t end_;
+    bool valid_;
+  };
+
+  static Record* At(uint64_t index) {
+    // Doesn't really have to be pow2, but if not the compiler will emit
+    // arithmetic operations to compute the modulo instead of a bitwise AND.
+    static_assert(!(kCapacity & (kCapacity - 1)), "kCapacity must be pow2");
+    PERFETTO_DCHECK(index >= rd_index_);
+    PERFETTO_DCHECK(index <= wr_index_);
+    return &records_[index % kCapacity];
   }
-  cur_ = nullptr;
-  cur_size_ = 0;
-  return false;
+
+  // Must be called on the same task runner passed to Enable()
+  static ReadIterator GetReadIterator() {
+    PERFETTO_DCHECK(RingBuffer::IsOnValidTaskRunner());
+    return ReadIterator(rd_index_.load(std::memory_order_acquire),
+                        wr_index_.load(std::memory_order_acquire));
+  }
+
+  static Record* AppendNewRecord();
+  static void Reset();
+
+  static bool has_overruns() {
+    return has_overruns_.load(std::memory_order_acquire);
+  }
+
+  // Can temporarily return a value >= kCapacity but is eventually consistent.
+  // This would happen in case of overruns until threads hit the --wr_index_
+  // in AppendNewRecord().
+  static uint64_t GetSizeForTesting() {
+    auto wr_index = wr_index_.load(std::memory_order_relaxed);
+    auto rd_index = rd_index_.load(std::memory_order_relaxed);
+    PERFETTO_DCHECK(wr_index >= rd_index);
+    return wr_index - rd_index;
+  }
+
+ private:
+  friend class ReadIterator;
+
+  // Returns true if the caller is on the task runner passed to Enable().
+  // Used only for DCHECKs.
+  static bool IsOnValidTaskRunner();
+
+  static std::array<Record, kCapacity> records_;
+  static std::atomic<bool> read_task_queued_;
+  static std::atomic<uint64_t> wr_index_;
+  static std::atomic<uint64_t> rd_index_;
+  static std::atomic<bool> has_overruns_;
+  static Record bankruptcy_record_;  // Used in case of overruns.
+};
+
+inline void TraceCounter(uint32_t tag, uint16_t id, int32_t value) {
+  // memory_order_relaxed is okay because the storage has static lifetime.
+  // It is safe to accidentally log an event soon after disabling.
+  auto enabled_tags = g_enabled_tags.load(std::memory_order_relaxed);
+  if (PERFETTO_LIKELY((enabled_tags & tag) == 0))
+    return;
+  Record* record = RingBuffer::AppendNewRecord();
+  record->thread_id = static_cast<uint32_t>(base::GetThreadId());
+  record->set_timestamp(TraceTimeNowNs());
+  record->counter_value = value;
+  record->type_and_id.store(Record::kTypeCounter | id,
+                            std::memory_order_release);
 }

-}  // namespace base
+class ScopedEvent {
+ public:
+  ScopedEvent(uint32_t tag, uint16_t event_id) {
+    auto enabled_tags = g_enabled_tags.load(std::memory_order_relaxed);
+    if (PERFETTO_LIKELY((enabled_tags & tag) == 0))
+      return;
+    event_id_ = event_id;
+    record_ = RingBuffer::AppendNewRecord();
+    record_->thread_id = static_cast<uint32_t>(base::GetThreadId());
+    record_->set_timestamp(TraceTimeNowNs());
+  }
+
+  ~ScopedEvent() {
+    if (PERFETTO_LIKELY(!record_))
+      return;
+    auto now = TraceTimeNowNs();
+    record_->duration_ns = static_cast<uint32_t>(now - record_->timestamp_ns());
+    record_->type_and_id.store(Record::kTypeEvent | event_id_,
+                               std::memory_order_release);
+  }
+
+ private:
+  Record* record_ = nullptr;
+  uint16_t event_id_ = 0;
+  ScopedEvent(const ScopedEvent&) = delete;
+  ScopedEvent& operator=(const ScopedEvent&) = delete;
+};
+
+// Boilerplate to derive a unique variable name for the event.
+#define PERFETTO_METATRACE_UID2(a, b) a##b
+#define PERFETTO_METATRACE_UID(x) PERFETTO_METATRACE_UID2(metatrace_, x)
+
+#define PERFETTO_METATRACE_SCOPED(TAG, ID)                                \
+  ::perfetto::metatrace::ScopedEvent PERFETTO_METATRACE_UID(__COUNTER__)( \
+      ::perfetto::metatrace::TAG, ::perfetto::metatrace::ID)
+
+#define PERFETTO_METATRACE_COUNTER(TAG, ID, VALUE)                \
+  ::perfetto::metatrace::TraceCounter(::perfetto::metatrace::TAG, \
+                                      ::perfetto::metatrace::ID,  \
+                                      static_cast<int32_t>(VALUE))
+
+}  // namespace metatrace
 }  // namespace perfetto
-// gen_amalgamated begin source: src/base/string_utils.cc
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_METATRACE_H_
 /*
  * Copyright (C) 2018 The Android Open Source Project
  *
@@ -8710,393 +9157,303 @@ bool StringSplitter::Next() {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
-
-#include <locale.h>
-#include <stdarg.h>
-#include <string.h>
+// gen_amalgamated expanded: #include "perfetto/ext/base/metatrace.h"

-#include <algorithm>
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_annotations.h"

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-#include <xlocale.h>
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <windows.h>
-#endif
+namespace perfetto {
+namespace metatrace {

-#include <cinttypes>
+std::atomic<uint32_t> g_enabled_tags{0};
+std::atomic<uint64_t> g_enabled_timestamp{0};

-// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// static members
+std::array<Record, RingBuffer::kCapacity> RingBuffer::records_;
+std::atomic<bool> RingBuffer::read_task_queued_;
+std::atomic<uint64_t> RingBuffer::wr_index_;
+std::atomic<uint64_t> RingBuffer::rd_index_;
+std::atomic<bool> RingBuffer::has_overruns_;
+Record RingBuffer::bankruptcy_record_;

-namespace perfetto {
-namespace base {
+namespace {

-// Locale-independent as possible version of strtod.
-double StrToD(const char* nptr, char** endptr) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) ||           \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-  static auto c_locale = newlocale(LC_ALL, "C", nullptr);
-  return strtod_l(nptr, endptr, c_locale);
-#else
-  return strtod(nptr, endptr);
-#endif
-}
+// std::function<> is not trivially de/constructible. This struct wraps it in a
+// heap-allocated struct to avoid static initializers.
+struct Delegate {
+  static Delegate* GetInstance() {
+    static Delegate* instance = new Delegate();
+    return instance;
+  }

-bool StartsWith(const std::string& str, const std::string& prefix) {
-  return str.compare(0, prefix.length(), prefix) == 0;
-}
+  base::TaskRunner* task_runner = nullptr;
+  std::function<void()> read_task;
+};

-bool StartsWithAny(const std::string& str,
-                   const std::vector<std::string>& prefixes) {
-  return std::any_of(
-      prefixes.begin(), prefixes.end(),
-      [&str](const std::string& prefix) { return StartsWith(str, prefix); });
-}
+}  // namespace

-bool EndsWith(const std::string& str, const std::string& suffix) {
-  if (suffix.size() > str.size())
+bool Enable(std::function<void()> read_task,
+            base::TaskRunner* task_runner,
+            uint32_t tags) {
+  PERFETTO_DCHECK(read_task);
+  PERFETTO_DCHECK(task_runner->RunsTasksOnCurrentThread());
+  if (g_enabled_tags.load(std::memory_order_acquire))
     return false;
-  return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
-}

-bool Contains(const std::string& haystack, const std::string& needle) {
-  return haystack.find(needle) != std::string::npos;
+  Delegate* dg = Delegate::GetInstance();
+  dg->task_runner = task_runner;
+  dg->read_task = std::move(read_task);
+  RingBuffer::Reset();
+  g_enabled_timestamp.store(TraceTimeNowNs(), std::memory_order_relaxed);
+  g_enabled_tags.store(tags, std::memory_order_release);
+  return true;
 }

-bool Contains(const std::string& haystack, const char needle) {
-  return haystack.find(needle) != std::string::npos;
+void Disable() {
+  g_enabled_tags.store(0, std::memory_order_release);
+  Delegate* dg = Delegate::GetInstance();
+  PERFETTO_DCHECK(!dg->task_runner ||
+                  dg->task_runner->RunsTasksOnCurrentThread());
+  dg->task_runner = nullptr;
+  dg->read_task = nullptr;
 }

-bool Contains(const std::vector<std::string>& haystack,
-              const std::string& needle) {
-  return std::find(haystack.begin(), haystack.end(), needle) != haystack.end();
+// static
+void RingBuffer::Reset() {
+  bankruptcy_record_.clear();
+  for (Record& record : records_)
+    record.clear();
+  wr_index_ = 0;
+  rd_index_ = 0;
+  has_overruns_ = false;
+  read_task_queued_ = false;
 }

-size_t Find(const StringView& needle, const StringView& haystack) {
-  if (needle.empty())
-    return 0;
-  if (needle.size() > haystack.size())
-    return std::string::npos;
-  for (size_t i = 0; i < haystack.size() - (needle.size() - 1); ++i) {
-    if (strncmp(haystack.data() + i, needle.data(), needle.size()) == 0)
-      return i;
-  }
-  return std::string::npos;
-}
+// static
+Record* RingBuffer::AppendNewRecord() {
+  auto wr_index = wr_index_.fetch_add(1, std::memory_order_acq_rel);

-bool CaseInsensitiveEqual(const std::string& first, const std::string& second) {
-  return first.size() == second.size() &&
-         std::equal(
-             first.begin(), first.end(), second.begin(),
-             [](char a, char b) { return Lowercase(a) == Lowercase(b); });
-}
+  // rd_index can only monotonically increase, we don't care if we read an
+  // older value, we'll just hit the slow-path a bit earlier if it happens.
+  auto rd_index = rd_index_.load(std::memory_order_relaxed);

-std::string Join(const std::vector<std::string>& parts,
-                 const std::string& delim) {
-  std::string acc;
-  for (size_t i = 0; i < parts.size(); ++i) {
-    acc += parts[i];
-    if (i + 1 != parts.size()) {
-      acc += delim;
+  PERFETTO_DCHECK(wr_index >= rd_index);
+  auto size = wr_index - rd_index;
+  if (PERFETTO_LIKELY(size < kCapacity / 2))
+    return At(wr_index);
+
+  // Slow-path: Enqueue the read task and handle overruns.
+  bool expected = false;
+  if (RingBuffer::read_task_queued_.compare_exchange_strong(expected, true)) {
+    Delegate* dg = Delegate::GetInstance();
+    if (dg->task_runner) {
+      dg->task_runner->PostTask([] {
+        // Meta-tracing might have been disabled in the meantime.
+        auto read_task = Delegate::GetInstance()->read_task;
+        if (read_task)
+          read_task();
+        RingBuffer::read_task_queued_ = false;
+      });
     }
   }
-  return acc;
-}

-std::vector<std::string> SplitString(const std::string& text,
-                                     const std::string& delimiter) {
-  PERFETTO_CHECK(!delimiter.empty());
+  if (PERFETTO_LIKELY(size < kCapacity))
+    return At(wr_index);

-  std::vector<std::string> output;
-  size_t start = 0;
-  size_t next;
-  for (;;) {
-    next = std::min(text.find(delimiter, start), text.size());
-    if (next > start)
-      output.emplace_back(&text[start], next - start);
-    start = next + delimiter.size();
-    if (start >= text.size())
-      break;
-  }
-  return output;
-}
+  has_overruns_.store(true, std::memory_order_release);
+  wr_index_.fetch_sub(1, std::memory_order_acq_rel);

-std::string_view TrimWhitespace(std::string_view str) {
-  static constexpr char kWhitespaces[] = "\t\n ";
-  size_t front_idx = str.find_first_not_of(kWhitespaces);
-  if (front_idx == std::string_view::npos)
-    return {};
-  std::string_view front_trimmed = str.substr(front_idx);
-  size_t end_idx = front_trimmed.find_last_not_of(kWhitespaces);
-  PERFETTO_DCHECK(end_idx != std::string_view::npos);
-  return front_trimmed.substr(0, end_idx + 1);
+  // In the case of overflows, threads will race writing on the same memory
+  // location and TSan will rightly complain. This is fine though because nobody
+  // will read the bankruptcy record and it's designed to contain garbage.
+  PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(&bankruptcy_record_, sizeof(Record),
+                                      "nothing reads bankruptcy_record_")
+  return &bankruptcy_record_;
 }

-std::string TrimWhitespace(const std::string& str) {
-  return std::string(TrimWhitespace(std::string_view(str)));
+// static
+bool RingBuffer::IsOnValidTaskRunner() {
+  auto* task_runner = Delegate::GetInstance()->task_runner;
+  return task_runner && task_runner->RunsTasksOnCurrentThread();
 }

-std::string_view TrimWhitespace(const char* str) {
-  return TrimWhitespace(std::string_view(str));
-}
+}  // namespace metatrace
+}  // namespace perfetto
+// gen_amalgamated begin source: src/base/paged_memory.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/paged_memory.h
+// gen_amalgamated begin header: include/perfetto/ext/base/container_annotations.h
+/*
+ * 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.
+ */

-std::string StripPrefix(const std::string& str, const std::string& prefix) {
-  return StartsWith(str, prefix) ? str.substr(prefix.size()) : str;
-}
+#ifndef INCLUDE_PERFETTO_EXT_BASE_CONTAINER_ANNOTATIONS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_CONTAINER_ANNOTATIONS_H_

-std::string StripSuffix(const std::string& str, const std::string& suffix) {
-  return EndsWith(str, suffix) ? str.substr(0, str.size() - suffix.size())
-                               : str;
-}
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-std::string ToUpper(const std::string& str) {
-  // Don't use toupper(), it depends on the locale.
-  std::string res(str);
-  auto end = res.end();
-  for (auto c = res.begin(); c != end; ++c)
-    *c = Uppercase(*c);
-  return res;
-}
+// Windows ASAN doesn't currently support these annotations.
+#if defined(ADDRESS_SANITIZER) && !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
+    !defined(ADDRESS_SANITIZER_WITHOUT_INSTRUMENTATION)

-std::string ToLower(const std::string& str) {
-  // Don't use tolower(), it depends on the locale.
-  std::string res(str);
-  auto end = res.end();
-  for (auto c = res.begin(); c != end; ++c)
-    *c = Lowercase(*c);
-  return res;
-}
+#include <sanitizer/common_interface_defs.h>

-std::string ToHex(const char* data, size_t size) {
-  std::string hex(2 * size + 1, 'x');
-  for (size_t i = 0; i < size; ++i) {
-    // snprintf prints 3 characters, the two hex digits and a null byte. As we
-    // write left to right, we keep overwriting the nullbytes, except for the
-    // last call to snprintf.
-    snprintf(&(hex[2 * i]), 3, "%02hhx", data[i]);
+#define ANNOTATE_NEW_BUFFER(buffer, capacity, new_size)                      \
+  if (buffer) {                                                              \
+    __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
+                                              (buffer) + (capacity),         \
+                                              (buffer) + (new_size));        \
   }
-  // Remove the trailing nullbyte produced by the last snprintf.
-  hex.resize(2 * size);
-  return hex;
-}
-
-std::string IntToHexString(uint32_t number) {
-  size_t max_size = 11;  // Max uint32 is 0xFFFFFFFF + 1 for null byte.
-  std::string buf;
-  buf.resize(max_size);
-  size_t final_len = SprintfTrunc(&buf[0], max_size, "0x%02x", number);
-  buf.resize(static_cast<size_t>(final_len));  // Cuts off the final null byte.
-  return buf;
-}
+#define ANNOTATE_DELETE_BUFFER(buffer, capacity, old_size)                   \
+  if (buffer) {                                                              \
+    __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
+                                              (buffer) + (old_size),         \
+                                              (buffer) + (capacity));        \
+  }
+#define ANNOTATE_CHANGE_SIZE(buffer, capacity, old_size, new_size)           \
+  if (buffer) {                                                              \
+    __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
+                                              (buffer) + (old_size),         \
+                                              (buffer) + (new_size));        \
+  }
+#define ANNOTATE_CHANGE_CAPACITY(buffer, old_capacity, buffer_size, \
+                                 new_capacity)                      \
+  ANNOTATE_DELETE_BUFFER(buffer, old_capacity, buffer_size);        \
+  ANNOTATE_NEW_BUFFER(buffer, new_capacity, buffer_size);
+// Annotations require buffers to begin on an 8-byte boundary.
+#else  // defined(ADDRESS_SANITIZER)
+#define ANNOTATE_NEW_BUFFER(buffer, capacity, new_size)
+#define ANNOTATE_DELETE_BUFFER(buffer, capacity, old_size)
+#define ANNOTATE_CHANGE_SIZE(buffer, capacity, old_size, new_size)
+#define ANNOTATE_CHANGE_CAPACITY(buffer, old_capacity, buffer_size, \
+                                 new_capacity)
+#endif  // defined(ADDRESS_SANITIZER)

-std::string Uint64ToHexString(uint64_t number) {
-  return "0x" + Uint64ToHexStringNoPrefix(number);
-}
+#endif  // INCLUDE_PERFETTO_EXT_BASE_CONTAINER_ANNOTATIONS_H_
+/*
+ * 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.
+ */

-std::string Uint64ToHexStringNoPrefix(uint64_t number) {
-  size_t max_size = 17;  // Max uint64 is FFFFFFFFFFFFFFFF + 1 for null byte.
-  std::string buf;
-  buf.resize(max_size);
-  size_t final_len = SprintfTrunc(&buf[0], max_size, "%" PRIx64 "", number);
-  buf.resize(static_cast<size_t>(final_len));  // Cuts off the final null byte.
-  return buf;
-}
+#ifndef INCLUDE_PERFETTO_EXT_BASE_PAGED_MEMORY_H_
+#define INCLUDE_PERFETTO_EXT_BASE_PAGED_MEMORY_H_

-std::string StripChars(const std::string& str,
-                       const std::string& chars,
-                       char replacement) {
-  std::string res(str);
-  const char* start = res.c_str();
-  const char* remove = chars.c_str();
-  for (const char* c = strpbrk(start, remove); c; c = strpbrk(c + 1, remove))
-    res[static_cast<uintptr_t>(c - start)] = replacement;
-  return res;
-}
+#include <cstddef>
+#include <memory>

-std::string ReplaceAll(std::string str,
-                       const std::string& to_replace,
-                       const std::string& replacement) {
-  PERFETTO_CHECK(!to_replace.empty());
-  size_t pos = 0;
-  while ((pos = str.find(to_replace, pos)) != std::string::npos) {
-    str.replace(pos, to_replace.length(), replacement);
-    pos += replacement.length();
-  }
-  return str;
-}
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/container_annotations.h"

-bool CheckAsciiAndRemoveInvalidUTF8(base::StringView str, std::string& output) {
-  bool is_ascii = std::all_of(str.begin(), str.end(), [](char c) {
-    return (static_cast<unsigned char>(c) & 0b10000000) == 0b00000000;
-  });
-  if (is_ascii) {
-    return true;
-  }
+// We need to track the committed size on windows and when ASAN is enabled.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) || defined(ADDRESS_SANITIZER)
+#define TRACK_COMMITTED_SIZE() 1
+#else
+#define TRACK_COMMITTED_SIZE() 0
+#endif

-  // https://www.rfc-editor.org/rfc/rfc3629.txt
-  output.clear();
-  output.reserve(str.size());
-  for (size_t i = 0; i < str.size();) {
-    unsigned char c = static_cast<unsigned char>(str.data()[i]);
-    size_t num_bytes = 0;
-    bool valid_sequence = true;
+namespace perfetto {
+namespace base {

-    if ((c & 0b10000000) == 0b00000000) {
-      num_bytes = 1;
-    } else if ((c & 0b11100000) == 0b11000000) {
-      num_bytes = 2;
-    } else if ((c & 0b11110000) == 0b11100000) {
-      num_bytes = 3;
-    } else if ((c & 0b11111000) == 0b11110000) {
-      num_bytes = 4;
-    } else {
-      valid_sequence = false;
-      // Skip this byte
-      num_bytes = 1;
-    }
+class PagedMemory {
+ public:
+  // Initializes an invalid PagedMemory pointing to nullptr.
+  PagedMemory();

-    if (valid_sequence) {
-      // Check if enough bytes are available in the string
-      if (i + num_bytes > str.size()) {
-        valid_sequence = false;
-        num_bytes = 1;  // Treat as a single invalid byte for advancement
-      } else {
-        // Check for overlong encodings, surrogates, and out-of-range
-        if (num_bytes == 2 && c < 0b11000010) {  // 0xC2
-          valid_sequence = false;                // Overlong
-        } else if (num_bytes == 3) {
-          unsigned char byte2 = static_cast<unsigned char>(str.data()[i + 1]);
-          if ((c == 0b11100000 && byte2 < 0b10100000) ||   // Overlong E0
-              (c == 0b11101101 && byte2 >= 0b10100000)) {  // Surrogate ED
-            valid_sequence = false;
-          }
-        } else if (num_bytes == 4) {
-          unsigned char byte2 = static_cast<unsigned char>(str.data()[i + 1]);
-          if ((c == 0b11110000 && byte2 < 0b10010000) ||  // Overlong F0
-              (c == 0b11110100 && byte2 > 0b10001111)) {  // Out of range F4
-            valid_sequence = false;
-          }
-        }
+  ~PagedMemory();

-        if (valid_sequence && num_bytes > 1) {
-          for (size_t j = 1; j < num_bytes; ++j) {
-            unsigned char continuation_byte =
-                static_cast<unsigned char>(str.data()[i + j]);
-            if ((continuation_byte & 0b11000000) != 0b10000000) {
-              valid_sequence = false;
-              break;
-            }
-          }
-        }
-      }
-    }
+  PagedMemory(PagedMemory&& other) noexcept;
+  PagedMemory& operator=(PagedMemory&& other);

-    if (valid_sequence) {
-      for (size_t j = 0; j < num_bytes; ++j) {
-        output.push_back(str.data()[i + j]);
-      }
-    }
+  enum AllocationFlags {
+    // By default, Allocate() crashes if the underlying mmap fails (e.g., if out
+    // of virtual address space). When this flag is provided, an invalid
+    // PagedMemory pointing to nullptr is returned in this case instead.
+    kMayFail = 1 << 0,

-    i += num_bytes;
-  }
-  return false;
-}
+    // By default, Allocate() commits the allocated memory immediately. When
+    // this flag is provided, the memory virtual address space may only be
+    // reserved and the user should call EnsureCommitted() before writing to
+    // memory addresses.
+    kDontCommit = 1 << 1,
+  };

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-bool WideToUTF8(const std::wstring& source, std::string& output) {
-  if (source.empty() ||
-      source.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
-    return false;
-  }
-  int size = ::WideCharToMultiByte(CP_UTF8, 0, &source[0],
-                                   static_cast<int>(source.size()), nullptr, 0,
-                                   nullptr, nullptr);
-  output.assign(static_cast<size_t>(size), '\0');
-  if (::WideCharToMultiByte(CP_UTF8, 0, &source[0],
-                            static_cast<int>(source.size()), &output[0], size,
-                            nullptr, nullptr) != size) {
-    return false;
-  }
-  return true;
-}
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // Allocates |size| bytes using mmap(MAP_ANONYMOUS). The returned memory is
+  // guaranteed to be page-aligned and guaranteed to be zeroed.
+  // For |flags|, see the AllocationFlags enum above.
+  static PagedMemory Allocate(size_t size, int flags = 0);

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-bool UTF8ToWide(const std::string& source, std::wstring& output) {
-  if (source.empty() ||
-      source.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
-    return false;
-  }
-  int size = ::MultiByteToWideChar(CP_UTF8, 0, &source[0],
-                                   static_cast<int>(source.size()), nullptr, 0);
-  output.assign(static_cast<size_t>(size), L'\0');
-  if (::MultiByteToWideChar(CP_UTF8, 0, &source[0],
-                            static_cast<int>(source.size()), &output[0],
-                            size) != size) {
-    return false;
-  }
-  return true;
-}
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // Hint to the OS that the whole pages within [offset, size()) are not needed
+  // and can be discarded.
+  // Partial pages at both ends of the range are preserved.
+  // The memory remains accessible, and the discarded pages may keep their
+  // contents or be zeroed.
+  // This function may be a NOP on some platforms.
+  // Requires a valid allocation and |offset| <= size().
+  void AdviseDontNeedAfter(size_t offset);

-size_t SprintfTrunc(char* dst, size_t dst_size, const char* fmt, ...) {
-  if (PERFETTO_UNLIKELY(dst_size == 0))
-    return 0;
+  // Ensures that at least the first |committed_size| bytes of the allocated
+  // memory region are committed. The implementation may commit memory in larger
+  // chunks above |committed_size|. Crashes if the memory couldn't be committed.
+#if TRACK_COMMITTED_SIZE()
+  void EnsureCommitted(size_t committed_size);
+#else   // TRACK_COMMITTED_SIZE()
+  void EnsureCommitted(size_t /*committed_size*/) {}
+#endif  // TRACK_COMMITTED_SIZE()

-  va_list args;
-  va_start(args, fmt);
-  int src_size = vsnprintf(dst, dst_size, fmt, args);
-  va_end(args);
+  inline void* Get() const noexcept { return p_; }
+  inline bool IsValid() const noexcept { return !!p_; }
+  inline size_t size() const noexcept { return size_; }

-  if (PERFETTO_UNLIKELY(src_size <= 0)) {
-    dst[0] = '\0';
-    return 0;
-  }
+ private:
+  PagedMemory(char* p, size_t size);

-  size_t res;
-  if (PERFETTO_LIKELY(src_size < static_cast<int>(dst_size))) {
-    // Most common case.
-    res = static_cast<size_t>(src_size);
-  } else {
-    // Truncation case.
-    res = dst_size - 1;
-  }
+  PagedMemory(const PagedMemory&) = delete;
+  // Defaulted for implementation of move constructor + assignment.
+  PagedMemory& operator=(const PagedMemory&) = default;

-  PERFETTO_DCHECK(res < dst_size);
-  PERFETTO_DCHECK(dst[res] == '\0');
-  return res;
-}
+  char* p_ = nullptr;

-std::optional<LineWithOffset> FindLineWithOffset(base::StringView str,
-                                                 uint32_t offset) {
-  static constexpr char kNewLine = '\n';
-  uint32_t line_offset = 0;
-  uint32_t line_count = 1;
-  for (uint32_t i = 0; i < str.size(); ++i) {
-    if (str.at(i) == kNewLine) {
-      line_offset = i + 1;
-      line_count++;
-      continue;
-    }
-    if (i == offset) {
-      size_t end_offset = str.find(kNewLine, i);
-      if (end_offset == std::string::npos) {
-        end_offset = str.size();
-      }
-      base::StringView line = str.substr(line_offset, end_offset - line_offset);
-      return LineWithOffset{line, offset - line_offset, line_count};
-    }
-  }
-  return std::nullopt;
-}
+  // The size originally passed to Allocate(). The actual virtual memory
+  // reservation will be larger due to: (i) guard pages; (ii) rounding up to
+  // the system page size.
+  size_t size_ = 0;
+
+#if TRACK_COMMITTED_SIZE()
+  size_t committed_size_ = 0u;
+#endif  // TRACK_COMMITTED_SIZE()
+};

 }  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/base/string_view.cc
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_PAGED_MEMORY_H_
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -9111,33 +9468,168 @@ std::optional<LineWithOffset> FindLineWithOffset(base::StringView str,
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/paged_memory.h"
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <windows.h>
+#else  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <sys/mman.h>
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/container_annotations.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"

 namespace perfetto {
 namespace base {

-// Without ignoring this warning we get the message:
-//   error: out-of-line definition of constexpr static data member is redundant
-//   in C++17 and is deprecated
-// when using clang-cl in Windows.
-#if defined(__GNUC__)  // GCC & clang
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated"
-#endif  // __GNUC__
+namespace {
+
+#if TRACK_COMMITTED_SIZE()
+constexpr size_t kCommitChunkSize = 4 * 1024 * 1024;  // 4MB
+#endif
+
+size_t RoundUpToSysPageSize(size_t req_size) {
+  const size_t page_size = GetSysPageSize();
+  return (req_size + page_size - 1) & ~(page_size - 1);
+}
+
+size_t GuardSize() {
+  return GetSysPageSize();
+}
+
+}  // namespace

 // static
-constexpr size_t StringView::npos;
+PagedMemory PagedMemory::Allocate(size_t req_size, int flags) {
+  size_t rounded_up_size = RoundUpToSysPageSize(req_size);
+  PERFETTO_CHECK(rounded_up_size >= req_size);
+  size_t outer_size = rounded_up_size + GuardSize() * 2;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  void* ptr = VirtualAlloc(nullptr, outer_size, MEM_RESERVE, PAGE_NOACCESS);
+  if (!ptr && (flags & kMayFail))
+    return PagedMemory();
+  PERFETTO_CHECK(ptr);
+  char* usable_region = reinterpret_cast<char*>(ptr) + GuardSize();
+#else   // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  void* ptr = mmap(nullptr, outer_size, PROT_READ | PROT_WRITE,
+                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (ptr == MAP_FAILED && (flags & kMayFail))
+    return PagedMemory();
+  PERFETTO_CHECK(ptr && ptr != MAP_FAILED);
+  char* usable_region = reinterpret_cast<char*>(ptr) + GuardSize();
+  int res = mprotect(ptr, GuardSize(), PROT_NONE);
+  res |= mprotect(usable_region + rounded_up_size, GuardSize(), PROT_NONE);
+  PERFETTO_CHECK(res == 0);
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)

-#if defined(__GNUC__)
-#pragma GCC diagnostic pop
-#endif
+  auto memory = PagedMemory(usable_region, req_size);
+#if TRACK_COMMITTED_SIZE()
+  size_t initial_commit = req_size;
+  if (flags & kDontCommit)
+    initial_commit = std::min(initial_commit, kCommitChunkSize);
+  memory.EnsureCommitted(initial_commit);
+#endif  // TRACK_COMMITTED_SIZE()
+  return memory;
+}
+
+PagedMemory::PagedMemory() {}
+
+// clang-format off
+PagedMemory::PagedMemory(char* p, size_t size) : p_(p), size_(size) {
+  ANNOTATE_NEW_BUFFER(p_, size_, committed_size_)
+}
+
+PagedMemory::PagedMemory(PagedMemory&& other) noexcept {
+  *this = other;
+  other.p_ = nullptr;
+}
+// clang-format on
+
+PagedMemory& PagedMemory::operator=(PagedMemory&& other) {
+  this->~PagedMemory();
+  new (this) PagedMemory(std::move(other));
+  return *this;
+}
+
+PagedMemory::~PagedMemory() {
+  if (!p_)
+    return;
+  PERFETTO_CHECK(size_);
+  char* start = p_ - GuardSize();
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  BOOL res = VirtualFree(start, 0, MEM_RELEASE);
+  PERFETTO_CHECK(res != 0);
+#else   // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  const size_t outer_size = RoundUpToSysPageSize(size_) + GuardSize() * 2;
+  int res = munmap(start, outer_size);
+  PERFETTO_CHECK(res == 0);
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  ANNOTATE_DELETE_BUFFER(p_, size_, committed_size_)
+}
+
+void PagedMemory::AdviseDontNeedAfter(size_t offset) {
+  PERFETTO_CHECK(p_ && offset <= size_);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) || PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
+  // Discarding pages on Windows has more CPU cost than is justified for the
+  // possible memory savings.
+#else
+  const size_t release_begin = RoundUpToSysPageSize(offset);
+  const size_t release_end = AlignDown(size_, GetSysPageSize());
+  if (release_begin >= release_end)
+    return;
+  char* const release_ptr = p_ + release_begin;
+  const size_t release_size = release_end - release_begin;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_QNX)
+  int res = posix_madvise(release_ptr, release_size, POSIX_MADV_DISCARD_NP);
+#else
+  // http://man7.org/linux/man-pages/man2/madvise.2.html
+  int res = madvise(release_ptr, release_size, MADV_DONTNEED);
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_QNX)
+  PERFETTO_DCHECK(res == 0);
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) ||
+        // PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
+}
+
+#if TRACK_COMMITTED_SIZE()
+void PagedMemory::EnsureCommitted(size_t committed_size) {
+  PERFETTO_DCHECK(committed_size <= size_);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  if (committed_size_ >= committed_size)
+    return;
+  // Rounding up.
+  size_t delta = committed_size - committed_size_;
+  size_t num_additional_chunks =
+      (delta + kCommitChunkSize - 1) / kCommitChunkSize;
+  PERFETTO_DCHECK(num_additional_chunks * kCommitChunkSize >= delta);
+  // Don't commit more than the total size.
+  size_t commit_size = std::min(num_additional_chunks * kCommitChunkSize,
+                                size_ - committed_size_);
+  void* res = VirtualAlloc(p_ + committed_size_, commit_size, MEM_COMMIT,
+                           PAGE_READWRITE);
+  PERFETTO_CHECK(res);
+  ANNOTATE_CHANGE_SIZE(p_, size_, committed_size_,
+                       committed_size_ + commit_size)
+  committed_size_ += commit_size;
+#else   // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // mmap commits automatically as needed, so we only track here for ASAN.
+  committed_size = std::max(committed_size_, committed_size);
+  ANNOTATE_CHANGE_SIZE(p_, size_, committed_size_, committed_size)
+  committed_size_ = committed_size;
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+}
+#endif  // TRACK_COMMITTED_SIZE()

 }  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/base/string_view_splitter.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/string_view_splitter.h
+// gen_amalgamated begin source: src/base/periodic_task.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/periodic_task.h
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -9152,77 +9644,82 @@ constexpr size_t StringView::npos;
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_SPLITTER_H_
-#define INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_SPLITTER_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_PERIODIC_TASK_H_
+#define INCLUDE_PERFETTO_EXT_BASE_PERIODIC_TASK_H_

-// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
+#include <functional>
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/weak_ptr.h"

 namespace perfetto {
 namespace base {

-// C++ version of strtok(). Splits a StringView without making copies or any
-// heap allocations. Supports the special case of using \0 as a delimiter.
-// The token returned in output are valid as long as the input string is valid.
-class StringViewSplitter {
+class TaskRunner;
+
+// A periodic task utility class. It wraps the logic necessary to do periodic
+// tasks using a TaskRunner, taking care of subtleties like ensuring that
+// outstanding tasks are cancelled after reset/dtor.
+// Tasks are aligned on wall time (unless they are |one_shot|). This is to
+// ensure that when using multiple periodic tasks, they happen at the same time,
+// minimizing context switches.
+// On Linux/Android it also supports suspend-aware mode (via timerfd). On other
+// operating systems it falls back to PostDelayedTask, which is not
+// suspend-aware.
+// TODO(primiano): this should probably become a periodic timer scheduler, so we
+// can use one FD for everything rather than one FD per task. For now we take
+// the hit of a FD-per-task to keep this low-risk.
+// TODO(primiano): consider renaming this class to TimerTask. When |one_shot|
+// is set, the "Periodic" part of the class name becomes a lie.
+class PeriodicTask {
  public:
-  // Whether an empty string (two delimiters side-to-side) is a valid token.
-  enum class EmptyTokenMode {
-    DISALLOW_EMPTY_TOKENS,
-    ALLOW_EMPTY_TOKENS,
+  explicit PeriodicTask(base::TaskRunner*);
+  ~PeriodicTask();  // Calls Reset().

-    DEFAULT = DISALLOW_EMPTY_TOKENS,
+  struct Args {
+    uint32_t period_ms = 0;
+    std::function<void()> task = nullptr;
+    bool start_first_task_immediately = false;
+    bool use_suspend_aware_timer = false;
+    bool one_shot = false;
   };

-  // Can take ownership of the string if passed via std::move(), e.g.:
-  // StringViewSplitter(std::move(str), '\n');
-  StringViewSplitter(base::StringView,
-                     char delimiter,
-                     EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);
-
-  // Splits the current token from an outer StringViewSplitter instance. This is
-  // to chain splitters as follows: for (base::StringViewSplitter lines(x,
-  // '\n'); ss.Next();)
-  //   for (base::StringViewSplitter words(&lines, ' '); words.Next();)
-  StringViewSplitter(StringViewSplitter*,
-                     char delimiter,
-                     EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);
-
-  // Returns true if a token is found (in which case it will be stored in
-  // cur_token()), false if no more tokens are found.
-  bool Next();
+  void Start(Args);

-  // Returns the next token if, found (in which case it will be stored in
-  // cur_token()), and the empty string if no more tokens are found.
-  base::StringView NextToken() { return Next() ? cur_token() : ""; }
+  // Safe to be called multiple times, even without calling Start():
+  void Reset();

-  // Returns the current token iff last call to Next() returned true.
-  // In all other cases (before the 1st call to Next() and after Next() returns
-  // false) returns the empty string.
-  base::StringView cur_token() { return cur_; }
+  // No copy or move. WeakPtr-wrapped pointers to |this| are posted on the
+  // task runner, this class is not easily movable.
+  PeriodicTask(const PeriodicTask&) = delete;
+  PeriodicTask& operator=(const PeriodicTask&) = delete;
+  PeriodicTask(PeriodicTask&&) = delete;
+  PeriodicTask& operator=(PeriodicTask&&) = delete;

-  // Returns the remainder of the current input string that has not yet been
-  // tokenized.
-  base::StringView remainder() { return next_; }
+  base::PlatformHandle timer_fd_for_testing() { return *timer_fd_; }

  private:
-  StringViewSplitter(const StringViewSplitter&) = delete;
-  StringViewSplitter& operator=(const StringViewSplitter&) = delete;
-  void Initialize(base::StringView);
+  static void RunTaskAndPostNext(base::WeakPtr<PeriodicTask>,
+                                 uint32_t generation);
+  void PostNextTask();
+  void ResetTimerFd();

-  base::StringView str_;
-  base::StringView cur_;
-  base::StringView next_;
-  bool end_of_input_;
-  const char delimiter_;
-  const EmptyTokenMode empty_token_mode_;
+  base::TaskRunner* const task_runner_;
+  Args args_;
+  uint32_t generation_ = 0;
+  base::ScopedPlatformHandle timer_fd_;
+
+  PERFETTO_THREAD_CHECKER(thread_checker_)
+  base::WeakPtrFactory<PeriodicTask> weak_ptr_factory_;  // Keep last.
 };

 }  // namespace base
 }  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_SPLITTER_H_
+#endif  // INCLUDE_PERFETTO_EXT_BASE_PERIODIC_TASK_H_
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -9237,170 +9734,178 @@ class StringViewSplitter {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/string_view_splitter.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/periodic_task.h"

-#include <utility>
+#include <limits>

+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) ||           \
+    (PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && __ANDROID_API__ >= 19)
+#include <sys/timerfd.h>
+#endif

 namespace perfetto {
 namespace base {

-StringViewSplitter::StringViewSplitter(base::StringView str,
-                                       char delimiter,
-                                       EmptyTokenMode empty_token_mode)
-    : str_(std::move(str)),
-      delimiter_(delimiter),
-      empty_token_mode_(empty_token_mode) {
-  Initialize(str);
-}
+namespace {

-StringViewSplitter::StringViewSplitter(StringViewSplitter* outer,
-                                       char delimiter,
-                                       EmptyTokenMode empty_token_mode)
-    : delimiter_(delimiter), empty_token_mode_(empty_token_mode) {
-  Initialize(outer->cur_token());
-}
+uint32_t GetNextDelayMs(const TimeMillis& now_ms,
+                        const PeriodicTask::Args& args) {
+  if (args.one_shot)
+    return args.period_ms;

-void StringViewSplitter::Initialize(base::StringView str) {
-  next_ = str;
-  cur_ = "";
-  end_of_input_ = false;
+  return args.period_ms -
+         static_cast<uint32_t>(now_ms.count() % args.period_ms);
 }

-bool StringViewSplitter::Next() {
-  if (end_of_input_) {
-    cur_ = next_ = "";
-    return false;
-  }
+ScopedPlatformHandle CreateTimerFd(const PeriodicTask::Args& args) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) ||           \
+    (PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && __ANDROID_API__ >= 19)
+  ScopedPlatformHandle tfd(
+      timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK));
+  uint32_t phase_ms = GetNextDelayMs(GetBootTimeMs(), args);

-  size_t substr_start = 0;
-  if (empty_token_mode_ == EmptyTokenMode::DISALLOW_EMPTY_TOKENS) {
-    while (substr_start < next_.size() &&
-           next_.at(substr_start) == delimiter_) {
-      substr_start++;
-    }
+  struct itimerspec its{};
+  // The "1 +" is to make sure that we never pass a zero it_value in the
+  // unlikely case of phase_ms being 0. That would cause the timer to be
+  // considered disarmed by timerfd_settime.
+  its.it_value.tv_sec = static_cast<time_t>(phase_ms / 1000u);
+  its.it_value.tv_nsec = 1 + static_cast<long>((phase_ms % 1000u) * 1000000u);
+  if (args.one_shot) {
+    its.it_interval.tv_sec = 0;
+    its.it_interval.tv_nsec = 0;
+  } else {
+    const uint32_t period_ms = args.period_ms;
+    its.it_interval.tv_sec = static_cast<time_t>(period_ms / 1000u);
+    its.it_interval.tv_nsec = static_cast<long>((period_ms % 1000u) * 1000000u);
   }
+  if (timerfd_settime(*tfd, 0, &its, nullptr) < 0)
+    return ScopedPlatformHandle();
+  return tfd;
+#else
+  ignore_result(args);
+  return ScopedPlatformHandle();
+#endif
+}

-  if (substr_start >= next_.size()) {
-    end_of_input_ = true;
-    cur_ = next_ = "";
-    return !cur_.empty() ||
-           empty_token_mode_ == EmptyTokenMode::ALLOW_EMPTY_TOKENS;
-  }
+}  // namespace

-  size_t delimiter_start = next_.find(delimiter_, substr_start);
-  if (delimiter_start == base::StringView::npos) {
-    cur_ = next_.substr(substr_start);
-    next_ = "";
-    end_of_input_ = true;
-    return !cur_.empty() ||
-           empty_token_mode_ == EmptyTokenMode::ALLOW_EMPTY_TOKENS;
-  }
+PeriodicTask::PeriodicTask(TaskRunner* task_runner)
+    : task_runner_(task_runner), weak_ptr_factory_(this) {}

-  size_t delimiter_end = delimiter_start + 1;
+PeriodicTask::~PeriodicTask() {
+  Reset();
+}

-  if (empty_token_mode_ == EmptyTokenMode::DISALLOW_EMPTY_TOKENS) {
-    while (delimiter_end < next_.size() &&
-           next_.at(delimiter_end) == delimiter_) {
-      delimiter_end++;
-    }
-    if (delimiter_end >= next_.size()) {
-      end_of_input_ = true;
-    }
+void PeriodicTask::Start(Args args) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  Reset();
+  if (args.period_ms == 0 || !args.task) {
+    PERFETTO_DCHECK(args.period_ms > 0);
+    PERFETTO_DCHECK(args.task);
+    return;
   }
+  args_ = std::move(args);
+  if (args_.use_suspend_aware_timer) {
+    timer_fd_ = CreateTimerFd(args_);
+    if (timer_fd_) {
+      auto weak_this = weak_ptr_factory_.GetWeakPtr();
+      task_runner_->AddFileDescriptorWatch(
+          *timer_fd_,
+          std::bind(PeriodicTask::RunTaskAndPostNext, weak_this, generation_));
+    } else {
+      PERFETTO_DPLOG("timerfd not supported, falling back on PostDelayedTask");
+    }
+  }  // if (use_suspend_aware_timer).

-  cur_ = next_.substr(substr_start, delimiter_start - substr_start);
-  next_ = next_.substr(delimiter_end);
+  if (!timer_fd_)
+    PostNextTask();

-  return !cur_.empty() ||
-         empty_token_mode_ == EmptyTokenMode::ALLOW_EMPTY_TOKENS;
+  if (args_.start_first_task_immediately)
+    args_.task();
 }

-}  // namespace base
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/temp_file.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/temp_file.h
-/*
- * 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.
- */
-
-#ifndef INCLUDE_PERFETTO_EXT_BASE_TEMP_FILE_H_
-#define INCLUDE_PERFETTO_EXT_BASE_TEMP_FILE_H_
-
-#include <string>
-
-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
-
-namespace perfetto {
-namespace base {
-
-std::string GetSysTempDir();
-
-class TempFile {
- public:
-  static TempFile CreateUnlinked();
-  static TempFile Create();
-
-  TempFile(TempFile&&) noexcept;
-  TempFile& operator=(TempFile&&);
-  ~TempFile();
-
-  const std::string& path() const { return path_; }
-  int fd() const { return *fd_; }
-  int operator*() const { return *fd_; }
-
-  // Unlinks the file from the filesystem but keeps the fd() open.
-  // It is safe to call this multiple times.
-  void Unlink();
-
-  // Releases the underlying file descriptor. Will unlink the file from the
-  // filesystem if it was created via CreateUnlinked().
-  ScopedFile ReleaseFD();
-
- private:
-  TempFile();
-  TempFile(const TempFile&) = delete;
-  TempFile& operator=(const TempFile&) = delete;
+void PeriodicTask::PostNextTask() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  PERFETTO_DCHECK(args_.period_ms > 0);
+  PERFETTO_DCHECK(!timer_fd_);
+  uint32_t delay_ms = GetNextDelayMs(GetWallTimeMs(), args_);
+  auto weak_this = weak_ptr_factory_.GetWeakPtr();
+  task_runner_->PostDelayedTask(
+      std::bind(PeriodicTask::RunTaskAndPostNext, weak_this, generation_),
+      delay_ms);
+}

-  ScopedFile fd_;
-  std::string path_;
-};
+// static
+// This function can be called in two ways (both from the TaskRunner):
+// 1. When using a timerfd, this task is registered as a FD watch.
+// 2. When using PostDelayedTask, this is the task posted on the TaskRunner.
+void PeriodicTask::RunTaskAndPostNext(WeakPtr<PeriodicTask> thiz,
+                                      uint32_t generation) {
+  if (!thiz || !thiz->args_.task || generation != thiz->generation_)
+    return;  // Destroyed or Reset() in the meanwhile.
+  PERFETTO_DCHECK_THREAD(thiz->thread_checker_);
+  if (thiz->timer_fd_) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    PERFETTO_FATAL("timerfd for periodic tasks unsupported on Windows");
+#else
+    // If we are using a timerfd there is no need to repeatedly call
+    // PostDelayedTask(). The kernel will wakeup the timer fd periodically. We
+    // just need to read() it.
+    uint64_t ignored = 0;
+    errno = 0;
+    auto rsize = Read(*thiz->timer_fd_, &ignored, sizeof(&ignored));
+    if (rsize != sizeof(uint64_t)) {
+      if (errno == EAGAIN)
+        return;  // A spurious wakeup. Rare, but can happen, just ignore.
+      PERFETTO_PLOG("read(timerfd) failed, falling back on PostDelayedTask");
+      thiz->ResetTimerFd();
+    }
+#endif
+  }

-class TempDir {
- public:
-  static TempDir Create();
+  // Create a copy of the task to deal with either:
+  // 1. one_shot causing a Reset().
+  // 2. task() invoking internally Reset().
+  // That would cause a reset of the args_.task itself, which would invalidate
+  // the task bind state while we are invoking it.
+  auto task = thiz->args_.task;

-  TempDir(TempDir&&) noexcept;
-  TempDir& operator=(TempDir&&);
-  ~TempDir();
+  // The repetition of the if() is to deal with the ResetTimerFd() case above.
+  if (thiz->args_.one_shot) {
+    thiz->Reset();
+  } else if (!thiz->timer_fd_) {
+    thiz->PostNextTask();
+  }

-  const std::string& path() const { return path_; }
+  task();
+}

- private:
-  TempDir();
-  TempDir(const TempDir&) = delete;
-  TempDir& operator=(const TempDir&) = delete;
+void PeriodicTask::Reset() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  ++generation_;
+  args_ = Args();
+  PERFETTO_DCHECK(!args_.task);
+  ResetTimerFd();
+}

-  std::string path_;
-};
+void PeriodicTask::ResetTimerFd() {
+  if (!timer_fd_)
+    return;
+  task_runner_->RemoveFileDescriptorWatch(*timer_fd_);
+  timer_fd_.reset();
+}

 }  // namespace base
 }  // namespace perfetto
-
-#endif  // INCLUDE_PERFETTO_EXT_BASE_TEMP_FILE_H_
+// gen_amalgamated begin source: src/base/pipe.cc
 /*
  * Copyright (C) 2018 The Android Open Source Project
  *
@@ -9417,151 +9922,67 @@ class TempDir {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/temp_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/pipe.h"

 // gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <fcntl.h>  // For O_BINARY (Windows) and F_SETxx (UNIX)

 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 #include <windows.h>

-#include <direct.h>
-#include <fileapi.h>
-#include <io.h>
+#include <namedpipeapi.h>
 #else
+#include <sys/types.h>
 #include <unistd.h>
 #endif

 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"

 namespace perfetto {
 namespace base {

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-namespace {
-std::string GetTempFilePathWin() {
-  std::string tmplt = GetSysTempDir() + "\\perfetto-XXXXXX";
-  StackString<255> name("%s\\perfetto-XXXXXX", GetSysTempDir().c_str());
-  PERFETTO_CHECK(_mktemp_s(name.mutable_data(), name.len() + 1) == 0);
-  return name.ToStdString();
-}
-}  // namespace
-#endif
-
-std::string GetSysTempDir() {
-  const char* tmpdir = nullptr;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  if ((tmpdir = getenv("TMP")))
-    return tmpdir;
-  if ((tmpdir = getenv("TEMP")))
-    return tmpdir;
-  return "C:\\TEMP";
-#else
-  if ((tmpdir = getenv("TMPDIR")))
-    return base::StripSuffix(tmpdir, "/");
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  return "/data/local/tmp";
-#else
-  return "/tmp";
-#endif  // !OS_ANDROID
-#endif  // !OS_WIN
-}
-
-// static
-TempFile TempFile::Create() {
-  TempFile temp_file;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  temp_file.path_ = GetTempFilePathWin();
-  // Several tests want to read-back the temp file while still open. On Windows,
-  // that requires FILE_SHARE_READ. FILE_SHARE_READ is NOT settable when using
-  // the POSIX-compat equivalent function _open(). Hence the CreateFileA +
-  // _open_osfhandle dance here.
-  HANDLE h =
-      ::CreateFileA(temp_file.path_.c_str(), GENERIC_READ | GENERIC_WRITE,
-                    FILE_SHARE_DELETE | FILE_SHARE_READ, nullptr, CREATE_ALWAYS,
-                    FILE_ATTRIBUTE_TEMPORARY, nullptr);
-  PERFETTO_CHECK(PlatformHandleChecker::IsValid(h));
-  // According to MSDN, when using _open_osfhandle the caller must not call
-  // CloseHandle(). Ownership is moved to the file descriptor, which then needs
-  // to be closed with just with _close().
-  temp_file.fd_.reset(_open_osfhandle(reinterpret_cast<intptr_t>(h), 0));
-#else
-  temp_file.path_ = GetSysTempDir() + "/perfetto-XXXXXXXX";
-  temp_file.fd_.reset(mkstemp(&temp_file.path_[0]));
-#endif
-  if (PERFETTO_UNLIKELY(!temp_file.fd_)) {
-    PERFETTO_FATAL("Could not create temp file %s", temp_file.path_.c_str());
-  }
-  return temp_file;
-}
-
-// static
-TempFile TempFile::CreateUnlinked() {
-  TempFile temp_file = TempFile::Create();
-  temp_file.Unlink();
-  return temp_file;
-}
-
-TempFile::TempFile() = default;
-
-TempFile::~TempFile() {
-  Unlink();
-}
-
-ScopedFile TempFile::ReleaseFD() {
-  Unlink();
-  return std::move(fd_);
-}
+Pipe::Pipe() = default;
+Pipe::Pipe(Pipe&&) noexcept = default;
+Pipe& Pipe::operator=(Pipe&&) = default;

-void TempFile::Unlink() {
-  if (path_.empty())
-    return;
+Pipe Pipe::Create(Flags flags) {
+  PlatformHandle fds[2];
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  // If the FD is still open DeleteFile will mark the file as pending deletion
-  // and delete it only when the process exists.
-  PERFETTO_CHECK(DeleteFileA(path_.c_str()));
+  PERFETTO_CHECK(::CreatePipe(&fds[0], &fds[1], /*lpPipeAttributes=*/nullptr,
+                              0 /*default size*/));
 #else
-  PERFETTO_CHECK(unlink(path_.c_str()) == 0);
+  PERFETTO_CHECK(pipe(fds) == 0);
+  PERFETTO_CHECK(fcntl(fds[0], F_SETFD, FD_CLOEXEC) == 0);
+  PERFETTO_CHECK(fcntl(fds[1], F_SETFD, FD_CLOEXEC) == 0);
 #endif
-  path_.clear();
-}
+  Pipe p;
+  p.rd.reset(fds[0]);
+  p.wr.reset(fds[1]);

-TempFile::TempFile(TempFile&&) noexcept = default;
-TempFile& TempFile::operator=(TempFile&&) = default;
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  if (flags == kBothNonBlock || flags == kRdNonBlock) {
+    int cur_flags = fcntl(*p.rd, F_GETFL, 0);
+    PERFETTO_CHECK(cur_flags >= 0);
+    PERFETTO_CHECK(fcntl(*p.rd, F_SETFL, cur_flags | O_NONBLOCK) == 0);
+  }

-// static
-TempDir TempDir::Create() {
-  TempDir temp_dir;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  temp_dir.path_ = GetTempFilePathWin();
-  PERFETTO_CHECK(_mkdir(temp_dir.path_.c_str()) == 0);
+  if (flags == kBothNonBlock || flags == kWrNonBlock) {
+    int cur_flags = fcntl(*p.wr, F_GETFL, 0);
+    PERFETTO_CHECK(cur_flags >= 0);
+    PERFETTO_CHECK(fcntl(*p.wr, F_SETFL, cur_flags | O_NONBLOCK) == 0);
+  }
 #else
-  temp_dir.path_ = GetSysTempDir() + "/perfetto-XXXXXXXX";
-  PERFETTO_CHECK(mkdtemp(&temp_dir.path_[0]));
+  PERFETTO_CHECK(flags == kBothBlock);
 #endif
-  return temp_dir;
-}
-
-TempDir::TempDir() = default;
-TempDir::TempDir(TempDir&&) noexcept = default;
-TempDir& TempDir::operator=(TempDir&&) = default;
-
-TempDir::~TempDir() {
-  if (path_.empty())
-    return;  // For objects that get std::move()d.
-  PERFETTO_CHECK(Rmdir(path_));
+  return p;
 }

 }  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/base/thread_checker.cc
+// gen_amalgamated begin source: src/base/progress_reporter.cc
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -9576,61 +9997,159 @@ TempDir::~TempDir() {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/progress_reporter.h"
+
+#include <algorithm>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <mutex>
+#include <string_view>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"

 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 #include <windows.h>
+#elif !PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
 #endif

-namespace perfetto {
-namespace base {
-
+namespace perfetto::base {
 namespace {
-constexpr ThreadID kDetached{};

-ThreadID CurrentThreadId() {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  return ::GetCurrentThreadId();
+constexpr int64_t kMinUpdateIntervalMs = 100;
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
+bool StderrIsInteractiveTerminal() {
+#if PERFETTO_BUILDFLAG(PERFETTO_CHROMIUM_BUILD)
+  // isatty() is blocked by the Chrome seccomp-bpf sandbox (b/191235714).
+  return false;
 #else
-  return pthread_self();
+  const char* term = getenv("TERM");
+  return IsTty(stderr) && (!term || strcmp(term, "dumb") != 0);
+#endif
+}
+#endif
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+// Fails when stderr is not a console or the console predates ANSI support.
+bool EnableVirtualTerminalProcessing() {
+  HANDLE handle = GetStdHandle(STD_ERROR_HANDLE);
+  DWORD mode = 0;
+  if (handle == INVALID_HANDLE_VALUE || !GetConsoleMode(handle, &mode))
+    return false;
+  return SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0;
+}
 #endif
+
+size_t TerminalWidth() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  CONSOLE_SCREEN_BUFFER_INFO info{};
+  if (GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &info))
+    return static_cast<size_t>(info.srWindow.Right - info.srWindow.Left + 1);
+#elif !PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
+  struct winsize size{};
+  if (ioctl(STDERR_FILENO, TIOCGWINSZ, &size) == 0 && size.ws_col)
+    return size.ws_col;
+#endif
+  return 80;
 }
+
 }  // namespace

-ThreadChecker::ThreadChecker() {
-  thread_id_.store(CurrentThreadId());
+bool StderrSupportsProgress() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
+  return true;
+#else
+  return StderrIsInteractiveTerminal();
+#endif
 }

-ThreadChecker::~ThreadChecker() = default;
+bool StderrSupportsColor() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // Attempt this even when colors are forced, so that escapes are interpreted
+  // wherever the console can.
+  static const bool vt_enabled = EnableVirtualTerminalProcessing();
+#endif
+  const char* force_color = getenv("FORCE_COLOR");
+  if (force_color && *force_color)
+    return true;
+  const char* no_color = getenv("NO_COLOR");
+  if (no_color && *no_color)
+    return false;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
+  return false;
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  return vt_enabled && StderrIsInteractiveTerminal();
+#else
+  return StderrIsInteractiveTerminal();
+#endif
+}

-ThreadChecker::ThreadChecker(const ThreadChecker& other)
-    : thread_id_(other.thread_id_.load()) {}
+ProgressReporter& ProgressReporter::GetInstance() {
+  static ProgressReporter* instance = new ProgressReporter();
+  return *instance;
+}

-ThreadChecker& ThreadChecker::operator=(const ThreadChecker& other) {
-  thread_id_ = other.thread_id_.load();
-  return *this;
+void ProgressReporter::set_enabled(bool enabled) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  enabled_ = enabled;
 }

-bool ThreadChecker::CalledOnValidThread() const {
-  auto self = CurrentThreadId();
+void ProgressReporter::Update(std::string_view message) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  if (!enabled_ || !StderrSupportsProgress())
+    return;
+  int64_t now = GetWallTimeMs().count();
+  if (last_update_ms_ && now - last_update_ms_ < kMinUpdateIntervalMs)
+    return;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
+  last_update_ms_ = now;
+  // stdio reaches the embedder only when a write ends with a newline.
+  fprintf(stderr, "%.*s\n", static_cast<int>(message.size()), message.data());
+#else
+  // Leave one column unused to avoid wrapping, including on narrow terminals.
+  size_t width = TerminalWidth();
+  size_t new_width = std::min(message.size(), width > 1 ? width - 1 : 0);
+  if (new_width < visible_width_)
+    ClearLocked();
+  // Set after clearing, which resets the throttle for the next phase.
+  last_update_ms_ = now;
+  if (!new_width)
+    return;
+  fprintf(stderr, "\r%.*s", static_cast<int>(new_width), message.data());
+  fflush(stderr);
+  visible_width_ = new_width;
+#endif
+}

-  // Will re-attach if previously detached using DetachFromThread().
-  auto prev_value = kDetached;
-  if (thread_id_.compare_exchange_strong(prev_value, self))
-    return true;
-  return prev_value == self;
+void ProgressReporter::ClearLocked() {
+  if (!visible_width_)
+    return;
+  // Bound clearing by the current width in case the terminal was resized.
+  size_t width = TerminalWidth();
+  size_t clear = std::min(visible_width_, width > 1 ? width - 1 : 0);
+  fprintf(stderr, "\r%*s\r", static_cast<int>(clear), "");
+  fflush(stderr);
+  visible_width_ = 0;
+  // The next phase's first update should not be throttled by this one.
+  last_update_ms_ = 0;
 }

-void ThreadChecker::DetachFromThread() {
-  thread_id_.store(kDetached);
+void ProgressReporter::Clear() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ClearLocked();
 }

-}  // namespace base
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/thread_utils.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/thread_utils.h
+}  // namespace perfetto::base
+// gen_amalgamated begin source: src/base/rt_mutex.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/rt_mutex.h
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -9645,177 +10164,197 @@ void ThreadChecker::DetachFromThread() {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_RT_MUTEX_H_
+#define INCLUDE_PERFETTO_EXT_BASE_RT_MUTEX_H_

-#include <string>
+// This header introduces the RtMutex class for priority-inheritance mutexes.
+// RtMutex is NOT a blanket replacement for std::mutex and should be used only
+// in cases where we know we are expect to be used on a RT thread.
+// In the contended case RtMutex is generally slower than a std::mutex (or any
+// non-RT implementation).
+// Under the hoods this class does the following:
+// - Android: it uses PI futexes.
+// - Linux/MacOS/iOS: it uses pthread_mutex with PTHREAD_PRIO_INHERIT.
+// - Other platforms: falls back on a standard std::mutex. On Windows 11+
+//   std::mutex has effectively PI semantics due to AutoBoost
+//   https://github.com/MicrosoftDocs/win32/commit/a43cb3b5039c5cfc53642bfcea174003a2f1168f

 // gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/base/export.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+// gen_amalgamated expanded: #include "perfetto/base/thread_annotations.h"
+// gen_amalgamated expanded: #include "perfetto/public/compiler.h"

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+#define _PERFETTO_MUTEX_MODE_STD 0
+#define _PERFETTO_MUTEX_MODE_RT_FUTEX 1
+#define _PERFETTO_MUTEX_MODE_RT_MUTEX 2
+
+// The logic below determines which mutex implementation to use.
+// For Android platform builds, the choice is controlled by aconfig flags.
+// For other builds, it's determined by OS support and GN build arguments.
+//
+// Rationale for platform-specific choices:
+// 1. `RtFutex` is enabled only on Android because it relies on `gettid()` being
+//    a cheap thread-local storage access provided by Bionic. On Linux with
+//    glibc, `gettid()` is a full syscall, making the pthread-based
+//    implementation faster.
+// 2. The pthread-based `RtPosixMutex` is not viable on all Android versions, as
+//    `pthread_mutexattr_setprotocol` was introduced in API level 28. Using
+//    `dlsym` to backport it can lead to deadlocks with the loader lock if
+//    tracing is initialized from a static constructor (see b/443178555).
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
+    PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+#define _PERFETTO_MUTEX_MODE _PERFETTO_MUTEX_MODE_RT_FUTEX
+#elif PERFETTO_BUILDFLAG(PERFETTO_ENABLE_RT_MUTEX)
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#define _PERFETTO_MUTEX_MODE _PERFETTO_MUTEX_MODE_RT_FUTEX
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
     PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-#include <pthread.h>
-#include <string.h>
-#include <algorithm>
+#define _PERFETTO_MUTEX_MODE _PERFETTO_MUTEX_MODE_RT_MUTEX
+#endif
 #endif

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#include <sys/prctl.h>
+// If no RT implementation was selected, default to std::mutex.
+#ifndef _PERFETTO_MUTEX_MODE
+#define _PERFETTO_MUTEX_MODE _PERFETTO_MUTEX_MODE_STD
 #endif

-// Internal implementation utils that aren't as widely useful/supported as
-// base/thread_utils.h.
+// Public macros for conditional compilation based on the selected mutex type.
+#define PERFETTO_HAS_POSIX_RT_MUTEX() \
+  (_PERFETTO_MUTEX_MODE == _PERFETTO_MUTEX_MODE_RT_MUTEX)
+#define PERFETTO_HAS_RT_FUTEX() \
+  (_PERFETTO_MUTEX_MODE == _PERFETTO_MUTEX_MODE_RT_FUTEX)

-namespace perfetto {
-namespace base {
+#include <atomic>
+#include <mutex>
+#include <type_traits>

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-// Sets the "comm" of the calling thread to the first 15 chars of the given
-// string.
-inline bool MaybeSetThreadName(const std::string& name) {
-  char buf[16] = {};
-  StringCopy(buf, name.c_str(), sizeof(buf));
+#if PERFETTO_HAS_POSIX_RT_MUTEX()
+#include <pthread.h>
+#endif

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-  return pthread_setname_np(buf) == 0;
-#else
-  return pthread_setname_np(pthread_self(), buf) == 0;
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <unistd.h>  // For gettid().
 #endif
-}

-inline bool GetThreadName(std::string& out_result) {
-  char buf[16] = {};
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  if (prctl(PR_GET_NAME, buf) != 0)
-    return false;
-#else
-  if (pthread_getname_np(pthread_self(), buf, sizeof(buf)) != 0)
-    return false;
-#endif
-  out_result = std::string(buf);
-  return true;
-}
+namespace perfetto::base {

-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+namespace internal {

-PERFETTO_EXPORT_COMPONENT bool MaybeSetThreadName(const std::string& name);
-PERFETTO_EXPORT_COMPONENT bool GetThreadName(std::string& out_result);
+#if PERFETTO_HAS_RT_FUTEX()
+// A wrapper around PI Futexes. A futex is a wrapper around an atomic integer
+// with an ABI shared with the kernel to handle the slowpath in the cases when
+// the mutex is held, or we find out that there are waiters queued when we
+// unlock. The operating principle is the following:
+// - In the no-contention case, a futex boils down to an atomic
+//   compare-and-exchange, without involving the kernel.
+// - If a lock is contented at acquire time, we have to enter the kernel to
+//   suspend our execution and join a wait chain.
+// - It could still happen that we acquire the mutex via the fastpath (without
+//   involving the kernel) but other waiters might queue up while we hold the
+//   mutex. In that case the kernel will add a bit to the atomic int. That bit
+//   will cause the unlock() compare-and-exchange to fail (because it no longer
+//   matches our tid) which in turn will signal us to do a syscall to notify the
+//   waiters.
+class PERFETTO_LOCKABLE RtFutex {
+ public:
+  RtFutex() { PERFETTO_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); }
+  ~RtFutex() { PERFETTO_TSAN_MUTEX_DESTROY(this, __tsan_mutex_not_static); }

-#else
-inline bool MaybeSetThreadName(const std::string&) {
-  return false;
-}
-inline bool GetThreadName(std::string&) {
-  return false;
-}
-#endif
+  // Disable copy or move. Copy doesn't make sense. Move isn't feasible because
+  // the pointer to the atomic integer is the handle used by the kernel to setup
+  // the wait chain. A movable futex would require the atomic integer to be heap
+  // allocated, but that would create an indirection layer that is not needed in
+  // most cases. If you really need a movable RtMutex, wrap it in a unique_ptr.
+  RtFutex(const RtFutex&) = delete;
+  RtFutex& operator=(const RtFutex&) = delete;
+  RtFutex(RtFutex&&) = delete;
+  RtFutex& operator=(RtFutex&&) = delete;

-}  // namespace base
-}  // namespace perfetto
+  inline bool TryLockFastpath() noexcept {
+    int expected = 0;
+    return lock_.compare_exchange_strong(expected, ::gettid(),
+                                         std::memory_order_acquire,
+                                         std::memory_order_relaxed);
+  }

-#endif  // INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
-/*
- * 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.
- */
+  bool try_lock() noexcept PERFETTO_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+    PERFETTO_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock);
+    if (PERFETTO_LIKELY(TryLockFastpath()) || TryLockSlowpath()) {
+      PERFETTO_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_try_lock, 0);
+      return true;
+    }
+    PERFETTO_TSAN_MUTEX_POST_LOCK(
+        this, __tsan_mutex_try_lock | __tsan_mutex_try_lock_failed, 0);
+    return false;
+  }

-// gen_amalgamated expanded: #include "perfetto/base/thread_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/thread_utils.h"
+  void lock() PERFETTO_EXCLUSIVE_LOCK_FUNCTION() {
+    PERFETTO_TSAN_MUTEX_PRE_LOCK(this, 0);
+    if (!PERFETTO_LIKELY(TryLockFastpath())) {
+      LockSlowpath();
+    }
+    PERFETTO_TSAN_MUTEX_POST_LOCK(this, 0, 0);
+  }

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+  void unlock() noexcept PERFETTO_UNLOCK_FUNCTION() {
+    PERFETTO_TSAN_MUTEX_PRE_UNLOCK(this, 0);
+    int expected = ::gettid();
+    // If the current value is our tid, we can unlock without a syscall since
+    // there are no current waiters.
+    if (!PERFETTO_LIKELY(lock_.compare_exchange_strong(
+            expected, 0, std::memory_order_release,
+            std::memory_order_relaxed))) {
+      // The tid doesn't match because the kernel appended the FUTEX_WAITERS
+      // bit. There are waiters, tell the kernel to notify them and unlock.
+      UnlockSlowpath();
+    }
+    PERFETTO_TSAN_MUTEX_POST_UNLOCK(this, 0);
+  }

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
-#include <zircon/process.h>
-#include <zircon/syscalls.h>
-#include <zircon/types.h>
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <windows.h>
-#endif
+ private:
+  std::atomic<int> lock_{};

-namespace perfetto {
-namespace base {
+  void LockSlowpath();
+  bool TryLockSlowpath();
+  void UnlockSlowpath();
+};

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
-static PlatformThreadId ResolveThreadId() {
-  zx_info_handle_basic_t basic;
-  return (zx_object_get_info(zx_thread_self(), ZX_INFO_HANDLE_BASIC, &basic,
-                             sizeof(basic), nullptr, nullptr) == ZX_OK)
-             ? basic.koid
-             : ZX_KOID_INVALID;
-}
-PlatformThreadId GetThreadId() {
-  thread_local static PlatformThreadId thread_id = ResolveThreadId();
-  return thread_id;
-}
+#endif  // PERFETTO_HAS_RT_FUTEX

-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#if PERFETTO_HAS_POSIX_RT_MUTEX()
+class PERFETTO_LOCKABLE RtPosixMutex {
+ public:
+  RtPosixMutex() noexcept;
+  ~RtPosixMutex() noexcept;

-// The SetThreadDescription API was brought in version 1607 of Windows 10.
-typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread,
-                                              PCWSTR lpThreadDescription);
+  RtPosixMutex(const RtPosixMutex&) = delete;
+  RtPosixMutex& operator=(const RtPosixMutex&) = delete;
+  RtPosixMutex(RtPosixMutex&&) = delete;
+  RtPosixMutex& operator=(RtPosixMutex&&) = delete;

-// The SetThreadDescription API was brought in version 1607 of Windows 10.
-typedef HRESULT(WINAPI* GetThreadDescription)(HANDLE hThread,
-                                              PWSTR* ppszThreadDescription);
+  bool try_lock() noexcept PERFETTO_EXCLUSIVE_TRYLOCK_FUNCTION(true);
+  void lock() noexcept PERFETTO_EXCLUSIVE_LOCK_FUNCTION();
+  void unlock() noexcept PERFETTO_UNLOCK_FUNCTION();

-bool MaybeSetThreadName(const std::string& name) {
-  // The SetThreadDescription API works even if no debugger is attached.
-  static auto set_thread_description_func =
-      reinterpret_cast<SetThreadDescription>(
-          reinterpret_cast<void*>(::GetProcAddress(
-              ::GetModuleHandleA("Kernel32.dll"), "SetThreadDescription")));
-  if (!set_thread_description_func) {
-    return false;
-  }
-  std::wstring wide_thread_name;
-  if (!UTF8ToWide(name, wide_thread_name)) {
-    return false;
-  }
-  HRESULT result = set_thread_description_func(::GetCurrentThread(),
-                                               wide_thread_name.c_str());
-  return !FAILED(result);
-}
+ private:
+  pthread_mutex_t mutex_;
+};

-bool GetThreadName(std::string& out_result) {
-  static auto get_thread_description_func =
-      reinterpret_cast<GetThreadDescription>(
-          reinterpret_cast<void*>(::GetProcAddress(
-              ::GetModuleHandleA("Kernel32.dll"), "GetThreadDescription")));
-  if (!get_thread_description_func) {
-    return false;
-  }
-  wchar_t* wide_thread_name;
-  HRESULT result =
-      get_thread_description_func(::GetCurrentThread(), &wide_thread_name);
-  if (SUCCEEDED(result)) {
-    bool success = WideToUTF8(std::wstring(wide_thread_name), out_result);
-    LocalFree(wide_thread_name);
-    return success;
-  }
-  return false;
-}
+#endif  // PERFETTO_HAS_POSIX_RT_MUTEX
+}  // namespace internal

-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+// Select the best real-time mutex implementation for the target platform, or
+// fall back to std::mutex if none is available.
+#if PERFETTO_HAS_RT_FUTEX()
+using MaybeRtMutex = internal::RtFutex;
+#elif PERFETTO_HAS_POSIX_RT_MUTEX()
+using MaybeRtMutex = internal::RtPosixMutex;
+#else
+using MaybeRtMutex = std::mutex;
+#endif

-}  // namespace base
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/time.cc
+}  // namespace perfetto::base
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_RT_MUTEX_H_
 /*
  * Copyright (C) 2018 The Android Open Source Project
  *
@@ -9832,209 +10371,94 @@ bool GetThreadName(std::string& out_result) {
  * limitations under the License.
  */

-#include <atomic>
+// gen_amalgamated expanded: #include "perfetto/ext/base/rt_mutex.h"

-// gen_amalgamated expanded: #include "perfetto/base/time.h"
+#include <errno.h>

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <windows.h>
-#else
+#if PERFETTO_HAS_RT_FUTEX()
+#include <linux/futex.h>
+#include <sys/syscall.h>
 #include <unistd.h>
 #endif

-namespace perfetto {
-namespace base {
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#if !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
-namespace {
-
-// Returns the current value of the performance counter.
-int64_t QPCNowRaw() {
-  LARGE_INTEGER perf_counter_now = {};
-  // According to the MSDN documentation for QueryPerformanceCounter(), this
-  // will never fail on systems that run XP or later.
-  // https://msdn.microsoft.com/library/windows/desktop/ms644904.aspx
-  ::QueryPerformanceCounter(&perf_counter_now);
-  return perf_counter_now.QuadPart;
-}
-
-double TSCTicksPerSecond() {
-  // The value returned by QueryPerformanceFrequency() cannot be used as the TSC
-  // frequency, because there is no guarantee that the TSC frequency is equal to
-  // the performance counter frequency.
-  // The TSC frequency is cached in a static variable because it takes some time
-  // to compute it.
-  static std::atomic<double> tsc_ticks_per_second = 0;
-  double value = tsc_ticks_per_second.load(std::memory_order_relaxed);
-  if (value != 0)
-    return value;
-
-  // Increase the thread priority to reduces the chances of having a context
-  // switch during a reading of the TSC and the performance counter.
-  const int previous_priority = ::GetThreadPriority(::GetCurrentThread());
-  ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
-
-  // The first time that this function is called, make an initial reading of the
-  // TSC and the performance counter. Initialization of static variable is
-  // thread-safe. Threads can race initializing tsc_initial vs
-  // perf_counter_initial, although they should be storing very similar values.
-
-  static const uint64_t tsc_initial = __rdtsc();
-  static const int64_t perf_counter_initial = QPCNowRaw();
-
-  // Make a another reading of the TSC and the performance counter every time
-  // that this function is called.
-  const uint64_t tsc_now = __rdtsc();
-  const int64_t perf_counter_now = QPCNowRaw();
-
-  // Reset the thread priority.
-  ::SetThreadPriority(::GetCurrentThread(), previous_priority);
-
-  // Make sure that at least 50 ms elapsed between the 2 readings. The first
-  // time that this function is called, we don't expect this to be the case.
-  // Note: The longer the elapsed time between the 2 readings is, the more
-  //   accurate the computed TSC frequency will be. The 50 ms value was
-  //   chosen because local benchmarks show that it allows us to get a
-  //   stddev of less than 1 tick/us between multiple runs.
-  // Note: According to the MSDN documentation for QueryPerformanceFrequency(),
-  //   this will never fail on systems that run XP or later.
-  //   https://msdn.microsoft.com/library/windows/desktop/ms644905.aspx
-  LARGE_INTEGER perf_counter_frequency = {};
-  ::QueryPerformanceFrequency(&perf_counter_frequency);
-  PERFETTO_CHECK(perf_counter_now >= perf_counter_initial);
-  const int64_t perf_counter_ticks = perf_counter_now - perf_counter_initial;
-  const double elapsed_time_seconds =
-      static_cast<double>(perf_counter_ticks) /
-      static_cast<double>(perf_counter_frequency.QuadPart);
+namespace perfetto::base {

-  constexpr double kMinimumEvaluationPeriodSeconds = 0.05;
-  if (elapsed_time_seconds < kMinimumEvaluationPeriodSeconds)
-    return 0;
+namespace internal {

-  // Compute the frequency of the TSC.
-  PERFETTO_CHECK(tsc_now >= tsc_initial);
-  const uint64_t tsc_ticks = tsc_now - tsc_initial;
-  // Racing with another thread to write |tsc_ticks_per_second| is benign
-  // because both threads will write a valid result.
-  tsc_ticks_per_second.store(
-      static_cast<double>(tsc_ticks) / elapsed_time_seconds,
-      std::memory_order_relaxed);
+#if PERFETTO_HAS_RT_FUTEX()

-  return tsc_ticks_per_second.load(std::memory_order_relaxed);
+void RtFutex::LockSlowpath() {
+  auto res = PERFETTO_EINTR(
+      syscall(SYS_futex, &lock_, FUTEX_LOCK_PI_PRIVATE, 0, nullptr));
+  PERFETTO_CHECK(res == 0);
 }

-}  // namespace
-#endif  // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
-
-TimeNanos GetWallTimeNs() {
-  LARGE_INTEGER freq;
-  ::QueryPerformanceFrequency(&freq);
-  LARGE_INTEGER counter;
-  ::QueryPerformanceCounter(&counter);
-  double elapsed_nanoseconds = (1e9 * static_cast<double>(counter.QuadPart)) /
-                               static_cast<double>(freq.QuadPart);
-  return TimeNanos(static_cast<uint64_t>(elapsed_nanoseconds));
+bool RtFutex::TryLockSlowpath() {
+  auto res = PERFETTO_EINTR(
+      syscall(SYS_futex, &lock_, FUTEX_TRYLOCK_PI_PRIVATE, 0, nullptr));
+  if (res == 0)
+    return true;
+  if (errno == EBUSY || errno == EDEADLK)
+    return false;
+  PERFETTO_FATAL("FUTEX_TRYLOCK_PI_PRIVATE failed");
 }

-TimeNanos GetThreadCPUTimeNs() {
-#if PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
-  // QueryThreadCycleTime versus TSCTicksPerSecond doesn't have much relation to
-  // actual elapsed time on Windows on Arm, because QueryThreadCycleTime is
-  // backed by the actual number of CPU cycles executed, rather than a
-  // constant-rate timer like Intel. To work around this, use GetThreadTimes
-  // (which isn't as accurate but is meaningful as a measure of elapsed
-  // per-thread time).
-  FILETIME dummy, kernel_ftime, user_ftime;
-  ::GetThreadTimes(GetCurrentThread(), &dummy, &dummy, &kernel_ftime,
-                   &user_ftime);
-  uint64_t kernel_time =
-      kernel_ftime.dwHighDateTime * 0x100000000 + kernel_ftime.dwLowDateTime;
-  uint64_t user_time =
-      user_ftime.dwHighDateTime * 0x100000000 + user_ftime.dwLowDateTime;
+void RtFutex::UnlockSlowpath() {
+  auto res = PERFETTO_EINTR(
+      syscall(SYS_futex, &lock_, FUTEX_UNLOCK_PI_PRIVATE, 0, nullptr));
+  PERFETTO_CHECK(res == 0);
+}

-  return TimeNanos((kernel_time + user_time) * 100);
-#else   // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
-  // Get the number of TSC ticks used by the current thread.
-  ULONG64 thread_cycle_time = 0;
-  ::QueryThreadCycleTime(GetCurrentThread(), &thread_cycle_time);
+#endif  // PERFETTO_HAS_RT_FUTEX

-  // Get the frequency of the TSC.
-  const double tsc_ticks_per_second = TSCTicksPerSecond();
-  if (tsc_ticks_per_second == 0)
-    return TimeNanos();
+#if PERFETTO_HAS_POSIX_RT_MUTEX()

-  // Return the CPU time of the current thread.
-  const double thread_time_seconds =
-      static_cast<double>(thread_cycle_time) / tsc_ticks_per_second;
-  constexpr int64_t kNanosecondsPerSecond = 1000 * 1000 * 1000;
-  return TimeNanos(
-      static_cast<int64_t>(thread_time_seconds * kNanosecondsPerSecond));
-#endif  // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+RtPosixMutex::RtPosixMutex() noexcept {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && __ANDROID_API__ < 28
+  // pthread_mutexattr_setprotocol is only available on API 28.
+#error \
+    "Priority-inheritance RtMutex is not available in this version of Android."
+#endif
+  pthread_mutexattr_t at{};
+  PERFETTO_CHECK(pthread_mutexattr_init(&at) == 0);
+  PERFETTO_CHECK(pthread_mutexattr_setprotocol(&at, PTHREAD_PRIO_INHERIT) == 0);
+  PERFETTO_CHECK(pthread_mutex_init(&mutex_, &at) == 0);
 }

-void SleepMicroseconds(unsigned interval_us) {
-  // The Windows Sleep function takes a millisecond count. Round up so that
-  // short sleeps don't turn into a busy wait. Note that the sleep granularity
-  // on Windows can dynamically vary from 1 ms to ~16 ms, so don't count on this
-  // being a short sleep.
-  ::Sleep(static_cast<DWORD>((interval_us + 999) / 1000));
+RtPosixMutex::~RtPosixMutex() noexcept {
+  pthread_mutex_destroy(&mutex_);
 }

-void InitializeTime() {
-#if !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
-  // Make an early first call to TSCTicksPerSecond() to start 50 ms elapsed time
-  // (see comment in TSCTicksPerSecond()).
-  TSCTicksPerSecond();
-#endif  // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+bool RtPosixMutex::try_lock() noexcept {
+  int res = pthread_mutex_trylock(&mutex_);
+  if (res == 0)
+    return true;
+  // NOTE: Unlike most Linux APIs, pthread_mutex_trylock "returns" the error
+  // code, it does NOT use errno.
+  if (res == EBUSY)
+    return false;
+  PERFETTO_FATAL("pthread_mutex_trylock() failed");
 }

-#else  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-
-void SleepMicroseconds(unsigned interval_us) {
-  ::usleep(static_cast<useconds_t>(interval_us));
+void RtPosixMutex::lock() noexcept {
+  PERFETTO_CHECK(pthread_mutex_lock(&mutex_) == 0);
 }

-void InitializeTime() {}
-
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-
-std::string GetTimeFmt(const std::string& fmt) {
-  time_t raw_time;
-  time(&raw_time);
-  struct tm local_tm;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  PERFETTO_CHECK(localtime_s(&local_tm, &raw_time) == 0);
-#else
-  tzset();
-  PERFETTO_CHECK(localtime_r(&raw_time, &local_tm) != nullptr);
-#endif
-  char buf[128];
-  PERFETTO_CHECK(strftime(buf, 80, fmt.c_str(), &local_tm) > 0);
-  return buf;
+void RtPosixMutex::unlock() noexcept {
+  PERFETTO_CHECK(pthread_mutex_unlock(&mutex_) == 0);
 }

-std::optional<int32_t> GetTimezoneOffsetMins() {
-  std::string tz = GetTimeFmt("%z");
-  if (tz.size() != 5 || (tz[0] != '+' && tz[0] != '-'))
-    return std::nullopt;
-  char sign = '\0';
-  int32_t hh = 0;
-  int32_t mm = 0;
-  if (sscanf(tz.c_str(), "%c%2d%2d", &sign, &hh, &mm) != 3)
-    return std::nullopt;
-  return (hh * 60 + mm) * (sign == '-' ? -1 : 1);
-}
+#endif  // PERFETTO_HAS_POSIX_RT_MUTEX

-}  // namespace base
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/utils.cc
+}  // namespace internal
+}  // namespace perfetto::base
+// gen_amalgamated begin source: src/base/scoped_mmap.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/scoped_mmap.h
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -10049,354 +10473,263 @@ std::optional<int32_t> GetTimezoneOffsetMins() {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_SCOPED_MMAP_H_
+#define INCLUDE_PERFETTO_EXT_BASE_SCOPED_MMAP_H_

-#include <string>
+#include <cstddef>

 // gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/pipe.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"

 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
     PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) ||   \
     PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
-#include <limits.h>
-#include <stdlib.h>  // For _exit()
-#endif
-
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-// For isatty(), getpagesize(), geteuid(), fork() & sysconf().
-#include <unistd.h>
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#define PERFETTO_HAS_MMAP() 1
+#else
+#define PERFETTO_HAS_MMAP() 0
 #endif

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-#include <mach-o/dyld.h>
-#include <mach/vm_page_size.h>
-#endif
+namespace perfetto::base {

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD)
-#include <sys/sysctl.h>
-#endif
+// RAII wrapper that holds ownership of an mmap()d area and of a file. Calls
+// unmap() and close() on destruction.
+class ScopedMmap {
+ public:
+  // Creates a memory mapping for the first `length` bytes of `file`.
+  static ScopedMmap FromHandle(base::ScopedPlatformHandle file, size_t length);

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#include <sys/prctl.h>
+  ScopedMmap() {}
+  ~ScopedMmap();
+  ScopedMmap(ScopedMmap&& other) noexcept;

-#ifndef PR_GET_TAGGED_ADDR_CTRL
-#define PR_GET_TAGGED_ADDR_CTRL 56
-#endif
+  ScopedMmap& operator=(ScopedMmap&& other) noexcept;

-#ifndef PR_TAGGED_ADDR_ENABLE
-#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
-#endif
+  // Returns a pointer to the mapped memory area. Only valid if `IsValid()` is
+  // true.
+  void* data() const { return ptr_; }

-#ifndef PR_MTE_TCF_SYNC
-#define PR_MTE_TCF_SYNC (1UL << 1)
-#endif
+  // Returns true if this object contains a successfully mapped area.
+  bool IsValid() const { return ptr_ != nullptr; }

-#endif  // OS_LINUX | OS_ANDROID
+  // Returns the length of the mapped area.
+  size_t length() const { return length_; }

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <windows.h>
+  // Unmaps the area and closes the file. Returns false if this held a mmap()d
+  // area and unmapping failed. In any case, after this method, `IsValid()` will
+  // return false.
+  bool reset() noexcept;

-#include <io.h>
-#include <malloc.h>  // For _aligned_malloc().
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+  // Takes ownership of an mmap()d area that starts at `data`, `size` bytes
+  // long. `data` should not be MAP_FAILED.
+  static ScopedMmap InheritMmappedRange(void* data, size_t size);
 #endif

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#include <dlfcn.h>
-#include <malloc.h>
-
-#ifdef M_PURGE
-#define PERFETTO_M_PURGE M_PURGE
-#else
-// Only available in in-tree builds and on newer SDKs.
-#define PERFETTO_M_PURGE -101
-#endif  // M_PURGE
-
-#ifdef M_PURGE_ALL
-#define PERFETTO_M_PURGE_ALL M_PURGE_ALL
-#else
-// Only available in in-tree builds and on newer SDKs.
-#define PERFETTO_M_PURGE_ALL -104
-#endif  // M_PURGE
+ private:
+  ScopedMmap(const ScopedMmap&) = delete;
+  ScopedMmap& operator=(const ScopedMmap&) = delete;

-namespace {
-extern "C" {
-using MalloptType = int (*)(int, int);
-}
-}  // namespace
-#endif  // OS_ANDROID
+  size_t length_ = 0;
+  void* ptr_ = nullptr;
+  ScopedPlatformHandle file_;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  ScopedPlatformHandle map_;
+#endif
+};

-namespace perfetto {
-namespace base {
+// Tries to open `file_path` and maps its first `length` bytes in memory.
+ScopedMmap ReadMmapFilePart(const std::string& file_path, size_t length);

-namespace internal {
+// Tries to open `file_path` and maps the whole file into memory.
+ScopedMmap ReadMmapWholeFile(const std::string& file_path);

-std::atomic<uint32_t> g_cached_page_size{0};
+}  // namespace perfetto::base

-uint32_t GetSysPageSizeSlowpath() {
-  uint32_t page_size = 0;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  const int page_size_int = getpagesize();
-  // If sysconf() fails for obscure reasons (e.g. SELinux denial) assume the
-  // page size is 4KB. This is to avoid regressing subtle SDK usages, as old
-  // versions of this code had a static constant baked in.
-  page_size = static_cast<uint32_t>(page_size_int > 0 ? page_size_int : 4096);
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-  page_size = static_cast<uint32_t>(vm_page_size);
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD)
-  page_size = static_cast<uint32_t>(sysconf(_SC_PAGESIZE));
-#else
-  page_size = 4096;
-#endif
+#endif  // INCLUDE_PERFETTO_EXT_BASE_SCOPED_MMAP_H_
+/*
+ * Copyright (C) 2024 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.
+ */

-  PERFETTO_CHECK(page_size > 0 && page_size % 4096 == 0);
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_mmap.h"

-  // Races here are fine because any thread will write the same value.
-  g_cached_page_size.store(page_size, std::memory_order_relaxed);
-  return page_size;
-}
+#include <utility>

-}  // namespace internal
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"

-void MaybeReleaseAllocatorMemToOS() {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  // mallopt() on Android requires SDK level 26. Many targets and embedders
-  // still depend on a lower SDK level. Given mallopt() is a quite simple API,
-  // use reflection to do this rather than bumping the SDK level for all
-  // embedders. This keeps the behavior of standalone builds aligned with
-  // in-tree builds.
-  static MalloptType mallopt_fn =
-      reinterpret_cast<MalloptType>(dlsym(RTLD_DEFAULT, "mallopt"));
-  if (!mallopt_fn)
-    return;
-  if (mallopt_fn(PERFETTO_M_PURGE_ALL, 0) == 0) {
-    mallopt_fn(PERFETTO_M_PURGE, 0);
-  }
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <sys/mman.h>
+#include <unistd.h>
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <windows.h>
 #endif
-}

-uid_t GetCurrentUserId() {
+namespace perfetto::base {
+namespace {
+
+ScopedPlatformHandle OpenFileForMmap(const std::string& file_path) {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
     PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
     PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-  return geteuid();
+  return OpenFile(file_path, O_RDONLY);
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // This does not use base::OpenFile to avoid getting an exclusive lock.
+  //
+  // The share flags mirror the POSIX open(O_RDONLY) above, which has no notion
+  // of share modes. Same flags as LLVM's openNativeFileInternal():
+  // https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/Windows/Path.inc
+  return ScopedPlatformHandle(
+      CreateFileA(file_path.c_str(), GENERIC_READ,
+                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                  nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
 #else
-  // TODO(primiano): On Windows we could hash the current user SID and derive a
-  // numeric user id [1]. It is not clear whether we need that. Right now that
-  // would not bring any benefit. Returning 0 unil we can prove we need it.
-  // [1]:https://android-review.googlesource.com/c/platform/external/perfetto/+/1513879/25/src/base/utils.cc
-  return 0;
+  // mmap is not supported. Do not even open the file.
+  base::ignore_result(file_path);
+  return ScopedPlatformHandle();
 #endif
 }

-void SetEnv(const std::string& key, const std::string& value) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  PERFETTO_CHECK(::_putenv_s(key.c_str(), value.c_str()) == 0);
-#else
-  PERFETTO_CHECK(::setenv(key.c_str(), value.c_str(), /*overwrite=*/true) == 0);
-#endif
-}
+}  // namespace

-void UnsetEnv(const std::string& key) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  PERFETTO_CHECK(::_putenv_s(key.c_str(), "") == 0);
-#else
-  PERFETTO_CHECK(::unsetenv(key.c_str()) == 0);
-#endif
+ScopedMmap::ScopedMmap(ScopedMmap&& other) noexcept {
+  *this = std::move(other);
 }

-bool IsTty(int fd) {
+ScopedMmap& ScopedMmap::operator=(ScopedMmap&& other) noexcept {
+  if (this == &other) {
+    return *this;
+  }
+  reset();
+  std::swap(ptr_, other.ptr_);
+  std::swap(length_, other.length_);
+  std::swap(file_, other.file_);
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  return ::_isatty(fd) != 0;
-#else
-  return ::isatty(fd) != 0;
+  std::swap(map_, other.map_);
 #endif
+  return *this;
 }

-bool IsTty(FILE* stream) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  return IsTty(::_fileno(stream));
-#else
-  return IsTty(::fileno(stream));
-#endif
+ScopedMmap::~ScopedMmap() {
+  reset();
 }

-void Daemonize(std::function<int(pid_t)> parent_cb) {
+// static
+ScopedMmap ScopedMmap::FromHandle(base::ScopedPlatformHandle file,
+                                  size_t length) {
+  ScopedMmap ret;
+  if (!file) {
+    return ret;
+  }
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
     PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
-    (PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) &&  \
-     !PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE_TVOS))
-  Pipe pipe = Pipe::Create(Pipe::kBothBlock);
-  pid_t pid;
-  switch (pid = fork()) {
-    case -1:
-      PERFETTO_FATAL("fork");
-    case 0: {
-      PERFETTO_CHECK(setsid() != -1);
-      base::ignore_result(chdir("/"));
-      base::ScopedFile null = base::OpenFile("/dev/null", O_RDONLY);
-      PERFETTO_CHECK(null);
-      PERFETTO_CHECK(dup2(*null, STDIN_FILENO) != -1);
-      PERFETTO_CHECK(dup2(*null, STDOUT_FILENO) != -1);
-      PERFETTO_CHECK(dup2(*null, STDERR_FILENO) != -1);
-      // Do not accidentally close stdin/stdout/stderr.
-      if (*null <= 2)
-        null.release();
-      WriteAll(*pipe.wr, "1", 1);
-      break;
-    }
-    default: {
-      // Wait for the child process to have reached the setsid() call. This is
-      // to avoid that 'adb shell perfetto -D' destroys the terminal (hence
-      // sending a SIGHUP to the child) before the child has detached from the
-      // terminal (see b/238644870).
-
-      // This is to unblock the read() below (with EOF, which will fail the
-      // CHECK) in the unlikely case of the child crashing before WriteAll("1").
-      pipe.wr.reset();
-      char one = '\0';
-      PERFETTO_CHECK(Read(*pipe.rd, &one, sizeof(one)) == 1 && one == '1');
-      int err = parent_cb(pid);
-      exit(err);
-    }
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+  void* ptr = mmap(nullptr, length, PROT_READ, MAP_PRIVATE, *file, 0);
+  if (ptr != MAP_FAILED) {
+    ret.ptr_ = ptr;
+    ret.length_ = length;
+    ret.file_ = std::move(file);
+  }
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  ScopedPlatformHandle map(
+      CreateFileMapping(*file, nullptr, PAGE_READONLY, 0, 0, nullptr));
+  if (!map) {
+    return ret;
+  }
+  void* ptr = MapViewOfFile(*map, FILE_MAP_READ, 0, 0, length);
+  if (ptr != nullptr) {
+    ret.ptr_ = ptr;
+    ret.length_ = length;
+    ret.file_ = std::move(file);
+    ret.map_ = std::move(map);
   }
 #else
-  // Avoid -Wunreachable warnings.
-  if (reinterpret_cast<intptr_t>(&Daemonize) != 16)
-    PERFETTO_FATAL("--background is only supported on Linux/Android/Mac");
-  ignore_result(parent_cb);
-#endif  // OS_WIN
+  base::ignore_result(length);
+#endif
+  return ret;
 }

-std::string GetCurExecutablePath() {
-  std::string self_path;
+bool ScopedMmap::reset() noexcept {
+  bool ret = true;
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
-  char buf[PATH_MAX];
-  ssize_t size = readlink("/proc/self/exe", buf, sizeof(buf));
-  PERFETTO_CHECK(size != -1);
-  // readlink does not null terminate.
-  self_path = std::string(buf, static_cast<size_t>(size));
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-  uint32_t size = 0;
-  PERFETTO_CHECK(_NSGetExecutablePath(nullptr, &size));
-  self_path.resize(size);
-  PERFETTO_CHECK(_NSGetExecutablePath(&self_path[0], &size) == 0);
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+  if (ptr_ != nullptr) {
+    ret = munmap(ptr_, length_) == 0;
+  }
 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  char buf[MAX_PATH];
-  auto len = ::GetModuleFileNameA(nullptr /*current*/, buf, sizeof(buf));
-  self_path = std::string(buf, len);
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD)
-  char buf[PATH_MAX];
-  int mib[4], ret;
-  size_t len = sizeof(buf);
-  mib[0] = CTL_KERN;
-  mib[1] = KERN_PROC;
-  mib[2] = KERN_PROC_PATHNAME;
-  mib[3] = -1;
-  ret = sysctl(mib, 4, buf, &len, NULL, 0);
-  PERFETTO_CHECK(ret == 0);
-  // This returns the full path; need to trim the executable
-  self_path = std::string(buf);
-#else
-  PERFETTO_FATAL(
-      "GetCurExecutableDir() not implemented on the current platform");
-#endif
-  return self_path;
-}
-
-std::string GetCurExecutableDir() {
-  auto path = GetCurExecutablePath();
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  // Paths in Windows can have both kinds of slashes (mingw vs msvc).
-  path = path.substr(0, path.find_last_of('\\'));
+  if (ptr_ != nullptr) {
+    ret = UnmapViewOfFile(ptr_);
+  }
+  map_.reset();
 #endif
-  path = path.substr(0, path.find_last_of('/'));
-  return path;
+  ptr_ = nullptr;
+  length_ = 0;
+  file_.reset();
+  return ret;
 }

-void* AlignedAlloc(size_t alignment, size_t size) {
-  void* res = nullptr;
-  alignment = AlignUp<sizeof(void*)>(alignment);  // At least pointer size.
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  // Window's _aligned_malloc() has a nearly identically signature to Unix's
-  // aligned_alloc() but its arguments are obviously swapped.
-  res = _aligned_malloc(size, alignment);
-#else
-  // aligned_alloc() has been introduced in Android only in API 28.
-  // Also NaCl and Fuchsia seems to have only posix_memalign().
-  ignore_result(posix_memalign(&res, alignment, size));
-#endif
-  PERFETTO_CHECK(res);
-  return res;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+// static
+ScopedMmap ScopedMmap::InheritMmappedRange(void* data, size_t size) {
+  ScopedMmap ret;
+  ret.ptr_ = data;
+  ret.length_ = size;
+  return ret;
 }
-
-void AlignedFree(void* ptr) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  _aligned_free(ptr);  // MSDN says it is fine to pass nullptr.
-#else
-  free(ptr);
 #endif
-}

-bool IsSyncMemoryTaggingEnabled() {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  // Compute only once per lifetime of the process.
-  static bool cached_value = [] {
-    const int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
-    if (res < 0)
-      return false;
-    const uint32_t actl = static_cast<uint32_t>(res);
-    return (actl & PR_TAGGED_ADDR_ENABLE) && (actl & PR_MTE_TCF_SYNC);
-  }();
-  return cached_value;
-#else
-  return false;
-#endif
+ScopedMmap ReadMmapFilePart(const std::string& fname, size_t length) {
+  return ScopedMmap::FromHandle(OpenFileForMmap(fname), length);
 }

-std::string HexDump(const void* data_void, size_t len, size_t bytes_per_line) {
-  const char* data = reinterpret_cast<const char*>(data_void);
-  std::string res;
-  static const size_t kPadding = bytes_per_line * 3 + 12;
-  std::unique_ptr<char[]> line(new char[bytes_per_line * 4 + 128]);
-  for (size_t i = 0; i < len; i += bytes_per_line) {
-    char* wptr = line.get();
-    wptr += base::SprintfTrunc(wptr, 19, "%08zX: ", i);
-    for (size_t j = i; j < i + bytes_per_line && j < len; j++) {
-      wptr += base::SprintfTrunc(wptr, 4, "%02X ",
-                                 static_cast<unsigned>(data[j]) & 0xFF);
-    }
-    for (size_t j = static_cast<size_t>(wptr - line.get()); j < kPadding; ++j)
-      *(wptr++) = ' ';
-    for (size_t j = i; j < i + bytes_per_line && j < len; j++) {
-      char c = data[j];
-      *(wptr++) = (c >= 32 && c < 127) ? c : '.';
-    }
-    *(wptr++) = '\n';
-    *(wptr++) = '\0';
-    res.append(line.get());
+ScopedMmap ReadMmapWholeFile(const std::string& fname) {
+  ScopedPlatformHandle file = OpenFileForMmap(fname);
+  if (!file) {
+    return ScopedMmap();
   }
-  return res;
+  std::optional<uint64_t> file_size = GetFileSize(file.get());
+  if (!file_size.has_value()) {
+    return ScopedMmap();
+  }
+  size_t size = static_cast<size_t>(*file_size);
+  if (static_cast<uint64_t>(size) != *file_size) {
+    return ScopedMmap();
+  }
+  return ScopedMmap::FromHandle(std::move(file), size);
 }

-}  // namespace base
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/uuid.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/uuid.h
+}  // namespace perfetto::base
+// gen_amalgamated begin source: src/base/scoped_sched_boost.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/scoped_sched_boost.h
+// gen_amalgamated begin header: include/perfetto/ext/base/status_or.h
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -10411,68 +10744,94 @@ std::string HexDump(const void* data_void, size_t len, size_t bytes_per_line) {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_UUID_H_
-#define INCLUDE_PERFETTO_EXT_BASE_UUID_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_STATUS_OR_H_
+#define INCLUDE_PERFETTO_EXT_BASE_STATUS_OR_H_

-#include <string.h>
-#include <array>
-#include <cstdint>
-#include <string>
+#include <optional>
+#include <utility>

-// gen_amalgamated expanded: #include "perfetto/base/export.h"
+// gen_amalgamated expanded: #include "perfetto/base/status.h"

 namespace perfetto {
 namespace base {

-class PERFETTO_EXPORT_COMPONENT Uuid {
+// Union of a object of type |T| with a |base::Status|. Useful for cases where
+// a |T| indicates a successful result of an operation and |base::Status|
+// represents an error.
+//
+// This class is modelled closely on absl::Status and should essentially 1:1
+// match it's API.
+template <typename T>
+class StatusOr {
  public:
-  explicit Uuid(const std::string& s);
-  explicit Uuid(int64_t lsb, int64_t msb);
-  Uuid();
-
-  std::array<uint8_t, 16>* data() { return &data_; }
-  const std::array<uint8_t, 16>* data() const { return &data_; }
-
-  bool operator==(const Uuid& other) const { return data_ == other.data_; }
+  // Matches naming of declarations in similar types e.g. std::optional,
+  // std::variant.
+  using value_type = T;

-  bool operator!=(const Uuid& other) const { return !(*this == other); }
+  // Intentionally implicit to allow idomatic usage (e.g. returning value/status
+  // from base::StatusOr returning function).
+  StatusOr(base::Status status) : StatusOr(std::move(status), std::nullopt) {
+    if (status_.ok()) {
+      // Matches what Abseil's approach towards OkStatus being passed to
+      // absl::StatusOr<T>.
+      PERFETTO_FATAL("base::OkStatus passed to StatusOr: this is not allowed");
+    }
+  }
+  StatusOr(T value) : StatusOr(base::OkStatus(), std::move(value)) {}

-  explicit operator bool() const { return *this != Uuid(); }
+  bool ok() const { return status_.ok(); }
+  const base::Status& status() const { return status_; }

-  int64_t msb() const {
-    int64_t result;
-    memcpy(&result, data_.data() + 8, 8);
-    return result;
+  // For an lvalue caller, if ok(), returns a reference to the value, so the
+  // StatusOr keeps owning it.
+  // If not, we log the error message and abort.
+  T& ValueOrDie() & {
+    if (!ok())
+      PERFETTO_FATAL("%s", status_.c_message());
+    return *value_;
   }

-  int64_t lsb() const {
-    int64_t result;
-    memcpy(&result, data_.data(), 8);
-    return result;
+  // For an rvalue caller, if ok(), moves the value out because the StatusOr
+  // won't be used again.
+  // If not, we log the error message and abort.
+  T ValueOrDie() && {
+    if (!ok())
+      PERFETTO_FATAL("%s", status_.c_message());
+    return std::move(*value_);
   }

-  void set_lsb_msb(int64_t lsb, int64_t msb) {
-    set_lsb(lsb);
-    set_msb(msb);
+  T& value() {
+    PERFETTO_DCHECK(status_.ok());
+    return *value_;
   }
-  void set_msb(int64_t msb) { memcpy(data_.data() + 8, &msb, 8); }
-  void set_lsb(int64_t lsb) { memcpy(data_.data(), &lsb, 8); }
+  const T& value() const { return *value_; }

-  std::string ToString() const;
-  std::string ToPrettyString() const;
+  T& operator*() { return value(); }
+  const T& operator*() const { return value(); }
+
+  T* operator->() { return &value(); }
+  const T* operator->() const { return &value(); }

  private:
-  std::array<uint8_t, 16> data_{};
+  StatusOr(base::Status status, std::optional<T> value)
+      : status_(std::move(status)), value_(std::move(value)) {
+    PERFETTO_DCHECK(!status_.ok() || value_.has_value());
+  }
+
+  base::Status status_;
+  std::optional<T> value_;
 };

-Uuid Uuidv4();
+// Deduction guide to make returning StatusOr less verbose.
+template <typename T>
+StatusOr(T) -> StatusOr<T>;

 }  // namespace base
 }  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_UUID_H_
+#endif  // INCLUDE_PERFETTO_EXT_BASE_STATUS_OR_H_
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -10487,96 +10846,91 @@ Uuid Uuidv4();
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/uuid.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_SCOPED_SCHED_BOOST_H_
+#define INCLUDE_PERFETTO_EXT_BASE_SCOPED_SCHED_BOOST_H_

-#include <random>
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"

-// gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+namespace perfetto::base {

-namespace perfetto {
-namespace base {
-namespace {
+// kSchedOther: it's the default policy (e.g., CFS on Linux). Range: 0-20.
+//              prio is interpreted as -(nice), i.e., 1 is slightly higher prio
+//              than default 0, 20 is the highest priority.
+//              Note that this is the opposite semantic of the cmdline nice, and
+//              is done for consistency with kSchedFifo, so higher
+//              number == higher prio.
+// kSchedFifo: real-time priority. Range: 1-99. 1 is the lowest priority, 99 the
+//             highest.
+struct SchedPolicyAndPrio {
+  enum class Policy {
+    kSchedOther,
+    kSchedFifo
+  };  // Order matters for operator<().

-constexpr char kHexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7',
-                            '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+  bool operator<(const SchedPolicyAndPrio& other) const {
+    return std::tie(policy, prio) < std::tie(other.policy, other.prio);
+  }

-}  // namespace
-
-// A globally unique 128-bit number.
-// In the early days of perfetto we were (sorta) respecting rfc4122. Later we
-// started replacing the LSB of the UUID with the statsd subscription ID in
-// other parts of the codebase (see perfetto_cmd.cc) for the convenience of
-// trace lookups, so rfc4122 made no sense as it just reduced entropy.
-Uuid Uuidv4() {
-  // Mix different sources of entropy to reduce the chances of collisions.
-  // Only using boot time is not enough. Under the assumption that most traces
-  // are started around the same time at boot, within a 1s window, the birthday
-  // paradox gives a chance of 90% collisions with 70k traces over a 1e9 space
-  // (Number of ns in a 1s window).
-  // We deliberately don't use /dev/urandom as that might block for
-  // unpredictable time if the system is idle (and is not portable).
-  // The UUID does NOT need to be cryptographically secure, but random enough
-  // to avoid collisions across a large number of devices.
-  uint64_t boot_ns = static_cast<uint64_t>(GetBootTimeNs().count());
-  uint64_t epoch_ns = static_cast<uint64_t>(GetWallTimeNs().count());
+  bool operator==(const SchedPolicyAndPrio& other) const {
+    return policy == other.policy && prio == other.prio;
+  }

-  // Use code ASLR as entropy source.
-  uint32_t code_ptr =
-      static_cast<uint32_t>(reinterpret_cast<uint64_t>(&Uuidv4) >> 12);
+  bool operator!=(const SchedPolicyAndPrio& other) const {
+    return !(*this == other);
+  }

-  // Use stack ASLR as a further entropy source.
-  uint32_t stack_ptr =
-      static_cast<uint32_t>(reinterpret_cast<uint64_t>(&code_ptr) >> 12);
+  Policy policy = Policy::kSchedOther;
+  uint32_t prio = 0;
+};

-  uint32_t entropy[] = {static_cast<uint32_t>(boot_ns >> 32),
-                        static_cast<uint32_t>(boot_ns),
-                        static_cast<uint32_t>(epoch_ns >> 32),
-                        static_cast<uint32_t>(epoch_ns),
-                        code_ptr,
-                        stack_ptr};
-  std::seed_seq entropy_seq(entropy, entropy + ArraySize(entropy));
+// Used to mock Linux scheduling policy and parameters API in tests.
+// In production code calls the Linux API, the class is stateless.
+class SchedOsHooks {
+ public:
+  struct SchedOsConfig {
+    int policy;
+    int rt_prio;
+    int nice;
+  };

-  auto words = std::array<uint32_t, 4>();
-  entropy_seq.generate(words.begin(), words.end());
-  uint64_t msb = static_cast<uint64_t>(words[0]) << 32u | words[1];
-  uint64_t lsb = static_cast<uint64_t>(words[2]) << 32u | words[3];
-  return Uuid(static_cast<int64_t>(lsb), static_cast<int64_t>(msb));
-}
+  virtual ~SchedOsHooks() = default;
+  static SchedOsHooks* GetInstance();
+  // virtual for testing
+  virtual Status SetSchedConfig(const SchedOsConfig& arg);
+  // virtual for testing
+  virtual StatusOr<SchedOsConfig> GetCurrentSchedConfig() const;
+};

-Uuid::Uuid() {}
+// RAII helper to temporarily boost the scheduler priority of the current
+// thread. The priority is reverted to the original value when the object goes
+// out of scope.
+// It is supported only on Linux/Android, fails on other platforms.
+class ScopedSchedBoost {
+ public:
+  static StatusOr<ScopedSchedBoost> Boost(SchedPolicyAndPrio);
+  ScopedSchedBoost(ScopedSchedBoost&&) noexcept;
+  ScopedSchedBoost& operator=(ScopedSchedBoost&&) noexcept;
+  ~ScopedSchedBoost();

-Uuid::Uuid(const std::string& s) {
-  PERFETTO_CHECK(s.size() == data_.size());
-  memcpy(data_.data(), s.data(), s.size());
-}
+  // No copy constructors.
+  ScopedSchedBoost(const ScopedSchedBoost&) = delete;
+  ScopedSchedBoost& operator=(const ScopedSchedBoost&) = delete;

-Uuid::Uuid(int64_t lsb, int64_t msb) {
-  set_lsb_msb(lsb, msb);
-}
+  static void ResetForTesting(SchedOsHooks*);

-std::string Uuid::ToString() const {
-  return std::string(reinterpret_cast<const char*>(data_.data()), data_.size());
-}
+ private:
+  explicit ScopedSchedBoost(SchedPolicyAndPrio p) : policy_and_prio_(p) {}

-std::string Uuid::ToPrettyString() const {
-  std::string s(data_.size() * 2 + 4, '-');
-  // Format is 123e4567-e89b-12d3-a456-426655443322.
-  size_t j = 0;
-  for (size_t i = 0; i < data_.size(); ++i) {
-    if (i == 4 || i == 6 || i == 8 || i == 10)
-      j++;
-    s[2 * i + j] = kHexmap[(data_[data_.size() - i - 1] & 0xf0) >> 4];
-    s[2 * i + 1 + j] = kHexmap[(data_[data_.size() - i - 1] & 0x0f)];
-  }
-  return s;
-}
+  std::optional<SchedPolicyAndPrio> policy_and_prio_;
+  ThreadChecker thread_checker_;
+};
+}  // namespace perfetto::base

-}  // namespace base
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/virtual_destructors.cc
+#endif  // INCLUDE_PERFETTO_EXT_BASE_SCOPED_SCHED_BOOST_H_
+// gen_amalgamated begin header: include/perfetto/ext/base/status_macros.h
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -10591,24 +10945,35 @@ std::string Uuid::ToPrettyString() const {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_STATUS_MACROS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_STATUS_MACROS_H_

-// This translation unit contains the definitions for the destructor of pure
-// virtual interfaces for the current build target. The alternative would be
-// introducing a one-liner .cc file for each pure virtual interface, which is
-// overkill. This is for compliance with -Wweak-vtables.
+// gen_amalgamated expanded: #include "perfetto/base/status.h"

-namespace perfetto {
-namespace base {
+// Evaluates |expr|, which should return a base::Status. If the status is an
+// error status, returns the status from the current function.
+#define RETURN_IF_ERROR(expr)                                       \
+  do {                                                              \
+    ::perfetto::base::Status status_macro_internal_status = (expr); \
+    if (!status_macro_internal_status.ok())                         \
+      return status_macro_internal_status;                          \
+  } while (0)

-TaskRunner::~TaskRunner() = default;
+#define PERFETTO_INTERNAL_CONCAT_IMPL(x, y) x##y
+#define PERFETTO_INTERNAL_MACRO_CONCAT(x, y) PERFETTO_INTERNAL_CONCAT_IMPL(x, y)

-}  // namespace base
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/waitable_event.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/waitable_event.h
+// Evalues |rhs| which should return a base::StatusOr<T> and assigns this
+// to |lhs|. If the status is an error status, returns the status from the
+// current function.
+#define ASSIGN_OR_RETURN(lhs, rhs)                                   \
+  PERFETTO_INTERNAL_MACRO_CONCAT(auto status_or, __LINE__) = rhs;    \
+  RETURN_IF_ERROR(                                                   \
+      PERFETTO_INTERNAL_MACRO_CONCAT(status_or, __LINE__).status()); \
+  lhs = std::move(PERFETTO_INTERNAL_MACRO_CONCAT(status_or, __LINE__).value())
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_STATUS_MACROS_H_
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -10623,46 +10988,249 @@ TaskRunner::~TaskRunner() = default;
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_WAITABLE_EVENT_H_
-#define INCLUDE_PERFETTO_EXT_BASE_WAITABLE_EVENT_H_
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_sched_boost.h"

-// gen_amalgamated expanded: #include "perfetto/base/thread_annotations.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_macros.h"

-#include <stdint.h>
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sched.h>         // for 'SCHED_' macros and 'sched_' functions
+#include <sys/resource.h>  // for 'setpriority', 'getpriority', 'PRIO_PROCESS'
+#include <sys/types.h>     // for 'pid_t'

-#include <condition_variable>
-#include <mutex>
+#include <algorithm>
+#include <vector>

-namespace perfetto {
-namespace base {
+#endif  // PERFETTO_OS_LINUX || PERFETTO_OS_ANDROID

-// A waitable event for cross-thread synchronization.
-// All methods on this class can be called from any thread.
-class WaitableEvent {
+namespace perfetto::base {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+
+namespace {
+constexpr pid_t kCurrentPid = 0;
+
+class ThreadMgr {
  public:
-  WaitableEvent();
-  ~WaitableEvent();
-  WaitableEvent(const WaitableEvent&) = delete;
-  WaitableEvent operator=(const WaitableEvent&) = delete;
+  // In this class we manage the boost state for the whole process.
+  // If we want to use it for each single thread, this needs to become
+  // 'GetInstanceForThread()'.
+  static ThreadMgr* GetInstance();

-  // Synchronously block until the event is notified `notification` times.
-  void Wait(uint64_t notifications = 1);
+  explicit ThreadMgr(SchedOsHooks*);

-  // Signal the event, waking up blocked waiters.
-  void Notify();
+  Status Add(SchedPolicyAndPrio);
+  void Remove(SchedPolicyAndPrio);
+  Status RecalcAndUpdatePrio();
+
+  ThreadMgr(const ThreadMgr&) = delete;
+  ThreadMgr& operator=(const ThreadMgr&) = delete;
+  ThreadMgr(ThreadMgr&&) = delete;
+  ThreadMgr& operator=(ThreadMgr&&) = delete;
+
+  void ResetForTesting(SchedOsHooks*);

  private:
-  std::mutex mutex_;
-  std::condition_variable event_ PERFETTO_GUARDED_BY(mutex_);
-  uint64_t notifications_ PERFETTO_GUARDED_BY(mutex_) = 0;
+  SchedOsHooks* os_hooks_;
+  SchedOsHooks::SchedOsConfig initial_config_{};
+  std::vector<SchedPolicyAndPrio> prios_;
 };

-}  // namespace base
-}  // namespace perfetto
+ThreadMgr* ThreadMgr::GetInstance() {
+  static auto* instance = new ThreadMgr(SchedOsHooks::GetInstance());
+  return instance;
+}
+ThreadMgr::ThreadMgr(SchedOsHooks* os_hooks) : os_hooks_(os_hooks) {
+  auto res = os_hooks_->GetCurrentSchedConfig();
+  if (!res.ok()) {
+    // Should never fail: even without CAP_SYS_NICE we can always get our own
+    // policy and prio. If something goes very wrong, log an error and use
+    // SCHED_OTHER as initial config.
+    PERFETTO_DFATAL_OR_ELOG("Failed to get default sched config: %s",
+                            res.status().c_message());
+    initial_config_ = SchedOsHooks::SchedOsConfig{SCHED_OTHER, 0, 0};
+  } else {
+    initial_config_ = res.value();
+  }
+}

-#endif  // INCLUDE_PERFETTO_EXT_BASE_WAITABLE_EVENT_H_
+Status ThreadMgr::Add(SchedPolicyAndPrio spp) {
+  prios_.push_back(spp);
+  return RecalcAndUpdatePrio();
+}
+
+void ThreadMgr::Remove(SchedPolicyAndPrio spp) {
+  auto it = std::find(prios_.begin(), prios_.end(), spp);
+  if (it != prios_.end()) {
+    prios_.erase(it);
+  }
+  // It is possible that we previously added the wrongly configured policy,
+  // that wasn't the max policy. In that case the policy will be validated now,
+  // in 'RecalcAndUpdatePrio'.
+  // This for loop makes us ignore all wrongly configured policies and fallback
+  // to the first correct (or the initial one).
+  for (;;) {
+    if (auto res = RecalcAndUpdatePrio(); !res.ok()) {
+      PERFETTO_ELOG("%s", res.c_message());
+    } else {
+      break;
+    }
+  }
+}
+
+Status ThreadMgr::RecalcAndUpdatePrio() {
+  if (prios_.empty()) {
+    return os_hooks_->SetSchedConfig(initial_config_);
+  }
+  // TODO(ktimofeev): Check previously set prio to skip unnecessary syscall?
+  auto max_prio = std::max_element(prios_.begin(), prios_.end());
+  SchedOsHooks::SchedOsConfig os_config{};
+  switch (max_prio->policy) {
+    case SchedPolicyAndPrio::Policy::kSchedOther:
+      os_config = SchedOsHooks::SchedOsConfig{
+          SCHED_OTHER, 0, -1 * static_cast<int>(max_prio->prio)};
+      break;
+    case SchedPolicyAndPrio::Policy::kSchedFifo:
+      os_config = SchedOsHooks::SchedOsConfig{
+          SCHED_FIFO, static_cast<int>(max_prio->prio), 0};
+      break;
+  }
+  Status res = os_hooks_->SetSchedConfig(os_config);
+  if (!res.ok()) {
+    prios_.erase(max_prio);
+    return res;
+  }
+  return OkStatus();
+}
+
+void ThreadMgr::ResetForTesting(SchedOsHooks* os_hooks) {
+  os_hooks_ = os_hooks;
+  initial_config_ = os_hooks_->GetCurrentSchedConfig().value();
+  prios_.clear();
+}
+
+}  // namespace
+
+SchedOsHooks* SchedOsHooks::GetInstance() {
+  static auto* instance = new SchedOsHooks();
+  return instance;
+}
+
+Status SchedOsHooks::SetSchedConfig(const SchedOsConfig& arg) {
+  sched_param param{};
+  param.sched_priority = arg.rt_prio;
+  int ret = sched_setscheduler(kCurrentPid, arg.policy, &param);
+  if (ret == -1) {
+    return ErrStatus("sched_setscheduler(%d, %d) failed (errno: %d, %s)",
+                     arg.policy, arg.rt_prio, errno, strerror(errno));
+  }
+  // For the real-time policies the 'arg.rt_prio' value is >= 1.
+  // The userspace policies (SCHED_OTHER, SCHED_BATCH, SCHED_IDLE) use the nice
+  // value.
+  if (arg.rt_prio == 0) {
+    ret = setpriority(PRIO_PROCESS, kCurrentPid, arg.nice);
+    if (ret == -1) {
+      return ErrStatus("setpriority(%d) failed (errno: %d, %s)", arg.nice,
+                       errno, strerror(errno));
+    }
+  }
+  return OkStatus();
+}
+
+StatusOr<SchedOsHooks::SchedOsConfig> SchedOsHooks::GetCurrentSchedConfig()
+    const {
+  int policy = sched_getscheduler(kCurrentPid);
+  if (policy == -1) {
+    return ErrStatus("sched_getscheduler failed (errno: %d, %s)", errno,
+                     strerror(errno));
+  }
+  sched_param param{};
+  if (sched_getparam(kCurrentPid, &param) == -1) {
+    return ErrStatus("sched_getparam failed (errno: %d, %s)", errno,
+                     strerror(errno));
+  }
+  int nice = 0;
+  // For the real-time policies the 'param.sched_priority' value is >= 1.
+  // The userspace policies (SCHED_OTHER, SCHED_BATCH, SCHED_IDLE) use the nice
+  // value.
+  if (param.sched_priority == 0) {
+    errno = 0;
+    nice = getpriority(PRIO_PROCESS, kCurrentPid);
+    if (nice == -1 && errno != 0) {
+      return ErrStatus("getpriority failed (errno: %d, %s)", errno,
+                       strerror(errno));
+    }
+  }
+  return SchedOsConfig{policy, param.sched_priority, nice};
+}
+
+// static
+StatusOr<ScopedSchedBoost> ScopedSchedBoost::Boost(SchedPolicyAndPrio spp) {
+  auto res = ThreadMgr::GetInstance()->Add(spp);
+  RETURN_IF_ERROR(res);
+  return ScopedSchedBoost(spp);
+}
+
+ScopedSchedBoost::ScopedSchedBoost(ScopedSchedBoost&& other) noexcept {
+  this->policy_and_prio_ = other.policy_and_prio_;
+  other.policy_and_prio_ = std::nullopt;
+}
+
+ScopedSchedBoost& ScopedSchedBoost::operator=(
+    ScopedSchedBoost&& other) noexcept {
+  if (this != &other) {
+    this->~ScopedSchedBoost();
+    new (this) ScopedSchedBoost(std::move(other));
+  }
+  return *this;
+}
+
+ScopedSchedBoost::~ScopedSchedBoost() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (!policy_and_prio_.has_value())
+    return;
+  ThreadMgr::GetInstance()->Remove(*policy_and_prio_);
+  policy_and_prio_ = std::nullopt;
+}
+
+// static
+void ScopedSchedBoost::ResetForTesting(SchedOsHooks* os_manager) {
+  ThreadMgr::GetInstance()->ResetForTesting(os_manager);
+}
+
+#else
+
+// static
+SchedOsHooks* SchedOsHooks::GetInstance() {
+  return nullptr;
+}
+
+Status SchedOsHooks::SetSchedConfig(const SchedOsHooks::SchedOsConfig&) {
+  return ErrStatus("SchedOsHooks is supported only on Linux/Android");
+}
+
+StatusOr<SchedOsHooks::SchedOsConfig> SchedOsHooks::GetCurrentSchedConfig()
+    const {
+  return ErrStatus("SchedOsHooks is supported only on Linux/Android");
+}
+
+// static
+StatusOr<ScopedSchedBoost> ScopedSchedBoost::Boost(SchedPolicyAndPrio) {
+  return ErrStatus("ScopedSchedBoost is supported only on Linux/Android");
+}
+
+ScopedSchedBoost::ScopedSchedBoost(ScopedSchedBoost&&) noexcept = default;
+ScopedSchedBoost& ScopedSchedBoost::operator=(ScopedSchedBoost&&) noexcept =
+    default;
+ScopedSchedBoost::~ScopedSchedBoost() = default;
+
+#endif  // PERFETTO_OS_LINUX || PERFETTO_OS_ANDROID
+
+}  // namespace perfetto::base
+// gen_amalgamated begin source: src/base/status.cc
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -10677,36 +11245,97 @@ class WaitableEvent {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/waitable_event.h"
+// gen_amalgamated expanded: #include "perfetto/base/status.h"

-namespace perfetto {
-namespace base {
+#include <algorithm>
+#include <cstdarg>
+#include <cstdio>
+#include <string>
+#include <utility>

-WaitableEvent::WaitableEvent() = default;
-WaitableEvent::~WaitableEvent() = default;
+namespace perfetto::base {

-void WaitableEvent::Wait(uint64_t notifications)
-    PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
-  // 'std::unique_lock' lock doesn't work well with thread annotations
-  // (see https://github.com/llvm/llvm-project/issues/63239),
-  // so we suppress thread safety static analysis for this method.
-  std::unique_lock<std::mutex> lock(mutex_);
-  return event_.wait(lock, [&]() PERFETTO_EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
-    return notifications_ >= notifications;
-  });
+Status ErrStatus(const char* format, ...) {
+  std::string buf;
+  buf.resize(1024);
+  for (;;) {
+    va_list ap;
+    va_start(ap, format);
+    int N = vsnprintf(buf.data(), buf.size() - 1, format, ap);
+    va_end(ap);
+
+    if (N <= 0) {
+      buf = "[printf format error]";
+      break;
+    }
+
+    auto sN = static_cast<size_t>(N);
+    if (sN > buf.size() - 1) {
+      // Indicates that the string was truncated and sN is the "number of
+      // non-null bytes which would be needed to fit the result". This is the
+      // C99 standard behaviour in the case of truncation. In that case, resize
+      // the buffer to match the returned value (with + 1 for the null
+      // terminator) and try again.
+      buf.resize(sN + 1);
+      continue;
+    }
+    if (sN == buf.size() - 1) {
+      // Indicates that the string was likely truncated and sN is just the
+      // number of bytes written into the string. This is the behaviour of
+      // non-standard compilers (MSVC) etc. In that case, just double the
+      // storage and try again.
+      buf.resize(sN * 2);
+      continue;
+    }
+
+    // Otherwise, indicates the string was written successfully: we need to
+    // resize to match the number of non-null bytes and return.
+    buf.resize(sN);
+    break;
+  }
+  return Status(std::move(buf));
 }

-void WaitableEvent::Notify() {
-  std::lock_guard<std::mutex> lock(mutex_);
-  ++notifications_;
-  event_.notify_all();
+std::optional<std::string_view> Status::GetPayload(
+    std::string_view type_url) const {
+  if (ok()) {
+    return std::nullopt;
+  }
+  for (const auto& kv : payloads_) {
+    if (kv.type_url == type_url) {
+      return kv.payload;
+    }
+  }
+  return std::nullopt;
 }

-}  // namespace base
-}  // namespace perfetto
-// gen_amalgamated begin source: src/base/watchdog_posix.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/watchdog.h
-// gen_amalgamated begin header: include/perfetto/ext/base/watchdog_noop.h
+void Status::SetPayload(std::string_view type_url, std::string value) {
+  if (ok()) {
+    return;
+  }
+  for (auto& kv : payloads_) {
+    if (kv.type_url == type_url) {
+      kv.payload = value;
+      return;
+    }
+  }
+  payloads_.push_back(Payload{std::string(type_url), std::move(value)});
+}
+
+bool Status::ErasePayload(std::string_view type_url) {
+  if (ok()) {
+    return false;
+  }
+  auto it = std::remove_if(
+      payloads_.begin(), payloads_.end(),
+      [type_url](const Payload& p) { return p.type_url == type_url; });
+  bool erased = it != payloads_.end();
+  payloads_.erase(it, payloads_.end());
+  return erased;
+}
+
+}  // namespace perfetto::base
+// gen_amalgamated begin source: src/base/string_splitter.cc
 /*
  * Copyright (C) 2018 The Android Open Source Project
  *
@@ -10723,149 +11352,83 @@ void WaitableEvent::Notify() {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_NOOP_H_
-#define INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_NOOP_H_
-
-#include <stdint.h>
-
-#include <functional>
-
-namespace perfetto {
-namespace base {
-
-class TaskRunner;
-
-enum class WatchdogCrashReason;  // Defined in watchdog.h.
-struct WatchdogCrashInfo;        // Defined in watchdog.h.
-
-class Watchdog {
- public:
-  class Timer {
-   public:
-    // Define an empty dtor to avoid "unused variable" errors on the call site.
-    Timer() {}
-    Timer(const Timer&) {}
-    ~Timer() {}
-  };
-  static Watchdog* GetInstance() {
-    static Watchdog* watchdog = new Watchdog();
-    return watchdog;
-  }
-  Timer CreateFatalTimer(uint32_t /*ms*/, WatchdogCrashReason) {
-    return Timer();
-  }
-  void Start(TaskRunner* /*task_runner*/ = nullptr,
-             std::function<void(WatchdogCrashInfo)> /*handler*/ = {}) {}
-  void SetMemoryLimit(uint64_t /*bytes*/, uint32_t /*window_ms*/) {}
-  void SetCpuLimit(uint32_t /*percentage*/, uint32_t /*window_ms*/) {}
-};
-
-}  // namespace base
-}  // namespace perfetto
-
-#endif  // INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_NOOP_H_
-/*
- * 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.
- */
-
-#ifndef INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_H_
-#define INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_H_
-
-#include <functional>
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_splitter.h"

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+#include <utility>

-// The POSIX watchdog is only supported on Linux and Android in non-embedder
-// builds.
-#if PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG)
-// gen_amalgamated expanded: #include "perfetto/ext/base/watchdog_posix.h"
-#else
-// gen_amalgamated expanded: #include "perfetto/ext/base/watchdog_noop.h"
-#endif
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"

 namespace perfetto {
 namespace base {

-// Used only to add more details to crash reporting.
-enum class WatchdogCrashReason {
-  kUnspecified = 0,
-  kCpuGuardrail = 1,
-  kMemGuardrail = 2,
-  kTaskRunnerHung = 3,
-  kTraceDidntStop = 4,
-};
-
-// Information about an imminent watchdog-triggered crash. Passed to the fatal
-// handler installed via Watchdog::Start() so embedders can record it (e.g. via
-// metatrace counters) before the process is force-killed.
-struct WatchdogCrashInfo {
-  int thread_id = 0;
-  WatchdogCrashReason reason = WatchdogCrashReason::kUnspecified;
-
-  // The time in CLOCK_BOOTTIME when the watchdog triggered.
-  int64_t alarm_time_ms = 0;
-
-  // The initial timeout passed to CreateFatalTimer.
-  int timeout_s = 0;
-
-  // Elapsed seconds, since the offending fatal timer was created, sampled
-  // from CLOCK_MONOTONIC, CLOCK_BOOTTIME and CLOCK_PROCESS_CPUTIME_ID
-  // respectively. These mirror the wdog_actual_{mono,boot,cpu} crash keys.
-  // They are 0 for cpu/memory guardrail crashes (no associated timer).
-  int actual_mono_s = 0;
-  int actual_boot_s = 0;
-  int actual_cpu_s = 0;
-};
-
-// Make the limits more relaxed on desktop, where multi-GB traces are likely.
-// Multi-GB traces can take bursts of cpu time to write into disk at the end of
-// the trace.
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-constexpr uint32_t kWatchdogDefaultCpuLimit = 75;
-constexpr uint32_t kWatchdogDefaultCpuWindow = 5 * 60 * 1000;  // 5 minutes.
-#else
-constexpr uint32_t kWatchdogDefaultCpuLimit = 90;
-constexpr uint32_t kWatchdogDefaultCpuWindow = 10 * 60 * 1000;  // 10 minutes.
-#endif
+StringSplitter::StringSplitter(std::string str,
+                               char delimiter,
+                               EmptyTokenMode empty_token_mode)
+    : str_(std::move(str)),
+      delimiter_(delimiter),
+      empty_token_mode_(empty_token_mode) {
+  // It's legal to access str[str.size()] in C++11 (it always returns \0),
+  // hence the +1 (which becomes just size() after the -1 in Initialize()).
+  Initialize(&str_[0], str_.size() + 1);
+}

-// The default memory margin we give to our processes. This is used as as a
-// constant to put on top of the trace buffers.
-// TODO(b/540573436): Retune the value once we have a proper solution.
-constexpr uint64_t kWatchdogDefaultMemorySlack = 64 * 1024 * 1024;  // 64 MiB.
-constexpr uint32_t kWatchdogDefaultMemoryWindow = 30 * 1000;  // 30 seconds.
+StringSplitter::StringSplitter(char* str,
+                               size_t size,
+                               char delimiter,
+                               EmptyTokenMode empty_token_mode)
+    : delimiter_(delimiter), empty_token_mode_(empty_token_mode) {
+  Initialize(str, size);
+}

-inline void RunTaskWithWatchdogGuard(const std::function<void()>& task) {
-  // The longest duration allowed for a single task within the TaskRunner.
-  // Exceeding this limit will trigger program termination.
-  constexpr int64_t kWatchdogMillis = 180000;  // 180s
+StringSplitter::StringSplitter(StringSplitter* outer,
+                               char delimiter,
+                               EmptyTokenMode empty_token_mode)
+    : delimiter_(delimiter), empty_token_mode_(empty_token_mode) {
+  Initialize(outer->cur_token(), outer->cur_token_size() + 1);
+}

-  Watchdog::Timer handle = base::Watchdog::GetInstance()->CreateFatalTimer(
-      kWatchdogMillis, WatchdogCrashReason::kTaskRunnerHung);
-  task();
+void StringSplitter::Initialize(char* str, size_t size) {
+  PERFETTO_DCHECK(!size || str);
+  next_ = str;
+  end_ = str + size;
+  cur_ = nullptr;
+  cur_size_ = 0;
+  if (size)
+    next_[size - 1] = '\0';
+}

-  // Suppress unused variable warnings in the client library amalgamated build.
-  (void)kWatchdogDefaultCpuLimit;
-  (void)kWatchdogDefaultCpuWindow;
-  (void)kWatchdogDefaultMemorySlack;
-  (void)kWatchdogDefaultMemoryWindow;
+bool StringSplitter::Next() {
+  for (; next_ < end_; next_++) {
+    if (*next_ == delimiter_ &&
+        empty_token_mode_ == EmptyTokenMode::DISALLOW_EMPTY_TOKENS) {
+      // If empty tokens are disallowed, find fist non-delimiter character.
+      continue;
+    }
+    cur_ = next_;
+    for (;; next_++) {
+      if (*next_ == delimiter_) {
+        cur_size_ = static_cast<size_t>(next_ - cur_);
+        *(next_++) = '\0';
+        break;
+      }
+      if (*next_ == '\0') {
+        cur_size_ = static_cast<size_t>(next_ - cur_);
+        next_ = end_;
+        break;
+      }
+    }
+    if (*cur_ || empty_token_mode_ == EmptyTokenMode::ALLOW_EMPTY_TOKENS)
+      return true;
+    break;
+  }
+  cur_ = nullptr;
+  cur_size_ = 0;
+  return false;
 }

 }  // namespace base
 }  // namespace perfetto
-
-#endif  // INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_H_
+// gen_amalgamated begin source: src/base/string_utils.cc
 /*
  * Copyright (C) 2018 The Android Open Source Project
  *
@@ -10882,501 +11445,433 @@ inline void RunTaskWithWatchdogGuard(const std::function<void()>& task) {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/platform.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/watchdog.h"
-
-#if PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG)

-#include <fcntl.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/syscall.h>
-#include <sys/timerfd.h>
-#include <unistd.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <string.h>

 #include <algorithm>
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <xlocale.h>
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <windows.h>
+#endif
+
 #include <cinttypes>
-#include <fstream>
-#include <thread>
+#include <string_view>

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
-// gen_amalgamated expanded: #include "perfetto/base/thread_utils.h"
-// gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/crash_keys.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"

 namespace perfetto {
 namespace base {

-namespace {
-
-constexpr uint32_t kDefaultPollingInterval = 30 * 1000;
-
-// Grace period given to a fatal handler (passed to Start()) to
-// crash the process on its own before the watchdog force-kills it.
-constexpr uint32_t kFatalHandlerGraceMs = 60 * 1000;
-
-base::CrashKey g_crash_key_reason("wdog_reason");
-base::CrashKey g_crash_key_timeout("wdog_timeout");
-base::CrashKey g_crash_key_actual_mono("wdog_actual_mono");
-base::CrashKey g_crash_key_actual_boot("wdog_actual_boot");
-base::CrashKey g_crash_key_actual_cpu("wdog_actual_cpu");
-
-bool IsMultipleOf(uint32_t number, uint32_t divisor) {
-  return number >= divisor && number % divisor == 0;
+// Locale-independent as possible version of strtod.
+double StrToD(const char* nptr, char** endptr) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) ||           \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+  static auto c_locale = newlocale(LC_ALL, "C", nullptr);
+  return strtod_l(nptr, endptr, c_locale);
+#else
+  return strtod(nptr, endptr);
+#endif
 }

-double MeanForArray(const uint64_t array[], size_t size) {
-  uint64_t total = 0;
-  for (size_t i = 0; i < size; i++) {
-    total += array[i];
-  }
-  return static_cast<double>(total / size);
+bool StartsWith(const std::string& str, const std::string& prefix) {
+  return str.compare(0, prefix.length(), prefix) == 0;
 }

-}  //  namespace
-
-bool ReadProcStat(int fd, ProcStat* out) {
-  char c[512];
-  size_t c_pos = 0;
-  while (c_pos < sizeof(c) - 1) {
-    ssize_t rd = PERFETTO_EINTR(read(fd, c + c_pos, sizeof(c) - c_pos));
-    if (rd < 0) {
-      PERFETTO_ELOG("Failed to read stat file to enforce resource limits.");
-      return false;
-    }
-    if (rd == 0)
-      break;
-    c_pos += static_cast<size_t>(rd);
-  }
-  PERFETTO_CHECK(c_pos < sizeof(c));
-  c[c_pos] = '\0';
+bool StartsWithAny(const std::string& str,
+                   const std::vector<std::string>& prefixes) {
+  return std::any_of(
+      prefixes.begin(), prefixes.end(),
+      [&str](const std::string& prefix) { return StartsWith(str, prefix); });
+}

-  if (sscanf(c,
-             "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu "
-             "%lu %*d %*d %*d %*d %*d %*d %*u %*u %ld",
-             &out->utime, &out->stime, &out->rss_pages) != 3) {
-    PERFETTO_ELOG("Invalid stat format: %s", c);
+bool EndsWith(const std::string& str, const std::string& suffix) {
+  if (suffix.size() > str.size())
     return false;
-  }
-  return true;
+  return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
 }

-Watchdog::Watchdog(uint32_t polling_interval_ms)
-    : polling_interval_ms_(polling_interval_ms) {}
-
-Watchdog::~Watchdog() {
-  if (!thread_.joinable()) {
-    PERFETTO_DCHECK(!enabled_);
-    return;
-  }
-  PERFETTO_DCHECK(enabled_);
-  enabled_ = false;
-  // Rearm the timer to 1ns from now. This will cause the watchdog thread to
-  // wakeup from the poll() and see |enabled_| == false.
-  // This code path is used only in tests. In production code the watchdog
-  // is a singleton and is never destroyed.
-  struct itimerspec ts{};
-  ts.it_value.tv_sec = 0;
-  ts.it_value.tv_nsec = 1;
-  timerfd_settime(*timer_fd_, /*flags=*/0, &ts, nullptr);
-
-  thread_.join();
+bool Contains(const std::string& haystack, const std::string& needle) {
+  return haystack.find(needle) != std::string::npos;
 }

-Watchdog* Watchdog::GetInstance() {
-  static Watchdog* watchdog = new Watchdog(kDefaultPollingInterval);
-  return watchdog;
+bool Contains(const std::string& haystack, const char needle) {
+  return haystack.find(needle) != std::string::npos;
 }

-// Can be called from any thread.
-Watchdog::Timer Watchdog::CreateFatalTimer(uint32_t ms,
-                                           WatchdogCrashReason crash_reason) {
-  if (!enabled_.load(std::memory_order_relaxed))
-    return Watchdog::Timer(this, 0, crash_reason);
+bool Contains(const std::vector<std::string>& haystack,
+              const std::string& needle) {
+  return std::find(haystack.begin(), haystack.end(), needle) != haystack.end();
+}

-  return Watchdog::Timer(this, ms, crash_reason);
+size_t Find(const StringView& needle, const StringView& haystack) {
+  if (needle.empty())
+    return 0;
+  if (needle.size() > haystack.size())
+    return std::string::npos;
+  for (size_t i = 0; i < haystack.size() - (needle.size() - 1); ++i) {
+    if (strncmp(haystack.data() + i, needle.data(), needle.size()) == 0)
+      return i;
+  }
+  return std::string::npos;
 }

-// Can be called from any thread.
-void Watchdog::AddFatalTimer(TimerData timer) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  timers_.emplace_back(std::move(timer));
-  RearmTimerFd_Locked();
+bool CaseInsensitiveEqual(std::string_view first, std::string_view second) {
+  return first.size() == second.size() &&
+         std::equal(
+             first.begin(), first.end(), second.begin(),
+             [](char a, char b) { return Lowercase(a) == Lowercase(b); });
 }

-// Can be called from any thread.
-void Watchdog::RemoveFatalTimer(TimerData timer) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  for (auto it = timers_.begin(); it != timers_.end(); it++) {
-    if (*it == timer) {
-      timers_.erase(it);
-      break;  // Remove only one. Doesn't matter which one.
+std::string Join(const std::vector<std::string>& parts,
+                 const std::string& delim) {
+  std::string acc;
+  for (size_t i = 0; i < parts.size(); ++i) {
+    acc += parts[i];
+    if (i + 1 != parts.size()) {
+      acc += delim;
     }
   }
-  RearmTimerFd_Locked();
+  return acc;
 }

-void Watchdog::RearmTimerFd_Locked() {
-  if (!enabled_)
-    return;
-  auto it = std::min_element(timers_.begin(), timers_.end());
+std::vector<std::string> SplitString(const std::string& text,
+                                     const std::string& delimiter) {
+  PERFETTO_CHECK(!delimiter.empty());

-  // We use one timerfd to handle all the outstanding |timers_|. Keep it armed
-  // to the task expiring soonest.
-  struct itimerspec ts{};
-  if (it != timers_.end()) {
-    ts.it_value = ToPosixTimespec(it->deadline);
+  std::vector<std::string> output;
+  size_t start = 0;
+  size_t next;
+  for (;;) {
+    next = std::min(text.find(delimiter, start), text.size());
+    if (next > start)
+      output.emplace_back(&text[start], next - start);
+    start = next + delimiter.size();
+    if (start >= text.size())
+      break;
   }
-  // If |timers_| is empty (it == end()) |ts.it_value| will remain
-  // zero-initialized and that will disarm the timer in the call below.
-  int res = timerfd_settime(*timer_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
-  PERFETTO_DCHECK(res == 0);
+  return output;
 }

-void Watchdog::Start(TaskRunner* task_runner,
-                     std::function<void(WatchdogCrashInfo)> fatal_handler) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (thread_.joinable()) {
-    PERFETTO_DCHECK(enabled_);
-  } else {
-    PERFETTO_DCHECK(!enabled_);
+std::string_view TrimWhitespace(std::string_view str) {
+  static constexpr char kWhitespaces[] = "\t\n ";
+  size_t front_idx = str.find_first_not_of(kWhitespaces);
+  if (front_idx == std::string_view::npos)
+    return {};
+  std::string_view front_trimmed = str.substr(front_idx);
+  size_t end_idx = front_trimmed.find_last_not_of(kWhitespaces);
+  PERFETTO_DCHECK(end_idx != std::string_view::npos);
+  return front_trimmed.substr(0, end_idx + 1);
+}

-    // |fatal_handler_*| are written here, before the watchdog thread is
-    // created. The watchdog thread is the only reader, so no locking is
-    // needed when accessing them from ThreadMain().
-    fatal_handler_task_runner_ = task_runner;
-    fatal_handler_ = std::move(fatal_handler);
+std::string TrimWhitespace(const std::string& str) {
+  return std::string(TrimWhitespace(std::string_view(str)));
+}

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-    // Kick the thread to start running but only on Android or Linux.
-    timer_fd_.reset(
-        timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK));
-    if (!timer_fd_) {
-      PERFETTO_PLOG(
-          "timerfd_create failed, the Perfetto watchdog is not available");
-      return;
-    }
-    enabled_ = true;
-    RearmTimerFd_Locked();  // Deal with timers created before Start().
-    thread_ = std::thread(&Watchdog::ThreadMain, this);
-#endif
-  }
+std::string_view TrimWhitespace(const char* str) {
+  return TrimWhitespace(std::string_view(str));
 }

-void Watchdog::SetMemoryLimit(uint64_t bytes, uint32_t window_ms) {
-  // Update the fields under the lock.
-  std::lock_guard<std::mutex> guard(mutex_);
+std::string StripPrefix(const std::string& str, const std::string& prefix) {
+  return StartsWith(str, prefix) ? str.substr(prefix.size()) : str;
+}

-  PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) || bytes == 0);
+std::string StripSuffix(const std::string& str, const std::string& suffix) {
+  return EndsWith(str, suffix) ? str.substr(0, str.size() - suffix.size())
+                               : str;
+}

-  size_t size = bytes == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
-  memory_window_bytes_.Reset(size);
-  memory_limit_bytes_ = bytes;
+std::string ToUpper(const std::string& str) {
+  // Don't use toupper(), it depends on the locale.
+  std::string res(str);
+  auto end = res.end();
+  for (auto c = res.begin(); c != end; ++c)
+    *c = Uppercase(*c);
+  return res;
 }

-void Watchdog::SetCpuLimit(uint32_t percentage, uint32_t window_ms) {
-  std::lock_guard<std::mutex> guard(mutex_);
+std::string ToLower(const std::string& str) {
+  // Don't use tolower(), it depends on the locale.
+  std::string res(str);
+  auto end = res.end();
+  for (auto c = res.begin(); c != end; ++c)
+    *c = Lowercase(*c);
+  return res;
+}

-  PERFETTO_CHECK(percentage <= 100);
-  PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) ||
-                 percentage == 0);
+std::string ToHex(const char* data, size_t size) {
+  std::string hex(2 * size + 1, 'x');
+  for (size_t i = 0; i < size; ++i) {
+    // snprintf prints 3 characters, the two hex digits and a null byte. As we
+    // write left to right, we keep overwriting the nullbytes, except for the
+    // last call to snprintf.
+    snprintf(&(hex[2 * i]), 3, "%02hhx", data[i]);
+  }
+  // Remove the trailing nullbyte produced by the last snprintf.
+  hex.resize(2 * size);
+  return hex;
+}

-  size_t size = percentage == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
-  cpu_window_time_ticks_.Reset(size);
-  cpu_limit_percentage_ = percentage;
+std::string IntToHexString(uint32_t number) {
+  size_t max_size = 11;  // Max uint32 is 0xFFFFFFFF + 1 for null byte.
+  std::string buf;
+  buf.resize(max_size);
+  size_t final_len = SprintfTrunc(&buf[0], max_size, "0x%02x", number);
+  buf.resize(static_cast<size_t>(final_len));  // Cuts off the final null byte.
+  return buf;
 }

-void Watchdog::ThreadMain() {
-  // Register crash keys explicitly to avoid running out of slots at crash time.
-  g_crash_key_reason.Register();
-  g_crash_key_timeout.Register();
-  g_crash_key_actual_boot.Register();
-  g_crash_key_actual_mono.Register();
-  g_crash_key_actual_cpu.Register();
+std::string Uint64ToHexString(uint64_t number) {
+  return "0x" + Uint64ToHexStringNoPrefix(number);
+}

-  base::ScopedFile stat_fd(base::OpenFile("/proc/self/stat", O_RDONLY));
-  if (!stat_fd) {
-    PERFETTO_ELOG("Failed to open stat file to enforce resource limits.");
-    return;
-  }
+std::string Uint64ToHexStringNoPrefix(uint64_t number) {
+  size_t max_size = 17;  // Max uint64 is FFFFFFFFFFFFFFFF + 1 for null byte.
+  std::string buf;
+  buf.resize(max_size);
+  size_t final_len = SprintfTrunc(&buf[0], max_size, "%" PRIx64 "", number);
+  buf.resize(static_cast<size_t>(final_len));  // Cuts off the final null byte.
+  return buf;
+}

-  PERFETTO_DCHECK(timer_fd_);
+std::string StripChars(const std::string& str,
+                       const std::string& chars,
+                       char replacement) {
+  std::string res(str);
+  const char* start = res.c_str();
+  const char* remove = chars.c_str();
+  for (const char* c = strpbrk(start, remove); c; c = strpbrk(c + 1, remove))
+    res[static_cast<uintptr_t>(c - start)] = replacement;
+  return res;
+}

-  constexpr uint8_t kFdCount = 1;
-  struct pollfd fds[kFdCount]{};
-  fds[0].fd = *timer_fd_;
-  fds[0].events = POLLIN;
+std::string ReplaceAll(std::string str,
+                       const std::string& to_replace,
+                       const std::string& replacement) {
+  PERFETTO_CHECK(!to_replace.empty());
+  size_t pos = 0;
+  while ((pos = str.find(to_replace, pos)) != std::string::npos) {
+    str.replace(pos, to_replace.length(), replacement);
+    pos += replacement.length();
+  }
+  return str;
+}

-  for (;;) {
-    // We use the poll() timeout to drive the periodic ticks for the cpu/memory
-    // checks. The only other case when the poll() unblocks is when we crash
-    // (or have to quit via enabled_ == false, but that happens only in tests).
-    platform::BeforeMaybeBlockingSyscall();
-    auto ret = poll(fds, kFdCount, static_cast<int>(polling_interval_ms_));
-    platform::AfterMaybeBlockingSyscall();
-    if (!enabled_)
-      return;
-    if (ret < 0) {
-      if (errno == ENOMEM || errno == EINTR) {
-        // Should happen extremely rarely.
-        std::this_thread::sleep_for(std::chrono::milliseconds(100));
-        continue;
-      }
-      PERFETTO_FATAL("watchdog poll() failed");
-    }
+bool CheckAsciiAndRemoveInvalidUTF8(base::StringView str, std::string& output) {
+  bool is_ascii = std::all_of(str.begin(), str.end(), [](char c) {
+    return (static_cast<unsigned char>(c) & 0b10000000) == 0b00000000;
+  });
+  if (is_ascii) {
+    return true;
+  }

-    // If we get here either:
-    // 1. poll() timed out, in which case we should process cpu/mem guardrails.
-    // 2. A timer expired, in which case we shall crash.
+  // https://www.rfc-editor.org/rfc/rfc3629.txt
+  output.clear();
+  output.reserve(str.size());
+  for (size_t i = 0; i < str.size();) {
+    unsigned char c = static_cast<unsigned char>(str.data()[i]);
+    size_t num_bytes = 0;
+    bool valid_sequence = true;

-    uint64_t expired = 0;  // Must be exactly 8 bytes.
-    auto res = PERFETTO_EINTR(read(*timer_fd_, &expired, sizeof(expired)));
-    PERFETTO_DCHECK((res < 0 && (errno == EAGAIN)) ||
-                    (res == sizeof(expired) && expired > 0));
-    const TimeMillis now_mono_ms = GetWallTimeMs();
+    if ((c & 0b10000000) == 0b00000000) {
+      num_bytes = 1;
+    } else if ((c & 0b11100000) == 0b11000000) {
+      num_bytes = 2;
+    } else if ((c & 0b11110000) == 0b11100000) {
+      num_bytes = 3;
+    } else if ((c & 0b11111000) == 0b11110000) {
+      num_bytes = 4;
+    } else {
+      valid_sequence = false;
+      // Skip this byte
+      num_bytes = 1;
+    }

-    // Check if any of the timers expired.
-    WatchdogCrashInfo info{};
-    {
-      std::lock_guard<std::mutex> guard(mutex_);
-      for (const auto& timer : timers_) {
-        if (now_mono_ms >= timer.deadline) {
-          info.thread_id = timer.thread_id;
-          info.reason = timer.crash_reason;
-          const TimeMillis now_boot_ms = GetBootTimeMs();
-          info.alarm_time_ms = now_boot_ms.count();
-          info.timeout_s =
-              static_cast<int>(std::chrono::duration_cast<TimeSeconds>(
-                                   timer.deadline - timer.ctime_mono)
-                                   .count());
-          info.actual_mono_s =
-              static_cast<int>(std::chrono::duration_cast<TimeSeconds>(
-                                   now_mono_ms - timer.ctime_mono)
-                                   .count());
-          info.actual_boot_s =
-              static_cast<int>(std::chrono::duration_cast<TimeSeconds>(
-                                   now_boot_ms - timer.ctime_boot)
-                                   .count());
-          info.actual_cpu_s =
-              static_cast<int>(std::chrono::duration_cast<TimeSeconds>(
-                                   GetProcessCPUTimeNs() - timer.ctime_cpu)
-                                   .count());
-          break;
+    if (valid_sequence) {
+      // Check if enough bytes are available in the string
+      if (i + num_bytes > str.size()) {
+        valid_sequence = false;
+        num_bytes = 1;  // Treat as a single invalid byte for advancement
+      } else {
+        // Check for overlong encodings, surrogates, and out-of-range
+        if (num_bytes == 2 && c < 0b11000010) {  // 0xC2
+          valid_sequence = false;                // Overlong
+        } else if (num_bytes == 3) {
+          unsigned char byte2 = static_cast<unsigned char>(str.data()[i + 1]);
+          if ((c == 0b11100000 && byte2 < 0b10100000) ||   // Overlong E0
+              (c == 0b11101101 && byte2 >= 0b10100000)) {  // Surrogate ED
+            valid_sequence = false;
+          }
+        } else if (num_bytes == 4) {
+          unsigned char byte2 = static_cast<unsigned char>(str.data()[i + 1]);
+          if ((c == 0b11110000 && byte2 < 0b10010000) ||  // Overlong F0
+              (c == 0b11110100 && byte2 > 0b10001111)) {  // Out of range F4
+            valid_sequence = false;
+          }
         }
-      }
-    }

-    if (info.thread_id) {
-      SerializeLogsAndKillThread(info.thread_id, info);
+        if (valid_sequence && num_bytes > 1) {
+          for (size_t j = 1; j < num_bytes; ++j) {
+            unsigned char continuation_byte =
+                static_cast<unsigned char>(str.data()[i + j]);
+            if ((continuation_byte & 0b11000000) != 0b10000000) {
+              valid_sequence = false;
+              break;
+            }
+          }
+        }
+      }
     }

-    // Check CPU and memory guardrails (if enabled).
-    lseek(stat_fd.get(), 0, SEEK_SET);
-    ProcStat stat;
-    if (!ReadProcStat(stat_fd.get(), &stat))
-      continue;
-    uint64_t cpu_time = stat.utime + stat.stime;
-    uint64_t rss_bytes =
-        static_cast<uint64_t>(stat.rss_pages) * base::GetSysPageSize();
-
-    bool threshold_exceeded = false;
-    WatchdogCrashReason crash_reason{};
-    {
-      std::lock_guard<std::mutex> guard(mutex_);
-      if (CheckMemory_Locked(rss_bytes) && !IsSyncMemoryTaggingEnabled()) {
-        threshold_exceeded = true;
-        crash_reason = WatchdogCrashReason::kMemGuardrail;
-      } else if (CheckCpu_Locked(cpu_time)) {
-        threshold_exceeded = true;
-        crash_reason = WatchdogCrashReason::kCpuGuardrail;
+    if (valid_sequence) {
+      for (size_t j = 0; j < num_bytes; ++j) {
+        output.push_back(str.data()[i + j]);
       }
     }

-    if (threshold_exceeded) {
-      info.reason = crash_reason;
-      // No associated timer: info.actual_*_s remain 0.
-      SerializeLogsAndKillThread(getpid(), info);
-    }
+    i += num_bytes;
   }
+  return false;
 }

-void Watchdog::SerializeLogsAndKillThread(int tid,
-                                          const WatchdogCrashInfo& info) {
-  g_crash_key_timeout.Set(info.timeout_s);
-  g_crash_key_actual_mono.Set(info.actual_mono_s);
-  g_crash_key_actual_boot.Set(info.actual_boot_s);
-  g_crash_key_actual_cpu.Set(info.actual_cpu_s);
-  g_crash_key_reason.Set(static_cast<int>(info.reason));
-
-  // If the embedder installed a fatal handler, give it a chance to run (e.g.
-  // to flush in-flight state) before we force-kill the process. The handler
-  // is expected to crash the process on its own (e.g. via PERFETTO_FATAL once
-  // it's done). If it doesn't crash within |kFatalHandlerGraceMs|, we proceed
-  // and force-kill from here.
-  // |fatal_handler_*| are set in Start() before the watchdog thread is
-  // created, so no locking is needed. Clearing them after invocation prevents
-  // re-posting if the same expired condition is observed again.
-  if (fatal_handler_ && fatal_handler_task_runner_) {
-    fatal_handler_task_runner_->PostTask(
-        [h = std::move(fatal_handler_), info] { h(info); });
-    fatal_handler_ = nullptr;
-    fatal_handler_task_runner_ = nullptr;
-    if (!disable_kill_failsafe_for_testing_) {
-      std::this_thread::sleep_for(
-          std::chrono::milliseconds(kFatalHandlerGraceMs));
-    }
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+bool WideToUTF8(const std::wstring& source, std::string& output) {
+  if (source.empty() ||
+      source.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
+    return false;
   }
-
-  // We are about to die. Serialize the logs into the crash buffer so the
-  // debuggerd crash handler picks them up and attaches to the bugreport.
-  // In the case of a PERFETTO_CHECK/PERFETTO_FATAL this is done in logging.h.
-  // But in the watchdog case, we don't hit that codepath and must do ourselves.
-  MaybeSerializeLastLogsForCrashReporting();
-
-  // Send a SIGABRT to the thread that armed the timer. This is to see the
-  // callstack of the thread that is stuck in a long task rather than the
-  // watchdog thread.
-  if (syscall(__NR_tgkill, getpid(), tid, SIGABRT) < 0) {
-    // At this point the process must die. If for any reason the tgkill doesn't
-    // work (e.g. the thread has disappeared), force a crash from here.
-    abort();
+  int size = ::WideCharToMultiByte(CP_UTF8, 0, &source[0],
+                                   static_cast<int>(source.size()), nullptr, 0,
+                                   nullptr, nullptr);
+  output.assign(static_cast<size_t>(size), '\0');
+  if (::WideCharToMultiByte(CP_UTF8, 0, &source[0],
+                            static_cast<int>(source.size()), &output[0], size,
+                            nullptr, nullptr) != size) {
+    return false;
   }
-
-  if (disable_kill_failsafe_for_testing_)
-    return;
-
-  // The tgkill() above will take some milliseconds to cause a crash, as it
-  // involves the kernel to queue the SIGABRT on the target thread (often the
-  // main thread, which is != watchdog thread) and do a scheduling round.
-  // If something goes wrong though (the target thread has signals masked or
-  // is stuck in an uninterruptible+wakekill syscall) force quit from this
-  // thread.
-  std::this_thread::sleep_for(std::chrono::seconds(10));
-  abort();
+  return true;
 }
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)

-bool Watchdog::CheckMemory_Locked(uint64_t rss_bytes) {
-  if (memory_limit_bytes_ == 0)
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+bool UTF8ToWide(const std::string& source, std::wstring& output) {
+  if (source.empty() ||
+      source.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
     return false;
-
-  // Add the current stat value to the ring buffer and check that the mean
-  // remains under our threshold.
-  if (memory_window_bytes_.Push(rss_bytes)) {
-    if (memory_window_bytes_.Mean() >
-        static_cast<double>(memory_limit_bytes_)) {
-      PERFETTO_ELOG(
-          "Memory watchdog trigger. Memory window of %f bytes is above the "
-          "%" PRIu64 " bytes limit.",
-          memory_window_bytes_.Mean(), memory_limit_bytes_);
-      return true;
-    }
   }
-  return false;
-}
-
-bool Watchdog::CheckCpu_Locked(uint64_t cpu_time) {
-  if (cpu_limit_percentage_ == 0)
+  int size = ::MultiByteToWideChar(CP_UTF8, 0, &source[0],
+                                   static_cast<int>(source.size()), nullptr, 0);
+  output.assign(static_cast<size_t>(size), L'\0');
+  if (::MultiByteToWideChar(CP_UTF8, 0, &source[0],
+                            static_cast<int>(source.size()), &output[0],
+                            size) != size) {
     return false;
-
-  // Add the cpu time to the ring buffer.
-  if (cpu_window_time_ticks_.Push(cpu_time)) {
-    // Compute the percentage over the whole window and check that it remains
-    // under the threshold.
-    uint64_t difference_ticks = cpu_window_time_ticks_.NewestWhenFull() -
-                                cpu_window_time_ticks_.OldestWhenFull();
-    double window_interval_ticks =
-        (static_cast<double>(WindowTimeForRingBuffer(cpu_window_time_ticks_)) /
-         1000.0) *
-        static_cast<double>(sysconf(_SC_CLK_TCK));
-    double percentage = static_cast<double>(difference_ticks) /
-                        static_cast<double>(window_interval_ticks) * 100;
-    if (percentage > cpu_limit_percentage_) {
-      PERFETTO_ELOG("CPU watchdog trigger. %f%% CPU use is above the %" PRIu32
-                    "%% CPU limit.",
-                    percentage, cpu_limit_percentage_);
-      return true;
-    }
   }
-  return false;
+  return true;
 }
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)

-uint32_t Watchdog::WindowTimeForRingBuffer(const WindowedInterval& window) {
-  return static_cast<uint32_t>(window.size() - 1) * polling_interval_ms_;
-}
+size_t SprintfTrunc(char* dst, size_t dst_size, const char* fmt, ...) {
+  if (PERFETTO_UNLIKELY(dst_size == 0))
+    return 0;

-bool Watchdog::WindowedInterval::Push(uint64_t sample) {
-  // Add the sample to the current position in the ring buffer.
-  buffer_[position_] = sample;
+  va_list args;
+  va_start(args, fmt);
+  int src_size = vsnprintf(dst, dst_size, fmt, args);
+  va_end(args);

-  // Update the position with next one circularily.
-  position_ = (position_ + 1) % size_;
+  if (PERFETTO_UNLIKELY(src_size <= 0)) {
+    dst[0] = '\0';
+    return 0;
+  }

-  // Set the filled flag the first time we wrap.
-  filled_ = filled_ || position_ == 0;
-  return filled_;
-}
+  size_t res;
+  if (PERFETTO_LIKELY(src_size < static_cast<int>(dst_size))) {
+    // Most common case.
+    res = static_cast<size_t>(src_size);
+  } else {
+    // Truncation case.
+    res = dst_size - 1;
+  }

-double Watchdog::WindowedInterval::Mean() const {
-  return MeanForArray(buffer_.get(), size_);
+  PERFETTO_DCHECK(res < dst_size);
+  PERFETTO_DCHECK(dst[res] == '\0');
+  return res;
 }

-void Watchdog::WindowedInterval::Clear() {
-  position_ = 0;
-  buffer_.reset(new uint64_t[size_]());
+std::optional<LineWithOffset> FindLineWithOffset(base::StringView str,
+                                                 uint32_t offset) {
+  static constexpr char kNewLine = '\n';
+  uint32_t line_offset = 0;
+  uint32_t line_count = 1;
+  for (uint32_t i = 0; i < str.size(); ++i) {
+    if (str.at(i) == kNewLine) {
+      line_offset = i + 1;
+      line_count++;
+      continue;
+    }
+    if (i == offset) {
+      size_t end_offset = str.find(kNewLine, i);
+      if (end_offset == std::string::npos) {
+        end_offset = str.size();
+      }
+      base::StringView line = str.substr(line_offset, end_offset - line_offset);
+      return LineWithOffset{line, offset - line_offset, line_count};
+    }
+  }
+  return std::nullopt;
 }

-void Watchdog::WindowedInterval::Reset(size_t new_size) {
-  position_ = 0;
-  size_ = new_size;
-  buffer_.reset(new_size == 0 ? nullptr : new uint64_t[new_size]());
-}
+}  // namespace base
+}  // namespace perfetto
+// gen_amalgamated begin source: src/base/string_view.cc
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */

-Watchdog::Timer::Timer(Watchdog* watchdog,
-                       uint32_t ms,
-                       WatchdogCrashReason crash_reason)
-    : watchdog_(watchdog) {
-  if (!ms)
-    return;  // No-op timer created when the watchdog is disabled.
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"

-  TimeMillis now_mono = GetWallTimeMs();
-  timer_data_.deadline = now_mono + std::chrono::milliseconds(ms);
-  timer_data_.ctime_mono = now_mono;
-  timer_data_.ctime_boot = GetBootTimeMs();
-  timer_data_.ctime_cpu =
-      std::chrono::duration_cast<TimeMillis>(GetProcessCPUTimeNs());
-  timer_data_.thread_id = GetThreadId();
-  timer_data_.crash_reason = crash_reason;
-  PERFETTO_DCHECK(watchdog_);
-  watchdog_->AddFatalTimer(timer_data_);
-}
+namespace perfetto {
+namespace base {

-Watchdog::Timer::~Timer() {
-  if (timer_data_.deadline.count())
-    watchdog_->RemoveFatalTimer(timer_data_);
-}
+// Without ignoring this warning we get the message:
+//   error: out-of-line definition of constexpr static data member is redundant
+//   in C++17 and is deprecated
+// when using clang-cl in Windows.
+#if defined(__GNUC__)  // GCC & clang
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated"
+#endif  // __GNUC__

-Watchdog::Timer::Timer(Timer&& other) noexcept {
-  watchdog_ = std::move(other.watchdog_);
-  other.watchdog_ = nullptr;
-  timer_data_ = std::move(other.timer_data_);
-  other.timer_data_ = TimerData();
-}
+// static
+constexpr size_t StringView::npos;
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif

 }  // namespace base
 }  // namespace perfetto
-
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG)
-// gen_amalgamated begin source: src/base/weak_runner.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/weak_runner.h
+// gen_amalgamated begin source: src/base/string_view_splitter.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/string_view_splitter.h
 /*
  * Copyright (C) 2024 The Android Open Source Project
  *
@@ -11393,52 +11888,75 @@ Watchdog::Timer::Timer(Timer&& other) noexcept {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_WEAK_RUNNER_H_
-#define INCLUDE_PERFETTO_EXT_BASE_WEAK_RUNNER_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_SPLITTER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_SPLITTER_H_

-#include <stdint.h>
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"

-#include <functional>
-#include <memory>
+namespace perfetto {
+namespace base {

-namespace perfetto::base {
+// C++ version of strtok(). Splits a StringView without making copies or any
+// heap allocations. Supports the special case of using \0 as a delimiter.
+// The token returned in output are valid as long as the input string is valid.
+class StringViewSplitter {
+ public:
+  // Whether an empty string (two delimiters side-to-side) is a valid token.
+  enum class EmptyTokenMode {
+    DISALLOW_EMPTY_TOKENS,
+    ALLOW_EMPTY_TOKENS,

-class TaskRunner;
+    DEFAULT = DISALLOW_EMPTY_TOKENS,
+  };

-// This is a wrapper around a `base::TaskRunner*`. It is intended to be used by
-// classes that want to post tasks on themselves. When the object is destroyed,
-// all posted tasks become noops.
-//
-// A class that embeds a WeakRunner can safely capture `this` on the posted
-// tasks.
-class WeakRunner {
- public:
-  explicit WeakRunner(base::TaskRunner* task_runner);
-  ~WeakRunner();
-  base::TaskRunner* task_runner() const { return task_runner_; }
+  // Can take ownership of the string if passed via std::move(), e.g.:
+  // StringViewSplitter(std::move(str), '\n');
+  StringViewSplitter(base::StringView,
+                     char delimiter,
+                     EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);

-  // Schedules `f` for immediate execution. `f` will not be executed is `*this`
-  // is destroyed.
-  //
-  // Can be called from any thread, but the caller needs to make sure that
-  // `*this` is alive while `PostTask` is running: this is not obvious when
-  // multiple threads are involved.
-  void PostTask(std::function<void()> f) const;
+  // Splits the current token from an outer StringViewSplitter instance. This is
+  // to chain splitters as follows: for (base::StringViewSplitter lines(x,
+  // '\n'); ss.Next();)
+  //   for (base::StringViewSplitter words(&lines, ' '); words.Next();)
+  StringViewSplitter(StringViewSplitter*,
+                     char delimiter,
+                     EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);

-  // Schedules `f` for execution after |delay_ms|.
-  // Can be called from any thread, but the caller needs to make sure that
-  // `*this` is alive while `PostDelayedTask` is running: this is not obvious
-  // when multiple threads are involved.
-  void PostDelayedTask(std::function<void()> f, uint32_t delay_ms) const;
+  // Returns true if a token is found (in which case it will be stored in
+  // cur_token()), false if no more tokens are found.
+  bool Next();
+
+  // Returns the next token if, found (in which case it will be stored in
+  // cur_token()), and the empty string if no more tokens are found.
+  base::StringView NextToken() { return Next() ? cur_token() : ""; }
+
+  // Returns the current token iff last call to Next() returned true.
+  // In all other cases (before the 1st call to Next() and after Next() returns
+  // false) returns the empty string.
+  base::StringView cur_token() { return cur_; }
+
+  // Returns the remainder of the current input string that has not yet been
+  // tokenized.
+  base::StringView remainder() { return next_; }

  private:
-  base::TaskRunner* const task_runner_;
-  std::shared_ptr<bool> destroyed_;
+  StringViewSplitter(const StringViewSplitter&) = delete;
+  StringViewSplitter& operator=(const StringViewSplitter&) = delete;
+  void Initialize(base::StringView);
+
+  base::StringView str_;
+  base::StringView cur_;
+  base::StringView next_;
+  bool end_of_input_;
+  const char delimiter_;
+  const EmptyTokenMode empty_token_mode_;
 };

-}  // namespace perfetto::base
+}  // namespace base
+}  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_WEAK_RUNNER_H_
+#endif  // INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_SPLITTER_H_
 /*
  * Copyright (C) 2024 The Android Open Source Project
  *
@@ -11455,46 +11973,92 @@ class WeakRunner {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/weak_runner.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_view_splitter.h"

-// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
+#include <utility>

-namespace perfetto::base {
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"

-WeakRunner::WeakRunner(base::TaskRunner* task_runner)
-    : task_runner_(task_runner), destroyed_(std::make_shared<bool>(false)) {}
+namespace perfetto {
+namespace base {

-WeakRunner::~WeakRunner() {
-  *destroyed_ = true;
+StringViewSplitter::StringViewSplitter(base::StringView str,
+                                       char delimiter,
+                                       EmptyTokenMode empty_token_mode)
+    : str_(std::move(str)),
+      delimiter_(delimiter),
+      empty_token_mode_(empty_token_mode) {
+  Initialize(str);
 }

-void WeakRunner::PostTask(std::function<void()> f) const {
-  task_runner_->PostTask([destroyed = destroyed_, f = std::move(f)]() {
-    if (*destroyed) {
-      return;
-    }
-    f();
-  });
+StringViewSplitter::StringViewSplitter(StringViewSplitter* outer,
+                                       char delimiter,
+                                       EmptyTokenMode empty_token_mode)
+    : delimiter_(delimiter), empty_token_mode_(empty_token_mode) {
+  Initialize(outer->cur_token());
 }

-void WeakRunner::PostDelayedTask(std::function<void()> f,
-                                 uint32_t delay_ms) const {
-  task_runner_->PostDelayedTask(
-      [destroyed = destroyed_, f = std::move(f)]() {
-        if (*destroyed) {
-          return;
-        }
-        f();
-      },
-      delay_ms);
+void StringViewSplitter::Initialize(base::StringView str) {
+  next_ = str;
+  cur_ = "";
+  end_of_input_ = false;
 }

-}  // namespace perfetto::base
-// gen_amalgamated begin source: src/base/lock_free_task_runner.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/lock_free_task_runner.h
-// gen_amalgamated begin header: include/perfetto/ext/base/flags.h
+bool StringViewSplitter::Next() {
+  if (end_of_input_) {
+    cur_ = next_ = "";
+    return false;
+  }
+
+  size_t substr_start = 0;
+  if (empty_token_mode_ == EmptyTokenMode::DISALLOW_EMPTY_TOKENS) {
+    while (substr_start < next_.size() &&
+           next_.at(substr_start) == delimiter_) {
+      substr_start++;
+    }
+  }
+
+  if (substr_start >= next_.size()) {
+    end_of_input_ = true;
+    cur_ = next_ = "";
+    return !cur_.empty() ||
+           empty_token_mode_ == EmptyTokenMode::ALLOW_EMPTY_TOKENS;
+  }
+
+  size_t delimiter_start = next_.find(delimiter_, substr_start);
+  if (delimiter_start == base::StringView::npos) {
+    cur_ = next_.substr(substr_start);
+    next_ = "";
+    end_of_input_ = true;
+    return !cur_.empty() ||
+           empty_token_mode_ == EmptyTokenMode::ALLOW_EMPTY_TOKENS;
+  }
+
+  size_t delimiter_end = delimiter_start + 1;
+
+  if (empty_token_mode_ == EmptyTokenMode::DISALLOW_EMPTY_TOKENS) {
+    while (delimiter_end < next_.size() &&
+           next_.at(delimiter_end) == delimiter_) {
+      delimiter_end++;
+    }
+    if (delimiter_end >= next_.size()) {
+      end_of_input_ = true;
+    }
+  }
+
+  cur_ = next_.substr(substr_start, delimiter_start - substr_start);
+  next_ = next_.substr(delimiter_end);
+
+  return !cur_.empty() ||
+         empty_token_mode_ == EmptyTokenMode::ALLOW_EMPTY_TOKENS;
+}
+
+}  // namespace base
+}  // namespace perfetto
+// gen_amalgamated begin source: src/base/temp_file.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/temp_file.h
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * 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.
@@ -11509,36 +12073,80 @@ void WeakRunner::PostDelayedTask(std::function<void()> f,
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_FLAGS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_FLAGS_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_TEMP_FILE_H_
+#define INCLUDE_PERFETTO_EXT_BASE_TEMP_FILE_H_

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+#include <string>

-#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) && \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-// Android: aconfig generates PERFETTO_FLAGS_* macros in perfetto_flags.h
-#include <perfetto_flags.h>
-#else
-// Non-Android: Define fallback PERFETTO_FLAGS_* macros
-// These match the pattern from Android's aconfig codegen
-#define PERFETTO_FLAGS(FLAG) PERFETTO_FLAGS_##FLAG
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"

-#define PERFETTO_FLAGS_TEST_READ_ONLY_FLAG false
-#define PERFETTO_FLAGS_USE_LOCKFREE_TASKRUNNER \
-  PERFETTO_BUILDFLAG(PERFETTO_ENABLE_LOCKFREE_TASKRUNNER)
-#define PERFETTO_FLAGS_BUFFER_CLONE_PRESERVE_READ_ITER true
-#define PERFETTO_FLAGS_USE_UNIX_SOCKET_INOTIFY \
-  PERFETTO_BUILDFLAG(PERFETTO_ENABLE_SOCK_INOTIFY)
-#define PERFETTO_FLAGS_TRIGGER_PERFETTO_ON_TRACED_PROBES_DISCONNECT false
-#define PERFETTO_FLAGS_USE_PCRE2 PERFETTO_BUILDFLAG(PERFETTO_PCRE2)
-#define PERFETTO_FLAGS_SYS_STATS_LARGE_READ true
+namespace perfetto {
+namespace base {

-#endif  // PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) && ...
+std::string GetSysTempDir();

-#endif  // INCLUDE_PERFETTO_EXT_BASE_FLAGS_H_
-// gen_amalgamated begin header: include/perfetto/ext/base/unix_task_runner.h
+class TempFile {
+ public:
+  enum class LinkMode { kLinked, kUnlinked };
+
+  // Creates a temporary file.
+  // If |mode| is kUnlinked, unlinks the file before returning, keeping fd()
+  // open and leaving path() empty.
+  // Returns an error status if creation or unlinking fails.
+  static StatusOr<TempFile> Create(LinkMode mode = LinkMode::kLinked);
+
+  TempFile(TempFile&&) noexcept;
+  TempFile& operator=(TempFile&&);
+  ~TempFile();
+
+  const std::string& path() const { return path_; }
+  int fd() const { return *fd_; }
+  int operator*() const { return *fd_; }
+
+  // Unlinks the file and clears path(), keeping fd() open.
+  // Returns true on success, or if the file is already unlinked.
+  // On failure, returns false and leaves path() unchanged.
+  bool Unlink();
+
+  // Unlinks the file if needed and transfers ownership of its descriptor to
+  // the caller.
+  // Aborts if unlinking fails.
+  ScopedFile ReleaseFD();
+
+ private:
+  TempFile(ScopedFile fd, std::string path);
+  TempFile(const TempFile&) = delete;
+  TempFile& operator=(const TempFile&) = delete;
+
+  ScopedFile fd_;
+  std::string path_;
+};
+
+class TempDir {
+ public:
+  static TempDir Create();
+
+  TempDir(TempDir&&) noexcept;
+  TempDir& operator=(TempDir&&);
+  ~TempDir();
+
+  const std::string& path() const { return path_; }
+
+ private:
+  TempDir();
+  TempDir(const TempDir&) = delete;
+  TempDir& operator=(const TempDir&) = delete;
+
+  std::string path_;
+};
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_TEMP_FILE_H_
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -11553,131 +12161,177 @@ void WeakRunner::PostDelayedTask(std::function<void()> f,
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_UNIX_TASK_RUNNER_H_
-#define INCLUDE_PERFETTO_EXT_BASE_UNIX_TASK_RUNNER_H_
+// gen_amalgamated expanded: #include "perfetto/ext/base/temp_file.h"

 // gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
-// gen_amalgamated expanded: #include "perfetto/base/thread_annotations.h"
-// gen_amalgamated expanded: #include "perfetto/base/thread_utils.h"
-// gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/event_fd.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/rt_mutex.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"

-#include <chrono>
-#include <deque>
-#include <map>
-#include <mutex>
-#include <vector>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>

-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <poll.h>
+#include <utility>
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <windows.h>
+
+#include <direct.h>
+#include <fileapi.h>
+#include <io.h>
+#else
+#include <unistd.h>
 #endif

+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_macros.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+
 namespace perfetto {
 namespace base {

-// Runs a task runner on the current thread.
-//
-// Implementation note: we currently assume (and enforce in debug builds) that
-// Run() is called from the thread that constructed the UnixTaskRunner. This is
-// not strictly necessary, and we could instead track the thread that invokes
-// Run(). However, a related property that *might* be important to enforce is
-// that the destructor runs on the task-running thread. Otherwise, if there are
-// still-pending tasks at the time of destruction, we would destroy those
-// outside of the task thread (which might be unexpected to the caller). On the
-// other hand, the std::function task interface discourages use of any
-// resource-owning tasks (as the callable needs to be copyable), so this might
-// not be important in practice.
-//
-// TODO(rsavitski): consider adding a thread-check in the destructor, after
-// auditing existing usages.
-// TODO(primiano): rename this to TaskRunnerImpl. The "Unix" part is misleading
-// now as it supports also Windows.
-class UnixTaskRunner : public TaskRunner {
- public:
-  UnixTaskRunner();
-  ~UnixTaskRunner() override;
-
-  // Start executing tasks. Doesn't return until Quit() is called. Run() may be
-  // called multiple times on the same task runner.
-  void Run();
-  void Quit();
-
-  // Checks whether there are any pending immediate tasks to run. Note that
-  // delayed tasks don't count even if they are due to run.
-  bool IsIdleForTesting();
+namespace {

-  // Pretends (for the purposes of running delayed tasks) that time advanced by
-  // `ms`.
-  void AdvanceTimeForTesting(uint32_t ms);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+StatusOr<std::string> GetTempFilePathWin() {
+  std::string path = GetSysTempDir() + "\\perfetto-XXXXXX";
+  int error = _mktemp_s(path.data(), path.size() + 1);
+  if (error != 0)
+    return ErrStatus("_mktemp_s() failed: %s", strerror(error));
+  return path;
+}
+#endif

-  // TaskRunner implementation:
-  void PostTask(std::function<void()>) override;
-  void PostDelayedTask(std::function<void()>, uint32_t delay_ms) override;
-  void AddFileDescriptorWatch(PlatformHandle, std::function<void()>) override;
-  void RemoveFileDescriptorWatch(PlatformHandle) override;
-  bool RunsTasksOnCurrentThread() const override;
+Status UnlinkTempFile(const std::string& path) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // If the file is still open, deletion completes when its last handle closes.
+  if (!DeleteFileA(path.c_str())) {
+    return ErrStatus("DeleteFileA(%s) failed: %lu", path.c_str(),
+                     GetLastError());
+  }
+#else
+  if (unlink(path.c_str()) != 0)
+    return ErrStatus("unlink(%s) failed: %s", path.c_str(), strerror(errno));
+#endif
+  return OkStatus();
+}

-  // Returns true if the task runner is quitting, or has quit and hasn't been
-  // restarted since. Exposed primarily for ThreadTaskRunner, not necessary for
-  // normal use of this class.
-  bool QuitCalled();
+}  // namespace

- private:
-  void WakeUp();
-  void UpdateWatchTasksLocked() PERFETTO_EXCLUSIVE_LOCKS_REQUIRED(lock_);
-  int GetDelayMsToNextTaskLocked() const
-      PERFETTO_EXCLUSIVE_LOCKS_REQUIRED(lock_);
-  void RunImmediateAndDelayedTask();
-  void PostFileDescriptorWatches(uint64_t windows_wait_result);
-  void RunFileDescriptorWatch(PlatformHandle);
+std::string GetSysTempDir() {
+  const char* tmpdir = nullptr;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  if ((tmpdir = getenv("TMP")))
+    return tmpdir;
+  if ((tmpdir = getenv("TEMP")))
+    return tmpdir;
+  return "C:\\TEMP";
+#else
+  if ((tmpdir = getenv("TMPDIR")))
+    return base::StripSuffix(tmpdir, "/");
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  return "/data/local/tmp";
+#else
+  return "/tmp";
+#endif  // !OS_ANDROID
+#endif  // !OS_WIN
+}

-  ThreadChecker thread_checker_;
-  std::atomic<PlatformThreadId> created_thread_id_ = GetThreadId();
+// static
+StatusOr<TempFile> TempFile::Create(LinkMode mode) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  ASSIGN_OR_RETURN(std::string path, GetTempFilePathWin());

-  EventFd event_;
+  // TempFile exposes both a descriptor and a path. Match POSIX by allowing the
+  // path to be reopened while the descriptor remains open. These sharing flags
+  // are not settable through _open(), hence the CreateFileA +
+  // _open_osfhandle dance.
+  ScopedPlatformHandle h(
+      ::CreateFileA(path.c_str(), GENERIC_READ | GENERIC_WRITE,
+                    FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+                    nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, nullptr));
+  if (!h) {
+    return ErrStatus("CreateFileA(%s) failed: %lu", path.c_str(),
+                     GetLastError());
+  }

-// The array of fds/handles passed to poll(2) / WaitForMultipleObjects().
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  std::vector<PlatformHandle> poll_fds_;
+  // According to MSDN, when using _open_osfhandle the caller must not call
+  // CloseHandle(). Ownership is moved to the file descriptor, which then needs
+  // to be closed with just with _close().
+  ScopedFile fd(_open_osfhandle(reinterpret_cast<intptr_t>(h.get()), 0));
+  if (!fd) {
+    auto status = ErrStatus("_open_osfhandle() failed: %s", strerror(errno));
+    UnlinkTempFile(path);  // Best effort.
+    return status;
+  }
+  h.release();
 #else
-  std::vector<struct pollfd> poll_fds_;
+  std::string path = GetSysTempDir() + "/perfetto-XXXXXXXX";
+  ScopedFile fd(mkstemp(path.data()));
+  if (!fd)
+    return ErrStatus("mkstemp(%s) failed: %s", path.c_str(), strerror(errno));
 #endif

-  base::MaybeRtMutex lock_;
+  // Transfer ownership to TempFile only once all fallible operations succeed,
+  // because ~TempFile() aborts if unlinking fails.
+  if (mode == LinkMode::kUnlinked) {
+    RETURN_IF_ERROR(UnlinkTempFile(path));
+    return TempFile(std::move(fd), std::string());
+  }
+  return TempFile(std::move(fd), std::move(path));
+}

-  std::deque<std::function<void()>> immediate_tasks_ PERFETTO_GUARDED_BY(lock_);
-  std::multimap<TimeMillis, std::function<void()>> delayed_tasks_
-      PERFETTO_GUARDED_BY(lock_);
-  bool quit_ PERFETTO_GUARDED_BY(lock_) = false;
-  TimeMillis advanced_time_for_testing_ PERFETTO_GUARDED_BY(lock_) =
-      TimeMillis(0);
+TempFile::TempFile(ScopedFile fd, std::string path)
+    : fd_(std::move(fd)), path_(std::move(path)) {}

-  struct WatchTask {
-    std::function<void()> callback;
+TempFile::~TempFile() {
+  PERFETTO_CHECK(Unlink());
+}
+
+ScopedFile TempFile::ReleaseFD() {
+  PERFETTO_CHECK(Unlink());
+  return std::move(fd_);
+}
+
+bool TempFile::Unlink() {
+  if (path_.empty())
+    return true;
+  if (!UnlinkTempFile(path_).ok())
+    return false;
+  path_.clear();
+  return true;
+}
+
+TempFile::TempFile(TempFile&&) noexcept = default;
+TempFile& TempFile::operator=(TempFile&&) = default;
+
+// static
+TempDir TempDir::Create() {
+  TempDir temp_dir;
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    // On UNIX systems we make the FD number negative in |poll_fds_| to avoid
-    // polling it again until the queued task runs. On Windows we can't do that.
-    // Instead we keep track of its state here.
-    bool pending = false;
+  temp_dir.path_ = GetTempFilePathWin().ValueOrDie();
+  PERFETTO_CHECK(_mkdir(temp_dir.path_.c_str()) == 0);
 #else
-    size_t poll_fd_index;  // Index into |poll_fds_|.
+  temp_dir.path_ = GetSysTempDir() + "/perfetto-XXXXXXXX";
+  PERFETTO_CHECK(mkdtemp(&temp_dir.path_[0]));
 #endif
-  };
+  return temp_dir;
+}

-  std::map<PlatformHandle, WatchTask> watch_tasks_ PERFETTO_GUARDED_BY(lock_);
-  bool watch_tasks_changed_ PERFETTO_GUARDED_BY(lock_) = false;
-};
+TempDir::TempDir() = default;
+TempDir::TempDir(TempDir&&) noexcept = default;
+TempDir& TempDir::operator=(TempDir&&) = default;
+
+TempDir::~TempDir() {
+  if (path_.empty())
+    return;  // For objects that get std::move()d.
+  PERFETTO_CHECK(Rmdir(path_));
+}

 }  // namespace base
 }  // namespace perfetto
-
-#endif  // INCLUDE_PERFETTO_EXT_BASE_UNIX_TASK_RUNNER_H_
+// gen_amalgamated begin source: src/base/thread_checker.cc
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * 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.
@@ -11692,272 +12346,151 @@ class UnixTaskRunner : public TaskRunner {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_LOCK_FREE_TASK_RUNNER_H_
-#define INCLUDE_PERFETTO_EXT_BASE_LOCK_FREE_TASK_RUNNER_H_
-
-// gen_amalgamated expanded: #include "perfetto/base/flat_set.h"
-// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
-// gen_amalgamated expanded: #include "perfetto/base/thread_annotations.h"
-// gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/event_fd.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/flags.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/unix_task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"

-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <poll.h>
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <windows.h>
 #endif

-#include <array>
-#include <atomic>
-#include <thread>
-#include <unordered_map>
-
 namespace perfetto {
 namespace base {

-namespace task_runner_internal {
-class ScopedRefcount;
-struct Slab;
+namespace {
+constexpr ThreadID kDetached{};

-// Exposed for testing
-constexpr uint32_t kNumRefcountBuckets = 32;
-constexpr size_t kSlabSize = 512;
+ThreadID CurrentThreadId() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  return ::GetCurrentThreadId();
+#else
+  return pthread_self();
+#endif
+}
+}  // namespace

-}  // namespace task_runner_internal
+ThreadChecker::ThreadChecker() {
+  thread_id_.store(CurrentThreadId());
+}

-template <typename T>
-class TaskRunnerTest;
+ThreadChecker::~ThreadChecker() = default;

-// This class implements a lock-less multi-producer single-consumer task runner.
-// This is achieved by using a linked list of "slabs". Each slab is a fixed-size
-// array of tasks.
-// See /docs/design-docs/lock-free-task-runner.md for more details
-class PERFETTO_EXPORT_COMPONENT LockFreeTaskRunner : public TaskRunner {
- public:
-  LockFreeTaskRunner();
-  ~LockFreeTaskRunner() override;
+ThreadChecker::ThreadChecker(const ThreadChecker& other)
+    : thread_id_(other.thread_id_.load()) {}

-  void Run();
-  void Quit();
+ThreadChecker& ThreadChecker::operator=(const ThreadChecker& other) {
+  thread_id_ = other.thread_id_.load();
+  return *this;
+}

-  // Checks whether there are any pending immediate tasks to run. Note that
-  // delayed tasks don't count even if they are due to run.
-  bool IsIdleForTesting();
+bool ThreadChecker::CalledOnValidThread() const {
+  auto self = CurrentThreadId();

-  // TaskRunner implementation:
-  void PostTask(std::function<void()>) override;
-  void PostDelayedTask(std::function<void()>, uint32_t delay_ms) override;
-  void AddFileDescriptorWatch(PlatformHandle, std::function<void()>) override;
-  void RemoveFileDescriptorWatch(PlatformHandle) override;
-  bool RunsTasksOnCurrentThread() const override;
+  // Will re-attach if previously detached using DetachFromThread().
+  auto prev_value = kDetached;
+  if (thread_id_.compare_exchange_strong(prev_value, self))
+    return true;
+  return prev_value == self;
+}

-  // Pretends (for the purposes of running delayed tasks) that time advanced by
-  // `ms`.
-  void AdvanceTimeForTesting(uint32_t ms);
+void ThreadChecker::DetachFromThread() {
+  thread_id_.store(kDetached);
+}

-  // Stats for testing.
-  size_t slabs_allocated() const {
-    return slabs_allocated_.load(std::memory_order_relaxed);
-  }
-  size_t slabs_freed() const {
-    return slabs_freed_.load(std::memory_order_relaxed);
-  }
+}  // namespace base
+}  // namespace perfetto
+// gen_amalgamated begin source: src/base/thread_utils.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/thread_utils.h
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */

- private:
-  using Slab = task_runner_internal::Slab;
-  using ScopedRefcount = task_runner_internal::ScopedRefcount;
-  friend class task_runner_internal::ScopedRefcount;
+#ifndef INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_

-  struct DelayedTask {
-    TimeMillis time;
-    uint64_t seq;
-    std::function<void()> task;
+#include <string>

-    // Note that the < operator keeps the DelayedTasks sorted in reverse order
-    // (the latest one is first, the earliest one is last). This is so we can
-    // have a FIFO queue using a vector by just doing an O(1) pop_back().
-    bool operator<(const DelayedTask& other) const {
-      if (time != other.time)
-        return time > other.time;
-      return seq > other.seq;
-    }
-    bool operator==(const DelayedTask& other) const {
-      return time == other.time && seq == other.seq;
-    }
-  };
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/export.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"

-  PERFETTO_ALWAYS_INLINE std::function<void()> PopNextImmediateTask();
-  std::function<void()> PopTaskRecursive(Slab*, Slab* next_slab);
-  std::function<void()> PopNextExpiredDelayedTask();
-  int GetDelayMsToNextTask() const;
-  void WakeUp() { wakeup_event_.Notify(); }
-  Slab* AllocNewSlab();
-  void DeleteSlab(Slab*);
-  void PostFileDescriptorWatches(uint64_t windows_wait_result);
-  void RunFileDescriptorWatch(PlatformHandle);
-  void UpdateWatchTasks();
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <pthread.h>
+#include <string.h>
+#include <algorithm>
+#endif

-  // These two are semantically a unique_ptr, but are accessed from different
-  // threads.
-  std::atomic<Slab*> tail_{};  // This is never null.
-  std::atomic<Slab*> free_slab_{};
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/prctl.h>
+#endif

-  EventFd wakeup_event_;
-  bool quit_ = false;
-  std::thread::id run_task_thread_id_;
+// Internal implementation utils that aren't as widely useful/supported as
+// base/thread_utils.h.

-  // Delayed tasks, accessed only by the main thread. Items are stored in
-  // reverse temporal order, see comment in the operator<.
-  FlatSet<DelayedTask> delayed_tasks_;
-  uint64_t next_delayed_task_seq_ = 0;
-  std::atomic<uint32_t> advanced_time_for_testing_{};
+namespace perfetto {
+namespace base {

-  // The array of fds/handles passed to poll(2) / WaitForMultipleObjects().
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  std::vector<PlatformHandle> poll_fds_;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+// Sets the "comm" of the calling thread to the first 15 chars of the given
+// string.
+inline bool MaybeSetThreadName(const std::string& name) {
+  char buf[16] = {};
+  StringCopy(buf, name.c_str(), sizeof(buf));
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+  return pthread_setname_np(buf) == 0;
 #else
-  std::vector<struct pollfd> poll_fds_;
+  return pthread_setname_np(pthread_self(), buf) == 0;
 #endif
+}

-  struct WatchTask {
-    std::function<void()> callback;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    // On UNIX systems we make the FD number negative in |poll_fds_| to avoid
-    // polling it again until the queued task runs. On Windows we can't do that.
-    // Instead we keep track of its state here.
-    bool pending = false;
+inline bool GetThreadName(std::string& out_result) {
+  char buf[16] = {};
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  if (prctl(PR_GET_NAME, buf) != 0)
+    return false;
 #else
-    size_t poll_fd_index;  // Index into |poll_fds_|.
+  if (pthread_getname_np(pthread_self(), buf, sizeof(buf)) != 0)
+    return false;
 #endif
-  };
-
-  // Accessed only from the main thread.
-  std::unordered_map<PlatformHandle, WatchTask> watch_tasks_;
-  bool watch_tasks_changed_ = false;
-
-  // An array of 32 refcount buckets. Every Slab* maps to a bucket via a hash
-  // function. Every PostTask() thread increases the refcount before accessing
-  // a slab, and decreases it when done.
-  // This allows the Run() main thread to tell if any thread has possibly been
-  // able to observe the Slab through the tail_ before deleting it.
-  std::array<std::atomic<int32_t>, task_runner_internal::kNumRefcountBuckets>
-      refcounts_{};
-
-  // Stats for testing.
-  std::atomic<size_t> slabs_allocated_{};
-  std::atomic<size_t> slabs_freed_{};
-};
-
-namespace task_runner_internal {
-
-// Returns the index of the refcount_ bucket for the passed Slab pointer.
-static uint32_t HashSlabPtr(Slab* slab) {
-  // This is a SplitMix64 hash, which is very fast and effective with pointers
-  // (See the test LockFreeTaskRunnerTest.HashSpreading).
-  uint64_t u = reinterpret_cast<uintptr_t>(slab);
-  u &= 0x00FFFFFFFFFFFFFFull;  // Clear asan/MTE top byte for tagged pointers.
-  u += 0x9E3779B97F4A7C15ull;
-  u = (u ^ (u >> 30)) * 0xBF58476D1CE4E5B9ull;
-  u = (u ^ (u >> 27)) * 0x94D049BB133111EBull;
-  return static_cast<uint32_t>((u ^ (u >> 31)) % kNumRefcountBuckets);
+  out_result = std::string(buf);
+  return true;
 }

-// A slab is a fixed-size array of tasks. The lifecycle of a task slot
-// within a slab goes through three phases:
-//
-// 1. Reservation: A writer thread atomically increments `next_task_slot` to
-//    reserve a slot in the `tasks` array. This reservation establishes the
-//    implicit order in which the consumer will attempt to read tasks (but
-//    only if they are published in the bitmap, see below).
-//
-// 2. Publishing: After writing the task into its reserved slot, the writer
-//    thread atomically sets the corresponding bit in the `tasks_written`
-//    bitmask. This acts as a memory barrier and makes the task visible to
-//    the consumer (main) thread.
-//
-// 3. Consumption: The main thread acquire-reads the `tasks_written` bitmask.
-//    For each bit that is set, it processes the task and then sets the
-//    corresponding bit in its private `tasks_read` bitmask to prevent
-//    reading the same task again.
-struct Slab {
-  Slab();
-  ~Slab();
-
-  std::atomic<size_t> next_task_slot{0};
-
-  // `tasks` and `next_task_slot` are accessed by writer threads only.
-  // The main thread can access `tasks[i]` but only after ensuring that the
-  // corresponding bit in `tasks_written` is set.
-  std::array<std::function<void()>, kSlabSize> tasks{};
-
-  // A bitmask indicating which tasks in the `tasks` array have been written
-  // and are ready to be read by the main thread.
-  // This is atomically updated by writer threads and read by the main thread.
-  using BitWord = size_t;
-  static constexpr size_t kBitsPerWord = sizeof(BitWord) * 8;
-  static constexpr size_t kNumWords = kSlabSize / kBitsPerWord;
-  std::array<std::atomic<BitWord>, kNumWords> tasks_written{};
-
-  // A bitmask indicating which tasks have been read by the main thread.
-  // This is accessed only by the main thread, so no atomicity is required.
-  std::array<BitWord, kNumWords> tasks_read{};
-
-  // The link to the previous slab.
-  // This is written by writer threads when they create a new slab and link it
-  // to the previous tail. But they do so when nobody else can see the Slab,
-  // so there is no need for an atomic ptr. After the initial creation,
-  // this is accessed only by the main thread when:
-  // 1. draining tasks (to walk back to the oldest slab)
-  // 2. deleting slabs, setting it to nullptr, when they are fully consumed.
-  Slab* prev = nullptr;
-};
-
-class ScopedRefcount {
- public:
-  ScopedRefcount(LockFreeTaskRunner* tr, LockFreeTaskRunner::Slab* slab) {
-    bucket_ = &tr->refcounts_[HashSlabPtr(slab)];
-    auto prev_value = bucket_->fetch_add(1);
-    PERFETTO_DCHECK(prev_value >= 0);
-  }
-
-  ~ScopedRefcount() {
-    if (bucket_) {
-      auto prev_value = bucket_->fetch_sub(1);
-      PERFETTO_DCHECK(prev_value > 0);
-    }
-  }
-
-  ScopedRefcount(ScopedRefcount&& other) noexcept {
-    bucket_ = other.bucket_;
-    other.bucket_ = nullptr;
-  }
-
-  ScopedRefcount& operator=(ScopedRefcount&& other) noexcept {
-    this->~ScopedRefcount();
-    new (this) ScopedRefcount(std::move(other));
-    return *this;
-  }
-
-  ScopedRefcount(const ScopedRefcount&) = delete;
-  ScopedRefcount& operator=(const ScopedRefcount&) = delete;
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)

-  std::atomic<int32_t>* bucket_{};
-};
-}  // namespace task_runner_internal
+PERFETTO_EXPORT_COMPONENT bool MaybeSetThreadName(const std::string& name);
+PERFETTO_EXPORT_COMPONENT bool GetThreadName(std::string& out_result);

-using MaybeLockFreeTaskRunner =
-    std::conditional_t<PERFETTO_FLAGS(USE_LOCKFREE_TASKRUNNER),
-                       LockFreeTaskRunner,
-                       UnixTaskRunner>;
+#else
+inline bool MaybeSetThreadName(const std::string&) {
+  return false;
+}
+inline bool GetThreadName(std::string&) {
+  return false;
+}
+#endif

 }  // namespace base
 }  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_LOCK_FREE_TASK_RUNNER_H_
-// gen_amalgamated begin header: include/perfetto/ext/base/bits.h
+#endif  // INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * 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.
@@ -11972,93 +12505,89 @@ using MaybeLockFreeTaskRunner =
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_BITS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_BITS_H_
-
-#include <cstddef>
-#include <cstdint>
+// gen_amalgamated expanded: #include "perfetto/base/thread_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_utils.h"

 // gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/base/compiler.h"

-#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
-#include <immintrin.h>
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+#include <zircon/process.h>
+#include <zircon/syscalls.h>
+#include <zircon/types.h>
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <windows.h>
 #endif

 namespace perfetto {
 namespace base {

-inline PERFETTO_ALWAYS_INLINE uint32_t CountLeadZeros32(uint32_t value) {
-#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
-  return static_cast<uint32_t>(_lzcnt_u32(value));
-#elif defined(__GNUC__) || defined(__clang__)
-  return value ? static_cast<uint32_t>(__builtin_clz(value)) : 32u;
-#else
-  unsigned long out;
-  return _BitScanReverse(&out, value) ? 31 - out : 32u;
-#endif
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+static PlatformThreadId ResolveThreadId() {
+  zx_info_handle_basic_t basic;
+  return (zx_object_get_info(zx_thread_self(), ZX_INFO_HANDLE_BASIC, &basic,
+                             sizeof(basic), nullptr, nullptr) == ZX_OK)
+             ? basic.koid
+             : ZX_KOID_INVALID;
 }
-
-inline PERFETTO_ALWAYS_INLINE uint32_t CountLeadZeros64(uint64_t value) {
-#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
-  return static_cast<uint32_t>(_lzcnt_u64(value));
-#elif defined(__GNUC__) || defined(__clang__)
-  return value ? static_cast<uint32_t>(__builtin_clzll(value)) : 64u;
-#else
-  unsigned long out;
-  return _BitScanReverse64(&out, value) ? 63 - out : 64u;
-#endif
+PlatformThreadId GetThreadId() {
+  thread_local static PlatformThreadId thread_id = ResolveThreadId();
+  return thread_id;
 }

-template <typename T>
-uint32_t CountLeadZeros(T value) {
-  if constexpr (sizeof(T) == 8)
-    return CountLeadZeros64(static_cast<uint64_t>(value));
-  return CountLeadZeros32(static_cast<uint32_t>(value));
-}
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)

-inline PERFETTO_ALWAYS_INLINE uint32_t CountTrailZeros64(uint64_t value) {
-#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
-  return static_cast<uint32_t>(_tzcnt_u64(value));
-#elif defined(__GNUC__) || defined(__clang__)
-  return value ? static_cast<uint32_t>(__builtin_ctzll(value)) : 64u;
-#else
-  unsigned long out;
-  return _BitScanForward64(&out, value) ? static_cast<uint32_t>(out) : 64u;
-#endif
-}
+// The SetThreadDescription API was brought in version 1607 of Windows 10.
+typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread,
+                                              PCWSTR lpThreadDescription);

-inline PERFETTO_ALWAYS_INLINE uint32_t CountTrailZeros32(uint32_t value) {
-#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
-  return _tzcnt_u32(value);
-#elif defined(__GNUC__) || defined(__clang__)
-  return value ? static_cast<uint32_t>(__builtin_ctz(value)) : 32u;
-#else
-  unsigned long out;
-  return _BitScanForward(&out, value) ? out : 32u;
-#endif
-}
+// The SetThreadDescription API was brought in version 1607 of Windows 10.
+typedef HRESULT(WINAPI* GetThreadDescription)(HANDLE hThread,
+                                              PWSTR* ppszThreadDescription);

-template <typename T>
-inline PERFETTO_ALWAYS_INLINE uint32_t CountTrailZeros(T value) {
-  if (sizeof(T) == 8) {
-    return CountTrailZeros64(static_cast<uint64_t>(value));
+bool MaybeSetThreadName(const std::string& name) {
+  // The SetThreadDescription API works even if no debugger is attached.
+  static auto set_thread_description_func =
+      reinterpret_cast<SetThreadDescription>(
+          reinterpret_cast<void*>(::GetProcAddress(
+              ::GetModuleHandleA("Kernel32.dll"), "SetThreadDescription")));
+  if (!set_thread_description_func) {
+    return false;
   }
-  return CountTrailZeros32(static_cast<uint32_t>(value));
+  std::wstring wide_thread_name;
+  if (!UTF8ToWide(name, wide_thread_name)) {
+    return false;
+  }
+  HRESULT result = set_thread_description_func(::GetCurrentThread(),
+                                               wide_thread_name.c_str());
+  return !FAILED(result);
 }

-template <typename T>
-constexpr bool AllBitsSet(T v) {
-  return v == static_cast<T>(-1);
+bool GetThreadName(std::string& out_result) {
+  static auto get_thread_description_func =
+      reinterpret_cast<GetThreadDescription>(
+          reinterpret_cast<void*>(::GetProcAddress(
+              ::GetModuleHandleA("Kernel32.dll"), "GetThreadDescription")));
+  if (!get_thread_description_func) {
+    return false;
+  }
+  wchar_t* wide_thread_name;
+  HRESULT result =
+      get_thread_description_func(::GetCurrentThread(), &wide_thread_name);
+  if (SUCCEEDED(result)) {
+    bool success = WideToUTF8(std::wstring(wide_thread_name), out_result);
+    LocalFree(wide_thread_name);
+    return success;
+  }
+  return false;
 }

+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+
 }  // namespace base
 }  // namespace perfetto
-
-#endif  // INCLUDE_PERFETTO_EXT_BASE_BITS_H_
-
+// gen_amalgamated begin source: src/base/time.cc
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * 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.
@@ -12066,546 +12595,216 @@ constexpr bool AllBitsSet(T v) {
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law of an "AS IS" BASIS,
+ * 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.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/lock_free_task_runner.h"
+#include <atomic>
+
+// gen_amalgamated expanded: #include "perfetto/base/time.h"

 // gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"

 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 #include <windows.h>
-
-// Keep the \n before to prevent clang-format reordering.
-#include <synchapi.h>
 #else
-#include <poll.h>
 #include <unistd.h>
 #endif

-#include <thread>
-
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/bits.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/platform.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/watchdog.h"
-
 namespace perfetto {
 namespace base {

-namespace task_runner_internal {
-Slab::Slab() = default;
-Slab::~Slab() {
-  PERFETTO_DCHECK(!prev);  // We should never delete linked slabs.
-}
-}  // namespace task_runner_internal
-
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#if !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
 namespace {
-static constexpr auto kSlabSize = task_runner_internal::kSlabSize;
-}

-LockFreeTaskRunner::LockFreeTaskRunner()
-    : run_task_thread_id_(std::this_thread::get_id()) {
-  static_assert((kSlabSize & (kSlabSize - 1)) == 0, "kSlabSize must be a pow2");
-  static_assert(kSlabSize >= Slab::kBitsPerWord);
+// Returns the current value of the performance counter.
+int64_t QPCNowRaw() {
+  LARGE_INTEGER perf_counter_now = {};
+  // According to the MSDN documentation for QueryPerformanceCounter(), this
+  // will never fail on systems that run XP or later.
+  // https://msdn.microsoft.com/library/windows/desktop/ms644904.aspx
+  ::QueryPerformanceCounter(&perf_counter_now);
+  return perf_counter_now.QuadPart;
+}

-  // Populate both the tail and the freelist, to minimize the chance of
-  // allocations at runtime.
-  tail_.store(AllocNewSlab());
-  free_slab_.store(AllocNewSlab());
+double TSCTicksPerSecond() {
+  // The value returned by QueryPerformanceFrequency() cannot be used as the TSC
+  // frequency, because there is no guarantee that the TSC frequency is equal to
+  // the performance counter frequency.
+  // The TSC frequency is cached in a static variable because it takes some time
+  // to compute it.
+  static std::atomic<double> tsc_ticks_per_second = 0;
+  double value = tsc_ticks_per_second.load(std::memory_order_relaxed);
+  if (value != 0)
+    return value;

-  AddFileDescriptorWatch(wakeup_event_.fd(), [] {
-    // Not reached -- see PostFileDescriptorWatches().
-    PERFETTO_DFATAL("Should be unreachable.");
-  });
-}
+  // Increase the thread priority to reduces the chances of having a context
+  // switch during a reading of the TSC and the performance counter.
+  const int previous_priority = ::GetThreadPriority(::GetCurrentThread());
+  ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

-LockFreeTaskRunner::~LockFreeTaskRunner() {
-  PERFETTO_DCHECK(RunsTasksOnCurrentThread());
+  // The first time that this function is called, make an initial reading of the
+  // TSC and the performance counter. Initialization of static variable is
+  // thread-safe. Threads can race initializing tsc_initial vs
+  // perf_counter_initial, although they should be storing very similar values.

-  for (Slab* slab = tail_.exchange(nullptr); slab;) {
-    Slab* prev = slab->prev;
-    slab->prev = nullptr;
-    delete slab;
-    slab = prev;
-  }
-  delete free_slab_.exchange(nullptr);
-}
+  static const uint64_t tsc_initial = __rdtsc();
+  static const int64_t perf_counter_initial = QPCNowRaw();

-void LockFreeTaskRunner::PostTask(std::function<void()> closure) {
-  // We use nullity of std::function in PopTaskRecursive() to determine exit
-  // criteria. Posting a null task would break that logic. Also a null task
-  // would cause an abort when trying to run it later on.
-  PERFETTO_CHECK(PERFETTO_LIKELY(closure));
-  using BitWord = Slab::BitWord;
-  for (;;) {
-    Slab* slab = tail_.load();
-    PERFETTO_DCHECK(slab);  // The tail_ must be always valid.
-    ScopedRefcount scoped_refcount(this, slab);
-    // After incrementing the refcount, re-verify that `slab` is still the
-    // current tail. If it isn't, the slab might have been deleted between
-    // our tail_.load() above and the refcount inc (the reader's check on
-    // refcount==0 may have happened before our inc). Retry with the new tail.
-    // If the second tail.load() returns the same slab, in seq_cst order
-    // any subsequent CAS that replaces tail_ comes after this load, hence
-    // any reader's later refcount check is also after our inc and will
-    // observe refcount > 0.
-    if (PERFETTO_UNLIKELY(tail_.load() != slab)) {
-      continue;
-    }
+  // Make a another reading of the TSC and the performance counter every time
+  // that this function is called.
+  const uint64_t tsc_now = __rdtsc();
+  const int64_t perf_counter_now = QPCNowRaw();

-    // Now that we have a slab, try appending a task to it (if there is space).
-    // We have 3 cases:
-    // 1. slot < kSlabSize: the nominal case. Append the task and return.
-    // 2. slot == kSlabSize: the common overflow case: The slab was full and we
-    //    tried to allocate the N+1 th element. We have to allocate a new Slab.
-    // 3. slot > kSlabSize: like 2, but but two (or more) threads raced on it.
-    //    One thread will win the race and alloc a new slab, the other one will
-    //    repeat.
-    size_t slot = slab->next_task_slot.fetch_add(1, std::memory_order_relaxed);
+  // Reset the thread priority.
+  ::SetThreadPriority(::GetCurrentThread(), previous_priority);

-    if (slot >= kSlabSize) {  // Cases 2,3
-      Slab* new_slab = AllocNewSlab();
-      new_slab->prev = slab;
-      new_slab->next_task_slot.store(1, std::memory_order_relaxed);
-      slot = 0;
-      if (PERFETTO_UNLIKELY(!tail_.compare_exchange_strong(slab, new_slab))) {
-        // If the cmpxcgh fails, another thread tried to allocate a new tail
-        // slab and won the race. Do another round, we'll observe the new slab.
-        // We have to release the prev pointer as at this point we found out
-        // another thread also has it.
-        new_slab->prev = nullptr;
-        DeleteSlab(new_slab);
-        continue;
-      }
+  // Make sure that at least 50 ms elapsed between the 2 readings. The first
+  // time that this function is called, we don't expect this to be the case.
+  // Note: The longer the elapsed time between the 2 readings is, the more
+  //   accurate the computed TSC frequency will be. The 50 ms value was
+  //   chosen because local benchmarks show that it allows us to get a
+  //   stddev of less than 1 tick/us between multiple runs.
+  // Note: According to the MSDN documentation for QueryPerformanceFrequency(),
+  //   this will never fail on systems that run XP or later.
+  //   https://msdn.microsoft.com/library/windows/desktop/ms644905.aspx
+  LARGE_INTEGER perf_counter_frequency = {};
+  ::QueryPerformanceFrequency(&perf_counter_frequency);
+  PERFETTO_CHECK(perf_counter_now >= perf_counter_initial);
+  const int64_t perf_counter_ticks = perf_counter_now - perf_counter_initial;
+  const double elapsed_time_seconds =
+      static_cast<double>(perf_counter_ticks) /
+      static_cast<double>(perf_counter_frequency.QuadPart);

-      slab = new_slab;
-      scoped_refcount = ScopedRefcount(this, new_slab);
-    }
+  constexpr double kMinimumEvaluationPeriodSeconds = 0.05;
+  if (elapsed_time_seconds < kMinimumEvaluationPeriodSeconds)
+    return 0;

-    // Nominal case: publish the task and return.
-    PERFETTO_DCHECK(!slab->tasks[slot]);
-    slab->tasks[slot] = std::move(closure);
-    size_t s_word = slot / Slab::kBitsPerWord;
-    size_t s_bit = slot % Slab::kBitsPerWord;
-    BitWord s_mask = BitWord(1) << s_bit;
-    PERFETTO_DCHECK(
-        (slab->tasks_written[s_word].load(std::memory_order_relaxed) &
-         s_mask) == 0);
-    slab->tasks_written[s_word].fetch_or(s_mask, std::memory_order_release);
+  // Compute the frequency of the TSC.
+  PERFETTO_CHECK(tsc_now >= tsc_initial);
+  const uint64_t tsc_ticks = tsc_now - tsc_initial;
+  // Racing with another thread to write |tsc_ticks_per_second| is benign
+  // because both threads will write a valid result.
+  tsc_ticks_per_second.store(
+      static_cast<double>(tsc_ticks) / elapsed_time_seconds,
+      std::memory_order_relaxed);

-    if (!RunsTasksOnCurrentThread()) {
-      // We don't need any clever logic to avoid spurious wake ups from other
-      // threads. Most PostTask()s in our codebase are done by the main thread.
-      // In the rare cases of a PostTask() coming from another thread, the odds
-      // of the main thread being woken up at the same time are tiny.
-      WakeUp();
-    }
-    return;
-  }
+  return tsc_ticks_per_second.load(std::memory_order_relaxed);
 }

-void LockFreeTaskRunner::Run() {
-  PERFETTO_CHECK(run_task_thread_id_ == std::this_thread::get_id());
-  quit_ = false;
+}  // namespace
+#endif  // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)

-  while (!quit_) {
-    // Pop one immediate and one delayed task, for fairness.
-    std::function<void()> immediate_task = PopNextImmediateTask();
-    std::function<void()> delayed_task = PopNextExpiredDelayedTask();
-    bool has_task = immediate_task || delayed_task;
-    int poll_timeout_ms = has_task ? 0 : GetDelayMsToNextTask();
+TimeNanos GetWallTimeNs() {
+  LARGE_INTEGER freq;
+  ::QueryPerformanceFrequency(&freq);
+  LARGE_INTEGER counter;
+  ::QueryPerformanceCounter(&counter);
+  double elapsed_nanoseconds = (1e9 * static_cast<double>(counter.QuadPart)) /
+                               static_cast<double>(freq.QuadPart);
+  return TimeNanos(static_cast<uint64_t>(elapsed_nanoseconds));
+}

-    // run the poll(). We need it for two different reasons:
-    // 1. Blocks until the next event on the horizon, which:
-    //    - If we pulled a task, poll in non-blocking mode (0 delay). There
-    //      might be other tasks ready and we want to run them soon after.
-    //    - If there is a delayed task, compute the time remaining for it.
-    //    - Otherwise polls indefinitely, waiting for a PostTask() or a Quit()
-    //      call from another thread.
-    // 2. Regardless of timing, we need to read the FD watches.
-    //    We want to do this even if we know already that we have an immediate
-    //    task (when poll_timeout_ms = 0) to ensure fairness.
-
-    //    TODO(primiano): we could optimize this and avoid a syscall for each
-    //    task when we have bursts of tasks (with some saturation, e.g. do still
-    //    a poll() every 10 tasks). Unfortunately, however, some users of
-    //    TestTaskRunner.RunUntilIdle() rely on the fact that FD watches are
-    //    polled before the current task is ran, so that the task can tell if
-    //    there are more tasks upcoming or it has reached quiescence.
-    //    TL;DR RunUntilIdle() was a mistake as it has ill-defined semantics but
-    //    now tests rely on those subtle semantics.
-
-    // Recompute the list of FDs to watch.
-    UpdateWatchTasks();
-
-    uint64_t windows_wait_res = 0;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    // Unlike poll(2), WaitForMultipleObjects() returns only *one* handle in the
-    // set, even when >1 is signalled. In order to avoid starvation,
-    // PostFileDescriptorWatches() will WaitForSingleObject() each other handle
-    // to ensure fairness. |windows_wait_res| is passed just to avoid an extra
-    // WaitForSingleObject() for the one handle that WaitForMultipleObject()
-    // returned.
-    DWORD timeout =
-        poll_timeout_ms >= 0 ? static_cast<DWORD>(poll_timeout_ms) : INFINITE;
-    windows_wait_res =
-        WaitForMultipleObjects(static_cast<DWORD>(poll_fds_.size()),
-                               &poll_fds_[0], /*bWaitAll=*/false, timeout);
-#else
-    platform::BeforeMaybeBlockingSyscall();
-    int ret = PERFETTO_EINTR(poll(
-        &poll_fds_[0], static_cast<nfds_t>(poll_fds_.size()), poll_timeout_ms));
-    platform::AfterMaybeBlockingSyscall();
-    PERFETTO_CHECK(ret >= 0);
-#endif
-    PostFileDescriptorWatches(windows_wait_res);
-
-    if (immediate_task) {
-      errno = 0;
-      RunTaskWithWatchdogGuard(std::move(immediate_task));
-    }
-
-    if (delayed_task) {
-      errno = 0;
-      RunTaskWithWatchdogGuard(std::move(delayed_task));
-    }
-  }
-
-  // Wait for all other threads to have finished the Quit's PostTask().
-  // This is to prevent the following race in tests:
-  // - Thread1 (!= main thread) invokes Quit, which in turn becomes a PostTask.
-  // - This function sees quit_=true and returns from Run().
-  // - The owner of the LFTR at that point is entitled to destroy LFTR.
-  // - Thread1 is still executing the epilogue of the PostTask, decrementing the
-  //   refcount, and ends up operating on invalid memory.
-  while (
-      std::any_of(refcounts_.begin(), refcounts_.end(),
-                  [](std::atomic<int32_t>& bucket) { return bucket.load(); })) {
-    std::this_thread::yield();
-  }
-}
-
-std::function<void()> LockFreeTaskRunner::PopNextImmediateTask() {
-  return PopTaskRecursive(tail_.load(), nullptr);
-}
-
-std::function<void()> LockFreeTaskRunner::PopTaskRecursive(Slab* slab,
-                                                           Slab* next_slab) {
-  PERFETTO_DCHECK(RunsTasksOnCurrentThread());
-  Slab* prev = slab->prev;
-  if (PERFETTO_UNLIKELY(prev)) {
-    // In practice it's extemely unlikely that a slab has >1 predecessors.
-    // In nominal conditions it is going to have 0 predecessors most of the
-    // times and 1 predecessors 1 every kSlabSize times.
-    auto task = PopTaskRecursive(prev, slab);
-    if (task)
-      return task;
-  }
-
-  size_t words_fully_consumed = 0;
-  for (size_t w = 0; w < Slab::kNumWords; ++w) {
-    using BitWord = Slab::BitWord;
-    BitWord wr_word = slab->tasks_written[w].load(std::memory_order_acquire);
-    BitWord rd_word = slab->tasks_read[w];
-    words_fully_consumed += base::AllBitsSet(rd_word) ? 1 : 0;
-    BitWord unread_word = wr_word & ~rd_word;
-
-    if (unread_word == 0)
-      continue;
-
-    // Find the first unread task in the word.
-    uint32_t bit = base::CountTrailZeros(unread_word);
-    BitWord bit_mask = BitWord(1) << bit;
-    size_t slot = w * Slab::kBitsPerWord + bit;
-    std::function<void()> task = std::move(slab->tasks[slot]);
-    slab->tasks[slot] = nullptr;
-    slab->tasks_read[w] |= bit_mask;
-    return task;
-  }  // for(word in tasks_written)
-
-  // There are no unconsumed tasks in this Slab. Reached this point, this
-  // invocation will return null. However, before doing so, if the slab is fully
-  // written (are no slots left) and fully consumed delete it.
-  // We never delete the latest slab (the one pointed to by tail_) because
-  // we want the tail_ to always point to a valid slab.
-  // In principle, we only delete non-tail slabs, and PostTask() threads only
-  // access the tail, never walk back on the list, which sounds safe. However
-  // there is a potential race we have to guard against:
-  // A PostTask() thread could observe Slab 1 (e.g., the only slab), then
-  // another PostTask thread() could append (and replace tail_ with) a new
-  // Slab 2 (because Slab 1 was full).
-  // If Run() comes along and sees the Slab 1 full it will delete it, without
-  // realizing that the first thread is still accessing it, thinking it was the
-  // tail.
-  // Essentially, it is safe to delete non-tail slabs as long as we can
-  // guarrantee that no other thread observed that slab through the `tail_` at
-  // some point in the recent past. In order to do so, we use a refcount
-  // mechanism. If the refcount is 0 we know that no other PostTask thread
-  // has loaded the tail, and hence we can safely delete any non-tail slab.
-  // If a PostTask thread comes immediately after our check they will observe
-  // the new tail (we never delete the tail slab, only the predecessors).
-
-  bool slab_fully_consumed = words_fully_consumed == Slab::kNumWords;
-
-  const uint32_t bucket = HashSlabPtr(slab);
-  if (slab_fully_consumed && next_slab && refcounts_[bucket].load() == 0) {
-    // NOTE: only the main thread follows the `prev` linked list, writers never
-    // look at `prev`. The only contention entrypoint is the `tail_` pointer,
-    // which can be modified both by us and by writers.
-    PERFETTO_DCHECK(next_slab->prev == slab);
-    next_slab->prev = slab->prev;
-    slab->prev = nullptr;
-    DeleteSlab(slab);
-  }
-
-  return nullptr;
-}
-
-void LockFreeTaskRunner::Quit() {
-  // We have to post the writing of the quit if we are not on the right thread.
-  // The alternative would be writing quit_ + WakeUp(), but that is racy:
-  // - We write quit_=true from another thread
-  // - The main thread sees it immediately (it just happens to be iterating)
-  //   and destroys itself.
-  // - We try to call WakeUp() while the main thread is being destroyed.
-  if (!RunsTasksOnCurrentThread()) {
-    PostTask([this] { this->Quit(); });
-    return;
-  }
-  quit_ = true;
-}
-
-bool LockFreeTaskRunner::IsIdleForTesting() {
-  PERFETTO_DCHECK(RunsTasksOnCurrentThread());
-  for (Slab* slab = tail_; slab; slab = slab->prev) {
-    for (size_t i = 0; i < Slab::kNumWords; ++i) {
-      if (slab->tasks_written[i] & ~slab->tasks_read[i]) {
-        return false;
-      }
-    }
-  }
-  return true;
-}
-
-std::function<void()> LockFreeTaskRunner::PopNextExpiredDelayedTask() {
-  PERFETTO_DCHECK(RunsTasksOnCurrentThread());
-  TimeMillis now =
-      GetWallTimeMs() +
-      TimeMillis(advanced_time_for_testing_.load(std::memory_order_relaxed));
-  if (!delayed_tasks_.empty() && delayed_tasks_.back().time <= now) {
-    auto task = std::move(delayed_tasks_.back().task);
-    delayed_tasks_.pop_back();
-    return task;
-  }
-  return nullptr;
-}
-
-int LockFreeTaskRunner::GetDelayMsToNextTask() const {
-  PERFETTO_DCHECK(RunsTasksOnCurrentThread());
-  if (delayed_tasks_.empty()) {
-    return -1;  // Forever.
-  }
-  TimeMillis now =
-      GetWallTimeMs() +
-      TimeMillis(advanced_time_for_testing_.load(std::memory_order_relaxed));
-  TimeMillis deadline = delayed_tasks_.back().time;
-  if (deadline <= now) {
-    return 0;
-  }
-  return static_cast<int>((deadline - now).count());
-}
-
-LockFreeTaskRunner::Slab* LockFreeTaskRunner::AllocNewSlab() {
-  Slab* free_slab = free_slab_.exchange(nullptr);
-  if (free_slab) {
-    free_slab->~Slab();
-    new (free_slab) Slab();
-    return free_slab;
-  }
-  slabs_allocated_.fetch_add(1, std::memory_order_relaxed);
-  return new Slab();
-}
+TimeNanos GetThreadCPUTimeNs() {
+#if PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+  // QueryThreadCycleTime versus TSCTicksPerSecond doesn't have much relation to
+  // actual elapsed time on Windows on Arm, because QueryThreadCycleTime is
+  // backed by the actual number of CPU cycles executed, rather than a
+  // constant-rate timer like Intel. To work around this, use GetThreadTimes
+  // (which isn't as accurate but is meaningful as a measure of elapsed
+  // per-thread time).
+  FILETIME dummy, kernel_ftime, user_ftime;
+  ::GetThreadTimes(GetCurrentThread(), &dummy, &dummy, &kernel_ftime,
+                   &user_ftime);
+  uint64_t kernel_time =
+      kernel_ftime.dwHighDateTime * 0x100000000 + kernel_ftime.dwLowDateTime;
+  uint64_t user_time =
+      user_ftime.dwHighDateTime * 0x100000000 + user_ftime.dwLowDateTime;

-void LockFreeTaskRunner::DeleteSlab(Slab* slab) {
-  PERFETTO_DCHECK(!slab->prev);  // We should never delete a linked slab.
-  Slab* null_slab = nullptr;
-  if (!free_slab_.compare_exchange_strong(null_slab, slab)) {
-    slabs_freed_.fetch_add(1, std::memory_order_relaxed);
-    delete slab;
-  }
-}
+  return TimeNanos((kernel_time + user_time) * 100);
+#else   // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+  // Get the number of TSC ticks used by the current thread.
+  ULONG64 thread_cycle_time = 0;
+  ::QueryThreadCycleTime(GetCurrentThread(), &thread_cycle_time);

-void LockFreeTaskRunner::PostDelayedTask(std::function<void()> task,
-                                         uint32_t delay_ms) {
-  if (!RunsTasksOnCurrentThread()) {
-    PostTask([this, task = std::move(task), delay_ms] {
-      this->PostDelayedTask(std::move(task), delay_ms);
-    });
-    return;
-  }
+  // Get the frequency of the TSC.
+  const double tsc_ticks_per_second = TSCTicksPerSecond();
+  if (tsc_ticks_per_second == 0)
+    return TimeNanos();

-  TimeMillis runtime =
-      GetWallTimeMs() + TimeMillis(delay_ms) +
-      TimeMillis(advanced_time_for_testing_.load(std::memory_order_relaxed));
-  delayed_tasks_.insert(
-      DelayedTask{runtime, next_delayed_task_seq_++, std::move(task)});
+  // Return the CPU time of the current thread.
+  const double thread_time_seconds =
+      static_cast<double>(thread_cycle_time) / tsc_ticks_per_second;
+  constexpr int64_t kNanosecondsPerSecond = 1000 * 1000 * 1000;
+  return TimeNanos(
+      static_cast<int64_t>(thread_time_seconds * kNanosecondsPerSecond));
+#endif  // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
 }

-void LockFreeTaskRunner::AdvanceTimeForTesting(uint32_t ms) {
-  advanced_time_for_testing_.fetch_add(ms);
-  WakeUp();
+void SleepMicroseconds(unsigned interval_us) {
+  // The Windows Sleep function takes a millisecond count. Round up so that
+  // short sleeps don't turn into a busy wait. Note that the sleep granularity
+  // on Windows can dynamically vary from 1 ms to ~16 ms, so don't count on this
+  // being a short sleep.
+  ::Sleep(static_cast<DWORD>((interval_us + 999) / 1000));
 }

-void LockFreeTaskRunner::PostFileDescriptorWatches(
-    uint64_t windows_wait_result) {
-  PERFETTO_DCHECK(RunsTasksOnCurrentThread());
-  for (size_t i = 0; i < poll_fds_.size(); i++) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    const PlatformHandle handle = poll_fds_[i];
-    // |windows_wait_result| is the result of WaitForMultipleObjects() call. If
-    // one of the objects was signalled, it will have a value between
-    // [0, poll_fds_.size()].
-    if (i != windows_wait_result &&
-        WaitForSingleObject(handle, 0) != WAIT_OBJECT_0) {
-      continue;
-    }
-#else
-    base::ignore_result(windows_wait_result);
-    const PlatformHandle handle = poll_fds_[i].fd;
-    if (!(poll_fds_[i].revents & (POLLIN | POLLHUP)))
-      continue;
-    poll_fds_[i].revents = 0;
-#endif
-
-    // The wake-up event is handled inline to avoid an infinite recursion of
-    // posted tasks.
-    if (handle == wakeup_event_.fd()) {
-      wakeup_event_.Clear();
-      continue;
-    }
-
-    // Binding to |this| is safe since we are the only object executing the
-    // task.
-    PostTask([this, handle] { this->RunFileDescriptorWatch(handle); });
-
-    // Flag the task as pending.
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    // On Windows this is done by marking the WatchTask entry as pending. This
-    // is more expensive than Linux as requires rebuilding the |poll_fds_|
-    // vector on each call. There doesn't seem to be a good alternative though.
-    auto it = watch_tasks_.find(handle);
-    PERFETTO_CHECK(it != watch_tasks_.end());
-    PERFETTO_DCHECK(!it->second.pending);
-    it->second.pending = true;
-#else
-    // On UNIX systems instead, we just make the fd negative while its task is
-    // pending. This makes poll(2) ignore the fd.
-    PERFETTO_DCHECK(poll_fds_[i].fd >= 0);
-    poll_fds_[i].fd = -poll_fds_[i].fd;
-#endif
-  }
+void InitializeTime() {
+#if !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+  // Make an early first call to TSCTicksPerSecond() to start 50 ms elapsed time
+  // (see comment in TSCTicksPerSecond()).
+  TSCTicksPerSecond();
+#endif  // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
 }

-void LockFreeTaskRunner::RunFileDescriptorWatch(PlatformHandle fd) {
-  PERFETTO_DCHECK(RunsTasksOnCurrentThread());
-
-  std::function<void()> task;
-  auto it = watch_tasks_.find(fd);
-  if (it == watch_tasks_.end())
-    return;
-  WatchTask& watch_task = it->second;
-
-  // Make poll(2) pay attention to the fd again. Since another thread may have
-  // updated this watch we need to refresh the set first.
-  UpdateWatchTasks();
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  // On Windows we manually track the presence of outstanding tasks for the
-  // watch. The UpdateWatchTasksLocked() in the Run() loop will re-add the
-  // task to the |poll_fds_| vector.
-  PERFETTO_DCHECK(watch_task.pending);
-  watch_task.pending = false;
-#else
-  size_t fd_index = watch_task.poll_fd_index;
-  PERFETTO_DCHECK(fd_index < poll_fds_.size());
-  PERFETTO_DCHECK(::abs(poll_fds_[fd_index].fd) == fd);
-  poll_fds_[fd_index].fd = fd;
-#endif
-  task = watch_task.callback;
-  errno = 0;
-  RunTaskWithWatchdogGuard(task);
-}
+#else  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)

-void LockFreeTaskRunner::UpdateWatchTasks() {
-  PERFETTO_DCHECK(RunsTasksOnCurrentThread());
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  if (!watch_tasks_changed_)
-    return;
-  watch_tasks_changed_ = false;
-#endif
-  poll_fds_.clear();
-  for (auto& it : watch_tasks_) {
-    PlatformHandle handle = it.first;
-    WatchTask& watch_task = it.second;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    if (!watch_task.pending)
-      poll_fds_.push_back(handle);
-#else
-    watch_task.poll_fd_index = poll_fds_.size();
-    poll_fds_.push_back({handle, POLLIN | POLLHUP, 0});
-#endif
-  }
+void SleepMicroseconds(unsigned interval_us) {
+  ::usleep(static_cast<useconds_t>(interval_us));
 }

-void LockFreeTaskRunner::AddFileDescriptorWatch(PlatformHandle fd,
-                                                std::function<void()> task) {
-  PERFETTO_DCHECK(PlatformHandleChecker::IsValid(fd));
+void InitializeTime() {}

-  if (!RunsTasksOnCurrentThread()) {
-    PostTask([this, task = std::move(task), fd] {
-      this->AddFileDescriptorWatch(fd, std::move(task));
-    });
-    return;
-  }
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)

-  PERFETTO_DCHECK(!watch_tasks_.count(fd));
-  WatchTask& watch_task = watch_tasks_[fd];
-  watch_task.callback = std::move(task);
+std::string GetTimeFmt(const std::string& fmt) {
+  time_t raw_time;
+  time(&raw_time);
+  struct tm local_tm;
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  watch_task.pending = false;
+  PERFETTO_CHECK(localtime_s(&local_tm, &raw_time) == 0);
 #else
-  watch_task.poll_fd_index = SIZE_MAX;
+  tzset();
+  PERFETTO_CHECK(localtime_r(&raw_time, &local_tm) != nullptr);
 #endif
-  watch_tasks_changed_ = true;
-}
-
-void LockFreeTaskRunner::RemoveFileDescriptorWatch(PlatformHandle fd) {
-  if (!RunsTasksOnCurrentThread()) {
-    PostTask([this, fd] { this->RemoveFileDescriptorWatch(fd); });
-    return;
-  }
-
-  PERFETTO_DCHECK(watch_tasks_.count(fd));
-  watch_tasks_.erase(fd);
-  watch_tasks_changed_ = true;
+  char buf[128];
+  PERFETTO_CHECK(strftime(buf, 80, fmt.c_str(), &local_tm) > 0);
+  return buf;
 }

-bool LockFreeTaskRunner::RunsTasksOnCurrentThread() const {
-  return run_task_thread_id_ == std::this_thread::get_id();
+std::optional<int32_t> GetTimezoneOffsetMins() {
+  std::string tz = GetTimeFmt("%z");
+  if (tz.size() != 5 || (tz[0] != '+' && tz[0] != '-'))
+    return std::nullopt;
+  char sign = '\0';
+  int32_t hh = 0;
+  int32_t mm = 0;
+  if (sscanf(tz.c_str(), "%c%2d%2d", &sign, &hh, &mm) != 3)
+    return std::nullopt;
+  return (hh * 60 + mm) * (sign == '-' ? -1 : 1);
 }

 }  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/base/thread_task_runner.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/thread_task_runner.h
+// gen_amalgamated begin source: src/base/utils.cc
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12620,227 +12819,353 @@ bool LockFreeTaskRunner::RunsTasksOnCurrentThread() const {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_THREAD_TASK_RUNNER_H_
-#define INCLUDE_PERFETTO_EXT_BASE_THREAD_TASK_RUNNER_H_
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"

-#include <functional>
-#include <thread>
+#include <string>

-// gen_amalgamated expanded: #include "perfetto/ext/base/lock_free_task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/pipe.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"

-namespace perfetto {
-namespace base {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+#include <limits.h>
+#include <stdlib.h>  // For _exit()
+#endif

-// A MaybeLockFreeTaskRunner backed by a dedicated task thread. Shuts down the
-// runner and joins the thread upon destruction. Can be moved to transfer
-// ownership.
-//
-// Guarantees that:
-// * the MaybeLockFreeTaskRunner will be constructed and destructed on the task
-// thread.
-// * the task thread will live for the lifetime of the MaybeLockFreeTaskRunner.
-//
-class PERFETTO_EXPORT_COMPONENT ThreadTaskRunner : public TaskRunner {
- public:
-  static ThreadTaskRunner CreateAndStart(const std::string& name = "") {
-    return ThreadTaskRunner(name);
-  }
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+// For isatty(), getpagesize(), geteuid(), fork() & sysconf().
+#include <unistd.h>
+#endif

-  ThreadTaskRunner(const ThreadTaskRunner&) = delete;
-  ThreadTaskRunner& operator=(const ThreadTaskRunner&) = delete;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <mach-o/dyld.h>
+#include <mach/vm_page_size.h>
+#endif

-  ThreadTaskRunner(ThreadTaskRunner&&) noexcept;
-  ThreadTaskRunner& operator=(ThreadTaskRunner&&);
-  ~ThreadTaskRunner() override;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD)
+#include <sys/sysctl.h>
+#endif

-  // Executes the given function on the task runner thread and blocks the caller
-  // thread until the function has run.
-  void PostTaskAndWaitForTesting(std::function<void()>);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/prctl.h>

-  // Can be called from another thread to get the CPU time of the thread the
-  // task-runner is executing on.
-  uint64_t GetThreadCPUTimeNsForTesting();
+#ifndef PR_GET_TAGGED_ADDR_CTRL
+#define PR_GET_TAGGED_ADDR_CTRL 56
+#endif

-  PlatformThreadId GetThreadIdForTesting();
+#ifndef PR_TAGGED_ADDR_ENABLE
+#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
+#endif

-  // Returns a pointer to the MaybeLockFreeTaskRunner, which is valid for the
-  // lifetime of this ThreadTaskRunner object (unless this object is moved-from,
-  // in which case the pointer remains valid for the lifetime of the new owning
-  // ThreadTaskRunner).
-  //
-  // Warning: do not call Quit() on the returned runner pointer, the termination
-  // should be handled exclusively by this class' destructor.
-  MaybeLockFreeTaskRunner* get() const { return task_runner_; }
+#ifndef PR_MTE_TCF_SYNC
+#define PR_MTE_TCF_SYNC (1UL << 1)
+#endif

-  // TaskRunner implementation.
-  // These methods just proxy to the underlying task_runner_.
-  void PostTask(std::function<void()>) override;
-  void PostDelayedTask(std::function<void()>, uint32_t delay_ms) override;
-  void AddFileDescriptorWatch(PlatformHandle, std::function<void()>) override;
-  void RemoveFileDescriptorWatch(PlatformHandle) override;
-  bool RunsTasksOnCurrentThread() const override;
+#endif  // OS_LINUX | OS_ANDROID

- private:
-  explicit ThreadTaskRunner(const std::string& name);
-  void RunTaskThread(std::function<void(MaybeLockFreeTaskRunner*)> initializer);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <windows.h>

-  std::thread thread_;
-  std::string name_;
-  MaybeLockFreeTaskRunner* task_runner_ = nullptr;
-};
+#include <io.h>
+#include <malloc.h>  // For _aligned_malloc().
+#endif

-}  // namespace base
-}  // namespace perfetto
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <dlfcn.h>
+#include <malloc.h>

-#endif  // INCLUDE_PERFETTO_EXT_BASE_THREAD_TASK_RUNNER_H_
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+#ifdef M_PURGE
+#define PERFETTO_M_PURGE M_PURGE
+#else
+// Only available in in-tree builds and on newer SDKs.
+#define PERFETTO_M_PURGE -101
+#endif  // M_PURGE

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+#ifdef M_PURGE_ALL
+#define PERFETTO_M_PURGE_ALL M_PURGE_ALL
+#else
+// Only available in in-tree builds and on newer SDKs.
+#define PERFETTO_M_PURGE_ALL -104
+#endif  // M_PURGE

-// gen_amalgamated expanded: #include "perfetto/ext/base/thread_task_runner.h"
+namespace {
+extern "C" {
+using MalloptType = int (*)(int, int);
+}
+}  // namespace
+#endif  // OS_ANDROID

-#include <condition_variable>
-#include <functional>
-#include <mutex>
-#include <thread>
+namespace perfetto {
+namespace base {

-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/lock_free_task_runner.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/thread_utils.h"
+namespace internal {

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+std::atomic<uint32_t> g_cached_page_size{0};
+
+uint32_t GetSysPageSizeSlowpath() {
+  uint32_t page_size = 0;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#include <sys/prctl.h>
+  const int page_size_int = getpagesize();
+  // If sysconf() fails for obscure reasons (e.g. SELinux denial) assume the
+  // page size is 4KB. This is to avoid regressing subtle SDK usages, as old
+  // versions of this code had a static constant baked in.
+  page_size = static_cast<uint32_t>(page_size_int > 0 ? page_size_int : 4096);
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+  page_size = static_cast<uint32_t>(vm_page_size);
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD)
+  page_size = static_cast<uint32_t>(sysconf(_SC_PAGESIZE));
+#else
+  page_size = 4096;
 #endif

-namespace perfetto {
-namespace base {
-
-ThreadTaskRunner::ThreadTaskRunner(ThreadTaskRunner&& other) noexcept
-    : thread_(std::move(other.thread_)), task_runner_(other.task_runner_) {
-  other.task_runner_ = nullptr;
-}
+  PERFETTO_CHECK(page_size > 0 && page_size % 4096 == 0);

-ThreadTaskRunner& ThreadTaskRunner::operator=(ThreadTaskRunner&& other) {
-  this->~ThreadTaskRunner();
-  new (this) ThreadTaskRunner(std::move(other));
-  return *this;
+  // Races here are fine because any thread will write the same value.
+  g_cached_page_size.store(page_size, std::memory_order_relaxed);
+  return page_size;
 }

-ThreadTaskRunner::~ThreadTaskRunner() {
-  if (task_runner_) {
-    task_runner_->Quit();
+}  // namespace internal

-    PERFETTO_DCHECK(thread_.joinable());
+void MaybeReleaseAllocatorMemToOS() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  // mallopt() on Android requires SDK level 26. Many targets and embedders
+  // still depend on a lower SDK level. Given mallopt() is a quite simple API,
+  // use reflection to do this rather than bumping the SDK level for all
+  // embedders. This keeps the behavior of standalone builds aligned with
+  // in-tree builds.
+  static MalloptType mallopt_fn =
+      reinterpret_cast<MalloptType>(dlsym(RTLD_DEFAULT, "mallopt"));
+  if (!mallopt_fn)
+    return;
+  if (mallopt_fn(PERFETTO_M_PURGE_ALL, 0) == 0) {
+    mallopt_fn(PERFETTO_M_PURGE, 0);
   }
-  if (thread_.joinable())
-    thread_.join();
+#endif
 }

-ThreadTaskRunner::ThreadTaskRunner(const std::string& name) : name_(name) {
-  std::mutex init_lock;
-  std::condition_variable init_cv;
-
-  std::function<void(MaybeLockFreeTaskRunner*)> initializer =
-      [this, &init_lock, &init_cv](MaybeLockFreeTaskRunner* task_runner) {
-        std::lock_guard<std::mutex> lock(init_lock);
-        task_runner_ = task_runner;
-        // Notify while still holding the lock, as init_cv ceases to exist as
-        // soon as the main thread observes a non-null task_runner_, and it can
-        // wake up spuriously (i.e. before the notify if we had unlocked before
-        // notifying).
-        init_cv.notify_one();
-      };
-
-  thread_ = std::thread(&ThreadTaskRunner::RunTaskThread, this,
-                        std::move(initializer));
+uid_t GetCurrentUserId() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+  return geteuid();
+#else
+  // TODO(primiano): On Windows we could hash the current user SID and derive a
+  // numeric user id [1]. It is not clear whether we need that. Right now that
+  // would not bring any benefit. Returning 0 unil we can prove we need it.
+  // [1]:https://android-review.googlesource.com/c/platform/external/perfetto/+/1513879/25/src/base/utils.cc
+  return 0;
+#endif
+}

-  std::unique_lock<std::mutex> lock(init_lock);
-  init_cv.wait(lock, [this] { return !!task_runner_; });
+void SetEnv(const std::string& key, const std::string& value) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  PERFETTO_CHECK(::_putenv_s(key.c_str(), value.c_str()) == 0);
+#else
+  PERFETTO_CHECK(::setenv(key.c_str(), value.c_str(), /*overwrite=*/true) == 0);
+#endif
 }

-void ThreadTaskRunner::RunTaskThread(
-    std::function<void(MaybeLockFreeTaskRunner*)> initializer) {
-  if (!name_.empty()) {
-    base::MaybeSetThreadName(name_);
-  }
+void UnsetEnv(const std::string& key) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  PERFETTO_CHECK(::_putenv_s(key.c_str(), "") == 0);
+#else
+  PERFETTO_CHECK(::unsetenv(key.c_str()) == 0);
+#endif
+}

-  MaybeLockFreeTaskRunner task_runner;
-  task_runner.PostTask(std::bind(std::move(initializer), &task_runner));
-  task_runner.Run();
+bool IsTty(int fd) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  return ::_isatty(fd) != 0;
+#else
+  return ::isatty(fd) != 0;
+#endif
 }

-void ThreadTaskRunner::PostTaskAndWaitForTesting(std::function<void()> fn) {
-  std::mutex mutex;
-  std::condition_variable cv;
+bool IsTty(FILE* stream) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  return IsTty(::_fileno(stream));
+#else
+  return IsTty(::fileno(stream));
+#endif
+}

-  std::unique_lock<std::mutex> lock(mutex);
-  bool done = false;
-  task_runner_->PostTask([&mutex, &cv, &done, &fn] {
-    fn();
+void Daemonize(std::function<int(pid_t)> parent_cb) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
+    (PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) &&  \
+     !PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE_TVOS))
+  Pipe pipe = Pipe::Create(Pipe::kBothBlock);
+  pid_t pid;
+  switch (pid = fork()) {
+    case -1:
+      PERFETTO_FATAL("fork");
+    case 0: {
+      PERFETTO_CHECK(setsid() != -1);
+      base::ignore_result(chdir("/"));
+      base::ScopedFile null = base::OpenFile("/dev/null", O_RDONLY);
+      PERFETTO_CHECK(null);
+      PERFETTO_CHECK(dup2(*null, STDIN_FILENO) != -1);
+      PERFETTO_CHECK(dup2(*null, STDOUT_FILENO) != -1);
+      PERFETTO_CHECK(dup2(*null, STDERR_FILENO) != -1);
+      // Do not accidentally close stdin/stdout/stderr.
+      if (*null <= 2)
+        null.release();
+      WriteAll(*pipe.wr, "1", 1);
+      break;
+    }
+    default: {
+      // Wait for the child process to have reached the setsid() call. This is
+      // to avoid that 'adb shell perfetto -D' destroys the terminal (hence
+      // sending a SIGHUP to the child) before the child has detached from the
+      // terminal (see b/238644870).

-    std::lock_guard<std::mutex> inner_lock(mutex);
-    done = true;
-    cv.notify_one();
-  });
-  cv.wait(lock, [&done] { return done; });
+      // This is to unblock the read() below (with EOF, which will fail the
+      // CHECK) in the unlikely case of the child crashing before WriteAll("1").
+      pipe.wr.reset();
+      char one = '\0';
+      PERFETTO_CHECK(Read(*pipe.rd, &one, sizeof(one)) == 1 && one == '1');
+      int err = parent_cb(pid);
+      exit(err);
+    }
+  }
+#else
+  // Avoid -Wunreachable warnings.
+  if (reinterpret_cast<intptr_t>(&Daemonize) != 16)
+    PERFETTO_FATAL("--background is only supported on Linux/Android/Mac");
+  ignore_result(parent_cb);
+#endif  // OS_WIN
 }

-uint64_t ThreadTaskRunner::GetThreadCPUTimeNsForTesting() {
-  uint64_t thread_time_ns = 0;
-  PostTaskAndWaitForTesting([&thread_time_ns] {
-    thread_time_ns = static_cast<uint64_t>(base::GetThreadCPUTimeNs().count());
-  });
-  return thread_time_ns;
-}
-PlatformThreadId ThreadTaskRunner::GetThreadIdForTesting() {
-  PlatformThreadId thread_id = static_cast<PlatformThreadId>(-1);
-  PostTaskAndWaitForTesting([&thread_id] { thread_id = GetThreadId(); });
-  return thread_id;
+std::string GetCurExecutablePath() {
+  std::string self_path;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+  char buf[PATH_MAX];
+  ssize_t size = readlink("/proc/self/exe", buf, sizeof(buf));
+  PERFETTO_CHECK(size != -1);
+  // readlink does not null terminate.
+  self_path = std::string(buf, static_cast<size_t>(size));
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+  uint32_t size = 0;
+  PERFETTO_CHECK(_NSGetExecutablePath(nullptr, &size));
+  self_path.resize(size);
+  PERFETTO_CHECK(_NSGetExecutablePath(&self_path[0], &size) == 0);
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  char buf[MAX_PATH];
+  auto len = ::GetModuleFileNameA(nullptr /*current*/, buf, sizeof(buf));
+  self_path = std::string(buf, len);
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD)
+  char buf[PATH_MAX];
+  int mib[4], ret;
+  size_t len = sizeof(buf);
+  mib[0] = CTL_KERN;
+  mib[1] = KERN_PROC;
+  mib[2] = KERN_PROC_PATHNAME;
+  mib[3] = -1;
+  ret = sysctl(mib, 4, buf, &len, NULL, 0);
+  PERFETTO_CHECK(ret == 0);
+  // This returns the full path; need to trim the executable
+  self_path = std::string(buf);
+#else
+  PERFETTO_FATAL(
+      "GetCurExecutableDir() not implemented on the current platform");
+#endif
+  return self_path;
 }

-void ThreadTaskRunner::PostTask(std::function<void()> task) {
-  task_runner_->PostTask(std::move(task));
+std::string GetCurExecutableDir() {
+  auto path = GetCurExecutablePath();
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // Paths in Windows can have both kinds of slashes (mingw vs msvc).
+  path = path.substr(0, path.find_last_of('\\'));
+#endif
+  path = path.substr(0, path.find_last_of('/'));
+  return path;
 }

-void ThreadTaskRunner::PostDelayedTask(std::function<void()> task,
-                                       uint32_t delay_ms) {
-  task_runner_->PostDelayedTask(std::move(task), delay_ms);
+void* AlignedAlloc(size_t alignment, size_t size) {
+  void* res = nullptr;
+  alignment = AlignUp<sizeof(void*)>(alignment);  // At least pointer size.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // Window's _aligned_malloc() has a nearly identically signature to Unix's
+  // aligned_alloc() but its arguments are obviously swapped.
+  res = _aligned_malloc(size, alignment);
+#else
+  // aligned_alloc() has been introduced in Android only in API 28.
+  // Also NaCl and Fuchsia seems to have only posix_memalign().
+  ignore_result(posix_memalign(&res, alignment, size));
+#endif
+  PERFETTO_CHECK(res);
+  return res;
 }

-void ThreadTaskRunner::AddFileDescriptorWatch(
-    PlatformHandle handle,
-    std::function<void()> watch_task) {
-  task_runner_->AddFileDescriptorWatch(handle, std::move(watch_task));
+void AlignedFree(void* ptr) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  _aligned_free(ptr);  // MSDN says it is fine to pass nullptr.
+#else
+  free(ptr);
+#endif
 }

-void ThreadTaskRunner::RemoveFileDescriptorWatch(PlatformHandle handle) {
-  task_runner_->RemoveFileDescriptorWatch(handle);
+bool IsSyncMemoryTaggingEnabled() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  // Compute only once per lifetime of the process.
+  static bool cached_value = [] {
+    const int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+    if (res < 0)
+      return false;
+    const uint32_t actl = static_cast<uint32_t>(res);
+    return (actl & PR_TAGGED_ADDR_ENABLE) && (actl & PR_MTE_TCF_SYNC);
+  }();
+  return cached_value;
+#else
+  return false;
+#endif
 }

-bool ThreadTaskRunner::RunsTasksOnCurrentThread() const {
-  return task_runner_->RunsTasksOnCurrentThread();
+std::string HexDump(const void* data_void, size_t len, size_t bytes_per_line) {
+  const char* data = reinterpret_cast<const char*>(data_void);
+  std::string res;
+  static const size_t kPadding = bytes_per_line * 3 + 12;
+  std::unique_ptr<char[]> line(new char[bytes_per_line * 4 + 128]);
+  for (size_t i = 0; i < len; i += bytes_per_line) {
+    char* wptr = line.get();
+    wptr += base::SprintfTrunc(wptr, 19, "%08zX: ", i);
+    for (size_t j = i; j < i + bytes_per_line && j < len; j++) {
+      wptr += base::SprintfTrunc(wptr, 4, "%02X ",
+                                 static_cast<unsigned>(data[j]) & 0xFF);
+    }
+    for (size_t j = static_cast<size_t>(wptr - line.get()); j < kPadding; ++j)
+      *(wptr++) = ' ';
+    for (size_t j = i; j < i + bytes_per_line && j < len; j++) {
+      char c = data[j];
+      *(wptr++) = (c >= 32 && c < 127) ? c : '.';
+    }
+    *(wptr++) = '\n';
+    *(wptr++) = '\0';
+    res.append(line.get());
+  }
+  return res;
 }

 }  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/base/unix_task_runner.cc
+// gen_amalgamated begin source: src/base/uuid.cc
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12855,311 +13180,128 @@ bool ThreadTaskRunner::RunsTasksOnCurrentThread() const {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-
-// gen_amalgamated expanded: #include "perfetto/ext/base/platform.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/unix_task_runner.h"
-
-#include <errno.h>
-#include <stdlib.h>
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <windows.h>
-
-#include <synchapi.h>
-#else
-#include <unistd.h>
-#endif
+// gen_amalgamated expanded: #include "perfetto/ext/base/uuid.h"

-#include <algorithm>
-#include <limits>
+#include <random>

-// gen_amalgamated expanded: #include "perfetto/ext/base/watchdog.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"

 namespace perfetto {
 namespace base {
+namespace {

-UnixTaskRunner::UnixTaskRunner() {
-  AddFileDescriptorWatch(event_.fd(), [] {
-    // Not reached -- see PostFileDescriptorWatches().
-    PERFETTO_DFATAL("Should be unreachable.");
-  });
-}
+constexpr char kHexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+                            '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

-UnixTaskRunner::~UnixTaskRunner() = default;
+}  // namespace

-void UnixTaskRunner::WakeUp() {
-  event_.Notify();
-}
+// A globally unique 128-bit number.
+// In the early days of perfetto we were (sorta) respecting rfc4122. Later we
+// started replacing the LSB of the UUID with the statsd subscription ID in
+// other parts of the codebase (see perfetto_cmd.cc) for the convenience of
+// trace lookups, so rfc4122 made no sense as it just reduced entropy.
+Uuid Uuidv4() {
+  // Mix different sources of entropy to reduce the chances of collisions.
+  // Only using boot time is not enough. Under the assumption that most traces
+  // are started around the same time at boot, within a 1s window, the birthday
+  // paradox gives a chance of 90% collisions with 70k traces over a 1e9 space
+  // (Number of ns in a 1s window).
+  // We deliberately don't use /dev/urandom as that might block for
+  // unpredictable time if the system is idle (and is not portable).
+  // The UUID does NOT need to be cryptographically secure, but random enough
+  // to avoid collisions across a large number of devices.
+  uint64_t boot_ns = static_cast<uint64_t>(GetBootTimeNs().count());
+  uint64_t epoch_ns = static_cast<uint64_t>(GetWallTimeNs().count());

-void UnixTaskRunner::Run() {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  created_thread_id_.store(GetThreadId(), std::memory_order_relaxed);
-  {
-    std::lock_guard<base::MaybeRtMutex> lock(lock_);
-    quit_ = false;
-  }
-  for (;;) {
-    int poll_timeout_ms;
-    {
-      std::lock_guard<base::MaybeRtMutex> lock(lock_);
-      if (quit_)
-        return;
-      poll_timeout_ms = GetDelayMsToNextTaskLocked();
-      UpdateWatchTasksLocked();
-    }
+  // Use code ASLR as entropy source.
+  uint32_t code_ptr =
+      static_cast<uint32_t>(reinterpret_cast<uint64_t>(&Uuidv4) >> 12);

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    DWORD timeout =
-        poll_timeout_ms >= 0 ? static_cast<DWORD>(poll_timeout_ms) : INFINITE;
-    DWORD ret =
-        WaitForMultipleObjects(static_cast<DWORD>(poll_fds_.size()),
-                               &poll_fds_[0], /*bWaitAll=*/false, timeout);
-    // Unlike poll(2), WaitForMultipleObjects() returns only *one* handle in the
-    // set, even when >1 is signalled. In order to avoid starvation,
-    // PostFileDescriptorWatches() will WaitForSingleObject() each other handle
-    // to ensure fairness. |ret| here is passed just to avoid an extra
-    // WaitForSingleObject() for the one handle that WaitForMultipleObject()
-    // returned.
-    PostFileDescriptorWatches(ret);
-#else
-    platform::BeforeMaybeBlockingSyscall();
-    int ret = PERFETTO_EINTR(poll(
-        &poll_fds_[0], static_cast<nfds_t>(poll_fds_.size()), poll_timeout_ms));
-    platform::AfterMaybeBlockingSyscall();
-    PERFETTO_CHECK(ret >= 0);
-    PostFileDescriptorWatches(0 /*ignored*/);
-#endif
+  // Use stack ASLR as a further entropy source.
+  uint32_t stack_ptr =
+      static_cast<uint32_t>(reinterpret_cast<uint64_t>(&code_ptr) >> 12);

-    // To avoid starvation we always interleave all types of tasks -- immediate,
-    // delayed and file descriptor watches.
-    RunImmediateAndDelayedTask();
-  }
-}
+  uint32_t entropy[] = {static_cast<uint32_t>(boot_ns >> 32),
+                        static_cast<uint32_t>(boot_ns),
+                        static_cast<uint32_t>(epoch_ns >> 32),
+                        static_cast<uint32_t>(epoch_ns),
+                        code_ptr,
+                        stack_ptr};
+  std::seed_seq entropy_seq(entropy, entropy + ArraySize(entropy));

-void UnixTaskRunner::Quit() {
-  std::lock_guard<base::MaybeRtMutex> lock(lock_);
-  quit_ = true;
-  WakeUp();
+  auto words = std::array<uint32_t, 4>();
+  entropy_seq.generate(words.begin(), words.end());
+  uint64_t msb = static_cast<uint64_t>(words[0]) << 32u | words[1];
+  uint64_t lsb = static_cast<uint64_t>(words[2]) << 32u | words[3];
+  return Uuid(static_cast<int64_t>(lsb), static_cast<int64_t>(msb));
 }

-bool UnixTaskRunner::QuitCalled() {
-  std::lock_guard<base::MaybeRtMutex> lock(lock_);
-  return quit_;
+Uuid::Uuid() {}
+
+Uuid::Uuid(const std::string& s) {
+  PERFETTO_CHECK(s.size() == data_.size());
+  memcpy(data_.data(), s.data(), s.size());
 }

-bool UnixTaskRunner::IsIdleForTesting() {
-  std::lock_guard<base::MaybeRtMutex> lock(lock_);
-  return immediate_tasks_.empty();
+Uuid::Uuid(int64_t lsb, int64_t msb) {
+  set_lsb_msb(lsb, msb);
 }

-void UnixTaskRunner::AdvanceTimeForTesting(uint32_t ms) {
-  std::lock_guard<base::MaybeRtMutex> lock(lock_);
-  advanced_time_for_testing_ += TimeMillis(ms);
+std::string Uuid::ToString() const {
+  return std::string(reinterpret_cast<const char*>(data_.data()), data_.size());
 }

-void UnixTaskRunner::UpdateWatchTasksLocked() {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  if (!watch_tasks_changed_)
-    return;
-  watch_tasks_changed_ = false;
-#endif
-  poll_fds_.clear();
-  for (auto& it : watch_tasks_) {
-    PlatformHandle handle = it.first;
-    WatchTask& watch_task = it.second;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    if (!watch_task.pending)
-      poll_fds_.push_back(handle);
-#else
-    watch_task.poll_fd_index = poll_fds_.size();
-    poll_fds_.push_back({handle, POLLIN | POLLHUP, 0});
-#endif
-  }
-}
-
-void UnixTaskRunner::RunImmediateAndDelayedTask() {
-  // If locking overhead becomes an issue, add a separate work queue.
-  std::function<void()> immediate_task;
-  std::function<void()> delayed_task;
-  TimeMillis now = GetWallTimeMs();
-  {
-    std::lock_guard<base::MaybeRtMutex> lock(lock_);
-    if (!immediate_tasks_.empty()) {
-      immediate_task = std::move(immediate_tasks_.front());
-      immediate_tasks_.pop_front();
-    }
-    if (!delayed_tasks_.empty()) {
-      auto it = delayed_tasks_.begin();
-      if (now + advanced_time_for_testing_ >= it->first) {
-        delayed_task = std::move(it->second);
-        delayed_tasks_.erase(it);
-      }
-    }
-  }
-
-  errno = 0;
-  if (immediate_task)
-    RunTaskWithWatchdogGuard(immediate_task);
-  errno = 0;
-  if (delayed_task)
-    RunTaskWithWatchdogGuard(delayed_task);
-}
-
-void UnixTaskRunner::PostFileDescriptorWatches(uint64_t windows_wait_result) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  for (size_t i = 0; i < poll_fds_.size(); i++) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    const PlatformHandle handle = poll_fds_[i];
-    // |windows_wait_result| is the result of WaitForMultipleObjects() call. If
-    // one of the objects was signalled, it will have a value between
-    // [0, poll_fds_.size()].
-    if (i != windows_wait_result &&
-        WaitForSingleObject(handle, 0) != WAIT_OBJECT_0) {
-      continue;
-    }
-#else
-    base::ignore_result(windows_wait_result);
-    const PlatformHandle handle = poll_fds_[i].fd;
-    if (!(poll_fds_[i].revents & (POLLIN | POLLHUP)))
-      continue;
-    poll_fds_[i].revents = 0;
-#endif
-
-    // The wake-up event is handled inline to avoid an infinite recursion of
-    // posted tasks.
-    if (handle == event_.fd()) {
-      event_.Clear();
-      continue;
-    }
-
-    // Binding to |this| is safe since we are the only object executing the
-    // task.
-    PostTask(std::bind(&UnixTaskRunner::RunFileDescriptorWatch, this, handle));
-
-    // Flag the task as pending.
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    // On Windows this is done by marking the WatchTask entry as pending. This
-    // is more expensive than Linux as requires rebuilding the |poll_fds_|
-    // vector on each call. There doesn't seem to be a good alternative though.
-    auto it = watch_tasks_.find(handle);
-    PERFETTO_CHECK(it != watch_tasks_.end());
-    PERFETTO_DCHECK(!it->second.pending);
-    it->second.pending = true;
-#else
-    // On UNIX systems instead, we just make the fd negative while its task is
-    // pending. This makes poll(2) ignore the fd.
-    PERFETTO_DCHECK(poll_fds_[i].fd >= 0);
-    poll_fds_[i].fd = -poll_fds_[i].fd;
-#endif
-  }
-}
-
-void UnixTaskRunner::RunFileDescriptorWatch(PlatformHandle fd) {
-  std::function<void()> task;
-  {
-    std::lock_guard<base::MaybeRtMutex> lock(lock_);
-    auto it = watch_tasks_.find(fd);
-    if (it == watch_tasks_.end())
-      return;
-    WatchTask& watch_task = it->second;
-
-    // Make poll(2) pay attention to the fd again. Since another thread may have
-    // updated this watch we need to refresh the set first.
-    UpdateWatchTasksLocked();
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    // On Windows we manually track the presence of outstanding tasks for the
-    // watch. The UpdateWatchTasksLocked() in the Run() loop will re-add the
-    // task to the |poll_fds_| vector.
-    PERFETTO_DCHECK(watch_task.pending);
-    watch_task.pending = false;
-#else
-    size_t fd_index = watch_task.poll_fd_index;
-    PERFETTO_DCHECK(fd_index < poll_fds_.size());
-    PERFETTO_DCHECK(::abs(poll_fds_[fd_index].fd) == fd);
-    poll_fds_[fd_index].fd = fd;
-#endif
-    task = watch_task.callback;
-  }
-  errno = 0;
-  RunTaskWithWatchdogGuard(task);
-}
-
-int UnixTaskRunner::GetDelayMsToNextTaskLocked() const {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  if (!immediate_tasks_.empty())
-    return 0;
-  if (!delayed_tasks_.empty()) {
-    TimeMillis diff = delayed_tasks_.begin()->first - GetWallTimeMs() -
-                      advanced_time_for_testing_;
-    return std::max(0, static_cast<int>(diff.count()));
+std::string Uuid::ToPrettyString() const {
+  std::string s(data_.size() * 2 + 4, '-');
+  // Format is 123e4567-e89b-12d3-a456-426655443322.
+  size_t j = 0;
+  for (size_t i = 0; i < data_.size(); ++i) {
+    if (i == 4 || i == 6 || i == 8 || i == 10)
+      j++;
+    s[2 * i + j] = kHexmap[(data_[data_.size() - i - 1] & 0xf0) >> 4];
+    s[2 * i + 1 + j] = kHexmap[(data_[data_.size() - i - 1] & 0x0f)];
   }
-  return -1;
+  return s;
 }

-void UnixTaskRunner::PostTask(std::function<void()> task) {
-  bool was_empty;
-  {
-    std::lock_guard<base::MaybeRtMutex> lock(lock_);
-    was_empty = immediate_tasks_.empty();
-    immediate_tasks_.push_back(std::move(task));
-  }
-  if (was_empty)
-    WakeUp();
-}
+}  // namespace base
+}  // namespace perfetto
+// gen_amalgamated begin source: src/base/virtual_destructors.cc
+/*
+ * 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.
+ */

-void UnixTaskRunner::PostDelayedTask(std::function<void()> task,
-                                     uint32_t delay_ms) {
-  TimeMillis runtime = GetWallTimeMs() + TimeMillis(delay_ms);
-  {
-    std::lock_guard<base::MaybeRtMutex> lock(lock_);
-    delayed_tasks_.insert(
-        std::make_pair(runtime + advanced_time_for_testing_, std::move(task)));
-  }
-  WakeUp();
-}
+// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"

-void UnixTaskRunner::AddFileDescriptorWatch(PlatformHandle fd,
-                                            std::function<void()> task) {
-  PERFETTO_DCHECK(PlatformHandleChecker::IsValid(fd));
-  {
-    std::lock_guard<base::MaybeRtMutex> lock(lock_);
-    PERFETTO_DCHECK(!watch_tasks_.count(fd));
-    WatchTask& watch_task = watch_tasks_[fd];
-    watch_task.callback = std::move(task);
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    watch_task.pending = false;
-#else
-    watch_task.poll_fd_index = SIZE_MAX;
-#endif
-    watch_tasks_changed_ = true;
-  }
-  WakeUp();
-}
+// This translation unit contains the definitions for the destructor of pure
+// virtual interfaces for the current build target. The alternative would be
+// introducing a one-liner .cc file for each pure virtual interface, which is
+// overkill. This is for compliance with -Wweak-vtables.

-void UnixTaskRunner::RemoveFileDescriptorWatch(PlatformHandle fd) {
-  PERFETTO_DCHECK(PlatformHandleChecker::IsValid(fd));
-  {
-    std::lock_guard<base::MaybeRtMutex> lock(lock_);
-    PERFETTO_DCHECK(watch_tasks_.count(fd));
-    watch_tasks_.erase(fd);
-    watch_tasks_changed_ = true;
-  }
-  // No need to schedule a wake-up for this.
-}
+namespace perfetto {
+namespace base {

-bool UnixTaskRunner::RunsTasksOnCurrentThread() const {
-  return GetThreadId() == created_thread_id_.load(std::memory_order_relaxed);
-}
+TaskRunner::~TaskRunner() = default;

 }  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/base/subprocess.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/subprocess.h
+// gen_amalgamated begin source: src/base/waitable_event.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/waitable_event.h
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13174,278 +13316,46 @@ bool UnixTaskRunner::RunsTasksOnCurrentThread() const {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_
+#ifndef INCLUDE_PERFETTO_EXT_BASE_WAITABLE_EVENT_H_
+#define INCLUDE_PERFETTO_EXT_BASE_WAITABLE_EVENT_H_
+
+// gen_amalgamated expanded: #include "perfetto/base/thread_annotations.h"
+
+#include <stdint.h>

 #include <condition_variable>
-#include <functional>
-#include <initializer_list>
-#include <memory>
 #include <mutex>
-#include <optional>
-#include <string>
-#include <thread>
-#include <vector>
-
-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/base/platform_handle.h"
-// gen_amalgamated expanded: #include "perfetto/base/proc_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/event_fd.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/pipe.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"

 namespace perfetto {
 namespace base {

-// Handles creation and lifecycle management of subprocesses, taking care of
-// all subtleties involved in handling processes on UNIX.
-// This class allows to deal with macro two use-cases:
-// 1) fork() + exec() equivalent: for spawning a brand new process image.
-//    This happens when |args.exec_cmd| is not empty.
-//    This is safe to use even in a multi-threaded environment.
-// 2) fork(): for spawning a process and running a function.
-//    This happens when |args.posix_entrypoint_for_testing| is not empty.
-//    This is intended only for tests as it is extremely subtle.
-//    This mode must be used with extreme care. Before the entrypoint is
-//    invoked all file descriptors other than stdin/out/err and the ones
-//    specified in |args.preserve_fds| will be closed, to avoid each process
-//    retaining a dupe of other subprocesses pipes. This however means that
-//    any non trivial calls (including logging) must be avoided as they might
-//    refer to FDs that are now closed. The entrypoint should really be used
-//    just to signal a pipe or similar for synchronizing sequencing in tests.
-
-//
-// This class allows to control stdin/out/err pipe redirection and takes care
-// of keeping all the pipes pumped (stdin) / drained (stdout/err), in a similar
-// fashion of python's subprocess.Communicate()
-// stdin: is always piped and closed once the |args.input| buffer is written.
-// stdout/err can be either:
-//   - dup()ed onto the parent process stdout/err.
-//   - redirected onto /dev/null.
-//   - piped onto a buffer (see output() method). There is only one output
-//     buffer in total. If both stdout and stderr are set to kBuffer mode, they
-//     will be merged onto the same. There doesn't seem any use case where they
-//     are needed distinctly.
-//
-// Some caveats worth mentioning:
-// - It always waitpid()s, to avoid leaving zombies around. If the process is
-//   not terminated by the time the destructor is reached, the dtor will
-//   send a SIGKILL and wait for the termination.
-// - After fork()-ing it will close all file descriptors, preserving only
-//   stdin/out/err and the fds listed in |args.preserve_fds|.
-// - On Linux/Android, the child process will be SIGKILL-ed if the calling
-//   thread exists, even if the Subprocess is std::move()-d onto another thread.
-//   This happens by virtue PR_SET_PDEATHSIG, which is used to avoid that
-//   child processes are leaked in the case of a crash of the parent (frequent
-//   in tests). However, the child process might still be leaked if execing
-//   a setuid/setgid binary (see man 2 prctl).
-//
-// Usage:
-// base::Subprocess p({"/bin/cat", "-"});
-// (or equivalently:
-//     base::Subprocess p;
-//     p.args.exec_cmd.push_back("/bin/cat");
-//     p.args.exec_cmd.push_back("-");
-//  )
-// p.args.stdout_mode = base::Subprocess::kBuffer;
-// p.args.stderr_mode = base::Subprocess::kInherit;
-// p.args.input = "stdin contents";
-// p.Call();
-// (or equivalently:
-//     p.Start();
-//     p.Wait();
-// )
-// EXPECT_EQ(p.status(), base::Subprocess::kTerminated);
-// EXPECT_EQ(p.returncode(), 0);
-class Subprocess {
+// A waitable event for cross-thread synchronization.
+// All methods on this class can be called from any thread.
+class WaitableEvent {
  public:
-  enum Status {
-    kNotStarted = 0,  // Before calling Start() or Call().
-    kRunning,         // After calling Start(), before Wait().
-    kTerminated,      // The subprocess terminated, either successfully or not.
-                      // This includes crashes or other signals on UNIX.
-  };
-
-  enum class OutputMode {
-    kInherit = 0,  // Inherit's the caller process stdout/stderr.
-    kDevNull,      // dup() onto /dev/null.
-    kBuffer,       // dup() onto a pipe and move it into the output() buffer.
-    kFd,           // dup() onto the passed args.fd.
-  };
-
-  enum class InputMode {
-    kBuffer = 0,  // dup() onto a pipe and write args.input on it.
-    kDevNull,     // dup() onto /dev/null.
-  };
-
-  // Input arguments for configuring the subprocess behavior.
-  struct Args {
-    Args(std::initializer_list<std::string> _cmd = {}) : exec_cmd(_cmd) {}
-    Args(Args&&) noexcept;
-    Args& operator=(Args&&);
-    // If non-empty this will cause an exec() when Start()/Call() are called.
-    std::vector<std::string> exec_cmd;
-
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    // If non-empty, it changes the argv[0] argument passed to exec. If
-    // unset, argv[0] == exec_cmd[0]. This is to handle cases like:
-    // exec_cmd = {"/proc/self/exec"}, argv0: "my_custom_test_override".
-    std::string posix_argv0_override_for_testing;
-
-    // If non-empty this will be invoked on the fork()-ed child process, after
-    // stdin/out/err has been redirected and all other file descriptor are
-    // closed. It is valid to specify both |exec_cmd| AND
-    // |posix_entrypoint_for_testing|. In this case the latter will be invoked
-    // just before the exec() call, but after having closed all fds % stdin/o/e.
-    // This is for synchronization barriers in tests.
-    std::function<void()> posix_entrypoint_for_testing;
-
-    // When set, will will move the process to the given process group. If set
-    // and zero, it will create a new process group. Effectively this calls
-    // setpgid(0 /*self_pid*/, posix_proc_group_id).
-    // This can be used to avoid that subprocesses receive CTRL-C from the
-    // terminal, while still living in the same session.
-    std::optional<pid_t> posix_proc_group_id{};
-#endif
-
-    // If non-empty, replaces the environment passed to exec().
-    std::vector<std::string> env;
-
-    // The file descriptors in this list will not be closed.
-    std::vector<int> preserve_fds;
-
-    // The data to push in the child process stdin, if input_mode ==
-    // InputMode::kBuffer.
-    std::string input;
-
-    InputMode stdin_mode = InputMode::kBuffer;
-    OutputMode stdout_mode = OutputMode::kInherit;
-    OutputMode stderr_mode = OutputMode::kInherit;
-
-    base::ScopedPlatformHandle out_fd;
-
-    // Returns " ".join(exec_cmd), quoting arguments.
-    std::string GetCmdString() const;
-  };
-
-  struct ResourceUsage {
-    uint32_t cpu_utime_ms = 0;
-    uint32_t cpu_stime_ms = 0;
-    uint32_t max_rss_kb = 0;
-    uint32_t min_page_faults = 0;
-    uint32_t maj_page_faults = 0;
-    uint32_t vol_ctx_switch = 0;
-    uint32_t invol_ctx_switch = 0;
-
-    uint32_t cpu_time_ms() const { return cpu_utime_ms + cpu_stime_ms; }
-  };
-
-  explicit Subprocess(std::initializer_list<std::string> exec_cmd = {});
-  Subprocess(Subprocess&&) noexcept;
-  Subprocess& operator=(Subprocess&&);
-  ~Subprocess();  // It will KillAndWaitForTermination() if still alive.
-
-  // Starts the subprocess but doesn't wait for its termination. The caller
-  // is expected to either call Wait() or Poll() after this call.
-  void Start();
-
-  // Wait for process termination. Can be called more than once.
-  // Args:
-  //   |timeout_ms| = 0: wait indefinitely.
-  //   |timeout_ms| > 0: wait for at most |timeout_ms|.
-  // Returns:
-  //  True: The process terminated. See status() and returncode().
-  //  False: Timeout reached, the process is still running. In this case the
-  //         process will be left in the kRunning state.
-  bool Wait(int timeout_ms = 0);
-
-  // Equivalent of Start() + Wait();
-  // Returns true if the process exited cleanly with return code 0. False in
-  // any othe case.
-  bool Call(int timeout_ms = 0);
-
-  Status Poll();
-
-  // Kills the process (SIGKILL if not specified) but doesn't wait for its
-  // termination.
-  void Kill(int sig_num = 0);
-
-  // Sends a signal (SIGKILL if not specified) and wait for process termination.
-  void KillAndWaitForTermination(int sig_num = 0);
-
-  PlatformProcessId pid() const { return s_->pid; }
-
-  // The accessors below are updated only after a call to Poll(), Wait() or
-  // KillAndWaitForTermination().
-  // In most cases you want to call Poll() rather than these accessors.
-
-  Status status() const { return s_->status; }
-  int returncode() const { return s_->returncode; }
-  bool timed_out() const { return s_->timed_out; }
-
-  // This contains both stdout and stderr (if the corresponding _mode ==
-  // OutputMode::kBuffer). It's non-const so the caller can std::move() it.
-  std::string& output() { return s_->output; }
-  const std::string& output() const { return s_->output; }
+  WaitableEvent();
+  ~WaitableEvent();
+  WaitableEvent(const WaitableEvent&) = delete;
+  WaitableEvent operator=(const WaitableEvent&) = delete;

-  const ResourceUsage& posix_rusage() const { return *s_->rusage; }
+  // Synchronously block until the event is notified `notification` times.
+  void Wait(uint64_t notifications = 1);

-  Args args;
+  // Signal the event, waking up blocked waiters.
+  void Notify();

  private:
-  // The signal/exit code used when killing the process in case of a timeout.
-  static const int kTimeoutSignal;
-
-  Subprocess(const Subprocess&) = delete;
-  Subprocess& operator=(const Subprocess&) = delete;
-
-  // This is to deal robustly with the move operators, without having to
-  // manually maintain member-wise move instructions.
-  struct MovableState {
-    base::Pipe stdin_pipe;
-    base::Pipe stdouterr_pipe;
-    PlatformProcessId pid;
-    Status status = kNotStarted;
-    int returncode = -1;
-    std::string output;  // Stdin+stderr. Only when OutputMode::kBuffer.
-    std::unique_ptr<ResourceUsage> rusage{new ResourceUsage()};
-    bool timed_out = false;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    std::thread stdouterr_thread;
-    std::thread stdin_thread;
-    ScopedPlatformHandle win_proc_handle;
-    ScopedPlatformHandle win_thread_handle;
-
-    base::EventFd stdouterr_done_event;
-    std::mutex mutex;  // Protects locked_outerr_buf and the two pipes.
-    std::string locked_outerr_buf;
-#else
-    base::Pipe exit_status_pipe;
-    size_t input_written = 0;
-    std::thread waitpid_thread;
-#endif
-  };
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  static void StdinThread(MovableState*, const std::string& input);
-  static void StdoutErrThread(MovableState*);
-#else
-  void TryPushStdin();
-  void TryReadStdoutAndErr();
-  void TryReadExitStatus();
-  bool PollInternal(int poll_timeout_ms);
-#endif
-
-  std::unique_ptr<MovableState> s_;
+  std::mutex mutex_;
+  std::condition_variable event_ PERFETTO_GUARDED_BY(mutex_);
+  uint64_t notifications_ PERFETTO_GUARDED_BY(mutex_) = 0;
 };

 }  // namespace base
 }  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_
+#endif  // INCLUDE_PERFETTO_EXT_BASE_WAITABLE_EVENT_H_
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13460,73 +13370,138 @@ class Subprocess {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/subprocess.h"
-
-#include <tuple>
-
-// This file contains only the common bits (ctors / dtors / move operators).
-// The rest lives in subprocess_posix.cc and subprocess_windows.cc.
+// gen_amalgamated expanded: #include "perfetto/ext/base/waitable_event.h"

 namespace perfetto {
 namespace base {

-Subprocess::Args::Args(Args&&) noexcept = default;
-Subprocess::Args& Subprocess::Args::operator=(Args&&) = default;
+WaitableEvent::WaitableEvent() = default;
+WaitableEvent::~WaitableEvent() = default;

-Subprocess::Subprocess(std::initializer_list<std::string> a)
-    : args(a), s_(new MovableState()) {}
-
-Subprocess::Subprocess(Subprocess&& other) noexcept {
-  static_assert(sizeof(Subprocess) ==
-                    sizeof(std::tuple<std::unique_ptr<MovableState>, Args>),
-                "base::Subprocess' move ctor needs updating");
-  s_ = std::move(other.s_);
-  args = std::move(other.args);
-
-  // Reset the state of the moved-from object.
-  other.s_.reset(new MovableState());
-  other.~Subprocess();
-  new (&other) Subprocess();
+void WaitableEvent::Wait(uint64_t notifications)
+    PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
+  // 'std::unique_lock' lock doesn't work well with thread annotations
+  // (see https://github.com/llvm/llvm-project/issues/63239),
+  // so we suppress thread safety static analysis for this method.
+  std::unique_lock<std::mutex> lock(mutex_);
+  return event_.wait(lock, [&]() PERFETTO_EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
+    return notifications_ >= notifications;
+  });
 }

-Subprocess& Subprocess::operator=(Subprocess&& other) {
-  this->~Subprocess();
-  new (this) Subprocess(std::move(other));
-  return *this;
+void WaitableEvent::Notify() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  ++notifications_;
+  event_.notify_all();
 }

-Subprocess::~Subprocess() {
-  if (s_->status == kRunning)
-    KillAndWaitForTermination();
-}
+}  // namespace base
+}  // namespace perfetto
+// gen_amalgamated begin source: src/base/watchdog_posix.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/watchdog.h
+// gen_amalgamated begin header: include/perfetto/ext/base/flags.h
+/*
+ * Copyright (C) 2025 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.
+ */

-bool Subprocess::Call(int timeout_ms) {
-  PERFETTO_CHECK(s_->status == kNotStarted);
-  Start();
+#ifndef INCLUDE_PERFETTO_EXT_BASE_FLAGS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_FLAGS_H_

-  if (!Wait(timeout_ms)) {
-    s_->timed_out = true;
-    KillAndWaitForTermination(kTimeoutSignal);
-  }
-  PERFETTO_DCHECK(s_->status != kRunning);
-  return s_->status == kTerminated && s_->returncode == 0;
-}
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-std::string Subprocess::Args::GetCmdString() const {
-  std::string str;
-  for (size_t i = 0; i < exec_cmd.size(); i++) {
-    str += i > 0 ? " \"" : "";
-    str += exec_cmd[i];
-    str += i > 0 ? "\"" : "";
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) && \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+// Android: aconfig generates PERFETTO_FLAGS_* macros in perfetto_flags.h
+#include <perfetto_flags.h>
+#else
+// Non-Android: Define fallback PERFETTO_FLAGS_* macros
+// These match the pattern from Android's aconfig codegen
+#define PERFETTO_FLAGS(FLAG) PERFETTO_FLAGS_##FLAG
+
+#define PERFETTO_FLAGS_TEST_READ_ONLY_FLAG false
+#define PERFETTO_FLAGS_USE_LOCKFREE_TASKRUNNER \
+  PERFETTO_BUILDFLAG(PERFETTO_ENABLE_LOCKFREE_TASKRUNNER)
+#define PERFETTO_FLAGS_BUFFER_CLONE_PRESERVE_READ_ITER true
+#define PERFETTO_FLAGS_USE_UNIX_SOCKET_INOTIFY \
+  PERFETTO_BUILDFLAG(PERFETTO_ENABLE_SOCK_INOTIFY)
+#define PERFETTO_FLAGS_TRIGGER_PERFETTO_ON_TRACED_PROBES_DISCONNECT false
+#define PERFETTO_FLAGS_USE_ANON_RSS_IN_WATCHDOG true
+
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) && ...
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_FLAGS_H_
+// gen_amalgamated begin header: include/perfetto/ext/base/watchdog_noop.h
+/*
+ * 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.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_NOOP_H_
+#define INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_NOOP_H_
+
+#include <stdint.h>
+
+#include <functional>
+
+namespace perfetto {
+namespace base {
+
+class TaskRunner;
+
+enum class WatchdogCrashReason;  // Defined in watchdog.h.
+struct WatchdogCrashInfo;        // Defined in watchdog.h.
+
+class Watchdog {
+ public:
+  class Timer {
+   public:
+    // Define an empty dtor to avoid "unused variable" errors on the call site.
+    Timer() {}
+    Timer(const Timer&) {}
+    ~Timer() {}
+  };
+  static Watchdog* GetInstance() {
+    static Watchdog* watchdog = new Watchdog();
+    return watchdog;
   }
-  return str;
-}
+  Timer CreateFatalTimer(uint32_t /*ms*/, WatchdogCrashReason) {
+    return Timer();
+  }
+  void Start(TaskRunner* /*task_runner*/ = nullptr,
+             std::function<void(WatchdogCrashInfo)> /*handler*/ = {}) {}
+  void SetMemoryLimit(uint64_t /*bytes*/, uint32_t /*window_ms*/) {}
+  void SetCpuLimit(uint32_t /*percentage*/, uint32_t /*window_ms*/) {}
+};

 }  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/base/subprocess_posix.cc
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_NOOP_H_
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -13541,451 +13516,655 @@ std::string Subprocess::Args::GetCmdString() const {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/subprocess.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_H_
+#define INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_H_
+
+#include <functional>

 // gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/flags.h"

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+// The POSIX watchdog is only supported on Linux and Android in non-embedder
+// builds.
+#if PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG)
+// gen_amalgamated expanded: #include "perfetto/ext/base/watchdog_posix.h"
+#else
+// gen_amalgamated expanded: #include "perfetto/ext/base/watchdog_noop.h"
+#endif
+
+namespace perfetto {
+namespace base {
+
+// Used only to add more details to crash reporting.
+enum class WatchdogCrashReason {
+  kUnspecified = 0,
+  kCpuGuardrail = 1,
+  kMemGuardrail = 2,
+  kTaskRunnerHung = 3,
+  kTraceDidntStop = 4,
+};
+
+// Information about an imminent watchdog-triggered crash. Passed to the fatal
+// handler installed via Watchdog::Start() so embedders can record it (e.g. via
+// metatrace counters) before the process is force-killed.
+struct WatchdogCrashInfo {
+  int thread_id = 0;
+  WatchdogCrashReason reason = WatchdogCrashReason::kUnspecified;
+
+  // The time in CLOCK_BOOTTIME when the watchdog triggered.
+  int64_t alarm_time_ms = 0;
+
+  // The initial timeout passed to CreateFatalTimer.
+  int timeout_s = 0;
+
+  // Elapsed seconds, since the offending fatal timer was created, sampled
+  // from CLOCK_MONOTONIC, CLOCK_BOOTTIME and CLOCK_PROCESS_CPUTIME_ID
+  // respectively. These mirror the wdog_actual_{mono,boot,cpu} crash keys.
+  // They are 0 for cpu/memory guardrail crashes (no associated timer).
+  int actual_mono_s = 0;
+  int actual_boot_s = 0;
+  int actual_cpu_s = 0;
+};
+
+// Make the limits more relaxed on desktop, where multi-GB traces are likely.
+// Multi-GB traces can take bursts of cpu time to write into disk at the end of
+// the trace.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+constexpr uint32_t kWatchdogDefaultCpuLimit = 75;
+constexpr uint32_t kWatchdogDefaultCpuWindow = 5 * 60 * 1000;  // 5 minutes.
+#else
+constexpr uint32_t kWatchdogDefaultCpuLimit = 90;
+constexpr uint32_t kWatchdogDefaultCpuWindow = 10 * 60 * 1000;  // 10 minutes.
+#endif
+
+// The default memory margin we give to our processes. This is used as as a
+// constant to put on top of the trace buffers.
+constexpr uint64_t kWatchdogDefaultMemorySlack =
+    PERFETTO_FLAGS(USE_ANON_RSS_IN_WATCHDOG) ? 32 * 1024 * 1024   // 32 MiB
+                                             : 64 * 1024 * 1024;  // 64 MiB
+constexpr uint32_t kWatchdogDefaultMemoryWindow = 30 * 1000;      // 30 seconds.
+
+inline void RunTaskWithWatchdogGuard(const std::function<void()>& task) {
+  // The longest duration allowed for a single task within the TaskRunner.
+  // Exceeding this limit will trigger program termination.
+  constexpr int64_t kWatchdogMillis = 180000;  // 180s
+
+  Watchdog::Timer handle = base::Watchdog::GetInstance()->CreateFatalTimer(
+      kWatchdogMillis, WatchdogCrashReason::kTaskRunnerHung);
+  task();
+
+  // Suppress unused variable warnings in the client library amalgamated build.
+  (void)kWatchdogDefaultCpuLimit;
+  (void)kWatchdogDefaultCpuWindow;
+  (void)kWatchdogDefaultMemorySlack;
+  (void)kWatchdogDefaultMemoryWindow;
+}
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_H_
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/platform.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/watchdog.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG)

 #include <fcntl.h>
 #include <poll.h>
 #include <signal.h>
-#include <stdio.h>
-#include <sys/resource.h>
-#include <sys/types.h>
-#include <sys/wait.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/timerfd.h>
 #include <unistd.h>

 #include <algorithm>
+#include <cinttypes>
+#include <fstream>
+#include <optional>
 #include <thread>
-#include <tuple>
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#include <sys/prctl.h>
-#endif

+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/base/thread_utils.h"
 // gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/crash_keys.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/flags.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"

-// In MacOS this is not defined in any header.
-extern "C" char** environ;
-
 namespace perfetto {
 namespace base {

 namespace {

-struct ChildProcessArgs {
-  Subprocess::Args* create_args;
-  const char* exec_cmd = nullptr;
-  std::vector<char*> argv;
-  std::vector<char*> env;
-  int stdin_pipe_rd = -1;
-  int stdouterr_pipe_wr = -1;
-};
-
-// Don't add any dynamic allocation in this function. This will be invoked
-// under a fork(), potentially in a state where the allocator lock is held.
-void __attribute__((noreturn)) ChildProcess(ChildProcessArgs* args) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  // In no case we want a child process to outlive its parent process. This is
-  // relevant for tests, so that a test failure/crash doesn't leave child
-  // processes around that get reparented to init.
-  prctl(PR_SET_PDEATHSIG, SIGKILL);
-#endif
-
-  auto die = [args](const char* err) __attribute__((noreturn)) {
-    base::ignore_result(write(args->stdouterr_pipe_wr, err, strlen(err)));
-    base::ignore_result(write(args->stdouterr_pipe_wr, "\n", 1));
-    // From https://www.gnu.org/software/libc/manual/html_node/Exit-Status.html
-    // "In particular, the value 128 is used to indicate failure to execute
-    // another program in a subprocess. This convention is not universally
-    // obeyed, but it is a good idea to follow it in your programs."
-    _exit(128);
-  };
+constexpr uint32_t kDefaultPollingInterval = 30 * 1000;

-  if (args->create_args->posix_proc_group_id.has_value()) {
-    if (setpgid(0 /*self*/, args->create_args->posix_proc_group_id.value())) {
-      die("setpgid() failed");
-    }
-  }
+// Grace period given to a fatal handler (passed to Start()) to
+// crash the process on its own before the watchdog force-kills it.
+constexpr uint32_t kFatalHandlerGraceMs = 60 * 1000;

-  auto set_fd_close_on_exec = [&die](int fd, bool close_on_exec) {
-    int flags = fcntl(fd, F_GETFD, 0);
-    if (flags < 0)
-      die("fcntl(F_GETFD) failed");
-    flags = close_on_exec ? (flags | FD_CLOEXEC) : (flags & ~FD_CLOEXEC);
-    if (fcntl(fd, F_SETFD, flags) < 0)
-      die("fcntl(F_SETFD) failed");
-  };
+base::CrashKey g_crash_key_reason("wdog_reason");
+base::CrashKey g_crash_key_timeout("wdog_timeout");
+base::CrashKey g_crash_key_actual_mono("wdog_actual_mono");
+base::CrashKey g_crash_key_actual_boot("wdog_actual_boot");
+base::CrashKey g_crash_key_actual_cpu("wdog_actual_cpu");

-  if (getppid() == 1)
-    die("terminating because parent process died");
+bool IsMultipleOf(uint32_t number, uint32_t divisor) {
+  return number >= divisor && number % divisor == 0;
+}

-  switch (args->create_args->stdin_mode) {
-    case Subprocess::InputMode::kBuffer:
-      if (dup2(args->stdin_pipe_rd, STDIN_FILENO) == -1)
-        die("Failed to dup2(STDIN)");
-      close(args->stdin_pipe_rd);
-      break;
-    case Subprocess::InputMode::kDevNull:
-      if (dup2(open("/dev/null", O_RDONLY), STDIN_FILENO) == -1)
-        die("Failed to dup2(STDOUT)");
-      break;
+double MeanForArray(const uint64_t array[], size_t size) {
+  uint64_t total = 0;
+  for (size_t i = 0; i < size; i++) {
+    total += array[i];
   }
+  return static_cast<double>(total / size);
+}

-  switch (args->create_args->stdout_mode) {
-    case Subprocess::OutputMode::kInherit:
-      break;
-    case Subprocess::OutputMode::kDevNull: {
-      if (dup2(open("/dev/null", O_RDWR), STDOUT_FILENO) == -1)
-        die("Failed to dup2(STDOUT)");
-      break;
-    }
-    case Subprocess::OutputMode::kBuffer:
-      if (dup2(args->stdouterr_pipe_wr, STDOUT_FILENO) == -1)
-        die("Failed to dup2(STDOUT)");
-      break;
-    case Subprocess::OutputMode::kFd:
-      if (dup2(*args->create_args->out_fd, STDOUT_FILENO) == -1)
-        die("Failed to dup2(STDOUT)");
+bool ReadFileToString(int fd, char* buf, size_t size) {
+  size_t pos = 0;
+  while (pos < size - 1) {
+    ssize_t rd = PERFETTO_EINTR(read(fd, buf + pos, size - 1 - pos));
+    if (rd < 0)
+      return false;
+    if (rd == 0)
       break;
+    pos += static_cast<size_t>(rd);
   }
+  buf[pos] = '\0';
+  return true;
+}

-  switch (args->create_args->stderr_mode) {
-    case Subprocess::OutputMode::kInherit:
-      break;
-    case Subprocess::OutputMode::kDevNull: {
-      if (dup2(open("/dev/null", O_RDWR), STDERR_FILENO) == -1)
-        die("Failed to dup2(STDERR)");
-      break;
-    }
-    case Subprocess::OutputMode::kBuffer:
-      if (dup2(args->stdouterr_pipe_wr, STDERR_FILENO) == -1)
-        die("Failed to dup2(STDERR)");
-      break;
-    case Subprocess::OutputMode::kFd:
-      if (dup2(*args->create_args->out_fd, STDERR_FILENO) == -1)
-        die("Failed to dup2(STDERR)");
-      break;
-  }
+}  //  namespace

-  // Close all FDs % stdin/out/err and the ones that the client explicitly
-  // asked to retain. The reason for this is twofold:
-  // 1. For exec-only (i.e. entrypoint == empty) cases: it avoids leaking FDs
-  //    that didn't get marked as O_CLOEXEC by accident.
-  // 2. In fork() mode (entrypoint not empty) avoids retaining a dup of eventfds
-  //    that would prevent the parent process to receive EOFs (tests usually use
-  //    pipes as a synchronization mechanism between subprocesses).
-  const auto& preserve_fds = args->create_args->preserve_fds;
-  for (int i = 0; i < 512; i++) {
-    if (i != STDIN_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO &&
-        i != args->stdouterr_pipe_wr &&
-        !std::count(preserve_fds.begin(), preserve_fds.end(), i)) {
-      close(i);
-    }
+bool ReadProcStat(int fd, ProcStat* out) {
+  char str[512];
+  if (!ReadFileToString(fd, str, sizeof(str))) {
+    PERFETTO_ELOG("Failed to read stat file to enforce resource limits.");
+    return false;
   }

-  // Clears O_CLOEXEC from stdin/out/err and the |preserve_fds| list. These are
-  // the only FDs that we want to be preserved after the exec().
-  set_fd_close_on_exec(STDIN_FILENO, false);
-  set_fd_close_on_exec(STDOUT_FILENO, false);
-  set_fd_close_on_exec(STDERR_FILENO, false);
+  if (sscanf(str,
+             "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu "
+             "%lu %*d %*d %*d %*d %*d %*d %*u %*u %ld",
+             &out->utime, &out->stime, &out->rss_pages) != 3) {
+    PERFETTO_ELOG("Invalid stat format: %s", str);
+    return false;
+  }
+  return true;
+}

-  for (auto fd : preserve_fds)
-    set_fd_close_on_exec(fd, false);
+bool ReadProcStatm(int fd, ProcStatm* out) {
+  char str[256];
+  if (!ReadFileToString(fd, str, sizeof(str))) {
+    PERFETTO_ELOG("Failed to read statm file to enforce resource limits.");
+    return false;
+  }

-  // If the caller specified a std::function entrypoint, run that first.
-  if (args->create_args->posix_entrypoint_for_testing)
-    args->create_args->posix_entrypoint_for_testing();
+  if (sscanf(str,
+             "%" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64
+             " %" SCNu64 " %" SCNu64,
+             &out->size, &out->resident, &out->shared, &out->text, &out->lib,
+             &out->data, &out->dt) != 7) {
+    PERFETTO_ELOG("Invalid statm format: %s", str);
+    return false;
+  }
+  return true;
+}

-  // If the caller specified only an entrypoint, without any args, exit now.
-  // Otherwise proceed with the exec() below.
-  if (!args->exec_cmd)
-    _exit(0);
+Watchdog::Watchdog(uint32_t polling_interval_ms)
+    : polling_interval_ms_(polling_interval_ms) {}

-  // If |args[0]| is a path use execv() (which takes a path), otherwise use
-  // exevp(), which uses the shell and follows PATH.
-  if (strchr(args->exec_cmd, '/')) {
-    char** env = args->env.empty() ? environ : args->env.data();
-    execve(args->exec_cmd, args->argv.data(), env);
-  } else {
-    // There is no execvpe() on Mac.
-    if (!args->env.empty())
-      die("A full path is required for |exec_cmd| when setting |env|");
-    execvp(args->exec_cmd, args->argv.data());
+Watchdog::~Watchdog() {
+  if (!thread_.joinable()) {
+    PERFETTO_DCHECK(!enabled_);
+    return;
   }
+  PERFETTO_DCHECK(enabled_);
+  enabled_ = false;
+  // Rearm the timer to 1ns from now. This will cause the watchdog thread to
+  // wakeup from the poll() and see |enabled_| == false.
+  // This code path is used only in tests. In production code the watchdog
+  // is a singleton and is never destroyed.
+  struct itimerspec ts{};
+  ts.it_value.tv_sec = 0;
+  ts.it_value.tv_nsec = 1;
+  timerfd_settime(*timer_fd_, /*flags=*/0, &ts, nullptr);

-  // Reached only if execv fails.
-  die("execve() failed");
+  thread_.join();
 }

-}  // namespace
+Watchdog* Watchdog::GetInstance() {
+  static Watchdog* watchdog = new Watchdog(kDefaultPollingInterval);
+  return watchdog;
+}

-// static
-const int Subprocess::kTimeoutSignal = SIGKILL;
+// Can be called from any thread.
+Watchdog::Timer Watchdog::CreateFatalTimer(uint32_t ms,
+                                           WatchdogCrashReason crash_reason) {
+  if (!enabled_.load(std::memory_order_relaxed))
+    return Watchdog::Timer(this, 0, crash_reason);

-void Subprocess::Start() {
-  ChildProcessArgs proc_args;
-  proc_args.create_args = &args;
+  return Watchdog::Timer(this, ms, crash_reason);
+}

-  // Setup argv.
-  if (!args.exec_cmd.empty()) {
-    proc_args.exec_cmd = args.exec_cmd[0].c_str();
-    for (const std::string& arg : args.exec_cmd)
-      proc_args.argv.push_back(const_cast<char*>(arg.c_str()));
-    proc_args.argv.push_back(nullptr);
+// Can be called from any thread.
+void Watchdog::AddFatalTimer(TimerData timer) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  timers_.emplace_back(std::move(timer));
+  RearmTimerFd_Locked();
+}

-    if (!args.posix_argv0_override_for_testing.empty()) {
-      proc_args.argv[0] =
-          const_cast<char*>(args.posix_argv0_override_for_testing.c_str());
+// Can be called from any thread.
+void Watchdog::RemoveFatalTimer(TimerData timer) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  for (auto it = timers_.begin(); it != timers_.end(); it++) {
+    if (*it == timer) {
+      timers_.erase(it);
+      break;  // Remove only one. Doesn't matter which one.
     }
   }
+  RearmTimerFd_Locked();
+}

-  // Setup env.
-  if (!args.env.empty()) {
-    for (const std::string& str : args.env)
-      proc_args.env.push_back(const_cast<char*>(str.c_str()));
-    proc_args.env.push_back(nullptr);
-  }
+void Watchdog::RearmTimerFd_Locked() {
+  if (!enabled_)
+    return;
+  auto it = std::min_element(timers_.begin(), timers_.end());

-  // Setup the pipes for stdin/err redirection.
-  if (args.stdin_mode == InputMode::kBuffer) {
-    s_->stdin_pipe = base::Pipe::Create(base::Pipe::kWrNonBlock);
-    proc_args.stdin_pipe_rd = *s_->stdin_pipe.rd;
+  // We use one timerfd to handle all the outstanding |timers_|. Keep it armed
+  // to the task expiring soonest.
+  struct itimerspec ts{};
+  if (it != timers_.end()) {
+    ts.it_value = ToPosixTimespec(it->deadline);
   }
-  s_->stdouterr_pipe = base::Pipe::Create(base::Pipe::kRdNonBlock);
-  proc_args.stdouterr_pipe_wr = *s_->stdouterr_pipe.wr;
+  // If |timers_| is empty (it == end()) |ts.it_value| will remain
+  // zero-initialized and that will disarm the timer in the call below.
+  int res = timerfd_settime(*timer_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
+  PERFETTO_DCHECK(res == 0);
+}

-  // Spawn the child process that will exec().
-  s_->pid = fork();
-  PERFETTO_CHECK(s_->pid >= 0);
-  if (s_->pid == 0) {
-    // Close the parent-ends of the pipes.
-    s_->stdin_pipe.wr.reset();
-    s_->stdouterr_pipe.rd.reset();
-    ChildProcess(&proc_args);
-    // ChildProcess() doesn't return, not even in case of failures.
-    PERFETTO_FATAL("not reached");
+void Watchdog::Start(TaskRunner* task_runner,
+                     std::function<void(WatchdogCrashInfo)> fatal_handler) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (thread_.joinable()) {
+    PERFETTO_DCHECK(enabled_);
+  } else {
+    PERFETTO_DCHECK(!enabled_);
+
+    // |fatal_handler_*| are written here, before the watchdog thread is
+    // created. The watchdog thread is the only reader, so no locking is
+    // needed when accessing them from ThreadMain().
+    fatal_handler_task_runner_ = task_runner;
+    fatal_handler_ = std::move(fatal_handler);
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+    // Kick the thread to start running but only on Android or Linux.
+    timer_fd_.reset(
+        timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK));
+    if (!timer_fd_) {
+      PERFETTO_PLOG(
+          "timerfd_create failed, the Perfetto watchdog is not available");
+      return;
+    }
+    enabled_ = true;
+    RearmTimerFd_Locked();  // Deal with timers created before Start().
+    thread_ = std::thread(&Watchdog::ThreadMain, this);
+#endif
   }
+}

-  s_->status = kRunning;
+void Watchdog::SetMemoryLimit(uint64_t bytes, uint32_t window_ms) {
+  // Update the fields under the lock.
+  std::lock_guard<std::mutex> guard(mutex_);

-  // Close the child-end of the pipes.
-  // Deliberately NOT closing the s_->stdin_pipe.rd. This is to avoid crashing
-  // with a SIGPIPE if the process exits without consuming its stdin, while
-  // the parent tries to write() on the other end of the stdin pipe.
-  s_->stdouterr_pipe.wr.reset();
-  proc_args.create_args->out_fd.reset();
+  PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) || bytes == 0);

-  // Spawn a thread that is blocked on waitpid() and writes the termination
-  // status onto a pipe. The problem here is that waipid() doesn't have a
-  // timeout option and can't be passed to poll(). The alternative would be
-  // using a SIGCHLD handler, but anecdotally signal handlers introduce more
-  // problems than what they solve.
-  s_->exit_status_pipe = base::Pipe::Create(base::Pipe::kRdNonBlock);
+  size_t size = bytes == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
+  memory_window_bytes_.Reset(size);
+  memory_limit_bytes_ = bytes;
+}

-  // Both ends of the pipe are closed after the thread.join().
-  int pid = s_->pid;
-  int exit_status_pipe_wr = s_->exit_status_pipe.wr.release();
-  auto* rusage = s_->rusage.get();
-  s_->waitpid_thread = std::thread([pid, exit_status_pipe_wr, rusage] {
-    int pid_stat = -1;
-    struct rusage usg{};
-    int wait_res = PERFETTO_EINTR(wait4(pid, &pid_stat, 0, &usg));
-    PERFETTO_CHECK(wait_res == pid);
+void Watchdog::SetCpuLimit(uint32_t percentage, uint32_t window_ms) {
+  std::lock_guard<std::mutex> guard(mutex_);

-    auto tv_to_ms = [](const struct timeval& tv) {
-      return static_cast<uint32_t>(tv.tv_sec * 1000 + tv.tv_usec / 1000);
-    };
-    rusage->cpu_utime_ms = tv_to_ms(usg.ru_utime);
-    rusage->cpu_stime_ms = tv_to_ms(usg.ru_stime);
-    rusage->max_rss_kb = static_cast<uint32_t>(usg.ru_maxrss) / 1000;
-    rusage->min_page_faults = static_cast<uint32_t>(usg.ru_minflt);
-    rusage->maj_page_faults = static_cast<uint32_t>(usg.ru_majflt);
-    rusage->vol_ctx_switch = static_cast<uint32_t>(usg.ru_nvcsw);
-    rusage->invol_ctx_switch = static_cast<uint32_t>(usg.ru_nivcsw);
+  PERFETTO_CHECK(percentage <= 100);
+  PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) ||
+                 percentage == 0);

-    base::ignore_result(PERFETTO_EINTR(
-        write(exit_status_pipe_wr, &pid_stat, sizeof(pid_stat))));
-    PERFETTO_CHECK(close(exit_status_pipe_wr) == 0 || errno == EINTR);
-  });
+  size_t size = percentage == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
+  cpu_window_time_ticks_.Reset(size);
+  cpu_limit_percentage_ = percentage;
 }

-Subprocess::Status Subprocess::Poll() {
-  if (s_->status != kRunning)
-    return s_->status;  // Nothing to poll.
-  while (PollInternal(0 /* don't block*/)) {
-  }
-  return s_->status;
-}
+void Watchdog::ThreadMain() {
+  // Register crash keys explicitly to avoid running out of slots at crash time.
+  g_crash_key_reason.Register();
+  g_crash_key_timeout.Register();
+  g_crash_key_actual_boot.Register();
+  g_crash_key_actual_mono.Register();
+  g_crash_key_actual_cpu.Register();

-// |timeout_ms| semantic:
-//   -1: Block indefinitely.
-//    0: Don't block, return immediately.
-//   >0: Block for at most X ms.
-// Returns:
-//  True: Read at least one fd (so there might be more queued).
-//  False: if all fds reached quiescent (no data to read/write).
-bool Subprocess::PollInternal(int poll_timeout_ms) {
-  struct pollfd fds[3]{};
-  size_t num_fds = 0;
-  if (s_->exit_status_pipe.rd) {
-    fds[num_fds].fd = *s_->exit_status_pipe.rd;
-    fds[num_fds].events = POLLIN;
-    num_fds++;
-  }
-  if (s_->stdouterr_pipe.rd) {
-    fds[num_fds].fd = *s_->stdouterr_pipe.rd;
-    fds[num_fds].events = POLLIN;
-    num_fds++;
+  base::ScopedFile stat_fd(base::OpenFile("/proc/self/stat", O_RDONLY));
+  if (!stat_fd) {
+    PERFETTO_ELOG("Failed to open stat file to enforce resource limits.");
+    return;
   }
-  if (s_->stdin_pipe.wr) {
-    fds[num_fds].fd = *s_->stdin_pipe.wr;
-    fds[num_fds].events = POLLOUT;
-    num_fds++;
+
+  base::ScopedFile statm_fd;
+  if constexpr (PERFETTO_FLAGS(USE_ANON_RSS_IN_WATCHDOG)) {
+    statm_fd = (base::OpenFile("/proc/self/statm", O_RDONLY));
+    if (!statm_fd) {
+      PERFETTO_DLOG(
+          "Failed to open statm file to enforce resource limits. Using RSS for "
+          "the guardrail");
+    }
   }

-  if (num_fds == 0)
-    return false;
+  PERFETTO_DCHECK(timer_fd_);

-  auto nfds = static_cast<nfds_t>(num_fds);
-  int poll_res = PERFETTO_EINTR(poll(fds, nfds, poll_timeout_ms));
-  PERFETTO_CHECK(poll_res >= 0);
+  constexpr uint8_t kFdCount = 1;
+  struct pollfd fds[kFdCount]{};
+  fds[0].fd = *timer_fd_;
+  fds[0].events = POLLIN;

-  TryReadStdoutAndErr();
-  TryPushStdin();
-  TryReadExitStatus();
+  for (;;) {
+    // We use the poll() timeout to drive the periodic ticks for the cpu/memory
+    // checks. The only other case when the poll() unblocks is when we crash
+    // (or have to quit via enabled_ == false, but that happens only in tests).
+    platform::BeforeMaybeBlockingSyscall();
+    auto ret = poll(fds, kFdCount, static_cast<int>(polling_interval_ms_));
+    platform::AfterMaybeBlockingSyscall();
+    if (!enabled_)
+      return;
+    if (ret < 0) {
+      if (errno == ENOMEM || errno == EINTR) {
+        // Should happen extremely rarely.
+        std::this_thread::sleep_for(std::chrono::milliseconds(100));
+        continue;
+      }
+      PERFETTO_FATAL("watchdog poll() failed");
+    }

-  return poll_res > 0;
-}
+    // If we get here either:
+    // 1. poll() timed out, in which case we should process cpu/mem guardrails.
+    // 2. A timer expired, in which case we shall crash.

-bool Subprocess::Wait(int timeout_ms) {
-  PERFETTO_CHECK(s_->status != kNotStarted);
+    uint64_t expired = 0;  // Must be exactly 8 bytes.
+    auto res = PERFETTO_EINTR(read(*timer_fd_, &expired, sizeof(expired)));
+    PERFETTO_DCHECK((res < 0 && (errno == EAGAIN)) ||
+                    (res == sizeof(expired) && expired > 0));
+    const TimeMillis now_mono_ms = GetWallTimeMs();

-  // Break out of the loop only after both conditions are satisfied:
-  // - All stdout/stderr data has been read (if kBuffer).
-  // - The process exited.
-  // Note that the two events can happen arbitrary order. After the process
-  // exits, there might be still data in the pipe buffer, which we want to
-  // read fully.
-  //
-  // Instead, don't wait on the stdin to be fully written. The child process
-  // might exit prematurely (or crash). If that happens, we can end up in a
-  // state where the write(stdin_pipe_.wr) will never unblock.
+    // Check if any of the timers expired.
+    WatchdogCrashInfo info{};
+    {
+      std::lock_guard<std::mutex> guard(mutex_);
+      for (const auto& timer : timers_) {
+        if (now_mono_ms >= timer.deadline) {
+          info.thread_id = timer.thread_id;
+          info.reason = timer.crash_reason;
+          const TimeMillis now_boot_ms = GetBootTimeMs();
+          info.alarm_time_ms = now_boot_ms.count();
+          info.timeout_s =
+              static_cast<int>(std::chrono::duration_cast<TimeSeconds>(
+                                   timer.deadline - timer.ctime_mono)
+                                   .count());
+          info.actual_mono_s =
+              static_cast<int>(std::chrono::duration_cast<TimeSeconds>(
+                                   now_mono_ms - timer.ctime_mono)
+                                   .count());
+          info.actual_boot_s =
+              static_cast<int>(std::chrono::duration_cast<TimeSeconds>(
+                                   now_boot_ms - timer.ctime_boot)
+                                   .count());
+          info.actual_cpu_s =
+              static_cast<int>(std::chrono::duration_cast<TimeSeconds>(
+                                   GetProcessCPUTimeNs() - timer.ctime_cpu)
+                                   .count());
+          break;
+        }
+      }
+    }

-  const int64_t t_start = base::GetWallTimeMs().count();
-  while (s_->exit_status_pipe.rd || s_->stdouterr_pipe.rd) {
-    int poll_timeout_ms = -1;  // Block until a FD is ready.
-    if (timeout_ms > 0) {
-      const int64_t now = GetWallTimeMs().count();
-      poll_timeout_ms = timeout_ms - static_cast<int>(now - t_start);
-      if (poll_timeout_ms <= 0)
-        return false;
+    if (info.thread_id) {
+      SerializeLogsAndKillThread(info.thread_id, info);
     }
-    PollInternal(poll_timeout_ms);
-  }  // while(...)
-  return true;
-}

-void Subprocess::TryReadExitStatus() {
-  if (!s_->exit_status_pipe.rd)
-    return;
+    // Check CPU and memory guardrails (if enabled).
+    lseek(stat_fd.get(), 0, SEEK_SET);
+    ProcStat stat;
+    if (!ReadProcStat(stat_fd.get(), &stat))
+      continue;

-  int pid_stat = -1;
-  int64_t rsize = PERFETTO_EINTR(
-      read(*s_->exit_status_pipe.rd, &pid_stat, sizeof(pid_stat)));
-  if (rsize < 0 && errno == EAGAIN)
-    return;
+    const uint64_t cpu_time = stat.utime + stat.stime;
+    // max(stat.rss_pages, 0) is used since rss_pages is signed.
+    const uint64_t rss_bytes =
+        stat.rss_pages > 0
+            ? static_cast<uint64_t>(stat.rss_pages) * base::GetSysPageSize()
+            : 0;

-  if (rsize > 0) {
-    PERFETTO_CHECK(rsize == sizeof(pid_stat));
-  } else if (rsize < 0) {
-    PERFETTO_PLOG("Subprocess read(s_->exit_status_pipe) failed");
-  }
-  s_->waitpid_thread.join();
-  s_->exit_status_pipe.rd.reset();
+    std::optional<uint64_t> rss_anon_bytes = std::nullopt;
+    if (statm_fd) {
+      lseek(statm_fd.get(), 0, SEEK_SET);
+      ProcStatm statm;
+      if (ReadProcStatm(statm_fd.get(), &statm))
+        rss_anon_bytes = statm.rss_anon_pages() * base::GetSysPageSize();
+    }

-  s_->status = kTerminated;
-  if (WIFEXITED(pid_stat)) {
-    s_->returncode = WEXITSTATUS(pid_stat);
-  } else if (WIFSIGNALED(pid_stat)) {
-    s_->returncode = 128 + WTERMSIG(pid_stat);  // Follow bash convention.
-  } else {
-    PERFETTO_FATAL("waitpid() returned an unexpected value (%d)", pid_stat);
+    bool threshold_exceeded = false;
+    WatchdogCrashReason crash_reason{};
+    {
+      std::lock_guard<std::mutex> guard(mutex_);
+      if (CheckMemory_Locked(rss_anon_bytes.value_or(rss_bytes)) &&
+          !IsSyncMemoryTaggingEnabled()) {
+        threshold_exceeded = true;
+        crash_reason = WatchdogCrashReason::kMemGuardrail;
+      } else if (CheckCpu_Locked(cpu_time)) {
+        threshold_exceeded = true;
+        crash_reason = WatchdogCrashReason::kCpuGuardrail;
+      }
+    }
+
+    if (threshold_exceeded) {
+      info.reason = crash_reason;
+      // No associated timer: info.actual_*_s remain 0.
+      SerializeLogsAndKillThread(getpid(), info);
+    }
   }
 }

-// If the stidn pipe is still open, push input data and close it at the end.
-void Subprocess::TryPushStdin() {
-  if (!s_->stdin_pipe.wr)
+void Watchdog::SerializeLogsAndKillThread(int tid,
+                                          const WatchdogCrashInfo& info) {
+  g_crash_key_timeout.Set(info.timeout_s);
+  g_crash_key_actual_mono.Set(info.actual_mono_s);
+  g_crash_key_actual_boot.Set(info.actual_boot_s);
+  g_crash_key_actual_cpu.Set(info.actual_cpu_s);
+  g_crash_key_reason.Set(static_cast<int>(info.reason));
+
+  // If the embedder installed a fatal handler, give it a chance to run (e.g.
+  // to flush in-flight state) before we force-kill the process. The handler
+  // is expected to crash the process on its own (e.g. via PERFETTO_FATAL once
+  // it's done). If it doesn't crash within |kFatalHandlerGraceMs|, we proceed
+  // and force-kill from here.
+  // |fatal_handler_*| are set in Start() before the watchdog thread is
+  // created, so no locking is needed. Clearing them after invocation prevents
+  // re-posting if the same expired condition is observed again.
+  if (fatal_handler_ && fatal_handler_task_runner_) {
+    fatal_handler_task_runner_->PostTask(
+        [h = std::move(fatal_handler_), info] { h(info); });
+    fatal_handler_ = nullptr;
+    fatal_handler_task_runner_ = nullptr;
+    if (!disable_kill_failsafe_for_testing_) {
+      std::this_thread::sleep_for(
+          std::chrono::milliseconds(kFatalHandlerGraceMs));
+    }
+  }
+
+  // We are about to die. Serialize the logs into the crash buffer so the
+  // debuggerd crash handler picks them up and attaches to the bugreport.
+  // In the case of a PERFETTO_CHECK/PERFETTO_FATAL this is done in logging.h.
+  // But in the watchdog case, we don't hit that codepath and must do ourselves.
+  MaybeSerializeLastLogsForCrashReporting();
+
+  // Send a SIGABRT to the thread that armed the timer. This is to see the
+  // callstack of the thread that is stuck in a long task rather than the
+  // watchdog thread.
+  if (syscall(__NR_tgkill, getpid(), tid, SIGABRT) < 0) {
+    // At this point the process must die. If for any reason the tgkill doesn't
+    // work (e.g. the thread has disappeared), force a crash from here.
+    abort();
+  }
+
+  if (disable_kill_failsafe_for_testing_)
     return;

-  PERFETTO_DCHECK(args.input.empty() || s_->input_written < args.input.size());
-  if (!args.input.empty()) {
-    int64_t wsize =
-        PERFETTO_EINTR(write(*s_->stdin_pipe.wr, &args.input[s_->input_written],
-                             args.input.size() - s_->input_written));
-    if (wsize < 0 && errno == EAGAIN)
-      return;
+  // The tgkill() above will take some milliseconds to cause a crash, as it
+  // involves the kernel to queue the SIGABRT on the target thread (often the
+  // main thread, which is != watchdog thread) and do a scheduling round.
+  // If something goes wrong though (the target thread has signals masked or
+  // is stuck in an uninterruptible+wakekill syscall) force quit from this
+  // thread.
+  std::this_thread::sleep_for(std::chrono::seconds(10));
+  abort();
+}

-    if (wsize >= 0) {
-      // Whether write() can return 0 is one of the greatest mysteries of UNIX.
-      // Just ignore it.
-      s_->input_written += static_cast<size_t>(wsize);
-    } else {
-      PERFETTO_PLOG("Subprocess write(stdin) failed");
-      s_->stdin_pipe.wr.reset();
+bool Watchdog::CheckMemory_Locked(uint64_t rss_anon_bytes) {
+  if (memory_limit_bytes_ == 0)
+    return false;
+
+  // Add the current stat value to the ring buffer and check that the mean
+  // remains under our threshold.
+  if (memory_window_bytes_.Push(rss_anon_bytes)) {
+    if (memory_window_bytes_.Mean() >
+        static_cast<double>(memory_limit_bytes_)) {
+      PERFETTO_ELOG(
+          "Memory watchdog trigger. Memory window of %f bytes is above the "
+          "%" PRIu64 " bytes limit.",
+          memory_window_bytes_.Mean(), memory_limit_bytes_);
+      return true;
     }
   }
-  PERFETTO_DCHECK(s_->input_written <= args.input.size());
-  if (s_->input_written == args.input.size())
-    s_->stdin_pipe.wr.reset();  // Close stdin.
+  return false;
 }

-void Subprocess::TryReadStdoutAndErr() {
-  if (!s_->stdouterr_pipe.rd)
-    return;
-  char buf[4096];
-  int64_t rsize =
-      PERFETTO_EINTR(read(*s_->stdouterr_pipe.rd, buf, sizeof(buf)));
-  if (rsize < 0 && errno == EAGAIN)
-    return;
+bool Watchdog::CheckCpu_Locked(uint64_t cpu_time) {
+  if (cpu_limit_percentage_ == 0)
+    return false;

-  if (rsize > 0) {
-    s_->output.append(buf, static_cast<size_t>(rsize));
-  } else if (rsize == 0 /* EOF */) {
-    s_->stdouterr_pipe.rd.reset();
-  } else {
-    PERFETTO_PLOG("Subprocess read(stdout/err) failed");
-    s_->stdouterr_pipe.rd.reset();
+  // Add the cpu time to the ring buffer.
+  if (cpu_window_time_ticks_.Push(cpu_time)) {
+    // Compute the percentage over the whole window and check that it remains
+    // under the threshold.
+    uint64_t difference_ticks = cpu_window_time_ticks_.NewestWhenFull() -
+                                cpu_window_time_ticks_.OldestWhenFull();
+    double window_interval_ticks =
+        (static_cast<double>(WindowTimeForRingBuffer(cpu_window_time_ticks_)) /
+         1000.0) *
+        static_cast<double>(sysconf(_SC_CLK_TCK));
+    double percentage = static_cast<double>(difference_ticks) /
+                        static_cast<double>(window_interval_ticks) * 100;
+    if (percentage > cpu_limit_percentage_) {
+      PERFETTO_ELOG("CPU watchdog trigger. %f%% CPU use is above the %" PRIu32
+                    "%% CPU limit.",
+                    percentage, cpu_limit_percentage_);
+      return true;
+    }
   }
+  return false;
 }

-void Subprocess::Kill(int sig_num) {
-  kill(s_->pid, sig_num ? sig_num : SIGKILL);
+uint32_t Watchdog::WindowTimeForRingBuffer(const WindowedInterval& window) {
+  return static_cast<uint32_t>(window.size() - 1) * polling_interval_ms_;
 }

-void Subprocess::KillAndWaitForTermination(int sig_num) {
-  Kill(sig_num);
-  Wait();
-  // TryReadExitStatus must have joined the thread.
-  PERFETTO_DCHECK(!s_->waitpid_thread.joinable());
+bool Watchdog::WindowedInterval::Push(uint64_t sample) {
+  // Add the sample to the current position in the ring buffer.
+  buffer_[position_] = sample;
+
+  // Update the position with next one circularily.
+  position_ = (position_ + 1) % size_;
+
+  // Set the filled flag the first time we wrap.
+  filled_ = filled_ || position_ == 0;
+  return filled_;
+}
+
+double Watchdog::WindowedInterval::Mean() const {
+  return MeanForArray(buffer_.get(), size_);
+}
+
+void Watchdog::WindowedInterval::Clear() {
+  position_ = 0;
+  buffer_.reset(new uint64_t[size_]());
+}
+
+void Watchdog::WindowedInterval::Reset(size_t new_size) {
+  position_ = 0;
+  size_ = new_size;
+  buffer_.reset(new_size == 0 ? nullptr : new uint64_t[new_size]());
+}
+
+Watchdog::Timer::Timer(Watchdog* watchdog,
+                       uint32_t ms,
+                       WatchdogCrashReason crash_reason)
+    : watchdog_(watchdog) {
+  if (!ms)
+    return;  // No-op timer created when the watchdog is disabled.
+
+  TimeMillis now_mono = GetWallTimeMs();
+  timer_data_.deadline = now_mono + std::chrono::milliseconds(ms);
+  timer_data_.ctime_mono = now_mono;
+  timer_data_.ctime_boot = GetBootTimeMs();
+  timer_data_.ctime_cpu =
+      std::chrono::duration_cast<TimeMillis>(GetProcessCPUTimeNs());
+  timer_data_.thread_id = GetThreadId();
+  timer_data_.crash_reason = crash_reason;
+  PERFETTO_DCHECK(watchdog_);
+  watchdog_->AddFatalTimer(timer_data_);
+}
+
+Watchdog::Timer::~Timer() {
+  if (timer_data_.deadline.count())
+    watchdog_->RemoveFatalTimer(timer_data_);
+}
+
+Watchdog::Timer::Timer(Timer&& other) noexcept {
+  watchdog_ = std::move(other.watchdog_);
+  other.watchdog_ = nullptr;
+  timer_data_ = std::move(other.timer_data_);
+  other.timer_data_ = TimerData();
 }

 }  // namespace base
 }  // namespace perfetto

-#endif  // PERFETTO_OS_LINUX || PERFETTO_OS_ANDROID || PERFETTO_OS_APPLE
-// gen_amalgamated begin source: src/base/subprocess_windows.cc
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG)
+// gen_amalgamated begin source: src/base/weak_runner.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/weak_runner.h
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14000,335 +14179,54 @@ void Subprocess::KillAndWaitForTermination(int sig_num) {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/subprocess.h"
-
-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_WEAK_RUNNER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_WEAK_RUNNER_H_

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <stdint.h>

-#include <stdio.h>
+#include <functional>
+#include <memory>

-#include <algorithm>
-#include <mutex>
-#include <tuple>
+namespace perfetto::base {

-#include <windows.h>
+class TaskRunner;

-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/pipe.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
-
-namespace perfetto {
-namespace base {
-
-// static
-const int Subprocess::kTimeoutSignal = static_cast<int>(STATUS_TIMEOUT);
-
-void Subprocess::Start() {
-  if (args.exec_cmd.empty()) {
-    PERFETTO_ELOG("Subprocess.exec_cmd cannot be empty on Windows");
-    return;
-  }
-
-  // Quote arguments but only when ambiguous. When quoting, CreateProcess()
-  // assumes that the command is an absolute path and does not search in the
-  // %PATH%. If non quoted, instead, CreateProcess() tries both. This is to
-  // allow Subprocess("cmd.exe", "/c", "shell command").
-  std::string cmd;
-  for (const auto& part : args.exec_cmd) {
-    if (part.find(" ") != std::string::npos) {
-      cmd += "\"" + part + "\" ";
-    } else {
-      cmd += part + " ";
-    }
-  }
-  // Remove trailing space.
-  if (!cmd.empty())
-    cmd.resize(cmd.size() - 1);
-
-  if (args.stdin_mode == InputMode::kBuffer) {
-    s_->stdin_pipe = Pipe::Create();
-    // Allow the child process to inherit the other end of the pipe.
-    PERFETTO_CHECK(
-        ::SetHandleInformation(*s_->stdin_pipe.rd, HANDLE_FLAG_INHERIT, 1));
-  }
-
-  if (args.stderr_mode == OutputMode::kBuffer ||
-      args.stdout_mode == OutputMode::kBuffer) {
-    s_->stdouterr_pipe = Pipe::Create();
-    PERFETTO_CHECK(
-        ::SetHandleInformation(*s_->stdouterr_pipe.wr, HANDLE_FLAG_INHERIT, 1));
-  }
-
-  ScopedPlatformHandle nul_handle;
-  if (args.stderr_mode == OutputMode::kDevNull ||
-      args.stdout_mode == OutputMode::kDevNull) {
-    nul_handle.reset(::CreateFileA(
-        "NUL", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
-        nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
-    PERFETTO_CHECK(::SetHandleInformation(*nul_handle, HANDLE_FLAG_INHERIT, 1));
-  }
-
-  PROCESS_INFORMATION proc_info{};
-  STARTUPINFOA start_info{};
-  start_info.cb = sizeof(STARTUPINFOA);
-
-  if (args.stderr_mode == OutputMode::kInherit) {
-    start_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
-  } else if (args.stderr_mode == OutputMode::kBuffer) {
-    start_info.hStdError = *s_->stdouterr_pipe.wr;
-  } else if (args.stderr_mode == OutputMode::kDevNull) {
-    start_info.hStdError = *nul_handle;
-  } else if (args.stderr_mode == OutputMode::kFd) {
-    PERFETTO_CHECK(
-        ::SetHandleInformation(*args.out_fd, HANDLE_FLAG_INHERIT, 1));
-    start_info.hStdError = *args.out_fd;
-  } else {
-    PERFETTO_CHECK(false);
-  }
-
-  if (args.stdout_mode == OutputMode::kInherit) {
-    start_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
-  } else if (args.stdout_mode == OutputMode::kBuffer) {
-    start_info.hStdOutput = *s_->stdouterr_pipe.wr;
-  } else if (args.stdout_mode == OutputMode::kDevNull) {
-    start_info.hStdOutput = *nul_handle;
-  } else if (args.stdout_mode == OutputMode::kFd) {
-    PERFETTO_CHECK(
-        ::SetHandleInformation(*args.out_fd, HANDLE_FLAG_INHERIT, 1));
-    start_info.hStdOutput = *args.out_fd;
-  } else {
-    PERFETTO_CHECK(false);
-  }
-
-  if (args.stdin_mode == InputMode::kBuffer) {
-    start_info.hStdInput = *s_->stdin_pipe.rd;
-  } else if (args.stdin_mode == InputMode::kDevNull) {
-    start_info.hStdInput = *nul_handle;
-  }
-
-  start_info.dwFlags |= STARTF_USESTDHANDLES;
-
-  // Create the child process.
-  bool success =
-      ::CreateProcessA(nullptr,      // App name. Needs to be null to use PATH.
-                       &cmd[0],      // Command line.
-                       nullptr,      // Process security attributes.
-                       nullptr,      // Primary thread security attributes.
-                       true,         // Handles are inherited.
-                       0,            // Flags.
-                       nullptr,      // Use parent's environment.
-                       nullptr,      // Use parent's current directory.
-                       &start_info,  // STARTUPINFO pointer.
-                       &proc_info);  // Receives PROCESS_INFORMATION.
-
-  // Close on our side the pipe ends that we passed to the child process.
-  s_->stdin_pipe.rd.reset();
-  s_->stdouterr_pipe.wr.reset();
-  args.out_fd.reset();
-
-  if (!success) {
-    s_->returncode = ERROR_FILE_NOT_FOUND;
-    s_->status = kTerminated;
-    s_->stdin_pipe.wr.reset();
-    s_->stdouterr_pipe.rd.reset();
-    PERFETTO_ELOG("CreateProcess failed: %lx, cmd: %s", GetLastError(),
-                  &cmd[0]);
-    return;
-  }
-
-  s_->pid = proc_info.dwProcessId;
-  s_->win_proc_handle = ScopedPlatformHandle(proc_info.hProcess);
-  s_->win_thread_handle = ScopedPlatformHandle(proc_info.hThread);
-  s_->status = kRunning;
-
-  MovableState* s = s_.get();
-  if (args.stdin_mode == InputMode::kBuffer) {
-    s_->stdin_thread = std::thread(&Subprocess::StdinThread, s, args.input);
-  }
-
-  if (args.stderr_mode == OutputMode::kBuffer ||
-      args.stdout_mode == OutputMode::kBuffer) {
-    PERFETTO_DCHECK(s_->stdouterr_pipe.rd);
-    s_->stdouterr_thread = std::thread(&Subprocess::StdoutErrThread, s);
-  }
-}
-
-// static
-void Subprocess::StdinThread(MovableState* s, const std::string& input) {
-  size_t input_written = 0;
-  while (input_written < input.size()) {
-    DWORD wsize = 0;
-    if (::WriteFile(*s->stdin_pipe.wr, input.data() + input_written,
-                    static_cast<DWORD>(input.size() - input_written), &wsize,
-                    nullptr)) {
-      input_written += wsize;
-    } else {
-      // ERROR_BROKEN_PIPE is WAI when the child just closes stdin and stops
-      // accepting input.
-      auto err = ::GetLastError();
-      if (err != ERROR_BROKEN_PIPE)
-        PERFETTO_PLOG("Subprocess WriteFile(stdin) failed %lx", err);
-      break;
-    }
-  }  // while(...)
-  std::unique_lock<std::mutex> lock(s->mutex);
-  s->stdin_pipe.wr.reset();
-}
-
-// static
-void Subprocess::StdoutErrThread(MovableState* s) {
-  char buf[4096];
-  for (;;) {
-    DWORD rsize = 0;
-    bool res =
-        ::ReadFile(*s->stdouterr_pipe.rd, buf, sizeof(buf), &rsize, nullptr);
-    if (!res) {
-      auto err = GetLastError();
-      if (err != ERROR_BROKEN_PIPE)
-        PERFETTO_PLOG("Subprocess ReadFile(stdouterr) failed %lx", err);
-    }
-
-    if (rsize > 0) {
-      std::unique_lock<std::mutex> lock(s->mutex);
-      s->locked_outerr_buf.append(buf, static_cast<size_t>(rsize));
-    } else {  // EOF or some error.
-      break;
-    }
-  }  // For(..)
-
-  // Close the stdouterr_pipe. The main loop looks at the pipe closure to
-  // determine whether the stdout/err thread has completed.
-  {
-    std::unique_lock<std::mutex> lock(s->mutex);
-    s->stdouterr_pipe.rd.reset();
-  }
-  s->stdouterr_done_event.Notify();
-}
-
-Subprocess::Status Subprocess::Poll() {
-  if (s_->status != kRunning)
-    return s_->status;  // Nothing to poll.
-  Wait(1 /*ms*/);
-  return s_->status;
-}
-
-bool Subprocess::Wait(int timeout_ms) {
-  PERFETTO_CHECK(s_->status != kNotStarted);
-  const bool wait_forever = timeout_ms == 0;
-  const int64_t wait_start_ms = base::GetWallTimeMs().count();
+// This is a wrapper around a `base::TaskRunner*`. It is intended to be used by
+// classes that want to post tasks on themselves. When the object is destroyed,
+// all posted tasks become noops.
+//
+// A class that embeds a WeakRunner can safely capture `this` on the posted
+// tasks.
+class WeakRunner {
+ public:
+  explicit WeakRunner(base::TaskRunner* task_runner);
+  ~WeakRunner();
+  base::TaskRunner* task_runner() const { return task_runner_; }

-  // Break out of the loop only after both conditions are satisfied:
-  // - All stdout/stderr data has been read (if OutputMode::kBuffer).
-  // - The process exited.
-  // Note that the two events can happen arbitrary order. After the process
-  // exits, there might be still data in the pipe buffer, which we want to
-  // read fully.
-  // Note also that stdout/err might be "complete" before starting, if neither
-  // is operating in OutputMode::kBuffer mode. In that case we just want to wait
-  // for the process termination.
+  // Schedules `f` for immediate execution. `f` will not be executed is `*this`
+  // is destroyed.
   //
-  // Instead, don't wait on the stdin to be fully written. The child process
-  // might exit prematurely (or crash). If that happens, we can end up in a
-  // state where the write(stdin_pipe_.wr) will never unblock.
-  bool stdouterr_complete = false;
-  for (;;) {
-    HANDLE wait_handles[2]{};
-    DWORD num_handles = 0;
-
-    // Check if the process exited.
-    bool process_exited = !s_->win_proc_handle;
-    if (!process_exited) {
-      DWORD exit_code = STILL_ACTIVE;
-      PERFETTO_CHECK(::GetExitCodeProcess(*s_->win_proc_handle, &exit_code));
-      if (exit_code != STILL_ACTIVE) {
-        s_->returncode = static_cast<int>(exit_code);
-        s_->status = kTerminated;
-        s_->win_proc_handle.reset();
-        s_->win_thread_handle.reset();
-        process_exited = true;
-      }
-    } else {
-      PERFETTO_DCHECK(s_->status != kRunning);
-    }
-    if (!process_exited) {
-      wait_handles[num_handles++] = *s_->win_proc_handle;
-    }
-
-    // Check if there is more output and if the stdout/err pipe has been closed.
-    {
-      std::unique_lock<std::mutex> lock(s_->mutex);
-      // Move the output from the internal buffer shared with the
-      // stdouterr_thread to the final buffer exposed to the client.
-      if (!s_->locked_outerr_buf.empty()) {
-        s_->output.append(std::move(s_->locked_outerr_buf));
-        s_->locked_outerr_buf.clear();
-      }
-      stdouterr_complete = !s_->stdouterr_pipe.rd;
-      if (!stdouterr_complete) {
-        wait_handles[num_handles++] = s_->stdouterr_done_event.fd();
-      }
-    }  // lock(s_->mutex)
-
-    if (num_handles == 0) {
-      PERFETTO_DCHECK(process_exited && stdouterr_complete);
-      break;
-    }
-
-    DWORD wait_ms;  // Note: DWORD is unsigned.
-    if (wait_forever) {
-      wait_ms = INFINITE;
-    } else {
-      const int64_t now = GetWallTimeMs().count();
-      const int64_t wait_left_ms = timeout_ms - (now - wait_start_ms);
-      if (wait_left_ms <= 0)
-        return false;  // Timed out
-      wait_ms = static_cast<DWORD>(wait_left_ms);
-    }
-
-    auto wait_res =
-        ::WaitForMultipleObjects(num_handles, wait_handles, false, wait_ms);
-    PERFETTO_CHECK(wait_res != WAIT_FAILED);
-  }
-
-  PERFETTO_DCHECK(!s_->win_proc_handle);
-  PERFETTO_DCHECK(!s_->win_thread_handle);
-
-  if (s_->stdin_thread.joinable())  // Might not exist if CreateProcess failed.
-    s_->stdin_thread.join();
-  if (s_->stdouterr_thread.joinable())
-    s_->stdouterr_thread.join();
-
-  // The stdin pipe is closed by the dedicated stdin thread. However if that is
-  // not started (e.g. because of no redirection) force close it now. Needs to
-  // happen after the join() to be thread safe.
-  s_->stdin_pipe.wr.reset();
-  s_->stdouterr_pipe.rd.reset();
-
-  return true;
-}
+  // Can be called from any thread, but the caller needs to make sure that
+  // `*this` is alive while `PostTask` is running: this is not obvious when
+  // multiple threads are involved.
+  void PostTask(std::function<void()> f) const;

-void Subprocess::Kill(int exit_code) {
-  auto code = exit_code ? static_cast<DWORD>(exit_code) : STATUS_CONTROL_C_EXIT;
-  ::TerminateProcess(*s_->win_proc_handle, code);
-}
+  // Schedules `f` for execution after |delay_ms|.
+  // Can be called from any thread, but the caller needs to make sure that
+  // `*this` is alive while `PostDelayedTask` is running: this is not obvious
+  // when multiple threads are involved.
+  void PostDelayedTask(std::function<void()> f, uint32_t delay_ms) const;

-void Subprocess::KillAndWaitForTermination(int exit_code) {
-  Kill(exit_code);
-  Wait();
-  // TryReadExitStatus must have joined the threads.
-  PERFETTO_DCHECK(!s_->stdin_thread.joinable());
-  PERFETTO_DCHECK(!s_->stdouterr_thread.joinable());
-}
+ private:
+  base::TaskRunner* const task_runner_;
+  std::shared_ptr<bool> destroyed_;
+};

-}  // namespace base
-}  // namespace perfetto
+}  // namespace perfetto::base

-#endif  // PERFETTO_OS_WIN
-// gen_amalgamated begin source: src/protozero/field.cc
+#endif  // INCLUDE_PERFETTO_EXT_BASE_WEAK_RUNNER_H_
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14343,74 +14241,46 @@ void Subprocess::KillAndWaitForTermination(int exit_code) {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/weak_runner.h"

-// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"

-#if !PERFETTO_IS_LITTLE_ENDIAN()
-// The memcpy() for fixed32/64 below needs to be adjusted if we want to
-// support big endian CPUs. There doesn't seem to be a compelling need today.
-#error Unimplemented for big endian archs.
-#endif
+namespace perfetto::base {

-namespace protozero {
+WeakRunner::WeakRunner(base::TaskRunner* task_runner)
+    : task_runner_(task_runner), destroyed_(std::make_shared<bool>(false)) {}

-template <typename Container>
-void Field::SerializeAndAppendToInternal(Container* dst) const {
-  namespace pu = proto_utils;
-  size_t initial_size = dst->size();
-  dst->resize(initial_size + pu::kMaxSimpleFieldEncodedSize + size_);
-  uint8_t* start = reinterpret_cast<uint8_t*>(&(*dst)[initial_size]);
-  uint8_t* wptr = start;
-  switch (type_) {
-    case static_cast<int>(pu::ProtoWireType::kVarInt): {
-      wptr = pu::WriteVarInt(pu::MakeTagVarInt(id_), wptr);
-      wptr = pu::WriteVarInt(int_value_, wptr);
-      break;
-    }
-    case static_cast<int>(pu::ProtoWireType::kFixed32): {
-      wptr = pu::WriteVarInt(pu::MakeTagFixed<uint32_t>(id_), wptr);
-      uint32_t value32 = static_cast<uint32_t>(int_value_);
-      memcpy(wptr, &value32, sizeof(value32));
-      wptr += sizeof(uint32_t);
-      break;
-    }
-    case static_cast<int>(pu::ProtoWireType::kFixed64): {
-      wptr = pu::WriteVarInt(pu::MakeTagFixed<uint64_t>(id_), wptr);
-      memcpy(wptr, &int_value_, sizeof(int_value_));
-      wptr += sizeof(uint64_t);
-      break;
-    }
-    case static_cast<int>(pu::ProtoWireType::kLengthDelimited): {
-      ConstBytes payload = as_bytes();
-      wptr = pu::WriteVarInt(pu::MakeTagLengthDelimited(id_), wptr);
-      wptr = pu::WriteVarInt(payload.size, wptr);
-      memcpy(wptr, payload.data, payload.size);
-      wptr += payload.size;
-      break;
-    }
-    default:
-      PERFETTO_FATAL("Unknown field type %d", type_);
-  }
-  size_t written_size = static_cast<size_t>(wptr - start);
-  PERFETTO_DCHECK(written_size > 0 && written_size < pu::kMaxMessageLength);
-  PERFETTO_DCHECK(initial_size + written_size <= dst->size());
-  dst->resize(initial_size + written_size);
+WeakRunner::~WeakRunner() {
+  *destroyed_ = true;
 }

-void Field::SerializeAndAppendTo(std::string* dst) const {
-  SerializeAndAppendToInternal(dst);
+void WeakRunner::PostTask(std::function<void()> f) const {
+  task_runner_->PostTask([destroyed = destroyed_, f = std::move(f)]() {
+    if (*destroyed) {
+      return;
+    }
+    f();
+  });
 }

-void Field::SerializeAndAppendTo(std::vector<uint8_t>* dst) const {
-  SerializeAndAppendToInternal(dst);
+void WeakRunner::PostDelayedTask(std::function<void()> f,
+                                 uint32_t delay_ms) const {
+  task_runner_->PostDelayedTask(
+      [destroyed = destroyed_, f = std::move(f)]() {
+        if (*destroyed) {
+          return;
+        }
+        f();
+      },
+      delay_ms);
 }

-}  // namespace protozero
-// gen_amalgamated begin source: src/protozero/gen_field_helpers.cc
+}  // namespace perfetto::base
+// gen_amalgamated begin source: src/base/lock_free_task_runner.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/lock_free_task_runner.h
+// gen_amalgamated begin header: include/perfetto/ext/base/unix_task_runner.h
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14425,98 +14295,131 @@ void Field::SerializeAndAppendTo(std::vector<uint8_t>* dst) const {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_UNIX_TASK_RUNNER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_UNIX_TASK_RUNNER_H_

-namespace protozero {
-namespace internal {
-namespace gen_helpers {
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/base/thread_annotations.h"
+// gen_amalgamated expanded: #include "perfetto/base/thread_utils.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/event_fd.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/rt_mutex.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"

-void DeserializeString(const protozero::Field& field, std::string* dst) {
-  field.get(dst);
-}
+#include <chrono>
+#include <deque>
+#include <map>
+#include <mutex>
+#include <vector>

-template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
-                                        uint64_t>(const protozero::Field& field,
-                                                  std::vector<uint64_t>* dst);
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <poll.h>
+#endif

-template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
-                                        int64_t>(const protozero::Field& field,
-                                                 std::vector<int64_t>* dst);
+namespace perfetto {
+namespace base {

-template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
-                                        uint32_t>(const protozero::Field& field,
-                                                  std::vector<uint32_t>* dst);
+// Runs a task runner on the current thread.
+//
+// Implementation note: we currently assume (and enforce in debug builds) that
+// Run() is called from the thread that constructed the UnixTaskRunner. This is
+// not strictly necessary, and we could instead track the thread that invokes
+// Run(). However, a related property that *might* be important to enforce is
+// that the destructor runs on the task-running thread. Otherwise, if there are
+// still-pending tasks at the time of destruction, we would destroy those
+// outside of the task thread (which might be unexpected to the caller). On the
+// other hand, the std::function task interface discourages use of any
+// resource-owning tasks (as the callable needs to be copyable), so this might
+// not be important in practice.
+//
+// TODO(rsavitski): consider adding a thread-check in the destructor, after
+// auditing existing usages.
+// TODO(primiano): rename this to TaskRunnerImpl. The "Unix" part is misleading
+// now as it supports also Windows.
+class UnixTaskRunner : public TaskRunner {
+ public:
+  UnixTaskRunner();
+  ~UnixTaskRunner() override;

-template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
-                                        int32_t>(const protozero::Field& field,
-                                                 std::vector<int32_t>* dst);
+  // Start executing tasks. Doesn't return until Quit() is called. Run() may be
+  // called multiple times on the same task runner.
+  void Run();
+  void Quit();

-void SerializeTinyVarInt(uint32_t field_id, bool value, Message* msg) {
-  msg->AppendTinyVarInt(field_id, value);
-}
+  // Checks whether there are any pending immediate tasks to run. Note that
+  // delayed tasks don't count even if they are due to run.
+  bool IsIdleForTesting();

-template void SerializeExtendedVarInt<uint64_t>(uint32_t field_id,
-                                                uint64_t value,
-                                                Message* msg);
+  // Pretends (for the purposes of running delayed tasks) that time advanced by
+  // `ms`.
+  void AdvanceTimeForTesting(uint32_t ms);

-template void SerializeExtendedVarInt<uint32_t>(uint32_t field_id,
-                                                uint32_t value,
-                                                Message* msg);
+  // TaskRunner implementation:
+  void PostTask(std::function<void()>) override;
+  void PostDelayedTask(std::function<void()>, uint32_t delay_ms) override;
+  void AddFileDescriptorWatch(PlatformHandle, std::function<void()>) override;
+  void RemoveFileDescriptorWatch(PlatformHandle) override;
+  bool RunsTasksOnCurrentThread() const override;

-template void SerializeFixed<double>(uint32_t field_id,
-                                     double value,
-                                     Message* msg);
+  // Returns true if the task runner is quitting, or has quit and hasn't been
+  // restarted since. Exposed primarily for ThreadTaskRunner, not necessary for
+  // normal use of this class.
+  bool QuitCalled();

-template void SerializeFixed<float>(uint32_t field_id,
-                                    float value,
-                                    Message* msg);
-
-template void SerializeFixed<uint64_t>(uint32_t field_id,
-                                       uint64_t value,
-                                       Message* msg);
-
-template void SerializeFixed<int64_t>(uint32_t field_id,
-                                      int64_t value,
-                                      Message* msg);
-
-template void SerializeFixed<uint32_t>(uint32_t field_id,
-                                       uint32_t value,
-                                       Message* msg);
+ private:
+  void WakeUp();
+  void UpdateWatchTasksLocked() PERFETTO_EXCLUSIVE_LOCKS_REQUIRED(lock_);
+  int GetDelayMsToNextTaskLocked() const
+      PERFETTO_EXCLUSIVE_LOCKS_REQUIRED(lock_);
+  void RunImmediateAndDelayedTask();
+  void PostFileDescriptorWatches(uint64_t windows_wait_result);
+  void RunFileDescriptorWatch(PlatformHandle);

-template void SerializeFixed<int32_t>(uint32_t field_id,
-                                      int32_t value,
-                                      Message* msg);
+  ThreadChecker thread_checker_;
+  std::atomic<PlatformThreadId> created_thread_id_ = GetThreadId();

-void SerializeString(uint32_t field_id,
-                     const std::string& value,
-                     Message* msg) {
-  msg->AppendString(field_id, value);
-}
+  EventFd event_;

-void SerializeUnknownFields(const std::string& unknown_fields, Message* msg) {
-  msg->AppendRawProtoBytes(unknown_fields.data(), unknown_fields.size());
-}
+// The array of fds/handles passed to poll(2) / WaitForMultipleObjects().
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  std::vector<PlatformHandle> poll_fds_;
+#else
+  std::vector<struct pollfd> poll_fds_;
+#endif

-MessageSerializer::MessageSerializer() = default;
+  base::MaybeRtMutex lock_;

-MessageSerializer::~MessageSerializer() = default;
+  std::deque<std::function<void()>> immediate_tasks_ PERFETTO_GUARDED_BY(lock_);
+  std::multimap<TimeMillis, std::function<void()>> delayed_tasks_
+      PERFETTO_GUARDED_BY(lock_);
+  bool quit_ PERFETTO_GUARDED_BY(lock_) = false;
+  TimeMillis advanced_time_for_testing_ PERFETTO_GUARDED_BY(lock_) =
+      TimeMillis(0);

-std::vector<uint8_t> MessageSerializer::SerializeAsArray() {
-  return msg_.SerializeAsArray();
-}
+  struct WatchTask {
+    std::function<void()> callback;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    // On UNIX systems we make the FD number negative in |poll_fds_| to avoid
+    // polling it again until the queued task runs. On Windows we can't do that.
+    // Instead we keep track of its state here.
+    bool pending = false;
+#else
+    size_t poll_fd_index;  // Index into |poll_fds_|.
+#endif
+  };

-std::string MessageSerializer::SerializeAsString() {
-  return msg_.SerializeAsString();
-}
+  std::map<PlatformHandle, WatchTask> watch_tasks_ PERFETTO_GUARDED_BY(lock_);
+  bool watch_tasks_changed_ PERFETTO_GUARDED_BY(lock_) = false;
+};

-template bool EqualsField<std::string>(const std::string&, const std::string&);
+}  // namespace base
+}  // namespace perfetto

-}  // namespace gen_helpers
-}  // namespace internal
-}  // namespace protozero
-// gen_amalgamated begin source: src/protozero/message.cc
+#endif  // INCLUDE_PERFETTO_EXT_BASE_UNIX_TASK_RUNNER_H_
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14531,213 +14434,272 @@ template bool EqualsField<std::string>(const std::string&, const std::string&);
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_LOCK_FREE_TASK_RUNNER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_LOCK_FREE_TASK_RUNNER_H_
+
+// gen_amalgamated expanded: #include "perfetto/base/flat_set.h"
+// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/base/thread_annotations.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/event_fd.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/flags.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/unix_task_runner.h"
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <poll.h>
+#endif

+#include <array>
 #include <atomic>
-#include <type_traits>
+#include <thread>
+#include <unordered_map>

-// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message_arena.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message_handle.h"
+namespace perfetto {
+namespace base {

-#if !PERFETTO_IS_LITTLE_ENDIAN()
-// The memcpy() for float and double below needs to be adjusted if we want to
-// support big endian CPUs. There doesn't seem to be a compelling need today.
-#error Unimplemented for big endian archs.
-#endif
+namespace task_runner_internal {
+class ScopedRefcount;
+struct Slab;

-namespace protozero {
+// Exposed for testing
+constexpr uint32_t kNumRefcountBuckets = 32;
+constexpr size_t kSlabSize = 512;

-namespace {
+}  // namespace task_runner_internal

-constexpr int kBytesToCompact = proto_utils::kMessageLengthFieldSize - 1u;
+template <typename T>
+class TaskRunnerTest;

-#if PERFETTO_DCHECK_IS_ON()
-std::atomic<uint32_t> g_generation;
-#endif
+// This class implements a lock-less multi-producer single-consumer task runner.
+// This is achieved by using a linked list of "slabs". Each slab is a fixed-size
+// array of tasks.
+// See /docs/design-docs/lock-free-task-runner.md for more details
+class PERFETTO_EXPORT_COMPONENT LockFreeTaskRunner : public TaskRunner {
+ public:
+  LockFreeTaskRunner();
+  ~LockFreeTaskRunner() override;

-}  // namespace
+  void Run();
+  void Quit();

-// Do NOT put any code in the constructor or use default initialization.
-// Use the Reset() method below instead.
+  // Checks whether there are any pending immediate tasks to run. Note that
+  // delayed tasks don't count even if they are due to run.
+  bool IsIdleForTesting();

-// This method is called to initialize both root and nested messages.
-void Message::Reset(ScatteredStreamWriter* stream_writer, MessageArena* arena) {
-// Older versions of libstdcxx don't have is_trivially_constructible.
-#if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20170516
-  static_assert(std::is_trivially_constructible<Message>::value,
-                "Message must be trivially constructible");
+  // TaskRunner implementation:
+  void PostTask(std::function<void()>) override;
+  void PostDelayedTask(std::function<void()>, uint32_t delay_ms) override;
+  void AddFileDescriptorWatch(PlatformHandle, std::function<void()>) override;
+  void RemoveFileDescriptorWatch(PlatformHandle) override;
+  bool RunsTasksOnCurrentThread() const override;
+
+  // Pretends (for the purposes of running delayed tasks) that time advanced by
+  // `ms`.
+  void AdvanceTimeForTesting(uint32_t ms);
+
+  // Stats for testing.
+  size_t slabs_allocated() const {
+    return slabs_allocated_.load(std::memory_order_relaxed);
+  }
+  size_t slabs_freed() const {
+    return slabs_freed_.load(std::memory_order_relaxed);
+  }
+
+ private:
+  using Slab = task_runner_internal::Slab;
+  using ScopedRefcount = task_runner_internal::ScopedRefcount;
+  friend class task_runner_internal::ScopedRefcount;
+
+  struct DelayedTask {
+    TimeMillis time;
+    uint64_t seq;
+    std::function<void()> task;
+
+    // Note that the < operator keeps the DelayedTasks sorted in reverse order
+    // (the latest one is first, the earliest one is last). This is so we can
+    // have a FIFO queue using a vector by just doing an O(1) pop_back().
+    bool operator<(const DelayedTask& other) const {
+      if (time != other.time)
+        return time > other.time;
+      return seq > other.seq;
+    }
+    bool operator==(const DelayedTask& other) const {
+      return time == other.time && seq == other.seq;
+    }
+  };
+
+  PERFETTO_ALWAYS_INLINE std::function<void()> PopNextImmediateTask();
+  std::function<void()> PopTaskRecursive(Slab*, Slab* next_slab);
+  std::function<void()> PopNextExpiredDelayedTask();
+  int GetDelayMsToNextTask() const;
+  void WakeUp() { wakeup_event_.Notify(); }
+  Slab* AllocNewSlab();
+  void DeleteSlab(Slab*);
+  void PostFileDescriptorWatches(uint64_t windows_wait_result);
+  void RunFileDescriptorWatch(PlatformHandle);
+  void UpdateWatchTasks();
+
+  // These two are semantically a unique_ptr, but are accessed from different
+  // threads.
+  std::atomic<Slab*> tail_{};  // This is never null.
+  std::atomic<Slab*> free_slab_{};
+
+  EventFd wakeup_event_;
+  bool quit_ = false;
+  std::thread::id run_task_thread_id_;
+
+  // Delayed tasks, accessed only by the main thread. Items are stored in
+  // reverse temporal order, see comment in the operator<.
+  FlatSet<DelayedTask> delayed_tasks_;
+  uint64_t next_delayed_task_seq_ = 0;
+  std::atomic<uint32_t> advanced_time_for_testing_{};
+
+  // The array of fds/handles passed to poll(2) / WaitForMultipleObjects().
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  std::vector<PlatformHandle> poll_fds_;
+#else
+  std::vector<struct pollfd> poll_fds_;
 #endif

-  static_assert(std::is_trivially_destructible<Message>::value,
-                "Message must be trivially destructible");
-  stream_writer_ = stream_writer;
-  arena_ = arena;
-  size_ = 0;
-  size_field_ = nullptr;
-  nested_message_ = nullptr;
-  message_state_ = MessageState::kNotFinalized;
-#if PERFETTO_DCHECK_IS_ON()
-  handle_ = nullptr;
-  generation_ = g_generation.fetch_add(1, std::memory_order_relaxed);
+  struct WatchTask {
+    std::function<void()> callback;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    // On UNIX systems we make the FD number negative in |poll_fds_| to avoid
+    // polling it again until the queued task runs. On Windows we can't do that.
+    // Instead we keep track of its state here.
+    bool pending = false;
+#else
+    size_t poll_fd_index;  // Index into |poll_fds_|.
 #endif
-}
+  };

-void Message::AppendString(uint32_t field_id, const char* str) {
-  AppendBytes(field_id, str, strlen(str));
-}
+  // Accessed only from the main thread.
+  std::unordered_map<PlatformHandle, WatchTask> watch_tasks_;
+  bool watch_tasks_changed_ = false;

-void Message::AppendBytes(uint32_t field_id, const void* src, size_t size) {
-  PERFETTO_DCHECK(field_id);
-  if (nested_message_)
-    EndNestedMessage();
+  // An array of 32 refcount buckets. Every Slab* maps to a bucket via a hash
+  // function. Every PostTask() thread increases the refcount before accessing
+  // a slab, and decreases it when done.
+  // This allows the Run() main thread to tell if any thread has possibly been
+  // able to observe the Slab through the tail_ before deleting it.
+  std::array<std::atomic<int32_t>, task_runner_internal::kNumRefcountBuckets>
+      refcounts_{};

-  PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
-  // Write the proto preamble (field id, type and length of the field).
-  uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
-  uint8_t* pos = buffer;
-  pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
-                                 pos);
-  pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
-  WriteToStream(buffer, pos);
+  // Stats for testing.
+  std::atomic<size_t> slabs_allocated_{};
+  std::atomic<size_t> slabs_freed_{};
+};

-  const uint8_t* src_u8 = reinterpret_cast<const uint8_t*>(src);
-  WriteToStream(src_u8, src_u8 + size);
-}
+namespace task_runner_internal {

-size_t Message::AppendScatteredBytes(uint32_t field_id,
-                                     ContiguousMemoryRange* ranges,
-                                     size_t num_ranges) {
-  PERFETTO_DCHECK(field_id);
-  if (nested_message_)
-    EndNestedMessage();
+// Returns the index of the refcount_ bucket for the passed Slab pointer.
+static uint32_t HashSlabPtr(Slab* slab) {
+  // This is a SplitMix64 hash, which is very fast and effective with pointers
+  // (See the test LockFreeTaskRunnerTest.HashSpreading).
+  uint64_t u = reinterpret_cast<uintptr_t>(slab);
+  u &= 0x00FFFFFFFFFFFFFFull;  // Clear asan/MTE top byte for tagged pointers.
+  u += 0x9E3779B97F4A7C15ull;
+  u = (u ^ (u >> 30)) * 0xBF58476D1CE4E5B9ull;
+  u = (u ^ (u >> 27)) * 0x94D049BB133111EBull;
+  return static_cast<uint32_t>((u ^ (u >> 31)) % kNumRefcountBuckets);
+}

-  size_t size = 0;
-  for (size_t i = 0; i < num_ranges; ++i) {
-    size += ranges[i].size();
-  }
+// A slab is a fixed-size array of tasks. The lifecycle of a task slot
+// within a slab goes through three phases:
+//
+// 1. Reservation: A writer thread atomically increments `next_task_slot` to
+//    reserve a slot in the `tasks` array. This reservation establishes the
+//    implicit order in which the consumer will attempt to read tasks (but
+//    only if they are published in the bitmap, see below).
+//
+// 2. Publishing: After writing the task into its reserved slot, the writer
+//    thread atomically sets the corresponding bit in the `tasks_written`
+//    bitmask. This acts as a memory barrier and makes the task visible to
+//    the consumer (main) thread.
+//
+// 3. Consumption: The main thread acquire-reads the `tasks_written` bitmask.
+//    For each bit that is set, it processes the task and then sets the
+//    corresponding bit in its private `tasks_read` bitmask to prevent
+//    reading the same task again.
+struct Slab {
+  Slab();
+  ~Slab();

-  PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
+  std::atomic<size_t> next_task_slot{0};

-  uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
-  uint8_t* pos = buffer;
-  pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
-                                 pos);
-  pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
-  WriteToStream(buffer, pos);
+  // `tasks` and `next_task_slot` are accessed by writer threads only.
+  // The main thread can access `tasks[i]` but only after ensuring that the
+  // corresponding bit in `tasks_written` is set.
+  std::array<std::function<void()>, kSlabSize> tasks{};

-  for (size_t i = 0; i < num_ranges; ++i) {
-    auto& range = ranges[i];
-    WriteToStream(range.begin, range.end);
-  }
+  // A bitmask indicating which tasks in the `tasks` array have been written
+  // and are ready to be read by the main thread.
+  // This is atomically updated by writer threads and read by the main thread.
+  using BitWord = size_t;
+  static constexpr size_t kBitsPerWord = sizeof(BitWord) * 8;
+  static constexpr size_t kNumWords = kSlabSize / kBitsPerWord;
+  std::array<std::atomic<BitWord>, kNumWords> tasks_written{};

-  return size;
-}
+  // A bitmask indicating which tasks have been read by the main thread.
+  // This is accessed only by the main thread, so no atomicity is required.
+  std::array<BitWord, kNumWords> tasks_read{};

-uint32_t Message::Finalize() {
-  if (is_finalized())
-    return size_;
+  // The link to the previous slab.
+  // This is written by writer threads when they create a new slab and link it
+  // to the previous tail. But they do so when nobody else can see the Slab,
+  // so there is no need for an atomic ptr. After the initial creation,
+  // this is accessed only by the main thread when:
+  // 1. draining tasks (to walk back to the oldest slab)
+  // 2. deleting slabs, setting it to nullptr, when they are fully consumed.
+  Slab* prev = nullptr;
+};

-  if (nested_message_)
-    EndNestedMessage();
+class ScopedRefcount {
+ public:
+  ScopedRefcount(LockFreeTaskRunner* tr, LockFreeTaskRunner::Slab* slab) {
+    bucket_ = &tr->refcounts_[HashSlabPtr(slab)];
+    auto prev_value = bucket_->fetch_add(1);
+    PERFETTO_DCHECK(prev_value >= 0);
+  }

-  // Write the length of the nested message a posteriori, using a leading-zero
-  // redundant varint encoding. This can be nullptr for the root message, among
-  // many reasons, because the TraceWriterImpl delegate is keeping track of the
-  // root fragment size independently.
-  if (size_field_) {
-    PERFETTO_DCHECK(!is_finalized());
-    PERFETTO_DCHECK(size_ < proto_utils::kMaxMessageLength);
-    //
-    // Normally the size of a protozero message is written with 4 bytes just
-    // before the contents of the message itself:
-    //
-    //    size          message data
-    //   [aa bb cc dd] [01 23 45 67 ...]
-    //
-    // We always reserve 4 bytes for the size, because the real size of the
-    // message isn't known until the call to Finalize(). This is possible
-    // because we can use leading zero redundant varint coding to expand any
-    // size smaller than 256 MiB to 4 bytes.
-    //
-    // However this is wasteful for short, frequently written messages, so the
-    // code below uses a 1 byte size field when possible. This is done by
-    // shifting the already-written data (which should still be in the cache)
-    // back by 3 bytes, resulting in this layout:
-    //
-    //   size  message data
-    //   [aa] [01 23 45 67 ...]
-    //
-    // We can only do this optimization if the message is contained in a single
-    // chunk (since we can't modify previously committed chunks). We can check
-    // this by verifying that the size field is immediately before the message
-    // in memory and is fully contained by the current chunk.
-    //
-    if (PERFETTO_LIKELY(size_ <= proto_utils::kMaxOneByteMessageLength &&
-                        size_field_ ==
-                            stream_writer_->write_ptr() - size_ -
-                                proto_utils::kMessageLengthFieldSize &&
-                        size_field_ >= stream_writer_->cur_range().begin)) {
-      stream_writer_->Rewind(size_, kBytesToCompact);
-      PERFETTO_DCHECK(size_field_ == stream_writer_->write_ptr() - size_ - 1u);
-      *size_field_ = static_cast<uint8_t>(size_);
-      message_state_ = MessageState::kFinalizedWithCompaction;
-    } else {
-      proto_utils::WriteRedundantVarInt(size_, size_field_);
-      message_state_ = MessageState::kFinalized;
+  ~ScopedRefcount() {
+    if (bucket_) {
+      auto prev_value = bucket_->fetch_sub(1);
+      PERFETTO_DCHECK(prev_value > 0);
     }
-    size_field_ = nullptr;
-  } else {
-    message_state_ = MessageState::kFinalized;
   }

-#if PERFETTO_DCHECK_IS_ON()
-  if (handle_)
-    handle_->reset_message();
-#endif
-
-  return size_;
-}
+  ScopedRefcount(ScopedRefcount&& other) noexcept {
+    bucket_ = other.bucket_;
+    other.bucket_ = nullptr;
+  }

-Message* Message::BeginNestedMessageInternal(uint32_t field_id) {
-  PERFETTO_DCHECK(field_id);
-  if (nested_message_)
-    EndNestedMessage();
+  ScopedRefcount& operator=(ScopedRefcount&& other) noexcept {
+    this->~ScopedRefcount();
+    new (this) ScopedRefcount(std::move(other));
+    return *this;
+  }

-  // Write the proto preamble for the nested message.
-  uint8_t data[proto_utils::kMaxTagEncodedSize];
-  uint8_t* data_end = proto_utils::WriteVarInt(
-      proto_utils::MakeTagLengthDelimited(field_id), data);
-  WriteToStream(data, data_end);
+  ScopedRefcount(const ScopedRefcount&) = delete;
+  ScopedRefcount& operator=(const ScopedRefcount&) = delete;

-  Message* message = arena_->NewMessage();
-  message->Reset(stream_writer_, arena_);
+  std::atomic<int32_t>* bucket_{};
+};
+}  // namespace task_runner_internal

-  // The length of the nested message cannot be known upfront. So right now
-  // just reserve the bytes to encode the size after the nested message is done.
-  message->set_size_field(
-      stream_writer_->ReserveBytes(proto_utils::kMessageLengthFieldSize));
-  size_ += proto_utils::kMessageLengthFieldSize;
+using MaybeLockFreeTaskRunner =
+    std::conditional_t<PERFETTO_FLAGS(USE_LOCKFREE_TASKRUNNER),
+                       LockFreeTaskRunner,
+                       UnixTaskRunner>;

-  nested_message_ = message;
-  return message;
-}
+}  // namespace base
+}  // namespace perfetto

-void Message::EndNestedMessage() {
-  size_ += nested_message_->Finalize();
-  if (nested_message_->message_state_ ==
-      MessageState::kFinalizedWithCompaction) {
-    size_ -= kBytesToCompact;
-  }
-  arena_->DeleteLastMessage(nested_message_);
-  nested_message_ = nullptr;
-}
+#endif  // INCLUDE_PERFETTO_EXT_BASE_LOCK_FREE_TASK_RUNNER_H_

-}  // namespace protozero
-// gen_amalgamated begin source: src/protozero/message_arena.cc
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14745,586 +14707,546 @@ void Message::EndNestedMessage() {
  *
  *      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,
+ * Unless required by applicable law of 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.
  */

-// gen_amalgamated expanded: #include "perfetto/protozero/message_arena.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/lock_free_task_runner.h"

-#include <atomic>
-#include <type_traits>
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <windows.h>
+
+// Keep the \n before to prevent clang-format reordering.
+#include <synchapi.h>
+#else
+#include <poll.h>
+#include <unistd.h>
+#endif
+
+#include <thread>

 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message_handle.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/bits.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/platform.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/watchdog.h"

-namespace protozero {
+namespace perfetto {
+namespace base {

-MessageArena::MessageArena() {
-  // The code below assumes that there is always at least one block.
-  blocks_.emplace_front();
+namespace task_runner_internal {
+Slab::Slab() = default;
+Slab::~Slab() {
+  PERFETTO_DCHECK(!prev);  // We should never delete linked slabs.
 }
+}  // namespace task_runner_internal

-MessageArena::~MessageArena() = default;
+namespace {
+static constexpr auto kSlabSize = task_runner_internal::kSlabSize;
+}

-Message* MessageArena::NewMessage() {
-  PERFETTO_DCHECK(!blocks_.empty());  // Should never become empty.
+LockFreeTaskRunner::LockFreeTaskRunner()
+    : run_task_thread_id_(std::this_thread::get_id()) {
+  static_assert((kSlabSize & (kSlabSize - 1)) == 0, "kSlabSize must be a pow2");
+  static_assert(kSlabSize >= Slab::kBitsPerWord);

-  Block* block = &blocks_.front();
-  if (PERFETTO_UNLIKELY(block->entries >= Block::kCapacity)) {
-    blocks_.emplace_front();
-    block = &blocks_.front();
-  }
-  const auto idx = block->entries++;
-  void* storage = block->storage[idx];
-  PERFETTO_ASAN_UNPOISON(storage, sizeof(Message));
-  return new (storage) Message();
-}
-
-void MessageArena::DeleteLastMessageInternal() {
-  PERFETTO_DCHECK(!blocks_.empty());  // Should never be empty, see below.
-  Block* block = &blocks_.front();
-  PERFETTO_DCHECK(block->entries > 0);
+  // Populate both the tail and the freelist, to minimize the chance of
+  // allocations at runtime.
+  tail_.store(AllocNewSlab());
+  free_slab_.store(AllocNewSlab());

-  // This is the reason why there is no ~Message() call here.
-  // MessageArea::Reset() (see header) also relies on dtor being trivial.
-  static_assert(std::is_trivially_destructible<Message>::value,
-                "Message must be trivially destructible");
+  AddFileDescriptorWatch(wakeup_event_.fd(), [] {
+    // Not reached -- see PostFileDescriptorWatches().
+    PERFETTO_DFATAL("Should be unreachable.");
+  });
+}

-  --block->entries;
-  PERFETTO_ASAN_POISON(&block->storage[block->entries], sizeof(Message));
+LockFreeTaskRunner::~LockFreeTaskRunner() {
+  PERFETTO_DCHECK(RunsTasksOnCurrentThread());

-  // Don't remove the first block to avoid malloc/free calls when the root
-  // message is reset. Hitting the allocator all the times is a waste of time.
-  if (block->entries == 0 && std::next(blocks_.cbegin()) != blocks_.cend()) {
-    blocks_.pop_front();
+  for (Slab* slab = tail_.exchange(nullptr); slab;) {
+    Slab* prev = slab->prev;
+    slab->prev = nullptr;
+    delete slab;
+    slab = prev;
   }
+  delete free_slab_.exchange(nullptr);
 }

-}  // namespace protozero
-// gen_amalgamated begin source: src/protozero/packed_repeated_fields.cc
-/*
- * 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.
- */
+void LockFreeTaskRunner::PostTask(std::function<void()> closure) {
+  // We use nullity of std::function in PopTaskRecursive() to determine exit
+  // criteria. Posting a null task would break that logic. Also a null task
+  // would cause an abort when trying to run it later on.
+  PERFETTO_CHECK(PERFETTO_LIKELY(closure));
+  using BitWord = Slab::BitWord;
+  for (;;) {
+    Slab* slab = tail_.load();
+    PERFETTO_DCHECK(slab);  // The tail_ must be always valid.
+    ScopedRefcount scoped_refcount(this, slab);
+    // After incrementing the refcount, re-verify that `slab` is still the
+    // current tail. If it isn't, the slab might have been deleted between
+    // our tail_.load() above and the refcount inc (the reader's check on
+    // refcount==0 may have happened before our inc). Retry with the new tail.
+    // If the second tail.load() returns the same slab, in seq_cst order
+    // any subsequent CAS that replaces tail_ comes after this load, hence
+    // any reader's later refcount check is also after our inc and will
+    // observe refcount > 0.
+    if (PERFETTO_UNLIKELY(tail_.load() != slab)) {
+      continue;
+    }

-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+    // Now that we have a slab, try appending a task to it (if there is space).
+    // We have 3 cases:
+    // 1. slot < kSlabSize: the nominal case. Append the task and return.
+    // 2. slot == kSlabSize: the common overflow case: The slab was full and we
+    //    tried to allocate the N+1 th element. We have to allocate a new Slab.
+    // 3. slot > kSlabSize: like 2, but but two (or more) threads raced on it.
+    //    One thread will win the race and alloc a new slab, the other one will
+    //    repeat.
+    size_t slot = slab->next_task_slot.fetch_add(1, std::memory_order_relaxed);

-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+    if (slot >= kSlabSize) {  // Cases 2,3
+      Slab* new_slab = AllocNewSlab();
+      new_slab->prev = slab;
+      new_slab->next_task_slot.store(1, std::memory_order_relaxed);
+      slot = 0;
+      if (PERFETTO_UNLIKELY(!tail_.compare_exchange_strong(slab, new_slab))) {
+        // If the cmpxcgh fails, another thread tried to allocate a new tail
+        // slab and won the race. Do another round, we'll observe the new slab.
+        // We have to release the prev pointer as at this point we found out
+        // another thread also has it.
+        new_slab->prev = nullptr;
+        DeleteSlab(new_slab);
+        continue;
+      }

-namespace protozero {
+      slab = new_slab;
+      scoped_refcount = ScopedRefcount(this, new_slab);
+    }

-void PackedBufferBase::GrowSlowpath() {
-  size_t write_off = static_cast<size_t>(write_ptr_ - storage_begin_);
-  size_t old_size = static_cast<size_t>(storage_end_ - storage_begin_);
-  size_t new_size = old_size < 65536 ? (old_size * 2) : (old_size * 3 / 2);
-  new_size = perfetto::base::AlignUp<4096>(new_size);
-  std::unique_ptr<uint8_t[]> new_buf(new uint8_t[new_size]);
-  memcpy(new_buf.get(), storage_begin_, old_size);
-  heap_buf_ = std::move(new_buf);
-  storage_begin_ = heap_buf_.get();
-  storage_end_ = storage_begin_ + new_size;
-  write_ptr_ = storage_begin_ + write_off;
-}
+    // Nominal case: publish the task and return.
+    PERFETTO_DCHECK(!slab->tasks[slot]);
+    slab->tasks[slot] = std::move(closure);
+    size_t s_word = slot / Slab::kBitsPerWord;
+    size_t s_bit = slot % Slab::kBitsPerWord;
+    BitWord s_mask = BitWord(1) << s_bit;
+    PERFETTO_DCHECK(
+        (slab->tasks_written[s_word].load(std::memory_order_relaxed) &
+         s_mask) == 0);
+    slab->tasks_written[s_word].fetch_or(s_mask, std::memory_order_release);

-void PackedBufferBase::Reset() {
-  heap_buf_.reset();
-  storage_begin_ = reinterpret_cast<uint8_t*>(&stack_buf_[0]);
-  storage_end_ = reinterpret_cast<uint8_t*>(&stack_buf_[kOnStackStorageSize]);
-  write_ptr_ = storage_begin_;
+    if (!RunsTasksOnCurrentThread()) {
+      // We don't need any clever logic to avoid spurious wake ups from other
+      // threads. Most PostTask()s in our codebase are done by the main thread.
+      // In the rare cases of a PostTask() coming from another thread, the odds
+      // of the main thread being woken up at the same time are tiny.
+      WakeUp();
+    }
+    return;
+  }
 }

-}  // namespace protozero
-// gen_amalgamated begin source: src/protozero/proto_decoder.cc
-/*
- * 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.
- */
-
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-
-#include <string.h>
+void LockFreeTaskRunner::Run() {
+  PERFETTO_CHECK(run_task_thread_id_ == std::this_thread::get_id());
+  quit_ = false;

-#include <cinttypes>
-#include <limits>
-#include <memory>
+  while (!quit_) {
+    // Pop one immediate and one delayed task, for fairness.
+    std::function<void()> immediate_task = PopNextImmediateTask();
+    std::function<void()> delayed_task = PopNextExpiredDelayedTask();
+    bool has_task = immediate_task || delayed_task;
+    int poll_timeout_ms = has_task ? 0 : GetDelayMsToNextTask();

-// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
-// gen_amalgamated expanded: #include "perfetto/public/compiler.h"
+    // run the poll(). We need it for two different reasons:
+    // 1. Blocks until the next event on the horizon, which:
+    //    - If we pulled a task, poll in non-blocking mode (0 delay). There
+    //      might be other tasks ready and we want to run them soon after.
+    //    - If there is a delayed task, compute the time remaining for it.
+    //    - Otherwise polls indefinitely, waiting for a PostTask() or a Quit()
+    //      call from another thread.
+    // 2. Regardless of timing, we need to read the FD watches.
+    //    We want to do this even if we know already that we have an immediate
+    //    task (when poll_timeout_ms = 0) to ensure fairness.

-namespace protozero {
+    //    TODO(primiano): we could optimize this and avoid a syscall for each
+    //    task when we have bursts of tasks (with some saturation, e.g. do still
+    //    a poll() every 10 tasks). Unfortunately, however, some users of
+    //    TestTaskRunner.RunUntilIdle() rely on the fact that FD watches are
+    //    polled before the current task is ran, so that the task can tell if
+    //    there are more tasks upcoming or it has reached quiescence.
+    //    TL;DR RunUntilIdle() was a mistake as it has ill-defined semantics but
+    //    now tests rely on those subtle semantics.

-using namespace proto_utils;
+    // Recompute the list of FDs to watch.
+    UpdateWatchTasks();

-#if !PERFETTO_IS_LITTLE_ENDIAN()
-#error Unimplemented for big endian archs.
+    uint64_t windows_wait_res = 0;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    // Unlike poll(2), WaitForMultipleObjects() returns only *one* handle in the
+    // set, even when >1 is signalled. In order to avoid starvation,
+    // PostFileDescriptorWatches() will WaitForSingleObject() each other handle
+    // to ensure fairness. |windows_wait_res| is passed just to avoid an extra
+    // WaitForSingleObject() for the one handle that WaitForMultipleObject()
+    // returned.
+    DWORD timeout =
+        poll_timeout_ms >= 0 ? static_cast<DWORD>(poll_timeout_ms) : INFINITE;
+    windows_wait_res =
+        WaitForMultipleObjects(static_cast<DWORD>(poll_fds_.size()),
+                               &poll_fds_[0], /*bWaitAll=*/false, timeout);
+#else
+    platform::BeforeMaybeBlockingSyscall();
+    int ret = PERFETTO_EINTR(poll(
+        &poll_fds_[0], static_cast<nfds_t>(poll_fds_.size()), poll_timeout_ms));
+    platform::AfterMaybeBlockingSyscall();
+    PERFETTO_CHECK(ret >= 0);
 #endif
+    PostFileDescriptorWatches(windows_wait_res);

-const Field TypedProtoDecoderBase::kInvalidField{};
-
-namespace {
-
-struct ParseFieldResult {
-  enum ParseResult { kAbort, kSkip, kOk };
-  ParseResult parse_res;
-  const uint8_t* next;
-  Field field;
-};
+    if (immediate_task) {
+      errno = 0;
+      RunTaskWithWatchdogGuard(std::move(immediate_task));
+    }

-// Parses one field and returns the field itself and a pointer to the next
-// field to parse. If parsing fails, the returned |next| == |buffer|.
-// Force-inlined so that, after inlining, the result struct is decomposed by
-// the compiler and does not round-trip through memory.
-PERFETTO_ALWAYS_INLINE ParseFieldResult
-ParseOneField(const uint8_t* const buffer, const uint8_t* const end) {
-  ParseFieldResult res{ParseFieldResult::kAbort, buffer, Field{}};
+    if (delayed_task) {
+      errno = 0;
+      RunTaskWithWatchdogGuard(std::move(delayed_task));
+    }
+  }

-  const uint8_t* pos = buffer;
+  // Wait for all other threads to have finished the Quit's PostTask().
+  // This is to prevent the following race in tests:
+  // - Thread1 (!= main thread) invokes Quit, which in turn becomes a PostTask.
+  // - This function sees quit_=true and returns from Run().
+  // - The owner of the LFTR at that point is entitled to destroy LFTR.
+  // - Thread1 is still executing the epilogue of the PostTask, decrementing the
+  //   refcount, and ends up operating on invalid memory.
+  while (
+      std::any_of(refcounts_.begin(), refcounts_.end(),
+                  [](std::atomic<int32_t>& bucket) { return bucket.load(); })) {
+    std::this_thread::yield();
+  }
+}

-  // If we've already hit the end, just return an invalid field.
-  if (PERFETTO_UNLIKELY(pos >= end))
-    return res;
+std::function<void()> LockFreeTaskRunner::PopNextImmediateTask() {
+  return PopTaskRecursive(tail_.load(), nullptr);
+}

-  uint64_t preamble = 0;
-  if (PERFETTO_LIKELY(*pos < 0x80)) {  // Fastpath for fields with ID < 16.
-    preamble = *(pos++);
-  } else {
-    const uint8_t* next = ParseVarIntFast(pos, end, &preamble);
-    if (PERFETTO_UNLIKELY(!next))
-      return res;
-    pos = next;
+std::function<void()> LockFreeTaskRunner::PopTaskRecursive(Slab* slab,
+                                                           Slab* next_slab) {
+  PERFETTO_DCHECK(RunsTasksOnCurrentThread());
+  Slab* prev = slab->prev;
+  if (PERFETTO_UNLIKELY(prev)) {
+    // In practice it's extemely unlikely that a slab has >1 predecessors.
+    // In nominal conditions it is going to have 0 predecessors most of the
+    // times and 1 predecessors 1 every kSlabSize times.
+    auto task = PopTaskRecursive(prev, slab);
+    if (task)
+      return task;
   }

-  uint32_t field_id =
-      static_cast<uint32_t>(proto_utils::GetTagFieldId(preamble));
-  if (field_id == 0 || pos >= end)
-    return res;
+  size_t words_fully_consumed = 0;
+  for (size_t w = 0; w < Slab::kNumWords; ++w) {
+    using BitWord = Slab::BitWord;
+    BitWord wr_word = slab->tasks_written[w].load(std::memory_order_acquire);
+    BitWord rd_word = slab->tasks_read[w];
+    words_fully_consumed += base::AllBitsSet(rd_word) ? 1 : 0;
+    BitWord unread_word = wr_word & ~rd_word;

-  auto field_type =
-      static_cast<uint8_t>(proto_utils::GetTagFieldType(preamble));
-  const uint8_t* new_pos = pos;
-  uint64_t int_value = 0;
-  uint64_t size = 0;
+    if (unread_word == 0)
+      continue;

-  // If-chain ordered by observed frequency: wire types within a message are
-  // near-constant, so these branches predict better than a switch jump.
-  if (PERFETTO_LIKELY(field_type ==
-                      static_cast<uint8_t>(ProtoWireType::kVarInt))) {
-    new_pos = ParseVarIntFast(pos, end, &int_value);
+    // Find the first unread task in the word.
+    uint32_t bit = base::CountTrailZeros(unread_word);
+    BitWord bit_mask = BitWord(1) << bit;
+    size_t slot = w * Slab::kBitsPerWord + bit;
+    std::function<void()> task = std::move(slab->tasks[slot]);
+    slab->tasks[slot] = nullptr;
+    slab->tasks_read[w] |= bit_mask;
+    return task;
+  }  // for(word in tasks_written)

-    // new_pos being null means ParseVarIntFast could not fully parse the
-    // number. This is because we are out of space in the buffer. Set the id
-    // to zero and return but don't update the offset so a future read can
-    // read this field.
-    if (PERFETTO_UNLIKELY(!new_pos))
-      return res;
-  } else if (PERFETTO_LIKELY(
-                 field_type ==
-                 static_cast<uint8_t>(ProtoWireType::kLengthDelimited))) {
-    uint64_t payload_length;
-    new_pos = ParseVarIntFast(pos, end, &payload_length);
-    if (PERFETTO_UNLIKELY(!new_pos))
-      return res;
+  // There are no unconsumed tasks in this Slab. Reached this point, this
+  // invocation will return null. However, before doing so, if the slab is fully
+  // written (are no slots left) and fully consumed delete it.
+  // We never delete the latest slab (the one pointed to by tail_) because
+  // we want the tail_ to always point to a valid slab.
+  // In principle, we only delete non-tail slabs, and PostTask() threads only
+  // access the tail, never walk back on the list, which sounds safe. However
+  // there is a potential race we have to guard against:
+  // A PostTask() thread could observe Slab 1 (e.g., the only slab), then
+  // another PostTask thread() could append (and replace tail_ with) a new
+  // Slab 2 (because Slab 1 was full).
+  // If Run() comes along and sees the Slab 1 full it will delete it, without
+  // realizing that the first thread is still accessing it, thinking it was the
+  // tail.
+  // Essentially, it is safe to delete non-tail slabs as long as we can
+  // guarrantee that no other thread observed that slab through the `tail_` at
+  // some point in the recent past. In order to do so, we use a refcount
+  // mechanism. If the refcount is 0 we know that no other PostTask thread
+  // has loaded the tail, and hence we can safely delete any non-tail slab.
+  // If a PostTask thread comes immediately after our check they will observe
+  // the new tail (we never delete the tail slab, only the predecessors).

-    // ParseVarIntFast guarantees that |new_pos| <= |end| when it succeeds;
-    if (payload_length > static_cast<uint64_t>(end - new_pos))
-      return res;
+  bool slab_fully_consumed = words_fully_consumed == Slab::kNumWords;

-    const uintptr_t payload_start = reinterpret_cast<uintptr_t>(new_pos);
-    int_value = payload_start;
-    size = payload_length;
-    new_pos += payload_length;
-  } else if (field_type == static_cast<uint8_t>(ProtoWireType::kFixed64)) {
-    new_pos = pos + sizeof(uint64_t);
-    if (PERFETTO_UNLIKELY(new_pos > end))
-      return res;
-    memcpy(&int_value, pos, sizeof(uint64_t));
-  } else if (field_type == static_cast<uint8_t>(ProtoWireType::kFixed32)) {
-    new_pos = pos + sizeof(uint32_t);
-    if (PERFETTO_UNLIKELY(new_pos > end))
-      return res;
-    memcpy(&int_value, pos, sizeof(uint32_t));
-  } else {
-    PERFETTO_DLOG("Invalid proto field type: %u", field_type);
-    return res;
+  const uint32_t bucket = HashSlabPtr(slab);
+  if (slab_fully_consumed && next_slab && refcounts_[bucket].load() == 0) {
+    // NOTE: only the main thread follows the `prev` linked list, writers never
+    // look at `prev`. The only contention entrypoint is the `tail_` pointer,
+    // which can be modified both by us and by writers.
+    PERFETTO_DCHECK(next_slab->prev == slab);
+    next_slab->prev = slab->prev;
+    slab->prev = nullptr;
+    DeleteSlab(slab);
   }

-  res.next = new_pos;
+  return nullptr;
+}

-  if (PERFETTO_UNLIKELY(field_id > Field::kMaxId)) {
-    PERFETTO_DLOG("Skipping field %" PRIu32 " because its id > %" PRIu32,
-                  field_id, Field::kMaxId);
-    res.parse_res = ParseFieldResult::kSkip;
-    return res;
+void LockFreeTaskRunner::Quit() {
+  // We have to post the writing of the quit if we are not on the right thread.
+  // The alternative would be writing quit_ + WakeUp(), but that is racy:
+  // - We write quit_=true from another thread
+  // - The main thread sees it immediately (it just happens to be iterating)
+  //   and destroys itself.
+  // - We try to call WakeUp() while the main thread is being destroyed.
+  if (!RunsTasksOnCurrentThread()) {
+    PostTask([this] { this->Quit(); });
+    return;
   }
+  quit_ = true;
+}

-  if (PERFETTO_UNLIKELY(size > proto_utils::kMaxMessageLength)) {
-    PERFETTO_DLOG("Skipping field %" PRIu32 " because it's too big (%" PRIu64
-                  " KB)",
-                  field_id, size / 1024);
-    res.parse_res = ParseFieldResult::kSkip;
-    return res;
+bool LockFreeTaskRunner::IsIdleForTesting() {
+  PERFETTO_DCHECK(RunsTasksOnCurrentThread());
+  for (Slab* slab = tail_; slab; slab = slab->prev) {
+    for (size_t i = 0; i < Slab::kNumWords; ++i) {
+      if (slab->tasks_written[i] & ~slab->tasks_read[i]) {
+        return false;
+      }
+    }
   }
-
-  res.parse_res = ParseFieldResult::kOk;
-  res.field.initialize(field_id, field_type, int_value,
-                       static_cast<uint32_t>(size));
-  return res;
+  return true;
 }

-// Grows the selective-decoding spill area once the inline slots are
-// exhausted, moving the spilled fields to the heap. The append itself is
-// open-coded in the decode loop; see the comment there.
-PERFETTO_NO_INLINE void ExpandSpill(Field** spill,
-                                    uint32_t* spill_capacity,
-                                    uint32_t spill_size,
-                                    std::unique_ptr<Field[]>* heap_spill) {
-  // Double the capacity, with a floor so that an exhausted spill that somehow
-  // starts from zero capacity (e.g. a moved-from state) doesn't keep doubling
-  // zero. The floor matches SelectiveTypedProtoDecoder::kSpillStackCapacity.
-  const uint32_t new_capacity = std::max(*spill_capacity * 2, 16u);
-  PERFETTO_CHECK(new_capacity > spill_size);
-  std::unique_ptr<Field[]> new_storage(new Field[new_capacity]);
-  if (spill_size > 0)
-    memcpy(&new_storage[0], *spill, sizeof(Field) * spill_size);
-  *heap_spill = std::move(new_storage);
-  *spill = &(*heap_spill)[0];
-  *spill_capacity = new_capacity;
+std::function<void()> LockFreeTaskRunner::PopNextExpiredDelayedTask() {
+  PERFETTO_DCHECK(RunsTasksOnCurrentThread());
+  TimeMillis now =
+      GetWallTimeMs() +
+      TimeMillis(advanced_time_for_testing_.load(std::memory_order_relaxed));
+  if (!delayed_tasks_.empty() && delayed_tasks_.back().time <= now) {
+    auto task = std::move(delayed_tasks_.back().task);
+    delayed_tasks_.pop_back();
+    return task;
+  }
+  return nullptr;
 }

-}  // namespace
-
-Field ProtoDecoder::FindField(uint32_t field_id) {
-  Field res{};
-  auto old_position = read_ptr_;
-  read_ptr_ = begin_;
-  for (auto f = ReadField(); f.valid(); f = ReadField()) {
-    if (f.id() == field_id) {
-      res = f;
-      break;
-    }
+int LockFreeTaskRunner::GetDelayMsToNextTask() const {
+  PERFETTO_DCHECK(RunsTasksOnCurrentThread());
+  if (delayed_tasks_.empty()) {
+    return -1;  // Forever.
   }
-  read_ptr_ = old_position;
-  return res;
+  TimeMillis now =
+      GetWallTimeMs() +
+      TimeMillis(advanced_time_for_testing_.load(std::memory_order_relaxed));
+  TimeMillis deadline = delayed_tasks_.back().time;
+  if (deadline <= now) {
+    return 0;
+  }
+  return static_cast<int>((deadline - now).count());
 }

-Field ProtoDecoder::ReadField() {
-  ParseFieldResult res;
-  do {
-    res = ParseOneField(read_ptr_, end_);
-    read_ptr_ = res.next;
-  } while (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kSkip));
-  return res.field;
+LockFreeTaskRunner::Slab* LockFreeTaskRunner::AllocNewSlab() {
+  Slab* free_slab = free_slab_.exchange(nullptr);
+  if (free_slab) {
+    free_slab->~Slab();
+    new (free_slab) Slab();
+    return free_slab;
+  }
+  slabs_allocated_.fetch_add(1, std::memory_order_relaxed);
+  return new Slab();
 }

-void TypedProtoDecoderBase::ParseAllFields() {
-  ParseAllFieldsImpl<false>(nullptr, nullptr, nullptr, nullptr, nullptr);
+void LockFreeTaskRunner::DeleteSlab(Slab* slab) {
+  PERFETTO_DCHECK(!slab->prev);  // We should never delete a linked slab.
+  Slab* null_slab = nullptr;
+  if (!free_slab_.compare_exchange_strong(null_slab, slab)) {
+    slabs_freed_.fetch_add(1, std::memory_order_relaxed);
+    delete slab;
+  }
 }

-void TypedProtoDecoderBase::ParseAllFieldsSelective(
-    const uint64_t* dense_mask,
-    Field** spill,
-    uint32_t* spill_capacity,
-    uint32_t* spill_size,
-    std::unique_ptr<Field[]>* heap_spill) {
-  PERFETTO_DCHECK(dense_mask && spill && spill_capacity && spill_size &&
-                  heap_spill);
-  ParseAllFieldsImpl<true>(dense_mask, spill, spill_capacity, spill_size,
-                           heap_spill);
-}
-
-// Force-inlined into the two thin entry points above: the plain
-// instantiation compiles to a loop with no trace of the mask support and
-// the wrappers' arguments fold away entirely.
-template <bool kSelective>
-PERFETTO_ALWAYS_INLINE void TypedProtoDecoderBase::ParseAllFieldsImpl(
-    const uint64_t* dense_mask,
-    Field** spill,
-    uint32_t* spill_capacity,
-    uint32_t* spill_size,
-    std::unique_ptr<Field[]>* heap_spill) {
-  const uint8_t* cur = begin_;
-  ParseFieldResult res;
-  for (;;) {
-    res = ParseOneField(cur, end_);
-    PERFETTO_DCHECK(res.parse_res != ParseFieldResult::kOk || res.next != cur);
-    cur = res.next;
-    if (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kSkip))
-      continue;
-    if (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kAbort))
-      break;
-
-    PERFETTO_DCHECK(res.parse_res == ParseFieldResult::kOk);
-    PERFETTO_DCHECK(res.field.valid());
-    auto field_id = res.field.id();
-    if constexpr (kSelective) {
-      // Selective decoding: only allowlisted in-range ids go to the dense
-      // storage; everything else (including ids beyond the in-tree range) is
-      // appended, in wire order, to the spill area. |num_fields_| is capped
-      // at the mask's extent by SelectiveTypedProtoDecoder, so this also
-      // bounds the mask read.
-      const bool dense =
-          field_id < num_fields_ &&
-          (dense_mask[field_id / 64] & (1ULL << (field_id % 64))) != 0;
-      if (!dense) {
-        // The spill append is open-coded from the field's scalar components:
-        // a function taking the whole Field (by value or reference) makes
-        // the compiler materialize the packed Field representation on every
-        // loop iteration, including for fields that are not spilled.
-        if (PERFETTO_UNLIKELY(*spill_size >= *spill_capacity))
-          ExpandSpill(spill, spill_capacity, *spill_size, heap_spill);
-        (*spill)[(*spill_size)++].initialize(
-            field_id, static_cast<uint8_t>(res.field.type()),
-            res.field.raw_int_value(), res.field.raw_size());
-        continue;
-      }
-    } else if (PERFETTO_UNLIKELY(field_id >= num_fields_)) {
-      // Fields with an id beyond the highest field id known in-tree at compile
-      // time are out-of-tree "extension" fields (e.g. carved out of
-      // TracePacket's `extensions 1000 to 1999` range). They are intentionally
-      // not stored here because fields_ is indexed directly by field id, and
-      // extension ids are sparse and potentially very high. Callers that need
-      // them should decode selectively and consume them from unknown_fields().
-      continue;
-    }
-
-    // There are two reasons why we might want to expand the heap capacity:
-    // 1. We are writing a non-repeated field, which has an id >
-    //    INITIAL_STACK_CAPACITY. In this case ExpandHeapStorage() ensures to
-    //    allocate at least (num_fields_ + 1) slots.
-    // 2. We are writing a repeated field but ran out of capacity.
-    if (PERFETTO_UNLIKELY(field_id >= size_ || size_ >= capacity_))
-      ExpandHeapStorage();
-
-    PERFETTO_DCHECK(field_id < size_);
-    Field* fld = &fields_[field_id];
-    if (PERFETTO_LIKELY(!HasField(field_id))) {
-      // This is the first time we see this field.
-      SetField(field_id);
-      *fld = std::move(res.field);
-    } else {
-      // Repeated field case.
-      // In this case we need to:
-      // 1. Append the last value of the field to end of the repeated field
-      //    storage.
-      // 2. Replace the default instance at offset |field_id| with the current
-      //    value. This is because in case of repeated field a call to Get(X) is
-      //    supposed to return the last value of X, not the first one.
-      // This is so that the RepeatedFieldIterator will iterate in the right
-      // order, see comments on RepeatedFieldIterator.
-      if (num_fields_ > size_) {
-        ExpandHeapStorage();
-        fld = &fields_[field_id];
-      }
-
-      PERFETTO_DCHECK(size_ < capacity_);
-      fields_[size_++] = *fld;
-      *fld = std::move(res.field);
-    }
+void LockFreeTaskRunner::PostDelayedTask(std::function<void()> task,
+                                         uint32_t delay_ms) {
+  if (!RunsTasksOnCurrentThread()) {
+    PostTask([this, task = std::move(task), delay_ms] {
+      this->PostDelayedTask(std::move(task), delay_ms);
+    });
+    return;
   }
-  read_ptr_ = res.next;
-}
-
-void TypedProtoDecoderBase::ExpandHeapStorage() {
-  // When we expand the heap we must ensure that we have at very last capacity
-  // to deal with all known fields plus at least one repeated field. We go +2048
-  // here based on observations on a large 4GB android trace. This is to avoid
-  // trivial re-allocations when dealing with repeated fields of a message that
-  // has > INITIAL_STACK_CAPACITY fields.
-  const uint32_t min_capacity = num_fields_ + 2048;  // Any num >= +1 will do.
-  const uint32_t new_capacity = std::max(capacity_ * 2, min_capacity);
-  PERFETTO_CHECK(new_capacity > size_ && new_capacity > num_fields_);
-  std::unique_ptr<Field[]> new_storage(new Field[new_capacity]);

-  static_assert(std::is_trivially_constructible<Field>::value,
-                "Field must be trivially constructible");
-  static_assert(std::is_trivially_copyable<Field>::value,
-                "Field must be trivially copyable");
-
-  // There is no need to initialize the slots for known field IDs: reads of
-  // them are gated on the presence bitmap. There is also no need to initialize
-  // the repeated slots, because they are written linearly with no gaps and are
-  // always initialized before incrementing |size_|.
-  const uint32_t new_size = std::max(size_, num_fields_);
-  memcpy(&new_storage[0], fields_, sizeof(Field) * size_);
-
-  heap_storage_ = std::move(new_storage);
-  fields_ = &heap_storage_[0];
-  capacity_ = new_capacity;
-  size_ = new_size;
+  TimeMillis runtime =
+      GetWallTimeMs() + TimeMillis(delay_ms) +
+      TimeMillis(advanced_time_for_testing_.load(std::memory_order_relaxed));
+  delayed_tasks_.insert(
+      DelayedTask{runtime, next_delayed_task_seq_++, std::move(task)});
 }

-}  // namespace protozero
-// gen_amalgamated begin source: src/protozero/scattered_heap_buffer.cc
-/*
- * 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.
- */
-
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-
-#include <algorithm>
-
-namespace protozero {
-
-ScatteredHeapBuffer::Slice::Slice()
-    : buffer_(nullptr), size_(0u), unused_bytes_(0u) {}
-
-ScatteredHeapBuffer::Slice::Slice(size_t size)
-    : buffer_(std::unique_ptr<uint8_t[]>(new uint8_t[size])),
-      size_(size),
-      unused_bytes_(size) {
-  PERFETTO_DCHECK(size);
-  Clear();
+void LockFreeTaskRunner::AdvanceTimeForTesting(uint32_t ms) {
+  advanced_time_for_testing_.fetch_add(ms);
+  WakeUp();
 }

-ScatteredHeapBuffer::Slice::Slice(Slice&& slice) noexcept = default;
-
-ScatteredHeapBuffer::Slice::~Slice() = default;
+void LockFreeTaskRunner::PostFileDescriptorWatches(
+    uint64_t windows_wait_result) {
+  PERFETTO_DCHECK(RunsTasksOnCurrentThread());
+  for (size_t i = 0; i < poll_fds_.size(); i++) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    const PlatformHandle handle = poll_fds_[i];
+    // |windows_wait_result| is the result of WaitForMultipleObjects() call. If
+    // one of the objects was signalled, it will have a value between
+    // [0, poll_fds_.size()].
+    if (i != windows_wait_result &&
+        WaitForSingleObject(handle, 0) != WAIT_OBJECT_0) {
+      continue;
+    }
+#else
+    base::ignore_result(windows_wait_result);
+    const PlatformHandle handle = poll_fds_[i].fd;
+    if (!(poll_fds_[i].revents & (POLLIN | POLLHUP)))
+      continue;
+    poll_fds_[i].revents = 0;
+#endif

-ScatteredHeapBuffer::Slice& ScatteredHeapBuffer::Slice::operator=(Slice&&) =
-    default;
+    // The wake-up event is handled inline to avoid an infinite recursion of
+    // posted tasks.
+    if (handle == wakeup_event_.fd()) {
+      wakeup_event_.Clear();
+      continue;
+    }

-void ScatteredHeapBuffer::Slice::Clear() {
-  unused_bytes_ = size_;
-#if PERFETTO_DCHECK_IS_ON()
-  memset(start(), 0xff, size_);
-#endif  // PERFETTO_DCHECK_IS_ON()
-}
+    // Binding to |this| is safe since we are the only object executing the
+    // task.
+    PostTask([this, handle] { this->RunFileDescriptorWatch(handle); });

-ScatteredHeapBuffer::ScatteredHeapBuffer(size_t initial_slice_size_bytes,
-                                         size_t maximum_slice_size_bytes)
-    : next_slice_size_(initial_slice_size_bytes),
-      maximum_slice_size_(maximum_slice_size_bytes) {
-  PERFETTO_DCHECK(next_slice_size_ && maximum_slice_size_);
-  PERFETTO_DCHECK(maximum_slice_size_ >= initial_slice_size_bytes);
+    // Flag the task as pending.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    // On Windows this is done by marking the WatchTask entry as pending. This
+    // is more expensive than Linux as requires rebuilding the |poll_fds_|
+    // vector on each call. There doesn't seem to be a good alternative though.
+    auto it = watch_tasks_.find(handle);
+    PERFETTO_CHECK(it != watch_tasks_.end());
+    PERFETTO_DCHECK(!it->second.pending);
+    it->second.pending = true;
+#else
+    // On UNIX systems instead, we just make the fd negative while its task is
+    // pending. This makes poll(2) ignore the fd.
+    PERFETTO_DCHECK(poll_fds_[i].fd >= 0);
+    poll_fds_[i].fd = -poll_fds_[i].fd;
+#endif
+  }
 }

-ScatteredHeapBuffer::~ScatteredHeapBuffer() = default;
+void LockFreeTaskRunner::RunFileDescriptorWatch(PlatformHandle fd) {
+  PERFETTO_DCHECK(RunsTasksOnCurrentThread());

-protozero::ContiguousMemoryRange ScatteredHeapBuffer::GetNewBuffer() {
-  PERFETTO_CHECK(writer_);
-  AdjustUsedSizeOfCurrentSlice();
+  std::function<void()> task;
+  auto it = watch_tasks_.find(fd);
+  if (it == watch_tasks_.end())
+    return;
+  WatchTask& watch_task = it->second;

-  if (cached_slice_.start()) {
-    slices_.push_back(std::move(cached_slice_));
-    PERFETTO_DCHECK(!cached_slice_.start());
-  } else {
-    slices_.emplace_back(next_slice_size_);
-  }
-  next_slice_size_ = std::min(maximum_slice_size_, next_slice_size_ * 2);
-  return slices_.back().GetTotalRange();
-}
+  // Make poll(2) pay attention to the fd again. Since another thread may have
+  // updated this watch we need to refresh the set first.
+  UpdateWatchTasks();

-const std::vector<ScatteredHeapBuffer::Slice>&
-ScatteredHeapBuffer::GetSlices() {
-  AdjustUsedSizeOfCurrentSlice();
-  return slices_;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // On Windows we manually track the presence of outstanding tasks for the
+  // watch. The UpdateWatchTasksLocked() in the Run() loop will re-add the
+  // task to the |poll_fds_| vector.
+  PERFETTO_DCHECK(watch_task.pending);
+  watch_task.pending = false;
+#else
+  size_t fd_index = watch_task.poll_fd_index;
+  PERFETTO_DCHECK(fd_index < poll_fds_.size());
+  PERFETTO_DCHECK(::abs(poll_fds_[fd_index].fd) == fd);
+  poll_fds_[fd_index].fd = fd;
+#endif
+  task = watch_task.callback;
+  errno = 0;
+  RunTaskWithWatchdogGuard(task);
 }

-std::vector<uint8_t> ScatteredHeapBuffer::StitchSlices() {
-  size_t stitched_size = 0u;
-  const auto& slices = GetSlices();
-  for (const auto& slice : slices)
-    stitched_size += slice.size() - slice.unused_bytes();
-
-  std::vector<uint8_t> buffer;
-  buffer.reserve(stitched_size);
-  for (const auto& slice : slices) {
-    auto used_range = slice.GetUsedRange();
-    buffer.insert(buffer.end(), used_range.begin, used_range.end);
+void LockFreeTaskRunner::UpdateWatchTasks() {
+  PERFETTO_DCHECK(RunsTasksOnCurrentThread());
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  if (!watch_tasks_changed_)
+    return;
+  watch_tasks_changed_ = false;
+#endif
+  poll_fds_.clear();
+  for (auto& it : watch_tasks_) {
+    PlatformHandle handle = it.first;
+    WatchTask& watch_task = it.second;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    if (!watch_task.pending)
+      poll_fds_.push_back(handle);
+#else
+    watch_task.poll_fd_index = poll_fds_.size();
+    poll_fds_.push_back({handle, POLLIN | POLLHUP, 0});
+#endif
   }
-  return buffer;
 }

-std::pair<std::unique_ptr<uint8_t[]>, size_t>
-ScatteredHeapBuffer::StitchAsUniquePtr() {
-  size_t stitched_size = 0u;
-  const auto& slices = GetSlices();
-  for (const auto& slice : slices)
-    stitched_size += slice.size() - slice.unused_bytes();
+void LockFreeTaskRunner::AddFileDescriptorWatch(PlatformHandle fd,
+                                                std::function<void()> task) {
+  PERFETTO_DCHECK(PlatformHandleChecker::IsValid(fd));

-  std::unique_ptr<uint8_t[]> buffer(new uint8_t[stitched_size]);
-  uint8_t* ptr = buffer.get();
-  for (const auto& slice : slices) {
-    auto used_range = slice.GetUsedRange();
-    memcpy(ptr, used_range.begin, used_range.size());
-    ptr += used_range.size();
+  if (!RunsTasksOnCurrentThread()) {
+    PostTask([this, task = std::move(task), fd] {
+      this->AddFileDescriptorWatch(fd, std::move(task));
+    });
+    return;
   }

-  return std::make_pair(std::move(buffer), stitched_size);
-}
-
-std::vector<protozero::ContiguousMemoryRange> ScatteredHeapBuffer::GetRanges() {
-  std::vector<protozero::ContiguousMemoryRange> ranges;
-  for (const auto& slice : GetSlices())
-    ranges.push_back(slice.GetUsedRange());
-  return ranges;
-}
-
-void ScatteredHeapBuffer::AdjustUsedSizeOfCurrentSlice() {
-  if (!slices_.empty())
-    slices_.back().set_unused_bytes(writer_->bytes_available());
+  PERFETTO_DCHECK(!watch_tasks_.count(fd));
+  WatchTask& watch_task = watch_tasks_[fd];
+  watch_task.callback = std::move(task);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  watch_task.pending = false;
+#else
+  watch_task.poll_fd_index = SIZE_MAX;
+#endif
+  watch_tasks_changed_ = true;
 }

-size_t ScatteredHeapBuffer::GetTotalSize() {
-  size_t total_size = 0;
-  for (auto& slice : slices_) {
-    total_size += slice.size();
+void LockFreeTaskRunner::RemoveFileDescriptorWatch(PlatformHandle fd) {
+  if (!RunsTasksOnCurrentThread()) {
+    PostTask([this, fd] { this->RemoveFileDescriptorWatch(fd); });
+    return;
   }
-  return total_size;
+
+  PERFETTO_DCHECK(watch_tasks_.count(fd));
+  watch_tasks_.erase(fd);
+  watch_tasks_changed_ = true;
 }

-void ScatteredHeapBuffer::Reset() {
-  if (slices_.empty())
-    return;
-  cached_slice_ = std::move(slices_.front());
-  cached_slice_.Clear();
-  slices_.clear();
+bool LockFreeTaskRunner::RunsTasksOnCurrentThread() const {
+  return run_task_thread_id_ == std::this_thread::get_id();
 }

-}  // namespace protozero
-// gen_amalgamated begin source: src/protozero/scattered_stream_null_delegate.cc
+}  // namespace base
+}  // namespace perfetto
+// gen_amalgamated begin source: src/base/thread_task_runner.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/thread_task_runner.h
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15339,30 +15261,81 @@ void ScatteredHeapBuffer::Reset() {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_stream_null_delegate.h"
+#ifndef INCLUDE_PERFETTO_EXT_BASE_THREAD_TASK_RUNNER_H_
+#define INCLUDE_PERFETTO_EXT_BASE_THREAD_TASK_RUNNER_H_

-namespace protozero {
+#include <functional>
+#include <thread>

-// An implementation of ScatteredStreamWriter::Delegate which always returns
-// the same piece of memory.
-// This is used when we need to no-op the writers (e.g. during teardown or in
-// case of resource exhaustion), avoiding that the clients have to deal with
-// nullptr checks.
-ScatteredStreamWriterNullDelegate::ScatteredStreamWriterNullDelegate(
-    size_t chunk_size)
-    : chunk_size_(chunk_size),
-      chunk_(std::unique_ptr<uint8_t[]>(new uint8_t[chunk_size_])) {}
+// gen_amalgamated expanded: #include "perfetto/ext/base/lock_free_task_runner.h"

-ScatteredStreamWriterNullDelegate::~ScatteredStreamWriterNullDelegate() {}
+namespace perfetto {
+namespace base {

-ContiguousMemoryRange ScatteredStreamWriterNullDelegate::GetNewBuffer() {
-  return {chunk_.get(), chunk_.get() + chunk_size_};
-}
+// A MaybeLockFreeTaskRunner backed by a dedicated task thread. Shuts down the
+// runner and joins the thread upon destruction. Can be moved to transfer
+// ownership.
+//
+// Guarantees that:
+// * the MaybeLockFreeTaskRunner will be constructed and destructed on the task
+// thread.
+// * the task thread will live for the lifetime of the MaybeLockFreeTaskRunner.
+//
+class PERFETTO_EXPORT_COMPONENT ThreadTaskRunner : public TaskRunner {
+ public:
+  static ThreadTaskRunner CreateAndStart(const std::string& name = "") {
+    return ThreadTaskRunner(name);
+  }

-}  // namespace protozero
-// gen_amalgamated begin source: src/protozero/scattered_stream_writer.cc
+  ThreadTaskRunner(const ThreadTaskRunner&) = delete;
+  ThreadTaskRunner& operator=(const ThreadTaskRunner&) = delete;
+
+  ThreadTaskRunner(ThreadTaskRunner&&) noexcept;
+  ThreadTaskRunner& operator=(ThreadTaskRunner&&);
+  ~ThreadTaskRunner() override;
+
+  // Executes the given function on the task runner thread and blocks the caller
+  // thread until the function has run.
+  void PostTaskAndWaitForTesting(std::function<void()>);
+
+  // Can be called from another thread to get the CPU time of the thread the
+  // task-runner is executing on.
+  uint64_t GetThreadCPUTimeNsForTesting();
+
+  PlatformThreadId GetThreadIdForTesting();
+
+  // Returns a pointer to the MaybeLockFreeTaskRunner, which is valid for the
+  // lifetime of this ThreadTaskRunner object (unless this object is moved-from,
+  // in which case the pointer remains valid for the lifetime of the new owning
+  // ThreadTaskRunner).
+  //
+  // Warning: do not call Quit() on the returned runner pointer, the termination
+  // should be handled exclusively by this class' destructor.
+  MaybeLockFreeTaskRunner* get() const { return task_runner_; }
+
+  // TaskRunner implementation.
+  // These methods just proxy to the underlying task_runner_.
+  void PostTask(std::function<void()>) override;
+  void PostDelayedTask(std::function<void()>, uint32_t delay_ms) override;
+  void AddFileDescriptorWatch(PlatformHandle, std::function<void()>) override;
+  void RemoveFileDescriptorWatch(PlatformHandle) override;
+  bool RunsTasksOnCurrentThread() const override;
+
+ private:
+  explicit ThreadTaskRunner(const std::string& name);
+  void RunTaskThread(std::function<void(MaybeLockFreeTaskRunner*)> initializer);
+
+  std::thread thread_;
+  std::string name_;
+  MaybeLockFreeTaskRunner* task_runner_ = nullptr;
+};
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_THREAD_TASK_RUNNER_H_
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15377,123 +15350,138 @@ ContiguousMemoryRange ScatteredStreamWriterNullDelegate::GetNewBuffer() {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_stream_writer.h"
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-#include <algorithm>
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_task_runner.h"
+
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <thread>

 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/lock_free_task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_utils.h"

-namespace protozero {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/prctl.h>
+#endif

-ScatteredStreamWriter::Delegate::~Delegate() {}
+namespace perfetto {
+namespace base {

-uint8_t* ScatteredStreamWriter::Delegate::AnnotatePatch(uint8_t* patch_addr) {
-  // In most cases, a patch is transparent. The caller can write directly into
-  // `to_patch`, because its memory is not going away. TraceWriterImpl, however,
-  // requires a more complicated logic, because the chunks might be copied
-  // earlier.
-  return patch_addr;
+ThreadTaskRunner::ThreadTaskRunner(ThreadTaskRunner&& other) noexcept
+    : thread_(std::move(other.thread_)), task_runner_(other.task_runner_) {
+  other.task_runner_ = nullptr;
 }

-ScatteredStreamWriter::ScatteredStreamWriter(Delegate* delegate)
-    : delegate_(delegate),
-      cur_range_({nullptr, nullptr}),
-      write_ptr_(nullptr) {}
+ThreadTaskRunner& ThreadTaskRunner::operator=(ThreadTaskRunner&& other) {
+  this->~ThreadTaskRunner();
+  new (this) ThreadTaskRunner(std::move(other));
+  return *this;
+}

-ScatteredStreamWriter::~ScatteredStreamWriter() {}
+ThreadTaskRunner::~ThreadTaskRunner() {
+  if (task_runner_) {
+    task_runner_->Quit();

-void ScatteredStreamWriter::Reset(ContiguousMemoryRange range) {
-  written_previously_ += static_cast<uint64_t>(write_ptr_ - cur_range_.begin);
-  cur_range_ = range;
-  write_ptr_ = range.begin;
-  PERFETTO_DCHECK(!write_ptr_ || write_ptr_ < cur_range_.end);
+    PERFETTO_DCHECK(thread_.joinable());
+  }
+  if (thread_.joinable())
+    thread_.join();
 }

-void ScatteredStreamWriter::Extend() {
-  Reset(delegate_->GetNewBuffer());
+ThreadTaskRunner::ThreadTaskRunner(const std::string& name) : name_(name) {
+  std::mutex init_lock;
+  std::condition_variable init_cv;
+
+  std::function<void(MaybeLockFreeTaskRunner*)> initializer =
+      [this, &init_lock, &init_cv](MaybeLockFreeTaskRunner* task_runner) {
+        std::lock_guard<std::mutex> lock(init_lock);
+        task_runner_ = task_runner;
+        // Notify while still holding the lock, as init_cv ceases to exist as
+        // soon as the main thread observes a non-null task_runner_, and it can
+        // wake up spuriously (i.e. before the notify if we had unlocked before
+        // notifying).
+        init_cv.notify_one();
+      };
+
+  thread_ = std::thread(&ThreadTaskRunner::RunTaskThread, this,
+                        std::move(initializer));
+
+  std::unique_lock<std::mutex> lock(init_lock);
+  init_cv.wait(lock, [this] { return !!task_runner_; });
 }

-void ScatteredStreamWriter::WriteBytesSlowPath(const uint8_t* src,
-                                               size_t size) {
-  size_t bytes_left = size;
-  while (bytes_left > 0) {
-    if (write_ptr_ >= cur_range_.end)
-      Extend();
-    const size_t burst_size = std::min(bytes_available(), bytes_left);
-    WriteBytesUnsafe(src, burst_size);
-    bytes_left -= burst_size;
-    src += burst_size;
+void ThreadTaskRunner::RunTaskThread(
+    std::function<void(MaybeLockFreeTaskRunner*)> initializer) {
+  if (!name_.empty()) {
+    base::MaybeSetThreadName(name_);
   }
+
+  MaybeLockFreeTaskRunner task_runner;
+  task_runner.PostTask(std::bind(std::move(initializer), &task_runner));
+  task_runner.Run();
 }

-// TODO(primiano): perf optimization: I suspect that at the end this will always
-// be called with |size| == 4, in which case we might just hardcode it.
-uint8_t* ScatteredStreamWriter::ReserveBytes(size_t size) {
-  PERFETTO_DCHECK(write_ptr_ <= cur_range_.end);
-  if (size > static_cast<size_t>(cur_range_.end - write_ptr_)) {
-    // Assume the reservations are always < Delegate::GetNewBuffer().size(),
-    // so that one single call to Extend() will definitely give enough headroom.
-    Extend();
-    PERFETTO_DCHECK(write_ptr_ <= cur_range_.end);
-    PERFETTO_DCHECK(size <= static_cast<size_t>(cur_range_.end - write_ptr_));
-  }
-  uint8_t* begin = write_ptr_;
-  write_ptr_ += size;
-#if PERFETTO_DCHECK_IS_ON()
-  // In the past, the service had a matching DCHECK in
-  // TraceBuffer::TryPatchChunkContents, which was assuming that service and all
-  // producers are built with matching DCHECK levels. This turned out to be a
-  // source of problems and was removed in b/197340286. This memset is useless
-  // these days and is here only to maintain ABI compatibility between producers
-  // that use a v20+ SDK and older versions of the service that were built in
-  // debug mode. At some point around 2023 it should be safe to remove it.
-  // (running a debug version of traced in production seems a bad idea
-  // regardless).
-  memset(begin, 0, size);
-#endif
-  return begin;
+void ThreadTaskRunner::PostTaskAndWaitForTesting(std::function<void()> fn) {
+  std::mutex mutex;
+  std::condition_variable cv;
+
+  std::unique_lock<std::mutex> lock(mutex);
+  bool done = false;
+  task_runner_->PostTask([&mutex, &cv, &done, &fn] {
+    fn();
+
+    std::lock_guard<std::mutex> inner_lock(mutex);
+    done = true;
+    cv.notify_one();
+  });
+  cv.wait(lock, [&done] { return done; });
 }

-}  // namespace protozero
-// gen_amalgamated begin source: src/protozero/static_buffer.cc
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+uint64_t ThreadTaskRunner::GetThreadCPUTimeNsForTesting() {
+  uint64_t thread_time_ns = 0;
+  PostTaskAndWaitForTesting([&thread_time_ns] {
+    thread_time_ns = static_cast<uint64_t>(base::GetThreadCPUTimeNs().count());
+  });
+  return thread_time_ns;
+}
+PlatformThreadId ThreadTaskRunner::GetThreadIdForTesting() {
+  PlatformThreadId thread_id = static_cast<PlatformThreadId>(-1);
+  PostTaskAndWaitForTesting([&thread_id] { thread_id = GetThreadId(); });
+  return thread_id;
+}

-// gen_amalgamated expanded: #include "perfetto/protozero/static_buffer.h"
+void ThreadTaskRunner::PostTask(std::function<void()> task) {
+  task_runner_->PostTask(std::move(task));
+}

-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+void ThreadTaskRunner::PostDelayedTask(std::function<void()> task,
+                                       uint32_t delay_ms) {
+  task_runner_->PostDelayedTask(std::move(task), delay_ms);
+}

-namespace protozero {
+void ThreadTaskRunner::AddFileDescriptorWatch(
+    PlatformHandle handle,
+    std::function<void()> watch_task) {
+  task_runner_->AddFileDescriptorWatch(handle, std::move(watch_task));
+}

-StaticBufferDelegate::~StaticBufferDelegate() = default;
+void ThreadTaskRunner::RemoveFileDescriptorWatch(PlatformHandle handle) {
+  task_runner_->RemoveFileDescriptorWatch(handle);
+}

-ContiguousMemoryRange StaticBufferDelegate::GetNewBuffer() {
-  if (get_new_buffer_called_once_) {
-    // This is the 2nd time GetNewBuffer is called. The estimate is wrong. We
-    // shouldn't try to grow the buffer after the initial call.
-    PERFETTO_FATAL("Static buffer too small");
-  }
-  get_new_buffer_called_once_ = true;
-  return range_;
+bool ThreadTaskRunner::RunsTasksOnCurrentThread() const {
+  return task_runner_->RunsTasksOnCurrentThread();
 }

-}  // namespace protozero
-// gen_amalgamated begin source: src/protozero/virtual_destructors.cc
+}  // namespace base
+}  // namespace perfetto
+// gen_amalgamated begin source: src/base/unix_task_runner.cc
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -15508,2405 +15496,2692 @@ ContiguousMemoryRange StaticBufferDelegate::GetNewBuffer() {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/protozero/cpp_message_obj.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message_handle.h"
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-namespace protozero {
+// gen_amalgamated expanded: #include "perfetto/ext/base/platform.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/unix_task_runner.h"

-CppMessageObj::~CppMessageObj() = default;
-MessageFinalizationListener::~MessageFinalizationListener() = default;
+#include <errno.h>
+#include <stdlib.h>

-}  // namespace protozero
-// gen_amalgamated begin source: gen/protos/perfetto/protovm/vm_program.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <windows.h>
+
+#include <synchapi.h>
+#else
+#include <unistd.h>
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/protovm/vm_program.gen.h"

-namespace perfetto {
-namespace protos {
-namespace gen {
+#include <algorithm>
+#include <limits>

-VmOpDel::VmOpDel() = default;
-VmOpDel::~VmOpDel() = default;
-VmOpDel::VmOpDel(const VmOpDel&) = default;
-VmOpDel& VmOpDel::operator=(const VmOpDel&) = default;
-VmOpDel::VmOpDel(VmOpDel&&) noexcept = default;
-VmOpDel& VmOpDel::operator=(VmOpDel&&) = default;
+// gen_amalgamated expanded: #include "perfetto/ext/base/watchdog.h"

-bool VmOpDel::operator==(const VmOpDel& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
+namespace perfetto {
+namespace base {
+
+UnixTaskRunner::UnixTaskRunner() {
+  AddFileDescriptorWatch(event_.fd(), [] {
+    // Not reached -- see PostFileDescriptorWatches().
+    PERFETTO_DFATAL("Should be unreachable.");
+  });
 }

-bool VmOpDel::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+UnixTaskRunner::~UnixTaskRunner() = default;

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
+void UnixTaskRunner::WakeUp() {
+  event_.Notify();
+}
+
+void UnixTaskRunner::Run() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  created_thread_id_.store(GetThreadId(), std::memory_order_relaxed);
+  {
+    std::lock_guard<base::MaybeRtMutex> lock(lock_);
+    quit_ = false;
+  }
+  for (;;) {
+    int poll_timeout_ms;
+    {
+      std::lock_guard<base::MaybeRtMutex> lock(lock_);
+      if (quit_)
+        return;
+      poll_timeout_ms = GetDelayMsToNextTaskLocked();
+      UpdateWatchTasksLocked();
     }
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    DWORD timeout =
+        poll_timeout_ms >= 0 ? static_cast<DWORD>(poll_timeout_ms) : INFINITE;
+    DWORD ret =
+        WaitForMultipleObjects(static_cast<DWORD>(poll_fds_.size()),
+                               &poll_fds_[0], /*bWaitAll=*/false, timeout);
+    // Unlike poll(2), WaitForMultipleObjects() returns only *one* handle in the
+    // set, even when >1 is signalled. In order to avoid starvation,
+    // PostFileDescriptorWatches() will WaitForSingleObject() each other handle
+    // to ensure fairness. |ret| here is passed just to avoid an extra
+    // WaitForSingleObject() for the one handle that WaitForMultipleObject()
+    // returned.
+    PostFileDescriptorWatches(ret);
+#else
+    platform::BeforeMaybeBlockingSyscall();
+    int ret = PERFETTO_EINTR(poll(
+        &poll_fds_[0], static_cast<nfds_t>(poll_fds_.size()), poll_timeout_ms));
+    platform::AfterMaybeBlockingSyscall();
+    PERFETTO_CHECK(ret >= 0);
+    PostFileDescriptorWatches(0 /*ignored*/);
+#endif
+
+    // To avoid starvation we always interleave all types of tasks -- immediate,
+    // delayed and file descriptor watches.
+    RunImmediateAndDelayedTask();
   }
-  return !packed_error && !dec.bytes_left();
 }

-std::string VmOpDel::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
+void UnixTaskRunner::Quit() {
+  std::lock_guard<base::MaybeRtMutex> lock(lock_);
+  quit_ = true;
+  WakeUp();
 }

-std::vector<uint8_t> VmOpDel::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+bool UnixTaskRunner::QuitCalled() {
+  std::lock_guard<base::MaybeRtMutex> lock(lock_);
+  return quit_;
 }

-void VmOpDel::Serialize(::protozero::Message* msg) const {
-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+bool UnixTaskRunner::IsIdleForTesting() {
+  std::lock_guard<base::MaybeRtMutex> lock(lock_);
+  return immediate_tasks_.empty();
 }

-
-VmOpSet::VmOpSet() = default;
-VmOpSet::~VmOpSet() = default;
-VmOpSet::VmOpSet(const VmOpSet&) = default;
-VmOpSet& VmOpSet::operator=(const VmOpSet&) = default;
-VmOpSet::VmOpSet(VmOpSet&&) noexcept = default;
-VmOpSet& VmOpSet::operator=(VmOpSet&&) = default;
-
-bool VmOpSet::operator==(const VmOpSet& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
+void UnixTaskRunner::AdvanceTimeForTesting(uint32_t ms) {
+  std::lock_guard<base::MaybeRtMutex> lock(lock_);
+  advanced_time_for_testing_ += TimeMillis(ms);
 }

-bool VmOpSet::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+void UnixTaskRunner::UpdateWatchTasksLocked() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  if (!watch_tasks_changed_)
+    return;
+  watch_tasks_changed_ = false;
+#endif
+  poll_fds_.clear();
+  for (auto& it : watch_tasks_) {
+    PlatformHandle handle = it.first;
+    WatchTask& watch_task = it.second;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    if (!watch_task.pending)
+      poll_fds_.push_back(handle);
+#else
+    watch_task.poll_fd_index = poll_fds_.size();
+    poll_fds_.push_back({handle, POLLIN | POLLHUP, 0});
+#endif
+  }
+}

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
+void UnixTaskRunner::RunImmediateAndDelayedTask() {
+  // If locking overhead becomes an issue, add a separate work queue.
+  std::function<void()> immediate_task;
+  std::function<void()> delayed_task;
+  TimeMillis now = GetWallTimeMs();
+  {
+    std::lock_guard<base::MaybeRtMutex> lock(lock_);
+    if (!immediate_tasks_.empty()) {
+      immediate_task = std::move(immediate_tasks_.front());
+      immediate_tasks_.pop_front();
     }
-    switch (field.id()) {
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
+    if (!delayed_tasks_.empty()) {
+      auto it = delayed_tasks_.begin();
+      if (now + advanced_time_for_testing_ >= it->first) {
+        delayed_task = std::move(it->second);
+        delayed_tasks_.erase(it);
+      }
     }
   }
-  return !packed_error && !dec.bytes_left();
-}
-
-std::string VmOpSet::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}

-std::vector<uint8_t> VmOpSet::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+  errno = 0;
+  if (immediate_task)
+    RunTaskWithWatchdogGuard(immediate_task);
+  errno = 0;
+  if (delayed_task)
+    RunTaskWithWatchdogGuard(delayed_task);
 }

-void VmOpSet::Serialize(::protozero::Message* msg) const {
-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+void UnixTaskRunner::PostFileDescriptorWatches(uint64_t windows_wait_result) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  for (size_t i = 0; i < poll_fds_.size(); i++) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    const PlatformHandle handle = poll_fds_[i];
+    // |windows_wait_result| is the result of WaitForMultipleObjects() call. If
+    // one of the objects was signalled, it will have a value between
+    // [0, poll_fds_.size()].
+    if (i != windows_wait_result &&
+        WaitForSingleObject(handle, 0) != WAIT_OBJECT_0) {
+      continue;
+    }
+#else
+    base::ignore_result(windows_wait_result);
+    const PlatformHandle handle = poll_fds_[i].fd;
+    if (!(poll_fds_[i].revents & (POLLIN | POLLHUP)))
+      continue;
+    poll_fds_[i].revents = 0;
+#endif

+    // The wake-up event is handled inline to avoid an infinite recursion of
+    // posted tasks.
+    if (handle == event_.fd()) {
+      event_.Clear();
+      continue;
+    }

-VmOpMerge::VmOpMerge() = default;
-VmOpMerge::~VmOpMerge() = default;
-VmOpMerge::VmOpMerge(const VmOpMerge&) = default;
-VmOpMerge& VmOpMerge::operator=(const VmOpMerge&) = default;
-VmOpMerge::VmOpMerge(VmOpMerge&&) noexcept = default;
-VmOpMerge& VmOpMerge::operator=(VmOpMerge&&) = default;
+    // Binding to |this| is safe since we are the only object executing the
+    // task.
+    PostTask(std::bind(&UnixTaskRunner::RunFileDescriptorWatch, this, handle));

-bool VmOpMerge::operator==(const VmOpMerge& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(skip_submessages_, other.skip_submessages_)
-   && ::protozero::internal::gen_helpers::EqualsField(del_if_src_empty_, other.del_if_src_empty_);
+    // Flag the task as pending.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    // On Windows this is done by marking the WatchTask entry as pending. This
+    // is more expensive than Linux as requires rebuilding the |poll_fds_|
+    // vector on each call. There doesn't seem to be a good alternative though.
+    auto it = watch_tasks_.find(handle);
+    PERFETTO_CHECK(it != watch_tasks_.end());
+    PERFETTO_DCHECK(!it->second.pending);
+    it->second.pending = true;
+#else
+    // On UNIX systems instead, we just make the fd negative while its task is
+    // pending. This makes poll(2) ignore the fd.
+    PERFETTO_DCHECK(poll_fds_[i].fd >= 0);
+    poll_fds_[i].fd = -poll_fds_[i].fd;
+#endif
+  }
 }

-bool VmOpMerge::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+void UnixTaskRunner::RunFileDescriptorWatch(PlatformHandle fd) {
+  std::function<void()> task;
+  {
+    std::lock_guard<base::MaybeRtMutex> lock(lock_);
+    auto it = watch_tasks_.find(fd);
+    if (it == watch_tasks_.end())
+      return;
+    WatchTask& watch_task = it->second;

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* skip_submessages */:
-        field.get(&skip_submessages_);
-        break;
-      case 2 /* del_if_src_empty */:
-        field.get(&del_if_src_empty_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
-}
+    // Make poll(2) pay attention to the fd again. Since another thread may have
+    // updated this watch we need to refresh the set first.
+    UpdateWatchTasksLocked();

-std::string VmOpMerge::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    // On Windows we manually track the presence of outstanding tasks for the
+    // watch. The UpdateWatchTasksLocked() in the Run() loop will re-add the
+    // task to the |poll_fds_| vector.
+    PERFETTO_DCHECK(watch_task.pending);
+    watch_task.pending = false;
+#else
+    size_t fd_index = watch_task.poll_fd_index;
+    PERFETTO_DCHECK(fd_index < poll_fds_.size());
+    PERFETTO_DCHECK(::abs(poll_fds_[fd_index].fd) == fd);
+    poll_fds_[fd_index].fd = fd;
+#endif
+    task = watch_task.callback;
+  }
+  errno = 0;
+  RunTaskWithWatchdogGuard(task);
 }

-std::vector<uint8_t> VmOpMerge::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+int UnixTaskRunner::GetDelayMsToNextTaskLocked() const {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (!immediate_tasks_.empty())
+    return 0;
+  if (!delayed_tasks_.empty()) {
+    TimeMillis diff = delayed_tasks_.begin()->first - GetWallTimeMs() -
+                      advanced_time_for_testing_;
+    return std::max(0, static_cast<int>(diff.count()));
+  }
+  return -1;
 }

-void VmOpMerge::Serialize(::protozero::Message* msg) const {
-  // Field 1: skip_submessages
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, skip_submessages_, msg);
+void UnixTaskRunner::PostTask(std::function<void()> task) {
+  bool was_empty;
+  {
+    std::lock_guard<base::MaybeRtMutex> lock(lock_);
+    was_empty = immediate_tasks_.empty();
+    immediate_tasks_.push_back(std::move(task));
   }
+  if (was_empty)
+    WakeUp();
+}

-  // Field 2: del_if_src_empty
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, del_if_src_empty_, msg);
+void UnixTaskRunner::PostDelayedTask(std::function<void()> task,
+                                     uint32_t delay_ms) {
+  TimeMillis runtime = GetWallTimeMs() + TimeMillis(delay_ms);
+  {
+    std::lock_guard<base::MaybeRtMutex> lock(lock_);
+    delayed_tasks_.insert(
+        std::make_pair(runtime + advanced_time_for_testing_, std::move(task)));
   }
-
-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+  WakeUp();
 }

-
-VmOpRegLoad::VmOpRegLoad() = default;
-VmOpRegLoad::~VmOpRegLoad() = default;
-VmOpRegLoad::VmOpRegLoad(const VmOpRegLoad&) = default;
-VmOpRegLoad& VmOpRegLoad::operator=(const VmOpRegLoad&) = default;
-VmOpRegLoad::VmOpRegLoad(VmOpRegLoad&&) noexcept = default;
-VmOpRegLoad& VmOpRegLoad::operator=(VmOpRegLoad&&) = default;
-
-bool VmOpRegLoad::operator==(const VmOpRegLoad& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(cursor_, other.cursor_)
-   && ::protozero::internal::gen_helpers::EqualsField(dst_register_, other.dst_register_);
-}
-
-bool VmOpRegLoad::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
-
-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* cursor */:
-        field.get(&cursor_);
-        break;
-      case 2 /* dst_register */:
-        field.get(&dst_register_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
+void UnixTaskRunner::AddFileDescriptorWatch(PlatformHandle fd,
+                                            std::function<void()> task) {
+  PERFETTO_DCHECK(PlatformHandleChecker::IsValid(fd));
+  {
+    std::lock_guard<base::MaybeRtMutex> lock(lock_);
+    PERFETTO_DCHECK(!watch_tasks_.count(fd));
+    WatchTask& watch_task = watch_tasks_[fd];
+    watch_task.callback = std::move(task);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    watch_task.pending = false;
+#else
+    watch_task.poll_fd_index = SIZE_MAX;
+#endif
+    watch_tasks_changed_ = true;
   }
-  return !packed_error && !dec.bytes_left();
+  WakeUp();
 }

-std::string VmOpRegLoad::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
+void UnixTaskRunner::RemoveFileDescriptorWatch(PlatformHandle fd) {
+  PERFETTO_DCHECK(PlatformHandleChecker::IsValid(fd));
+  {
+    std::lock_guard<base::MaybeRtMutex> lock(lock_);
+    PERFETTO_DCHECK(watch_tasks_.count(fd));
+    watch_tasks_.erase(fd);
+    watch_tasks_changed_ = true;
+  }
+  // No need to schedule a wake-up for this.
 }

-std::vector<uint8_t> VmOpRegLoad::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+bool UnixTaskRunner::RunsTasksOnCurrentThread() const {
+  return GetThreadId() == created_thread_id_.load(std::memory_order_relaxed);
 }

-void VmOpRegLoad::Serialize(::protozero::Message* msg) const {
-  // Field 1: cursor
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, cursor_, msg);
-  }
-
-  // Field 2: dst_register
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, dst_register_, msg);
-  }
+}  // namespace base
+}  // namespace perfetto
+// gen_amalgamated begin source: src/base/subprocess.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/subprocess.h
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+#ifndef INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_

+#include <condition_variable>
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+#include <thread>
+#include <vector>

-VmOpSelect::VmOpSelect() = default;
-VmOpSelect::~VmOpSelect() = default;
-VmOpSelect::VmOpSelect(const VmOpSelect&) = default;
-VmOpSelect& VmOpSelect::operator=(const VmOpSelect&) = default;
-VmOpSelect::VmOpSelect(VmOpSelect&&) noexcept = default;
-VmOpSelect& VmOpSelect::operator=(VmOpSelect&&) = default;
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/platform_handle.h"
+// gen_amalgamated expanded: #include "perfetto/base/proc_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/event_fd.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/pipe.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"

-bool VmOpSelect::operator==(const VmOpSelect& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(cursor_, other.cursor_)
-   && ::protozero::internal::gen_helpers::EqualsField(relative_path_, other.relative_path_)
-   && ::protozero::internal::gen_helpers::EqualsField(create_if_not_exist_, other.create_if_not_exist_);
-}
+namespace perfetto {
+namespace base {

-int VmOpSelect::relative_path_size() const { return static_cast<int>(relative_path_.size()); }
-void VmOpSelect::clear_relative_path() { relative_path_.clear(); }
-VmOpSelect_PathComponent* VmOpSelect::add_relative_path() { relative_path_.emplace_back(); return &relative_path_.back(); }
-bool VmOpSelect::ParseFromArray(const void* raw, size_t size) {
-  relative_path_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
+// Handles creation and lifecycle management of subprocesses, taking care of
+// all subtleties involved in handling processes on UNIX.
+// This class allows to deal with macro two use-cases:
+// 1) fork() + exec() equivalent: for spawning a brand new process image.
+//    This happens when |args.exec_cmd| is not empty.
+//    This is safe to use even in a multi-threaded environment.
+// 2) fork(): for spawning a process and running a function.
+//    This happens when |args.posix_entrypoint_for_testing| is not empty.
+//    This is intended only for tests as it is extremely subtle.
+//    This mode must be used with extreme care. Before the entrypoint is
+//    invoked all file descriptors other than stdin/out/err and the ones
+//    specified in |args.preserve_fds| will be closed, to avoid each process
+//    retaining a dupe of other subprocesses pipes. This however means that
+//    any non trivial calls (including logging) must be avoided as they might
+//    refer to FDs that are now closed. The entrypoint should really be used
+//    just to signal a pipe or similar for synchronizing sequencing in tests.

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* cursor */:
-        field.get(&cursor_);
-        break;
-      case 2 /* relative_path */:
-        relative_path_.emplace_back();
-        relative_path_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 3 /* create_if_not_exist */:
-        field.get(&create_if_not_exist_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
-}
+//
+// This class allows to control stdin/out/err pipe redirection and takes care
+// of keeping all the pipes pumped (stdin) / drained (stdout/err), in a similar
+// fashion of python's subprocess.Communicate()
+// stdin: is always piped and closed once the |args.input| buffer is written.
+// stdout/err can be either:
+//   - dup()ed onto the parent process stdout/err.
+//   - redirected onto /dev/null.
+//   - piped onto a buffer (see output() method). There is only one output
+//     buffer in total. If both stdout and stderr are set to kBuffer mode, they
+//     will be merged onto the same. There doesn't seem any use case where they
+//     are needed distinctly.
+//
+// Some caveats worth mentioning:
+// - It always waitpid()s, to avoid leaving zombies around. If the process is
+//   not terminated by the time the destructor is reached, the dtor will
+//   send a SIGKILL and wait for the termination.
+// - After fork()-ing it will close all file descriptors, preserving only
+//   stdin/out/err and the fds listed in |args.preserve_fds|.
+// - On Linux/Android, the child process will be SIGKILL-ed if the calling
+//   thread exists, even if the Subprocess is std::move()-d onto another thread.
+//   This happens by virtue PR_SET_PDEATHSIG, which is used to avoid that
+//   child processes are leaked in the case of a crash of the parent (frequent
+//   in tests). However, the child process might still be leaked if execing
+//   a setuid/setgid binary (see man 2 prctl).
+//
+// Usage:
+// base::Subprocess p({"/bin/cat", "-"});
+// (or equivalently:
+//     base::Subprocess p;
+//     p.args.exec_cmd.push_back("/bin/cat");
+//     p.args.exec_cmd.push_back("-");
+//  )
+// p.args.stdout_mode = base::Subprocess::kBuffer;
+// p.args.stderr_mode = base::Subprocess::kInherit;
+// p.args.input = "stdin contents";
+// p.Call();
+// (or equivalently:
+//     p.Start();
+//     p.Wait();
+// )
+// EXPECT_EQ(p.status(), base::Subprocess::kTerminated);
+// EXPECT_EQ(p.returncode(), 0);
+class Subprocess {
+ public:
+  enum Status {
+    kNotStarted = 0,  // Before calling Start() or Call().
+    kRunning,         // After calling Start(), before Wait().
+    kTerminated,      // The subprocess terminated, either successfully or not.
+                      // This includes crashes or other signals on UNIX.
+  };

-std::string VmOpSelect::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+  enum class OutputMode {
+    kInherit = 0,  // Inherit's the caller process stdout/stderr.
+    kDevNull,      // dup() onto /dev/null.
+    kBuffer,       // dup() onto a pipe and move it into the output() buffer.
+    kFd,           // dup() onto the passed args.fd.
+  };

-std::vector<uint8_t> VmOpSelect::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  enum class InputMode {
+    kBuffer = 0,  // dup() onto a pipe and write args.input on it.
+    kDevNull,     // dup() onto /dev/null.
+  };

-void VmOpSelect::Serialize(::protozero::Message* msg) const {
-  // Field 1: cursor
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, cursor_, msg);
-  }
+  // Input arguments for configuring the subprocess behavior.
+  struct Args {
+    Args(std::initializer_list<std::string> _cmd = {}) : exec_cmd(_cmd) {}
+    Args(Args&&) noexcept;
+    Args& operator=(Args&&);
+    // If non-empty this will cause an exec() when Start()/Call() are called.
+    std::vector<std::string> exec_cmd;

-  // Field 2: relative_path
-  for (auto& it : relative_path_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
-  }
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    // If non-empty, it changes the argv[0] argument passed to exec. If
+    // unset, argv[0] == exec_cmd[0]. This is to handle cases like:
+    // exec_cmd = {"/proc/self/exec"}, argv0: "my_custom_test_override".
+    std::string posix_argv0_override_for_testing;

-  // Field 3: create_if_not_exist
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, create_if_not_exist_, msg);
-  }
+    // If non-empty this will be invoked on the fork()-ed child process, after
+    // stdin/out/err has been redirected and all other file descriptor are
+    // closed. It is valid to specify both |exec_cmd| AND
+    // |posix_entrypoint_for_testing|. In this case the latter will be invoked
+    // just before the exec() call, but after having closed all fds % stdin/o/e.
+    // This is for synchronization barriers in tests.
+    std::function<void()> posix_entrypoint_for_testing;

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+    // When set, will will move the process to the given process group. If set
+    // and zero, it will create a new process group. Effectively this calls
+    // setpgid(0 /*self_pid*/, posix_proc_group_id).
+    // This can be used to avoid that subprocesses receive CTRL-C from the
+    // terminal, while still living in the same session.
+    std::optional<pid_t> posix_proc_group_id{};
+#endif

+    // If non-empty, replaces the environment passed to exec().
+    std::vector<std::string> env;

-VmOpSelect_PathComponent::VmOpSelect_PathComponent() = default;
-VmOpSelect_PathComponent::~VmOpSelect_PathComponent() = default;
-VmOpSelect_PathComponent::VmOpSelect_PathComponent(const VmOpSelect_PathComponent&) = default;
-VmOpSelect_PathComponent& VmOpSelect_PathComponent::operator=(const VmOpSelect_PathComponent&) = default;
-VmOpSelect_PathComponent::VmOpSelect_PathComponent(VmOpSelect_PathComponent&&) noexcept = default;
-VmOpSelect_PathComponent& VmOpSelect_PathComponent::operator=(VmOpSelect_PathComponent&&) = default;
+    // The file descriptors in this list will not be closed.
+    std::vector<int> preserve_fds;

-bool VmOpSelect_PathComponent::operator==(const VmOpSelect_PathComponent& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_id_, other.field_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(array_index_, other.array_index_)
-   && ::protozero::internal::gen_helpers::EqualsField(map_key_field_id_, other.map_key_field_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(is_repeated_, other.is_repeated_)
-   && ::protozero::internal::gen_helpers::EqualsField(register_to_match_, other.register_to_match_)
-   && ::protozero::internal::gen_helpers::EqualsField(store_foreach_index_into_register_, other.store_foreach_index_into_register_);
-}
+    // The data to push in the child process stdin, if input_mode ==
+    // InputMode::kBuffer.
+    std::string input;

-bool VmOpSelect_PathComponent::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+    InputMode stdin_mode = InputMode::kBuffer;
+    OutputMode stdout_mode = OutputMode::kInherit;
+    OutputMode stderr_mode = OutputMode::kInherit;

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* field_id */:
-        field.get(&field_id_);
-        break;
-      case 2 /* array_index */:
-        field.get(&array_index_);
-        break;
-      case 3 /* map_key_field_id */:
-        field.get(&map_key_field_id_);
-        break;
-      case 5 /* is_repeated */:
-        field.get(&is_repeated_);
-        break;
-      case 6 /* register_to_match */:
-        field.get(&register_to_match_);
-        break;
-      case 7 /* store_foreach_index_into_register */:
-        field.get(&store_foreach_index_into_register_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
-}
+    base::ScopedPlatformHandle out_fd;

-std::string VmOpSelect_PathComponent::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+    // Returns " ".join(exec_cmd), quoting arguments.
+    std::string GetCmdString() const;
+  };

-std::vector<uint8_t> VmOpSelect_PathComponent::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  struct ResourceUsage {
+    uint32_t cpu_utime_ms = 0;
+    uint32_t cpu_stime_ms = 0;
+    uint32_t max_rss_kb = 0;
+    uint32_t min_page_faults = 0;
+    uint32_t maj_page_faults = 0;
+    uint32_t vol_ctx_switch = 0;
+    uint32_t invol_ctx_switch = 0;

-void VmOpSelect_PathComponent::Serialize(::protozero::Message* msg) const {
-  // Field 1: field_id
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, field_id_, msg);
-  }
+    uint32_t cpu_time_ms() const { return cpu_utime_ms + cpu_stime_ms; }
+  };

-  // Field 2: array_index
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, array_index_, msg);
-  }
+  explicit Subprocess(std::initializer_list<std::string> exec_cmd = {});
+  Subprocess(Subprocess&&) noexcept;
+  Subprocess& operator=(Subprocess&&);
+  ~Subprocess();  // It will KillAndWaitForTermination() if still alive.

-  // Field 3: map_key_field_id
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, map_key_field_id_, msg);
-  }
+  // Starts the subprocess but doesn't wait for its termination. The caller
+  // is expected to either call Wait() or Poll() after this call.
+  void Start();

-  // Field 5: is_repeated
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, is_repeated_, msg);
-  }
+  // Wait for process termination. Can be called more than once.
+  // Args:
+  //   |timeout_ms| = 0: wait indefinitely.
+  //   |timeout_ms| > 0: wait for at most |timeout_ms|.
+  // Returns:
+  //  True: The process terminated. See status() and returncode().
+  //  False: Timeout reached, the process is still running. In this case the
+  //         process will be left in the kRunning state.
+  bool Wait(int timeout_ms = 0);

-  // Field 6: register_to_match
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, register_to_match_, msg);
-  }
+  // Equivalent of Start() + Wait();
+  // Returns true if the process exited cleanly with return code 0. False in
+  // any othe case.
+  bool Call(int timeout_ms = 0);

-  // Field 7: store_foreach_index_into_register
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, store_foreach_index_into_register_, msg);
-  }
+  Status Poll();

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+  // Kills the process (SIGKILL if not specified) but doesn't wait for its
+  // termination.
+  void Kill(int sig_num = 0);

+  // Sends a signal (SIGKILL if not specified) and wait for process termination.
+  void KillAndWaitForTermination(int sig_num = 0);

-VmInstruction::VmInstruction() = default;
-VmInstruction::~VmInstruction() = default;
-VmInstruction::VmInstruction(const VmInstruction&) = default;
-VmInstruction& VmInstruction::operator=(const VmInstruction&) = default;
-VmInstruction::VmInstruction(VmInstruction&&) noexcept = default;
-VmInstruction& VmInstruction::operator=(VmInstruction&&) = default;
+  PlatformProcessId pid() const { return s_->pid; }

-bool VmInstruction::operator==(const VmInstruction& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(select_, other.select_)
-   && ::protozero::internal::gen_helpers::EqualsField(reg_load_, other.reg_load_)
-   && ::protozero::internal::gen_helpers::EqualsField(merge_, other.merge_)
-   && ::protozero::internal::gen_helpers::EqualsField(set_, other.set_)
-   && ::protozero::internal::gen_helpers::EqualsField(del_, other.del_)
-   && ::protozero::internal::gen_helpers::EqualsField(abort_level_, other.abort_level_)
-   && ::protozero::internal::gen_helpers::EqualsField(nested_instructions_, other.nested_instructions_);
-}
+  // The accessors below are updated only after a call to Poll(), Wait() or
+  // KillAndWaitForTermination().
+  // In most cases you want to call Poll() rather than these accessors.

-int VmInstruction::nested_instructions_size() const { return static_cast<int>(nested_instructions_.size()); }
-void VmInstruction::clear_nested_instructions() { nested_instructions_.clear(); }
-VmInstruction* VmInstruction::add_nested_instructions() { nested_instructions_.emplace_back(); return &nested_instructions_.back(); }
-bool VmInstruction::ParseFromArray(const void* raw, size_t size) {
-  nested_instructions_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
+  Status status() const { return s_->status; }
+  int returncode() const { return s_->returncode; }
+  bool timed_out() const { return s_->timed_out; }

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* select */:
-        (*select_).ParseFromArray(field.data(), field.size());
-        break;
-      case 2 /* reg_load */:
-        (*reg_load_).ParseFromArray(field.data(), field.size());
-        break;
-      case 3 /* merge */:
-        (*merge_).ParseFromArray(field.data(), field.size());
-        break;
-      case 4 /* set */:
-        (*set_).ParseFromArray(field.data(), field.size());
-        break;
-      case 5 /* del */:
-        (*del_).ParseFromArray(field.data(), field.size());
-        break;
-      case 6 /* abort_level */:
-        field.get(&abort_level_);
-        break;
-      case 7 /* nested_instructions */:
-        nested_instructions_.emplace_back();
-        nested_instructions_.back().ParseFromArray(field.data(), field.size());
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
-}
+  // This contains both stdout and stderr (if the corresponding _mode ==
+  // OutputMode::kBuffer). It's non-const so the caller can std::move() it.
+  std::string& output() { return s_->output; }
+  const std::string& output() const { return s_->output; }

-std::string VmInstruction::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+  const ResourceUsage& posix_rusage() const { return *s_->rusage; }

-std::vector<uint8_t> VmInstruction::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  Args args;

-void VmInstruction::Serialize(::protozero::Message* msg) const {
-  // Field 1: select
-  if (_has_field_[1]) {
-    (*select_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
-  }
+ private:
+  // The signal/exit code used when killing the process in case of a timeout.
+  static const int kTimeoutSignal;

-  // Field 2: reg_load
-  if (_has_field_[2]) {
-    (*reg_load_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
-  }
+  Subprocess(const Subprocess&) = delete;
+  Subprocess& operator=(const Subprocess&) = delete;

-  // Field 3: merge
-  if (_has_field_[3]) {
-    (*merge_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
-  }
+  // This is to deal robustly with the move operators, without having to
+  // manually maintain member-wise move instructions.
+  struct MovableState {
+    base::Pipe stdin_pipe;
+    base::Pipe stdouterr_pipe;
+    PlatformProcessId pid;
+    Status status = kNotStarted;
+    int returncode = -1;
+    std::string output;  // Stdin+stderr. Only when OutputMode::kBuffer.
+    std::unique_ptr<ResourceUsage> rusage{new ResourceUsage()};
+    bool timed_out = false;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    std::thread stdouterr_thread;
+    std::thread stdin_thread;
+    ScopedPlatformHandle win_proc_handle;
+    ScopedPlatformHandle win_thread_handle;

-  // Field 4: set
-  if (_has_field_[4]) {
-    (*set_).Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
-  }
+    base::EventFd stdouterr_done_event;
+    std::mutex mutex;  // Protects locked_outerr_buf and the two pipes.
+    std::string locked_outerr_buf;
+#else
+    base::Pipe exit_status_pipe;
+    size_t input_written = 0;
+    std::thread waitpid_thread;
+#endif
+  };

-  // Field 5: del
-  if (_has_field_[5]) {
-    (*del_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
-  }
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  static void StdinThread(MovableState*, const std::string& input);
+  static void StdoutErrThread(MovableState*);
+#else
+  void TryPushStdin();
+  void TryReadStdoutAndErr();
+  void TryReadExitStatus();
+  bool PollInternal(int poll_timeout_ms);
+#endif

-  // Field 6: abort_level
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, abort_level_, msg);
-  }
+  std::unique_ptr<MovableState> s_;
+};

-  // Field 7: nested_instructions
-  for (auto& it : nested_instructions_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(7));
-  }
+}  // namespace base
+}  // namespace perfetto

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+#endif  // INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */

+// gen_amalgamated expanded: #include "perfetto/ext/base/subprocess.h"

-VmProgram::VmProgram() = default;
-VmProgram::~VmProgram() = default;
-VmProgram::VmProgram(const VmProgram&) = default;
-VmProgram& VmProgram::operator=(const VmProgram&) = default;
-VmProgram::VmProgram(VmProgram&&) noexcept = default;
-VmProgram& VmProgram::operator=(VmProgram&&) = default;
+#include <tuple>

-bool VmProgram::operator==(const VmProgram& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(version_, other.version_)
-   && ::protozero::internal::gen_helpers::EqualsField(instructions_, other.instructions_);
-}
+// This file contains only the common bits (ctors / dtors / move operators).
+// The rest lives in subprocess_posix.cc and subprocess_windows.cc.

-int VmProgram::instructions_size() const { return static_cast<int>(instructions_.size()); }
-void VmProgram::clear_instructions() { instructions_.clear(); }
-VmInstruction* VmProgram::add_instructions() { instructions_.emplace_back(); return &instructions_.back(); }
-bool VmProgram::ParseFromArray(const void* raw, size_t size) {
-  instructions_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
+namespace perfetto {
+namespace base {

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* version */:
-        field.get(&version_);
-        break;
-      case 2 /* instructions */:
-        instructions_.emplace_back();
-        instructions_.back().ParseFromArray(field.data(), field.size());
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
+Subprocess::Args::Args(Args&&) noexcept = default;
+Subprocess::Args& Subprocess::Args::operator=(Args&&) = default;
+
+Subprocess::Subprocess(std::initializer_list<std::string> a)
+    : args(a), s_(new MovableState()) {}
+
+Subprocess::Subprocess(Subprocess&& other) noexcept {
+  static_assert(sizeof(Subprocess) ==
+                    sizeof(std::tuple<std::unique_ptr<MovableState>, Args>),
+                "base::Subprocess' move ctor needs updating");
+  s_ = std::move(other.s_);
+  args = std::move(other.args);
+
+  // Reset the state of the moved-from object.
+  other.s_.reset(new MovableState());
+  other.~Subprocess();
+  new (&other) Subprocess();
 }

-std::string VmProgram::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
+Subprocess& Subprocess::operator=(Subprocess&& other) {
+  this->~Subprocess();
+  new (this) Subprocess(std::move(other));
+  return *this;
 }

-std::vector<uint8_t> VmProgram::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+Subprocess::~Subprocess() {
+  if (s_->status == kRunning)
+    KillAndWaitForTermination();
 }

-void VmProgram::Serialize(::protozero::Message* msg) const {
-  // Field 1: version
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, version_, msg);
-  }
+bool Subprocess::Call(int timeout_ms) {
+  PERFETTO_CHECK(s_->status == kNotStarted);
+  Start();

-  // Field 2: instructions
-  for (auto& it : instructions_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+  if (!Wait(timeout_ms)) {
+    s_->timed_out = true;
+    KillAndWaitForTermination(kTimeoutSignal);
   }
+  PERFETTO_DCHECK(s_->status != kRunning);
+  return s_->status == kTerminated && s_->returncode == 0;
+}

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+std::string Subprocess::Args::GetCmdString() const {
+  std::string str;
+  for (size_t i = 0; i < exec_cmd.size(); i++) {
+    str += i > 0 ? " \"" : "";
+    str += exec_cmd[i];
+    str += i > 0 ? "\"" : "";
+  }
+  return str;
 }

+}  // namespace base
 }  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/android_energy_consumer_descriptor.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/android_energy_consumer_descriptor.gen.h"
+// gen_amalgamated begin source: src/base/subprocess_posix.cc
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */

-namespace perfetto {
-namespace protos {
-namespace gen {
+// gen_amalgamated expanded: #include "perfetto/ext/base/subprocess.h"

-AndroidEnergyConsumerDescriptor::AndroidEnergyConsumerDescriptor() = default;
-AndroidEnergyConsumerDescriptor::~AndroidEnergyConsumerDescriptor() = default;
-AndroidEnergyConsumerDescriptor::AndroidEnergyConsumerDescriptor(const AndroidEnergyConsumerDescriptor&) = default;
-AndroidEnergyConsumerDescriptor& AndroidEnergyConsumerDescriptor::operator=(const AndroidEnergyConsumerDescriptor&) = default;
-AndroidEnergyConsumerDescriptor::AndroidEnergyConsumerDescriptor(AndroidEnergyConsumerDescriptor&&) noexcept = default;
-AndroidEnergyConsumerDescriptor& AndroidEnergyConsumerDescriptor::operator=(AndroidEnergyConsumerDescriptor&&) = default;
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-bool AndroidEnergyConsumerDescriptor::operator==(const AndroidEnergyConsumerDescriptor& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(energy_consumers_, other.energy_consumers_);
-}
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)

-int AndroidEnergyConsumerDescriptor::energy_consumers_size() const { return static_cast<int>(energy_consumers_.size()); }
-void AndroidEnergyConsumerDescriptor::clear_energy_consumers() { energy_consumers_.clear(); }
-AndroidEnergyConsumer* AndroidEnergyConsumerDescriptor::add_energy_consumers() { energy_consumers_.emplace_back(); return &energy_consumers_.back(); }
-bool AndroidEnergyConsumerDescriptor::ParseFromArray(const void* raw, size_t size) {
-  energy_consumers_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* energy_consumers */:
-        energy_consumers_.emplace_back();
-        energy_consumers_.back().ParseFromArray(field.data(), field.size());
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
-}
+#include <algorithm>
+#include <thread>
+#include <tuple>

-std::string AndroidEnergyConsumerDescriptor::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/prctl.h>
+#endif

-std::vector<uint8_t> AndroidEnergyConsumerDescriptor::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"

-void AndroidEnergyConsumerDescriptor::Serialize(::protozero::Message* msg) const {
-  // Field 1: energy_consumers
-  for (auto& it : energy_consumers_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
-  }
+// In MacOS this is not defined in any header.
+extern "C" char** environ;

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+namespace perfetto {
+namespace base {

+namespace {

-AndroidEnergyConsumer::AndroidEnergyConsumer() = default;
-AndroidEnergyConsumer::~AndroidEnergyConsumer() = default;
-AndroidEnergyConsumer::AndroidEnergyConsumer(const AndroidEnergyConsumer&) = default;
-AndroidEnergyConsumer& AndroidEnergyConsumer::operator=(const AndroidEnergyConsumer&) = default;
-AndroidEnergyConsumer::AndroidEnergyConsumer(AndroidEnergyConsumer&&) noexcept = default;
-AndroidEnergyConsumer& AndroidEnergyConsumer::operator=(AndroidEnergyConsumer&&) = default;
+struct ChildProcessArgs {
+  Subprocess::Args* create_args;
+  const char* exec_cmd = nullptr;
+  std::vector<char*> argv;
+  std::vector<char*> env;
+  int stdin_pipe_rd = -1;
+  int stdouterr_pipe_wr = -1;
+};

-bool AndroidEnergyConsumer::operator==(const AndroidEnergyConsumer& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(energy_consumer_id_, other.energy_consumer_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(ordinal_, other.ordinal_)
-   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
-}
+// Don't add any dynamic allocation in this function. This will be invoked
+// under a fork(), potentially in a state where the allocator lock is held.
+void __attribute__((noreturn)) ChildProcess(ChildProcessArgs* args) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  // In no case we want a child process to outlive its parent process. This is
+  // relevant for tests, so that a test failure/crash doesn't leave child
+  // processes around that get reparented to init.
+  prctl(PR_SET_PDEATHSIG, SIGKILL);
+#endif

-bool AndroidEnergyConsumer::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+  auto die = [args](const char* err) __attribute__((noreturn)) {
+    base::ignore_result(write(args->stdouterr_pipe_wr, err, strlen(err)));
+    base::ignore_result(write(args->stdouterr_pipe_wr, "\n", 1));
+    // From https://www.gnu.org/software/libc/manual/html_node/Exit-Status.html
+    // "In particular, the value 128 is used to indicate failure to execute
+    // another program in a subprocess. This convention is not universally
+    // obeyed, but it is a good idea to follow it in your programs."
+    _exit(128);
+  };

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* energy_consumer_id */:
-        field.get(&energy_consumer_id_);
-        break;
-      case 2 /* ordinal */:
-        field.get(&ordinal_);
-        break;
-      case 3 /* type */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &type_);
-        break;
-      case 4 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
+  if (args->create_args->posix_proc_group_id.has_value()) {
+    if (setpgid(0 /*self*/, args->create_args->posix_proc_group_id.value())) {
+      die("setpgid() failed");
     }
   }
-  return !packed_error && !dec.bytes_left();
-}

-std::string AndroidEnergyConsumer::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+  auto set_fd_close_on_exec = [&die](int fd, bool close_on_exec) {
+    int flags = fcntl(fd, F_GETFD, 0);
+    if (flags < 0)
+      die("fcntl(F_GETFD) failed");
+    flags = close_on_exec ? (flags | FD_CLOEXEC) : (flags & ~FD_CLOEXEC);
+    if (fcntl(fd, F_SETFD, flags) < 0)
+      die("fcntl(F_SETFD) failed");
+  };

-std::vector<uint8_t> AndroidEnergyConsumer::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  if (getppid() == 1)
+    die("terminating because parent process died");

-void AndroidEnergyConsumer::Serialize(::protozero::Message* msg) const {
-  // Field 1: energy_consumer_id
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, energy_consumer_id_, msg);
+  switch (args->create_args->stdin_mode) {
+    case Subprocess::InputMode::kBuffer:
+      if (dup2(args->stdin_pipe_rd, STDIN_FILENO) == -1)
+        die("Failed to dup2(STDIN)");
+      close(args->stdin_pipe_rd);
+      break;
+    case Subprocess::InputMode::kDevNull:
+      if (dup2(open("/dev/null", O_RDONLY), STDIN_FILENO) == -1)
+        die("Failed to dup2(STDOUT)");
+      break;
   }

-  // Field 2: ordinal
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, ordinal_, msg);
+  switch (args->create_args->stdout_mode) {
+    case Subprocess::OutputMode::kInherit:
+      break;
+    case Subprocess::OutputMode::kDevNull: {
+      if (dup2(open("/dev/null", O_RDWR), STDOUT_FILENO) == -1)
+        die("Failed to dup2(STDOUT)");
+      break;
+    }
+    case Subprocess::OutputMode::kBuffer:
+      if (dup2(args->stdouterr_pipe_wr, STDOUT_FILENO) == -1)
+        die("Failed to dup2(STDOUT)");
+      break;
+    case Subprocess::OutputMode::kFd:
+      if (dup2(*args->create_args->out_fd, STDOUT_FILENO) == -1)
+        die("Failed to dup2(STDOUT)");
+      break;
   }

-  // Field 3: type
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeString(3, type_, msg);
+  switch (args->create_args->stderr_mode) {
+    case Subprocess::OutputMode::kInherit:
+      break;
+    case Subprocess::OutputMode::kDevNull: {
+      if (dup2(open("/dev/null", O_RDWR), STDERR_FILENO) == -1)
+        die("Failed to dup2(STDERR)");
+      break;
+    }
+    case Subprocess::OutputMode::kBuffer:
+      if (dup2(args->stdouterr_pipe_wr, STDERR_FILENO) == -1)
+        die("Failed to dup2(STDERR)");
+      break;
+    case Subprocess::OutputMode::kFd:
+      if (dup2(*args->create_args->out_fd, STDERR_FILENO) == -1)
+        die("Failed to dup2(STDERR)");
+      break;
   }

-  // Field 4: name
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeString(4, name_, msg);
+  // Close all FDs % stdin/out/err and the ones that the client explicitly
+  // asked to retain. The reason for this is twofold:
+  // 1. For exec-only (i.e. entrypoint == empty) cases: it avoids leaking FDs
+  //    that didn't get marked as O_CLOEXEC by accident.
+  // 2. In fork() mode (entrypoint not empty) avoids retaining a dup of eventfds
+  //    that would prevent the parent process to receive EOFs (tests usually use
+  //    pipes as a synchronization mechanism between subprocesses).
+  const auto& preserve_fds = args->create_args->preserve_fds;
+  for (int i = 0; i < 512; i++) {
+    if (i != STDIN_FILENO && i != STDERR_FILENO && i != STDOUT_FILENO &&
+        i != args->stdouterr_pipe_wr &&
+        !std::count(preserve_fds.begin(), preserve_fds.end(), i)) {
+      close(i);
+    }
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+  // Clears O_CLOEXEC from stdin/out/err and the |preserve_fds| list. These are
+  // the only FDs that we want to be preserved after the exec().
+  set_fd_close_on_exec(STDIN_FILENO, false);
+  set_fd_close_on_exec(STDOUT_FILENO, false);
+  set_fd_close_on_exec(STDERR_FILENO, false);

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/android_log_constants.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/android_log_constants.gen.h"
+  for (auto fd : preserve_fds)
+    set_fd_close_on_exec(fd, false);

-namespace perfetto {
-namespace protos {
-namespace gen {
-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/builtin_clock.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/builtin_clock.gen.h"
+  // If the caller specified a std::function entrypoint, run that first.
+  if (args->create_args->posix_entrypoint_for_testing)
+    args->create_args->posix_entrypoint_for_testing();

-namespace perfetto {
-namespace protos {
-namespace gen {
-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/commit_data_request.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/commit_data_request.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {
+  // If the caller specified only an entrypoint, without any args, exit now.
+  // Otherwise proceed with the exec() below.
+  if (!args->exec_cmd)
+    _exit(0);

-CommitDataRequest::CommitDataRequest() = default;
-CommitDataRequest::~CommitDataRequest() = default;
-CommitDataRequest::CommitDataRequest(const CommitDataRequest&) = default;
-CommitDataRequest& CommitDataRequest::operator=(const CommitDataRequest&) = default;
-CommitDataRequest::CommitDataRequest(CommitDataRequest&&) noexcept = default;
-CommitDataRequest& CommitDataRequest::operator=(CommitDataRequest&&) = default;
+  // If |args[0]| is a path use execv() (which takes a path), otherwise use
+  // exevp(), which uses the shell and follows PATH.
+  if (strchr(args->exec_cmd, '/')) {
+    char** env = args->env.empty() ? environ : args->env.data();
+    execve(args->exec_cmd, args->argv.data(), env);
+  } else {
+    // There is no execvpe() on Mac.
+    if (!args->env.empty())
+      die("A full path is required for |exec_cmd| when setting |env|");
+    execvp(args->exec_cmd, args->argv.data());
+  }

-bool CommitDataRequest::operator==(const CommitDataRequest& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunks_to_move_, other.chunks_to_move_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunks_to_patch_, other.chunks_to_patch_)
-   && ::protozero::internal::gen_helpers::EqualsField(flush_request_id_, other.flush_request_id_);
+  // Reached only if execv fails.
+  die("execve() failed");
 }

-int CommitDataRequest::chunks_to_move_size() const { return static_cast<int>(chunks_to_move_.size()); }
-void CommitDataRequest::clear_chunks_to_move() { chunks_to_move_.clear(); }
-CommitDataRequest_ChunksToMove* CommitDataRequest::add_chunks_to_move() { chunks_to_move_.emplace_back(); return &chunks_to_move_.back(); }
-int CommitDataRequest::chunks_to_patch_size() const { return static_cast<int>(chunks_to_patch_.size()); }
-void CommitDataRequest::clear_chunks_to_patch() { chunks_to_patch_.clear(); }
-CommitDataRequest_ChunkToPatch* CommitDataRequest::add_chunks_to_patch() { chunks_to_patch_.emplace_back(); return &chunks_to_patch_.back(); }
-bool CommitDataRequest::ParseFromArray(const void* raw, size_t size) {
-  chunks_to_move_.clear();
-  chunks_to_patch_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
+}  // namespace

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* chunks_to_move */:
-        chunks_to_move_.emplace_back();
-        chunks_to_move_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 2 /* chunks_to_patch */:
-        chunks_to_patch_.emplace_back();
-        chunks_to_patch_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 3 /* flush_request_id */:
-        field.get(&flush_request_id_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
-}
+// static
+const int Subprocess::kTimeoutSignal = SIGKILL;

-std::string CommitDataRequest::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+void Subprocess::Start() {
+  ChildProcessArgs proc_args;
+  proc_args.create_args = &args;

-std::vector<uint8_t> CommitDataRequest::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  // Setup argv.
+  if (!args.exec_cmd.empty()) {
+    proc_args.exec_cmd = args.exec_cmd[0].c_str();
+    for (const std::string& arg : args.exec_cmd)
+      proc_args.argv.push_back(const_cast<char*>(arg.c_str()));
+    proc_args.argv.push_back(nullptr);

-void CommitDataRequest::Serialize(::protozero::Message* msg) const {
-  // Field 1: chunks_to_move
-  for (auto& it : chunks_to_move_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+    if (!args.posix_argv0_override_for_testing.empty()) {
+      proc_args.argv[0] =
+          const_cast<char*>(args.posix_argv0_override_for_testing.c_str());
+    }
   }

-  // Field 2: chunks_to_patch
-  for (auto& it : chunks_to_patch_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+  // Setup env.
+  if (!args.env.empty()) {
+    for (const std::string& str : args.env)
+      proc_args.env.push_back(const_cast<char*>(str.c_str()));
+    proc_args.env.push_back(nullptr);
   }

-  // Field 3: flush_request_id
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, flush_request_id_, msg);
+  // Setup the pipes for stdin/err redirection.
+  if (args.stdin_mode == InputMode::kBuffer) {
+    s_->stdin_pipe = base::Pipe::Create(base::Pipe::kWrNonBlock);
+    proc_args.stdin_pipe_rd = *s_->stdin_pipe.rd;
   }
+  s_->stdouterr_pipe = base::Pipe::Create(base::Pipe::kRdNonBlock);
+  proc_args.stdouterr_pipe_wr = *s_->stdouterr_pipe.wr;

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
-
+  // Spawn the child process that will exec().
+  s_->pid = fork();
+  PERFETTO_CHECK(s_->pid >= 0);
+  if (s_->pid == 0) {
+    // Close the parent-ends of the pipes.
+    s_->stdin_pipe.wr.reset();
+    s_->stdouterr_pipe.rd.reset();
+    ChildProcess(&proc_args);
+    // ChildProcess() doesn't return, not even in case of failures.
+    PERFETTO_FATAL("not reached");
+  }

-CommitDataRequest_ChunkToPatch::CommitDataRequest_ChunkToPatch() = default;
-CommitDataRequest_ChunkToPatch::~CommitDataRequest_ChunkToPatch() = default;
-CommitDataRequest_ChunkToPatch::CommitDataRequest_ChunkToPatch(const CommitDataRequest_ChunkToPatch&) = default;
-CommitDataRequest_ChunkToPatch& CommitDataRequest_ChunkToPatch::operator=(const CommitDataRequest_ChunkToPatch&) = default;
-CommitDataRequest_ChunkToPatch::CommitDataRequest_ChunkToPatch(CommitDataRequest_ChunkToPatch&&) noexcept = default;
-CommitDataRequest_ChunkToPatch& CommitDataRequest_ChunkToPatch::operator=(CommitDataRequest_ChunkToPatch&&) = default;
+  s_->status = kRunning;

-bool CommitDataRequest_ChunkToPatch::operator==(const CommitDataRequest_ChunkToPatch& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(target_buffer_, other.target_buffer_)
-   && ::protozero::internal::gen_helpers::EqualsField(writer_id_, other.writer_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunk_id_, other.chunk_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(patches_, other.patches_)
-   && ::protozero::internal::gen_helpers::EqualsField(has_more_patches_, other.has_more_patches_);
-}
+  // Close the child-end of the pipes.
+  // Deliberately NOT closing the s_->stdin_pipe.rd. This is to avoid crashing
+  // with a SIGPIPE if the process exits without consuming its stdin, while
+  // the parent tries to write() on the other end of the stdin pipe.
+  s_->stdouterr_pipe.wr.reset();
+  proc_args.create_args->out_fd.reset();

-int CommitDataRequest_ChunkToPatch::patches_size() const { return static_cast<int>(patches_.size()); }
-void CommitDataRequest_ChunkToPatch::clear_patches() { patches_.clear(); }
-CommitDataRequest_ChunkToPatch_Patch* CommitDataRequest_ChunkToPatch::add_patches() { patches_.emplace_back(); return &patches_.back(); }
-bool CommitDataRequest_ChunkToPatch::ParseFromArray(const void* raw, size_t size) {
-  patches_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
+  // Spawn a thread that is blocked on waitpid() and writes the termination
+  // status onto a pipe. The problem here is that waipid() doesn't have a
+  // timeout option and can't be passed to poll(). The alternative would be
+  // using a SIGCHLD handler, but anecdotally signal handlers introduce more
+  // problems than what they solve.
+  s_->exit_status_pipe = base::Pipe::Create(base::Pipe::kRdNonBlock);

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* target_buffer */:
-        field.get(&target_buffer_);
-        break;
-      case 2 /* writer_id */:
-        field.get(&writer_id_);
-        break;
-      case 3 /* chunk_id */:
-        field.get(&chunk_id_);
-        break;
-      case 4 /* patches */:
-        patches_.emplace_back();
-        patches_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 5 /* has_more_patches */:
-        field.get(&has_more_patches_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
-}
+  // Both ends of the pipe are closed after the thread.join().
+  int pid = s_->pid;
+  int exit_status_pipe_wr = s_->exit_status_pipe.wr.release();
+  auto* rusage = s_->rusage.get();
+  s_->waitpid_thread = std::thread([pid, exit_status_pipe_wr, rusage] {
+    int pid_stat = -1;
+    struct rusage usg{};
+    int wait_res = PERFETTO_EINTR(wait4(pid, &pid_stat, 0, &usg));
+    PERFETTO_CHECK(wait_res == pid);

-std::string CommitDataRequest_ChunkToPatch::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+    auto tv_to_ms = [](const struct timeval& tv) {
+      return static_cast<uint32_t>(tv.tv_sec * 1000 + tv.tv_usec / 1000);
+    };
+    rusage->cpu_utime_ms = tv_to_ms(usg.ru_utime);
+    rusage->cpu_stime_ms = tv_to_ms(usg.ru_stime);
+    rusage->max_rss_kb = static_cast<uint32_t>(usg.ru_maxrss) / 1000;
+    rusage->min_page_faults = static_cast<uint32_t>(usg.ru_minflt);
+    rusage->maj_page_faults = static_cast<uint32_t>(usg.ru_majflt);
+    rusage->vol_ctx_switch = static_cast<uint32_t>(usg.ru_nvcsw);
+    rusage->invol_ctx_switch = static_cast<uint32_t>(usg.ru_nivcsw);

-std::vector<uint8_t> CommitDataRequest_ChunkToPatch::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+    base::ignore_result(PERFETTO_EINTR(
+        write(exit_status_pipe_wr, &pid_stat, sizeof(pid_stat))));
+    PERFETTO_CHECK(close(exit_status_pipe_wr) == 0 || errno == EINTR);
+  });
 }

-void CommitDataRequest_ChunkToPatch::Serialize(::protozero::Message* msg) const {
-  // Field 1: target_buffer
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, target_buffer_, msg);
-  }
-
-  // Field 2: writer_id
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, writer_id_, msg);
+Subprocess::Status Subprocess::Poll() {
+  if (s_->status != kRunning)
+    return s_->status;  // Nothing to poll.
+  while (PollInternal(0 /* don't block*/)) {
   }
+  return s_->status;
+}

-  // Field 3: chunk_id
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, chunk_id_, msg);
+// |timeout_ms| semantic:
+//   -1: Block indefinitely.
+//    0: Don't block, return immediately.
+//   >0: Block for at most X ms.
+// Returns:
+//  True: Read at least one fd (so there might be more queued).
+//  False: if all fds reached quiescent (no data to read/write).
+bool Subprocess::PollInternal(int poll_timeout_ms) {
+  struct pollfd fds[3]{};
+  size_t num_fds = 0;
+  if (s_->exit_status_pipe.rd) {
+    fds[num_fds].fd = *s_->exit_status_pipe.rd;
+    fds[num_fds].events = POLLIN;
+    num_fds++;
   }
-
-  // Field 4: patches
-  for (auto& it : patches_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
+  if (s_->stdouterr_pipe.rd) {
+    fds[num_fds].fd = *s_->stdouterr_pipe.rd;
+    fds[num_fds].events = POLLIN;
+    num_fds++;
   }
-
-  // Field 5: has_more_patches
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, has_more_patches_, msg);
+  if (s_->stdin_pipe.wr) {
+    fds[num_fds].fd = *s_->stdin_pipe.wr;
+    fds[num_fds].events = POLLOUT;
+    num_fds++;
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+  if (num_fds == 0)
+    return false;

+  auto nfds = static_cast<nfds_t>(num_fds);
+  int poll_res = PERFETTO_EINTR(poll(fds, nfds, poll_timeout_ms));
+  PERFETTO_CHECK(poll_res >= 0);

-CommitDataRequest_ChunkToPatch_Patch::CommitDataRequest_ChunkToPatch_Patch() = default;
-CommitDataRequest_ChunkToPatch_Patch::~CommitDataRequest_ChunkToPatch_Patch() = default;
-CommitDataRequest_ChunkToPatch_Patch::CommitDataRequest_ChunkToPatch_Patch(const CommitDataRequest_ChunkToPatch_Patch&) = default;
-CommitDataRequest_ChunkToPatch_Patch& CommitDataRequest_ChunkToPatch_Patch::operator=(const CommitDataRequest_ChunkToPatch_Patch&) = default;
-CommitDataRequest_ChunkToPatch_Patch::CommitDataRequest_ChunkToPatch_Patch(CommitDataRequest_ChunkToPatch_Patch&&) noexcept = default;
-CommitDataRequest_ChunkToPatch_Patch& CommitDataRequest_ChunkToPatch_Patch::operator=(CommitDataRequest_ChunkToPatch_Patch&&) = default;
+  TryReadStdoutAndErr();
+  TryPushStdin();
+  TryReadExitStatus();

-bool CommitDataRequest_ChunkToPatch_Patch::operator==(const CommitDataRequest_ChunkToPatch_Patch& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(offset_, other.offset_)
-   && ::protozero::internal::gen_helpers::EqualsField(data_, other.data_);
+  return poll_res > 0;
 }

-bool CommitDataRequest_ChunkToPatch_Patch::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+bool Subprocess::Wait(int timeout_ms) {
+  PERFETTO_CHECK(s_->status != kNotStarted);

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* offset */:
-        field.get(&offset_);
-        break;
-      case 2 /* data */:
-        field.get(&data_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
+  // Break out of the loop only after both conditions are satisfied:
+  // - All stdout/stderr data has been read (if kBuffer).
+  // - The process exited.
+  // Note that the two events can happen arbitrary order. After the process
+  // exits, there might be still data in the pipe buffer, which we want to
+  // read fully.
+  //
+  // Instead, don't wait on the stdin to be fully written. The child process
+  // might exit prematurely (or crash). If that happens, we can end up in a
+  // state where the write(stdin_pipe_.wr) will never unblock.
+
+  const int64_t t_start = base::GetWallTimeMs().count();
+  while (s_->exit_status_pipe.rd || s_->stdouterr_pipe.rd) {
+    int poll_timeout_ms = -1;  // Block until a FD is ready.
+    if (timeout_ms > 0) {
+      const int64_t now = GetWallTimeMs().count();
+      poll_timeout_ms = timeout_ms - static_cast<int>(now - t_start);
+      if (poll_timeout_ms <= 0)
+        return false;
     }
-  }
-  return !packed_error && !dec.bytes_left();
+    PollInternal(poll_timeout_ms);
+  }  // while(...)
+  return true;
 }

-std::string CommitDataRequest_ChunkToPatch_Patch::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+void Subprocess::TryReadExitStatus() {
+  if (!s_->exit_status_pipe.rd)
+    return;

-std::vector<uint8_t> CommitDataRequest_ChunkToPatch_Patch::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  int pid_stat = -1;
+  int64_t rsize = PERFETTO_EINTR(
+      read(*s_->exit_status_pipe.rd, &pid_stat, sizeof(pid_stat)));
+  if (rsize < 0 && errno == EAGAIN)
+    return;

-void CommitDataRequest_ChunkToPatch_Patch::Serialize(::protozero::Message* msg) const {
-  // Field 1: offset
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, offset_, msg);
+  if (rsize > 0) {
+    PERFETTO_CHECK(rsize == sizeof(pid_stat));
+  } else if (rsize < 0) {
+    PERFETTO_PLOG("Subprocess read(s_->exit_status_pipe) failed");
   }
+  s_->waitpid_thread.join();
+  s_->exit_status_pipe.rd.reset();

-  // Field 2: data
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, data_, msg);
+  s_->status = kTerminated;
+  if (WIFEXITED(pid_stat)) {
+    s_->returncode = WEXITSTATUS(pid_stat);
+  } else if (WIFSIGNALED(pid_stat)) {
+    s_->returncode = 128 + WTERMSIG(pid_stat);  // Follow bash convention.
+  } else {
+    PERFETTO_FATAL("waitpid() returned an unexpected value (%d)", pid_stat);
   }
-
-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+// If the stidn pipe is still open, push input data and close it at the end.
+void Subprocess::TryPushStdin() {
+  if (!s_->stdin_pipe.wr)
+    return;

-CommitDataRequest_ChunksToMove::CommitDataRequest_ChunksToMove() = default;
-CommitDataRequest_ChunksToMove::~CommitDataRequest_ChunksToMove() = default;
-CommitDataRequest_ChunksToMove::CommitDataRequest_ChunksToMove(const CommitDataRequest_ChunksToMove&) = default;
-CommitDataRequest_ChunksToMove& CommitDataRequest_ChunksToMove::operator=(const CommitDataRequest_ChunksToMove&) = default;
-CommitDataRequest_ChunksToMove::CommitDataRequest_ChunksToMove(CommitDataRequest_ChunksToMove&&) noexcept = default;
-CommitDataRequest_ChunksToMove& CommitDataRequest_ChunksToMove::operator=(CommitDataRequest_ChunksToMove&&) = default;
+  PERFETTO_DCHECK(args.input.empty() || s_->input_written < args.input.size());
+  if (!args.input.empty()) {
+    int64_t wsize =
+        PERFETTO_EINTR(write(*s_->stdin_pipe.wr, &args.input[s_->input_written],
+                             args.input.size() - s_->input_written));
+    if (wsize < 0 && errno == EAGAIN)
+      return;

-bool CommitDataRequest_ChunksToMove::operator==(const CommitDataRequest_ChunksToMove& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(page_, other.page_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunk_, other.chunk_)
-   && ::protozero::internal::gen_helpers::EqualsField(target_buffer_, other.target_buffer_)
-   && ::protozero::internal::gen_helpers::EqualsField(data_, other.data_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunk_incomplete_, other.chunk_incomplete_);
+    if (wsize >= 0) {
+      // Whether write() can return 0 is one of the greatest mysteries of UNIX.
+      // Just ignore it.
+      s_->input_written += static_cast<size_t>(wsize);
+    } else {
+      PERFETTO_PLOG("Subprocess write(stdin) failed");
+      s_->stdin_pipe.wr.reset();
+    }
+  }
+  PERFETTO_DCHECK(s_->input_written <= args.input.size());
+  if (s_->input_written == args.input.size())
+    s_->stdin_pipe.wr.reset();  // Close stdin.
 }

-bool CommitDataRequest_ChunksToMove::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+void Subprocess::TryReadStdoutAndErr() {
+  if (!s_->stdouterr_pipe.rd)
+    return;
+  char buf[4096];
+  int64_t rsize =
+      PERFETTO_EINTR(read(*s_->stdouterr_pipe.rd, buf, sizeof(buf)));
+  if (rsize < 0 && errno == EAGAIN)
+    return;

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* page */:
-        field.get(&page_);
-        break;
-      case 2 /* chunk */:
-        field.get(&chunk_);
-        break;
-      case 3 /* target_buffer */:
-        field.get(&target_buffer_);
-        break;
-      case 4 /* data */:
-        field.get(&data_);
-        break;
-      case 5 /* chunk_incomplete */:
-        field.get(&chunk_incomplete_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
+  if (rsize > 0) {
+    s_->output.append(buf, static_cast<size_t>(rsize));
+  } else if (rsize == 0 /* EOF */) {
+    s_->stdouterr_pipe.rd.reset();
+  } else {
+    PERFETTO_PLOG("Subprocess read(stdout/err) failed");
+    s_->stdouterr_pipe.rd.reset();
   }
-  return !packed_error && !dec.bytes_left();
 }

-std::string CommitDataRequest_ChunksToMove::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
+void Subprocess::Kill(int sig_num) {
+  kill(s_->pid, sig_num ? sig_num : SIGKILL);
 }

-std::vector<uint8_t> CommitDataRequest_ChunksToMove::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+void Subprocess::KillAndWaitForTermination(int sig_num) {
+  Kill(sig_num);
+  Wait();
+  // TryReadExitStatus must have joined the thread.
+  PERFETTO_DCHECK(!s_->waitpid_thread.joinable());
 }

-void CommitDataRequest_ChunksToMove::Serialize(::protozero::Message* msg) const {
-  // Field 1: page
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, page_, msg);
-  }
+}  // namespace base
+}  // namespace perfetto

-  // Field 2: chunk
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, chunk_, msg);
-  }
+#endif  // PERFETTO_OS_LINUX || PERFETTO_OS_ANDROID || PERFETTO_OS_APPLE
+// gen_amalgamated begin source: src/base/subprocess_windows.cc
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */

-  // Field 3: target_buffer
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, target_buffer_, msg);
-  }
+// gen_amalgamated expanded: #include "perfetto/ext/base/subprocess.h"

-  // Field 4: data
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeString(4, data_, msg);
-  }
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-  // Field 5: chunk_incomplete
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, chunk_incomplete_, msg);
-  }
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+#include <stdio.h>

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/data_source_descriptor.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/data_source_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/protovm/vm_program.gen.h"
+#include <algorithm>
+#include <mutex>
+#include <tuple>

-namespace perfetto {
-namespace protos {
-namespace gen {
+#include <windows.h>

-DataSourceDescriptor::DataSourceDescriptor() = default;
-DataSourceDescriptor::~DataSourceDescriptor() = default;
-DataSourceDescriptor::DataSourceDescriptor(const DataSourceDescriptor&) = default;
-DataSourceDescriptor& DataSourceDescriptor::operator=(const DataSourceDescriptor&) = default;
-DataSourceDescriptor::DataSourceDescriptor(DataSourceDescriptor&&) noexcept = default;
-DataSourceDescriptor& DataSourceDescriptor::operator=(DataSourceDescriptor&&) = default;
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/pipe.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"

-bool DataSourceDescriptor::operator==(const DataSourceDescriptor& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(id_, other.id_)
-   && ::protozero::internal::gen_helpers::EqualsField(will_notify_on_stop_, other.will_notify_on_stop_)
-   && ::protozero::internal::gen_helpers::EqualsField(will_notify_on_start_, other.will_notify_on_start_)
-   && ::protozero::internal::gen_helpers::EqualsField(handles_incremental_state_clear_, other.handles_incremental_state_clear_)
-   && ::protozero::internal::gen_helpers::EqualsField(no_flush_, other.no_flush_)
-   && ::protozero::internal::gen_helpers::EqualsField(protovm_program_, other.protovm_program_)
-   && ::protozero::internal::gen_helpers::EqualsField(gpu_counter_descriptor_, other.gpu_counter_descriptor_)
-   && ::protozero::internal::gen_helpers::EqualsField(track_event_descriptor_, other.track_event_descriptor_)
-   && ::protozero::internal::gen_helpers::EqualsField(ftrace_descriptor_, other.ftrace_descriptor_);
-}
+namespace perfetto {
+namespace base {

-bool DataSourceDescriptor::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+// static
+const int Subprocess::kTimeoutSignal = static_cast<int>(STATUS_TIMEOUT);

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 7 /* id */:
-        field.get(&id_);
-        break;
-      case 2 /* will_notify_on_stop */:
-        field.get(&will_notify_on_stop_);
-        break;
-      case 3 /* will_notify_on_start */:
-        field.get(&will_notify_on_start_);
-        break;
-      case 4 /* handles_incremental_state_clear */:
-        field.get(&handles_incremental_state_clear_);
-        break;
-      case 9 /* no_flush */:
-        field.get(&no_flush_);
-        break;
-      case 10 /* protovm_program */:
-        (*protovm_program_).ParseFromArray(field.data(), field.size());
-        break;
-      case 5 /* gpu_counter_descriptor */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &gpu_counter_descriptor_);
-        break;
-      case 6 /* track_event_descriptor */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &track_event_descriptor_);
-        break;
-      case 8 /* ftrace_descriptor */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &ftrace_descriptor_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
+void Subprocess::Start() {
+  if (args.exec_cmd.empty()) {
+    PERFETTO_ELOG("Subprocess.exec_cmd cannot be empty on Windows");
+    return;
   }
-  return !packed_error && !dec.bytes_left();
-}
-
-std::string DataSourceDescriptor::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
-
-std::vector<uint8_t> DataSourceDescriptor::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}

-void DataSourceDescriptor::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+  // Quote arguments but only when ambiguous. When quoting, CreateProcess()
+  // assumes that the command is an absolute path and does not search in the
+  // %PATH%. If non quoted, instead, CreateProcess() tries both. This is to
+  // allow Subprocess("cmd.exe", "/c", "shell command").
+  std::string cmd;
+  for (const auto& part : args.exec_cmd) {
+    if (part.find(" ") != std::string::npos) {
+      cmd += "\"" + part + "\" ";
+    } else {
+      cmd += part + " ";
+    }
   }
+  // Remove trailing space.
+  if (!cmd.empty())
+    cmd.resize(cmd.size() - 1);

-  // Field 7: id
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, id_, msg);
+  if (args.stdin_mode == InputMode::kBuffer) {
+    s_->stdin_pipe = Pipe::Create();
+    // Allow the child process to inherit the other end of the pipe.
+    PERFETTO_CHECK(
+        ::SetHandleInformation(*s_->stdin_pipe.rd, HANDLE_FLAG_INHERIT, 1));
   }

-  // Field 2: will_notify_on_stop
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, will_notify_on_stop_, msg);
+  if (args.stderr_mode == OutputMode::kBuffer ||
+      args.stdout_mode == OutputMode::kBuffer) {
+    s_->stdouterr_pipe = Pipe::Create();
+    PERFETTO_CHECK(
+        ::SetHandleInformation(*s_->stdouterr_pipe.wr, HANDLE_FLAG_INHERIT, 1));
   }

-  // Field 3: will_notify_on_start
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, will_notify_on_start_, msg);
+  ScopedPlatformHandle nul_handle;
+  if (args.stderr_mode == OutputMode::kDevNull ||
+      args.stdout_mode == OutputMode::kDevNull) {
+    nul_handle.reset(::CreateFileA(
+        "NUL", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+        nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
+    PERFETTO_CHECK(::SetHandleInformation(*nul_handle, HANDLE_FLAG_INHERIT, 1));
   }

-  // Field 4: handles_incremental_state_clear
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, handles_incremental_state_clear_, msg);
-  }
+  PROCESS_INFORMATION proc_info{};
+  STARTUPINFOA start_info{};
+  start_info.cb = sizeof(STARTUPINFOA);

-  // Field 9: no_flush
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, no_flush_, msg);
+  if (args.stderr_mode == OutputMode::kInherit) {
+    start_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
+  } else if (args.stderr_mode == OutputMode::kBuffer) {
+    start_info.hStdError = *s_->stdouterr_pipe.wr;
+  } else if (args.stderr_mode == OutputMode::kDevNull) {
+    start_info.hStdError = *nul_handle;
+  } else if (args.stderr_mode == OutputMode::kFd) {
+    PERFETTO_CHECK(
+        ::SetHandleInformation(*args.out_fd, HANDLE_FLAG_INHERIT, 1));
+    start_info.hStdError = *args.out_fd;
+  } else {
+    PERFETTO_CHECK(false);
   }

-  // Field 10: protovm_program
-  if (_has_field_[10]) {
-    (*protovm_program_).Serialize(msg->BeginNestedMessage<::protozero::Message>(10));
+  if (args.stdout_mode == OutputMode::kInherit) {
+    start_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
+  } else if (args.stdout_mode == OutputMode::kBuffer) {
+    start_info.hStdOutput = *s_->stdouterr_pipe.wr;
+  } else if (args.stdout_mode == OutputMode::kDevNull) {
+    start_info.hStdOutput = *nul_handle;
+  } else if (args.stdout_mode == OutputMode::kFd) {
+    PERFETTO_CHECK(
+        ::SetHandleInformation(*args.out_fd, HANDLE_FLAG_INHERIT, 1));
+    start_info.hStdOutput = *args.out_fd;
+  } else {
+    PERFETTO_CHECK(false);
   }

-  // Field 5: gpu_counter_descriptor
-  if (_has_field_[5]) {
-    msg->AppendString(5, gpu_counter_descriptor_);
+  if (args.stdin_mode == InputMode::kBuffer) {
+    start_info.hStdInput = *s_->stdin_pipe.rd;
+  } else if (args.stdin_mode == InputMode::kDevNull) {
+    start_info.hStdInput = *nul_handle;
   }

-  // Field 6: track_event_descriptor
-  if (_has_field_[6]) {
-    msg->AppendString(6, track_event_descriptor_);
-  }
+  start_info.dwFlags |= STARTF_USESTDHANDLES;

-  // Field 8: ftrace_descriptor
-  if (_has_field_[8]) {
-    msg->AppendString(8, ftrace_descriptor_);
-  }
+  // Create the child process.
+  bool success =
+      ::CreateProcessA(nullptr,      // App name. Needs to be null to use PATH.
+                       &cmd[0],      // Command line.
+                       nullptr,      // Process security attributes.
+                       nullptr,      // Primary thread security attributes.
+                       true,         // Handles are inherited.
+                       0,            // Flags.
+                       nullptr,      // Use parent's environment.
+                       nullptr,      // Use parent's current directory.
+                       &start_info,  // STARTUPINFO pointer.
+                       &proc_info);  // Receives PROCESS_INFORMATION.

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+  // Close on our side the pipe ends that we passed to the child process.
+  s_->stdin_pipe.rd.reset();
+  s_->stdouterr_pipe.wr.reset();
+  args.out_fd.reset();

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/descriptor.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/descriptor.gen.h"
+  if (!success) {
+    s_->returncode = ERROR_FILE_NOT_FOUND;
+    s_->status = kTerminated;
+    s_->stdin_pipe.wr.reset();
+    s_->stdouterr_pipe.rd.reset();
+    PERFETTO_ELOG("CreateProcess failed: %lx, cmd: %s", GetLastError(),
+                  &cmd[0]);
+    return;
+  }

-namespace perfetto {
-namespace protos {
-namespace gen {
+  s_->pid = proc_info.dwProcessId;
+  s_->win_proc_handle = ScopedPlatformHandle(proc_info.hProcess);
+  s_->win_thread_handle = ScopedPlatformHandle(proc_info.hThread);
+  s_->status = kRunning;

-OneofOptions::OneofOptions() = default;
-OneofOptions::~OneofOptions() = default;
-OneofOptions::OneofOptions(const OneofOptions&) = default;
-OneofOptions& OneofOptions::operator=(const OneofOptions&) = default;
-OneofOptions::OneofOptions(OneofOptions&&) noexcept = default;
-OneofOptions& OneofOptions::operator=(OneofOptions&&) = default;
+  MovableState* s = s_.get();
+  if (args.stdin_mode == InputMode::kBuffer) {
+    s_->stdin_thread = std::thread(&Subprocess::StdinThread, s, args.input);
+  }

-bool OneofOptions::operator==(const OneofOptions& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
+  if (args.stderr_mode == OutputMode::kBuffer ||
+      args.stdout_mode == OutputMode::kBuffer) {
+    PERFETTO_DCHECK(s_->stdouterr_pipe.rd);
+    s_->stdouterr_thread = std::thread(&Subprocess::StdoutErrThread, s);
+  }
 }

-bool OneofOptions::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
-
-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
+// static
+void Subprocess::StdinThread(MovableState* s, const std::string& input) {
+  size_t input_written = 0;
+  while (input_written < input.size()) {
+    DWORD wsize = 0;
+    if (::WriteFile(*s->stdin_pipe.wr, input.data() + input_written,
+                    static_cast<DWORD>(input.size() - input_written), &wsize,
+                    nullptr)) {
+      input_written += wsize;
+    } else {
+      // ERROR_BROKEN_PIPE is WAI when the child just closes stdin and stops
+      // accepting input.
+      auto err = ::GetLastError();
+      if (err != ERROR_BROKEN_PIPE)
+        PERFETTO_PLOG("Subprocess WriteFile(stdin) failed %lx", err);
+      break;
     }
-  }
-  return !packed_error && !dec.bytes_left();
+  }  // while(...)
+  std::unique_lock<std::mutex> lock(s->mutex);
+  s->stdin_pipe.wr.reset();
 }

-std::string OneofOptions::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+// static
+void Subprocess::StdoutErrThread(MovableState* s) {
+  char buf[4096];
+  for (;;) {
+    DWORD rsize = 0;
+    bool res =
+        ::ReadFile(*s->stdouterr_pipe.rd, buf, sizeof(buf), &rsize, nullptr);
+    if (!res) {
+      auto err = GetLastError();
+      if (err != ERROR_BROKEN_PIPE)
+        PERFETTO_PLOG("Subprocess ReadFile(stdouterr) failed %lx", err);
+    }

-std::vector<uint8_t> OneofOptions::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+    if (rsize > 0) {
+      std::unique_lock<std::mutex> lock(s->mutex);
+      s->locked_outerr_buf.append(buf, static_cast<size_t>(rsize));
+    } else {  // EOF or some error.
+      break;
+    }
+  }  // For(..)
+
+  // Close the stdouterr_pipe. The main loop looks at the pipe closure to
+  // determine whether the stdout/err thread has completed.
+  {
+    std::unique_lock<std::mutex> lock(s->mutex);
+    s->stdouterr_pipe.rd.reset();
+  }
+  s->stdouterr_done_event.Notify();
 }

-void OneofOptions::Serialize(::protozero::Message* msg) const {
-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+Subprocess::Status Subprocess::Poll() {
+  if (s_->status != kRunning)
+    return s_->status;  // Nothing to poll.
+  Wait(1 /*ms*/);
+  return s_->status;
 }

+bool Subprocess::Wait(int timeout_ms) {
+  PERFETTO_CHECK(s_->status != kNotStarted);
+  const bool wait_forever = timeout_ms == 0;
+  const int64_t wait_start_ms = base::GetWallTimeMs().count();

-EnumValueDescriptorProto::EnumValueDescriptorProto() = default;
-EnumValueDescriptorProto::~EnumValueDescriptorProto() = default;
-EnumValueDescriptorProto::EnumValueDescriptorProto(const EnumValueDescriptorProto&) = default;
-EnumValueDescriptorProto& EnumValueDescriptorProto::operator=(const EnumValueDescriptorProto&) = default;
-EnumValueDescriptorProto::EnumValueDescriptorProto(EnumValueDescriptorProto&&) noexcept = default;
-EnumValueDescriptorProto& EnumValueDescriptorProto::operator=(EnumValueDescriptorProto&&) = default;
+  // Break out of the loop only after both conditions are satisfied:
+  // - All stdout/stderr data has been read (if OutputMode::kBuffer).
+  // - The process exited.
+  // Note that the two events can happen arbitrary order. After the process
+  // exits, there might be still data in the pipe buffer, which we want to
+  // read fully.
+  // Note also that stdout/err might be "complete" before starting, if neither
+  // is operating in OutputMode::kBuffer mode. In that case we just want to wait
+  // for the process termination.
+  //
+  // Instead, don't wait on the stdin to be fully written. The child process
+  // might exit prematurely (or crash). If that happens, we can end up in a
+  // state where the write(stdin_pipe_.wr) will never unblock.
+  bool stdouterr_complete = false;
+  for (;;) {
+    HANDLE wait_handles[2]{};
+    DWORD num_handles = 0;

-bool EnumValueDescriptorProto::operator==(const EnumValueDescriptorProto& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(number_, other.number_);
-}
+    // Check if the process exited.
+    bool process_exited = !s_->win_proc_handle;
+    if (!process_exited) {
+      DWORD exit_code = STILL_ACTIVE;
+      PERFETTO_CHECK(::GetExitCodeProcess(*s_->win_proc_handle, &exit_code));
+      if (exit_code != STILL_ACTIVE) {
+        s_->returncode = static_cast<int>(exit_code);
+        s_->status = kTerminated;
+        s_->win_proc_handle.reset();
+        s_->win_thread_handle.reset();
+        process_exited = true;
+      }
+    } else {
+      PERFETTO_DCHECK(s_->status != kRunning);
+    }
+    if (!process_exited) {
+      wait_handles[num_handles++] = *s_->win_proc_handle;
+    }

-bool EnumValueDescriptorProto::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+    // Check if there is more output and if the stdout/err pipe has been closed.
+    {
+      std::unique_lock<std::mutex> lock(s_->mutex);
+      // Move the output from the internal buffer shared with the
+      // stdouterr_thread to the final buffer exposed to the client.
+      if (!s_->locked_outerr_buf.empty()) {
+        s_->output.append(std::move(s_->locked_outerr_buf));
+        s_->locked_outerr_buf.clear();
+      }
+      stdouterr_complete = !s_->stdouterr_pipe.rd;
+      if (!stdouterr_complete) {
+        wait_handles[num_handles++] = s_->stdouterr_done_event.fd();
+      }
+    }  // lock(s_->mutex)

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
+    if (num_handles == 0) {
+      PERFETTO_DCHECK(process_exited && stdouterr_complete);
+      break;
     }
-    switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 2 /* number */:
-        field.get(&number_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
+
+    DWORD wait_ms;  // Note: DWORD is unsigned.
+    if (wait_forever) {
+      wait_ms = INFINITE;
+    } else {
+      const int64_t now = GetWallTimeMs().count();
+      const int64_t wait_left_ms = timeout_ms - (now - wait_start_ms);
+      if (wait_left_ms <= 0)
+        return false;  // Timed out
+      wait_ms = static_cast<DWORD>(wait_left_ms);
     }
-  }
-  return !packed_error && !dec.bytes_left();
-}

-std::string EnumValueDescriptorProto::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+    auto wait_res =
+        ::WaitForMultipleObjects(num_handles, wait_handles, false, wait_ms);
+    PERFETTO_CHECK(wait_res != WAIT_FAILED);
+  }

-std::vector<uint8_t> EnumValueDescriptorProto::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  PERFETTO_DCHECK(!s_->win_proc_handle);
+  PERFETTO_DCHECK(!s_->win_thread_handle);

-void EnumValueDescriptorProto::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
-  }
+  if (s_->stdin_thread.joinable())  // Might not exist if CreateProcess failed.
+    s_->stdin_thread.join();
+  if (s_->stdouterr_thread.joinable())
+    s_->stdouterr_thread.join();

-  // Field 2: number
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, number_, msg);
-  }
+  // The stdin pipe is closed by the dedicated stdin thread. However if that is
+  // not started (e.g. because of no redirection) force close it now. Needs to
+  // happen after the join() to be thread safe.
+  s_->stdin_pipe.wr.reset();
+  s_->stdouterr_pipe.rd.reset();

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+  return true;
 }

+void Subprocess::Kill(int exit_code) {
+  auto code = exit_code ? static_cast<DWORD>(exit_code) : STATUS_CONTROL_C_EXIT;
+  ::TerminateProcess(*s_->win_proc_handle, code);
+}

-EnumDescriptorProto::EnumDescriptorProto() = default;
-EnumDescriptorProto::~EnumDescriptorProto() = default;
-EnumDescriptorProto::EnumDescriptorProto(const EnumDescriptorProto&) = default;
-EnumDescriptorProto& EnumDescriptorProto::operator=(const EnumDescriptorProto&) = default;
-EnumDescriptorProto::EnumDescriptorProto(EnumDescriptorProto&&) noexcept = default;
-EnumDescriptorProto& EnumDescriptorProto::operator=(EnumDescriptorProto&&) = default;
-
-bool EnumDescriptorProto::operator==(const EnumDescriptorProto& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(value_, other.value_)
-   && ::protozero::internal::gen_helpers::EqualsField(reserved_name_, other.reserved_name_);
+void Subprocess::KillAndWaitForTermination(int exit_code) {
+  Kill(exit_code);
+  Wait();
+  // TryReadExitStatus must have joined the threads.
+  PERFETTO_DCHECK(!s_->stdin_thread.joinable());
+  PERFETTO_DCHECK(!s_->stdouterr_thread.joinable());
 }

-int EnumDescriptorProto::value_size() const { return static_cast<int>(value_.size()); }
-void EnumDescriptorProto::clear_value() { value_.clear(); }
-EnumValueDescriptorProto* EnumDescriptorProto::add_value() { value_.emplace_back(); return &value_.back(); }
-bool EnumDescriptorProto::ParseFromArray(const void* raw, size_t size) {
-  value_.clear();
-  reserved_name_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
+}  // namespace base
+}  // namespace perfetto

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
+#endif  // PERFETTO_OS_WIN
+// gen_amalgamated begin source: src/protozero/field.cc
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
+
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/endian.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+
+namespace protozero {
+
+template <typename Container>
+void Field::SerializeAndAppendToInternal(Container* dst) const {
+  namespace pu = proto_utils;
+  size_t initial_size = dst->size();
+  dst->resize(initial_size + pu::kMaxSimpleFieldEncodedSize + size_);
+  uint8_t* start = reinterpret_cast<uint8_t*>(&(*dst)[initial_size]);
+  uint8_t* wptr = start;
+  switch (type_) {
+    case static_cast<int>(pu::ProtoWireType::kVarInt): {
+      wptr = pu::WriteVarInt(pu::MakeTagVarInt(id_), wptr);
+      wptr = pu::WriteVarInt(int_value_, wptr);
+      break;
     }
-    switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 2 /* value */:
-        value_.emplace_back();
-        value_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 5 /* reserved_name */:
-        reserved_name_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &reserved_name_.back());
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
+    case static_cast<int>(pu::ProtoWireType::kFixed32): {
+      wptr = pu::WriteVarInt(pu::MakeTagFixed<uint32_t>(id_), wptr);
+      uint32_t value32 =
+          perfetto::base::HostToLE(static_cast<uint32_t>(int_value_));
+      memcpy(wptr, &value32, sizeof(value32));
+      wptr += sizeof(uint32_t);
+      break;
+    }
+    case static_cast<int>(pu::ProtoWireType::kFixed64): {
+      wptr = pu::WriteVarInt(pu::MakeTagFixed<uint64_t>(id_), wptr);
+      uint64_t value64 = perfetto::base::HostToLE(int_value_);
+      memcpy(wptr, &value64, sizeof(value64));
+      wptr += sizeof(uint64_t);
+      break;
+    }
+    case static_cast<int>(pu::ProtoWireType::kLengthDelimited): {
+      ConstBytes payload = as_bytes();
+      wptr = pu::WriteVarInt(pu::MakeTagLengthDelimited(id_), wptr);
+      wptr = pu::WriteVarInt(payload.size, wptr);
+      memcpy(wptr, payload.data, payload.size);
+      wptr += payload.size;
+      break;
     }
+    default:
+      PERFETTO_FATAL("Unknown field type %d", type_);
   }
-  return !packed_error && !dec.bytes_left();
+  size_t written_size = static_cast<size_t>(wptr - start);
+  PERFETTO_DCHECK(written_size > 0 && written_size < pu::kMaxMessageLength);
+  PERFETTO_DCHECK(initial_size + written_size <= dst->size());
+  dst->resize(initial_size + written_size);
 }

-std::string EnumDescriptorProto::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
+void Field::SerializeAndAppendTo(std::string* dst) const {
+  SerializeAndAppendToInternal(dst);
 }

-std::vector<uint8_t> EnumDescriptorProto::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+void Field::SerializeAndAppendTo(std::vector<uint8_t>* dst) const {
+  SerializeAndAppendToInternal(dst);
 }

-void EnumDescriptorProto::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
-  }
+}  // namespace protozero
+// gen_amalgamated begin source: src/protozero/gen_field_helpers.cc
+/*
+ * 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.
+ */

-  // Field 2: value
-  for (auto& it : value_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
-  }
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"

-  // Field 5: reserved_name
-  for (auto& it : reserved_name_) {
-    ::protozero::internal::gen_helpers::SerializeString(5, it, msg);
-  }
+namespace protozero {
+namespace internal {
+namespace gen_helpers {

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+void DeserializeString(const protozero::Field& field, std::string* dst) {
+  field.get(dst);
 }

+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+                                        uint64_t>(const protozero::Field& field,
+                                                  std::vector<uint64_t>* dst);

-OneofDescriptorProto::OneofDescriptorProto() = default;
-OneofDescriptorProto::~OneofDescriptorProto() = default;
-OneofDescriptorProto::OneofDescriptorProto(const OneofDescriptorProto&) = default;
-OneofDescriptorProto& OneofDescriptorProto::operator=(const OneofDescriptorProto&) = default;
-OneofDescriptorProto::OneofDescriptorProto(OneofDescriptorProto&&) noexcept = default;
-OneofDescriptorProto& OneofDescriptorProto::operator=(OneofDescriptorProto&&) = default;
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+                                        int64_t>(const protozero::Field& field,
+                                                 std::vector<int64_t>* dst);

-bool OneofDescriptorProto::operator==(const OneofDescriptorProto& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(options_, other.options_);
-}
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+                                        uint32_t>(const protozero::Field& field,
+                                                  std::vector<uint32_t>* dst);

-bool OneofDescriptorProto::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+template bool DeserializePackedRepeated<proto_utils::ProtoWireType::kVarInt,
+                                        int32_t>(const protozero::Field& field,
+                                                 std::vector<int32_t>* dst);

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 2 /* options */:
-        (*options_).ParseFromArray(field.data(), field.size());
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
+void SerializeTinyVarInt(uint32_t field_id, bool value, Message* msg) {
+  msg->AppendTinyVarInt(field_id, value);
 }

-std::string OneofDescriptorProto::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+template void SerializeExtendedVarInt<uint64_t>(uint32_t field_id,
+                                                uint64_t value,
+                                                Message* msg);

-std::vector<uint8_t> OneofDescriptorProto::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+template void SerializeExtendedVarInt<uint32_t>(uint32_t field_id,
+                                                uint32_t value,
+                                                Message* msg);

-void OneofDescriptorProto::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
-  }
+template void SerializeFixed<double>(uint32_t field_id,
+                                     double value,
+                                     Message* msg);

-  // Field 2: options
-  if (_has_field_[2]) {
-    (*options_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
-  }
+template void SerializeFixed<float>(uint32_t field_id,
+                                    float value,
+                                    Message* msg);

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+template void SerializeFixed<uint64_t>(uint32_t field_id,
+                                       uint64_t value,
+                                       Message* msg);

+template void SerializeFixed<int64_t>(uint32_t field_id,
+                                      int64_t value,
+                                      Message* msg);

-FieldDescriptorProto::FieldDescriptorProto() = default;
-FieldDescriptorProto::~FieldDescriptorProto() = default;
-FieldDescriptorProto::FieldDescriptorProto(const FieldDescriptorProto&) = default;
-FieldDescriptorProto& FieldDescriptorProto::operator=(const FieldDescriptorProto&) = default;
-FieldDescriptorProto::FieldDescriptorProto(FieldDescriptorProto&&) noexcept = default;
-FieldDescriptorProto& FieldDescriptorProto::operator=(FieldDescriptorProto&&) = default;
+template void SerializeFixed<uint32_t>(uint32_t field_id,
+                                       uint32_t value,
+                                       Message* msg);

-bool FieldDescriptorProto::operator==(const FieldDescriptorProto& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(number_, other.number_)
-   && ::protozero::internal::gen_helpers::EqualsField(label_, other.label_)
-   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
-   && ::protozero::internal::gen_helpers::EqualsField(type_name_, other.type_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(extendee_, other.extendee_)
-   && ::protozero::internal::gen_helpers::EqualsField(default_value_, other.default_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(options_, other.options_)
-   && ::protozero::internal::gen_helpers::EqualsField(oneof_index_, other.oneof_index_);
-}
+template void SerializeFixed<int32_t>(uint32_t field_id,
+                                      int32_t value,
+                                      Message* msg);

-bool FieldDescriptorProto::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+void SerializeString(uint32_t field_id,
+                     const std::string& value,
+                     Message* msg) {
+  msg->AppendString(field_id, value);
+}

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 3 /* number */:
-        field.get(&number_);
-        break;
-      case 4 /* label */:
-        field.get(&label_);
-        break;
-      case 5 /* type */:
-        field.get(&type_);
-        break;
-      case 6 /* type_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &type_name_);
-        break;
-      case 2 /* extendee */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &extendee_);
-        break;
-      case 7 /* default_value */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &default_value_);
-        break;
-      case 8 /* options */:
-        (*options_).ParseFromArray(field.data(), field.size());
-        break;
-      case 9 /* oneof_index */:
-        field.get(&oneof_index_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
+void SerializeUnknownFields(const std::string& unknown_fields, Message* msg) {
+  msg->AppendRawProtoBytes(unknown_fields.data(), unknown_fields.size());
 }

-std::string FieldDescriptorProto::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
+MessageSerializer::MessageSerializer() = default;
+
+MessageSerializer::~MessageSerializer() = default;
+
+std::vector<uint8_t> MessageSerializer::SerializeAsArray() {
+  return msg_.SerializeAsArray();
 }

-std::vector<uint8_t> FieldDescriptorProto::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+std::string MessageSerializer::SerializeAsString() {
+  return msg_.SerializeAsString();
 }

-void FieldDescriptorProto::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
-  }
+template bool EqualsField<std::string>(const std::string&, const std::string&);

-  // Field 3: number
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, number_, msg);
-  }
+}  // namespace gen_helpers
+}  // namespace internal
+}  // namespace protozero
+// gen_amalgamated begin source: src/protozero/message.cc
+/*
+ * 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.
+ */

-  // Field 4: label
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, label_, msg);
-  }
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"

-  // Field 5: type
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, type_, msg);
-  }
+#include <atomic>
+#include <type_traits>

-  // Field 6: type_name
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeString(6, type_name_, msg);
-  }
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message_arena.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message_handle.h"

-  // Field 2: extendee
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, extendee_, msg);
-  }
+namespace protozero {

-  // Field 7: default_value
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeString(7, default_value_, msg);
-  }
+namespace {

-  // Field 8: options
-  if (_has_field_[8]) {
-    (*options_).Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
-  }
+constexpr int kBytesToCompact = proto_utils::kMessageLengthFieldSize - 1u;

-  // Field 9: oneof_index
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(9, oneof_index_, msg);
-  }
+#if PERFETTO_DCHECK_IS_ON()
+std::atomic<uint32_t> g_generation;
+#endif

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+}  // namespace

+// Do NOT put any code in the constructor or use default initialization.
+// Use the Reset() method below instead.

-FieldOptions::FieldOptions() = default;
-FieldOptions::~FieldOptions() = default;
-FieldOptions::FieldOptions(const FieldOptions&) = default;
-FieldOptions& FieldOptions::operator=(const FieldOptions&) = default;
-FieldOptions::FieldOptions(FieldOptions&&) noexcept = default;
-FieldOptions& FieldOptions::operator=(FieldOptions&&) = default;
+// This method is called to initialize both root and nested messages.
+void Message::Reset(ScatteredStreamWriter* stream_writer,
+                    MessageArena* arena,
+                    Encoding encoding) {
+// Older versions of libstdcxx don't have is_trivially_constructible.
+#if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20170516
+  static_assert(std::is_trivially_constructible<Message>::value,
+                "Message must be trivially constructible");
+#endif

-bool FieldOptions::operator==(const FieldOptions& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(packed_, other.packed_)
-   && ::protozero::internal::gen_helpers::EqualsField(uninterpreted_option_, other.uninterpreted_option_);
+  static_assert(std::is_trivially_destructible<Message>::value,
+                "Message must be trivially destructible");
+  stream_writer_ = stream_writer;
+  arena_ = arena;
+  size_ = 0;
+  size_field_ = nullptr;
+  nested_message_ = nullptr;
+  message_state_ = MessageState::kNotFinalized;
+  encoding_ = encoding;
+#if PERFETTO_DCHECK_IS_ON()
+  handle_ = nullptr;
+  generation_ = g_generation.fetch_add(1, std::memory_order_relaxed);
+#endif
 }

-int FieldOptions::uninterpreted_option_size() const { return static_cast<int>(uninterpreted_option_.size()); }
-void FieldOptions::clear_uninterpreted_option() { uninterpreted_option_.clear(); }
-UninterpretedOption* FieldOptions::add_uninterpreted_option() { uninterpreted_option_.emplace_back(); return &uninterpreted_option_.back(); }
-bool FieldOptions::ParseFromArray(const void* raw, size_t size) {
-  uninterpreted_option_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
-
-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 2 /* packed */:
-        field.get(&packed_);
-        break;
-      case 999 /* uninterpreted_option */:
-        uninterpreted_option_.emplace_back();
-        uninterpreted_option_.back().ParseFromArray(field.data(), field.size());
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
+void Message::AppendString(uint32_t field_id, const char* str) {
+  AppendBytes(field_id, str, strlen(str));
 }

-std::string FieldOptions::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+void Message::AppendBytes(uint32_t field_id, const void* src, size_t size) {
+  PERFETTO_DCHECK(field_id);
+  if (nested_message_)
+    EndNestedMessage();

-std::vector<uint8_t> FieldOptions::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+  PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);
+  // Write the proto preamble (field id, type and length of the field).
+  uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
+  uint8_t* pos = buffer;
+  pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
+                                 pos);
+  pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
+  WriteToStream(buffer, pos);
+
+  const uint8_t* src_u8 = reinterpret_cast<const uint8_t*>(src);
+  WriteToStream(src_u8, src_u8 + size);
 }

-void FieldOptions::Serialize(::protozero::Message* msg) const {
-  // Field 2: packed
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, packed_, msg);
-  }
+size_t Message::AppendScatteredBytes(uint32_t field_id,
+                                     ContiguousMemoryRange* ranges,
+                                     size_t num_ranges) {
+  PERFETTO_DCHECK(field_id);
+  if (nested_message_)
+    EndNestedMessage();

-  // Field 999: uninterpreted_option
-  for (auto& it : uninterpreted_option_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(999));
+  size_t size = 0;
+  for (size_t i = 0; i < num_ranges; ++i) {
+    size += ranges[i].size();
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+  PERFETTO_DCHECK(size < proto_utils::kMaxMessageLength);

+  uint8_t buffer[proto_utils::kMaxSimpleFieldEncodedSize];
+  uint8_t* pos = buffer;
+  pos = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
+                                 pos);
+  pos = proto_utils::WriteVarInt(static_cast<uint32_t>(size), pos);
+  WriteToStream(buffer, pos);

-UninterpretedOption::UninterpretedOption() = default;
-UninterpretedOption::~UninterpretedOption() = default;
-UninterpretedOption::UninterpretedOption(const UninterpretedOption&) = default;
-UninterpretedOption& UninterpretedOption::operator=(const UninterpretedOption&) = default;
-UninterpretedOption::UninterpretedOption(UninterpretedOption&&) noexcept = default;
-UninterpretedOption& UninterpretedOption::operator=(UninterpretedOption&&) = default;
+  for (size_t i = 0; i < num_ranges; ++i) {
+    auto& range = ranges[i];
+    WriteToStream(range.begin, range.end);
+  }

-bool UninterpretedOption::operator==(const UninterpretedOption& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(identifier_value_, other.identifier_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(positive_int_value_, other.positive_int_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(negative_int_value_, other.negative_int_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(double_value_, other.double_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(string_value_, other.string_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(aggregate_value_, other.aggregate_value_);
+  return size;
 }

-int UninterpretedOption::name_size() const { return static_cast<int>(name_.size()); }
-void UninterpretedOption::clear_name() { name_.clear(); }
-UninterpretedOption_NamePart* UninterpretedOption::add_name() { name_.emplace_back(); return &name_.back(); }
-bool UninterpretedOption::ParseFromArray(const void* raw, size_t size) {
-  name_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
+uint32_t Message::Finalize() {
+  if (is_finalized())
+    return size_;

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 2 /* name */:
-        name_.emplace_back();
-        name_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 3 /* identifier_value */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &identifier_value_);
-        break;
-      case 4 /* positive_int_value */:
-        field.get(&positive_int_value_);
-        break;
-      case 5 /* negative_int_value */:
-        field.get(&negative_int_value_);
-        break;
-      case 6 /* double_value */:
-        field.get(&double_value_);
-        break;
-      case 7 /* string_value */:
-        field.get(&string_value_);
-        break;
-      case 8 /* aggregate_value */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &aggregate_value_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
+  if (nested_message_)
+    EndNestedMessage();
+
+  // Write the length of the nested message a posteriori, using a leading-zero
+  // redundant varint encoding. This can be nullptr for the root message, among
+  // many reasons, because the TraceWriterImpl delegate is keeping track of the
+  // root fragment size independently.
+  if (size_field_) {
+    PERFETTO_DCHECK(encoding_ == Encoding::kLengthDelimited);
+    PERFETTO_DCHECK(!is_finalized());
+    PERFETTO_DCHECK(size_ < proto_utils::kMaxMessageLength);
+    //
+    // Normally the size of a protozero message is written with 4 bytes just
+    // before the contents of the message itself:
+    //
+    //    size          message data
+    //   [aa bb cc dd] [01 23 45 67 ...]
+    //
+    // We always reserve 4 bytes for the size, because the real size of the
+    // message isn't known until the call to Finalize(). This is possible
+    // because we can use leading zero redundant varint coding to expand any
+    // size smaller than 256 MiB to 4 bytes.
+    //
+    // However this is wasteful for short, frequently written messages, so the
+    // code below uses a 1 byte size field when possible. This is done by
+    // shifting the already-written data (which should still be in the cache)
+    // back by 3 bytes, resulting in this layout:
+    //
+    //   size  message data
+    //   [aa] [01 23 45 67 ...]
+    //
+    // We can only do this optimization if the message is contained in a single
+    // chunk (since we can't modify previously committed chunks). We can check
+    // this by verifying that the size field is immediately before the message
+    // in memory and is fully contained by the current chunk.
+    //
+    if (PERFETTO_LIKELY(size_ <= proto_utils::kMaxOneByteMessageLength &&
+                        size_field_ ==
+                            stream_writer_->write_ptr() - size_ -
+                                proto_utils::kMessageLengthFieldSize &&
+                        size_field_ >= stream_writer_->cur_range().begin)) {
+      stream_writer_->Rewind(size_, kBytesToCompact);
+      PERFETTO_DCHECK(size_field_ == stream_writer_->write_ptr() - size_ - 1u);
+      *size_field_ = static_cast<uint8_t>(size_);
+      message_state_ = MessageState::kFinalizedWithCompaction;
+    } else {
+      proto_utils::WriteRedundantVarInt(size_, size_field_);
+      message_state_ = MessageState::kFinalized;
     }
+    size_field_ = nullptr;
+  } else {
+    message_state_ = MessageState::kFinalized;
   }
-  return !packed_error && !dec.bytes_left();
-}

-std::string UninterpretedOption::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+#if PERFETTO_DCHECK_IS_ON()
+  if (handle_)
+    handle_->reset_message();
+#endif

-std::vector<uint8_t> UninterpretedOption::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+  return size_;
 }

-void UninterpretedOption::Serialize(::protozero::Message* msg) const {
-  // Field 2: name
-  for (auto& it : name_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
-  }
+Message* Message::BeginNestedMessageInternal(uint32_t field_id) {
+  PERFETTO_DCHECK(field_id);
+  if (nested_message_)
+    EndNestedMessage();

-  // Field 3: identifier_value
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeString(3, identifier_value_, msg);
-  }
+  // Write the proto preamble for the nested message.
+  const bool is_length_delimited = encoding_ == Encoding::kLengthDelimited;
+  const uint32_t tag = is_length_delimited
+                           ? proto_utils::MakeTagLengthDelimited(field_id)
+                           : proto_utils::MakeTagStartGroup(field_id);
+  uint8_t data[proto_utils::kMaxTagEncodedSize];
+  uint8_t* data_end = proto_utils::WriteVarInt(tag, data);
+  WriteToStream(data, data_end);

-  // Field 4: positive_int_value
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, positive_int_value_, msg);
-  }
+  Message* message = arena_->NewMessage();
+  message->Reset(stream_writer_, arena_, encoding_);

-  // Field 5: negative_int_value
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, negative_int_value_, msg);
+  // A proto group child needs no length field. EndNestedMessage() ends it with
+  // a closing byte.
+  if (is_length_delimited) {
+    // The length of the nested message cannot be known upfront. So right now
+    // just reserve the bytes to encode the size after the nested message is
+    // done.
+    message->set_size_field(
+        stream_writer_->ReserveBytes(proto_utils::kMessageLengthFieldSize));
+    size_ += proto_utils::kMessageLengthFieldSize;
   }

-  // Field 6: double_value
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(6, double_value_, msg);
-  }
+  nested_message_ = message;
+  return message;
+}

-  // Field 7: string_value
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeString(7, string_value_, msg);
+void Message::EndNestedMessage() {
+  size_ += nested_message_->Finalize();
+  if (nested_message_->message_state_ ==
+      MessageState::kFinalizedWithCompaction) {
+    size_ -= kBytesToCompact;
   }

-  // Field 8: aggregate_value
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeString(8, aggregate_value_, msg);
+  // Every write path of this message calls EndNestedMessage() first. So the
+  // closing byte follows the child's last byte, even if the child was
+  // finalized earlier.
+  if (encoding_ == Encoding::kProtoGroup) {
+    // Point at the constant. A local copy would make the compiler add a stack
+    // check to this function and to Finalize(), which inlines it.
+    const uint8_t* closing_byte = &proto_utils::kProtoGroupEndByte;
+    WriteToStream(closing_byte, closing_byte + 1);
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+  arena_->DeleteLastMessage(nested_message_);
+  nested_message_ = nullptr;
 }

+}  // namespace protozero
+// gen_amalgamated begin source: src/protozero/message_arena.cc
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */

-UninterpretedOption_NamePart::UninterpretedOption_NamePart() = default;
-UninterpretedOption_NamePart::~UninterpretedOption_NamePart() = default;
-UninterpretedOption_NamePart::UninterpretedOption_NamePart(const UninterpretedOption_NamePart&) = default;
-UninterpretedOption_NamePart& UninterpretedOption_NamePart::operator=(const UninterpretedOption_NamePart&) = default;
-UninterpretedOption_NamePart::UninterpretedOption_NamePart(UninterpretedOption_NamePart&&) noexcept = default;
-UninterpretedOption_NamePart& UninterpretedOption_NamePart::operator=(UninterpretedOption_NamePart&&) = default;
+// gen_amalgamated expanded: #include "perfetto/protozero/message_arena.h"

-bool UninterpretedOption_NamePart::operator==(const UninterpretedOption_NamePart& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_part_, other.name_part_)
-   && ::protozero::internal::gen_helpers::EqualsField(is_extension_, other.is_extension_);
-}
+#include <atomic>
+#include <type_traits>

-bool UninterpretedOption_NamePart::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message_handle.h"

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* name_part */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_part_);
-        break;
-      case 2 /* is_extension */:
-        field.get(&is_extension_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
-}
+namespace protozero {

-std::string UninterpretedOption_NamePart::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
+MessageArena::MessageArena() {
+  // The code below assumes that there is always at least one block.
+  blocks_.emplace_front();
 }

-std::vector<uint8_t> UninterpretedOption_NamePart::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+MessageArena::~MessageArena() = default;

-void UninterpretedOption_NamePart::Serialize(::protozero::Message* msg) const {
-  // Field 1: name_part
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_part_, msg);
-  }
+Message* MessageArena::NewMessage() {
+  PERFETTO_DCHECK(!blocks_.empty());  // Should never become empty.

-  // Field 2: is_extension
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, is_extension_, msg);
+  Block* block = &blocks_.front();
+  if (PERFETTO_UNLIKELY(block->entries >= Block::kCapacity)) {
+    blocks_.emplace_front();
+    block = &blocks_.front();
   }
-
-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+  const auto idx = block->entries++;
+  void* storage = block->storage[idx];
+  PERFETTO_ASAN_UNPOISON(storage, sizeof(Message));
+  return new (storage) Message();
 }

+void MessageArena::DeleteLastMessageInternal() {
+  PERFETTO_DCHECK(!blocks_.empty());  // Should never be empty, see below.
+  Block* block = &blocks_.front();
+  PERFETTO_DCHECK(block->entries > 0);

-DescriptorProto::DescriptorProto() = default;
-DescriptorProto::~DescriptorProto() = default;
-DescriptorProto::DescriptorProto(const DescriptorProto&) = default;
-DescriptorProto& DescriptorProto::operator=(const DescriptorProto&) = default;
-DescriptorProto::DescriptorProto(DescriptorProto&&) noexcept = default;
-DescriptorProto& DescriptorProto::operator=(DescriptorProto&&) = default;
-
-bool DescriptorProto::operator==(const DescriptorProto& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_, other.field_)
-   && ::protozero::internal::gen_helpers::EqualsField(extension_, other.extension_)
-   && ::protozero::internal::gen_helpers::EqualsField(nested_type_, other.nested_type_)
-   && ::protozero::internal::gen_helpers::EqualsField(enum_type_, other.enum_type_)
-   && ::protozero::internal::gen_helpers::EqualsField(oneof_decl_, other.oneof_decl_)
-   && ::protozero::internal::gen_helpers::EqualsField(reserved_range_, other.reserved_range_)
-   && ::protozero::internal::gen_helpers::EqualsField(reserved_name_, other.reserved_name_);
-}
+  // This is the reason why there is no ~Message() call here.
+  // MessageArea::Reset() (see header) also relies on dtor being trivial.
+  static_assert(std::is_trivially_destructible<Message>::value,
+                "Message must be trivially destructible");

-int DescriptorProto::field_size() const { return static_cast<int>(field_.size()); }
-void DescriptorProto::clear_field() { field_.clear(); }
-FieldDescriptorProto* DescriptorProto::add_field() { field_.emplace_back(); return &field_.back(); }
-int DescriptorProto::extension_size() const { return static_cast<int>(extension_.size()); }
-void DescriptorProto::clear_extension() { extension_.clear(); }
-FieldDescriptorProto* DescriptorProto::add_extension() { extension_.emplace_back(); return &extension_.back(); }
-int DescriptorProto::nested_type_size() const { return static_cast<int>(nested_type_.size()); }
-void DescriptorProto::clear_nested_type() { nested_type_.clear(); }
-DescriptorProto* DescriptorProto::add_nested_type() { nested_type_.emplace_back(); return &nested_type_.back(); }
-int DescriptorProto::enum_type_size() const { return static_cast<int>(enum_type_.size()); }
-void DescriptorProto::clear_enum_type() { enum_type_.clear(); }
-EnumDescriptorProto* DescriptorProto::add_enum_type() { enum_type_.emplace_back(); return &enum_type_.back(); }
-int DescriptorProto::oneof_decl_size() const { return static_cast<int>(oneof_decl_.size()); }
-void DescriptorProto::clear_oneof_decl() { oneof_decl_.clear(); }
-OneofDescriptorProto* DescriptorProto::add_oneof_decl() { oneof_decl_.emplace_back(); return &oneof_decl_.back(); }
-int DescriptorProto::reserved_range_size() const { return static_cast<int>(reserved_range_.size()); }
-void DescriptorProto::clear_reserved_range() { reserved_range_.clear(); }
-DescriptorProto_ReservedRange* DescriptorProto::add_reserved_range() { reserved_range_.emplace_back(); return &reserved_range_.back(); }
-bool DescriptorProto::ParseFromArray(const void* raw, size_t size) {
-  field_.clear();
-  extension_.clear();
-  nested_type_.clear();
-  enum_type_.clear();
-  oneof_decl_.clear();
-  reserved_range_.clear();
-  reserved_name_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
+  --block->entries;
+  PERFETTO_ASAN_POISON(&block->storage[block->entries], sizeof(Message));

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 2 /* field */:
-        field_.emplace_back();
-        field_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 6 /* extension */:
-        extension_.emplace_back();
-        extension_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 3 /* nested_type */:
-        nested_type_.emplace_back();
-        nested_type_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 4 /* enum_type */:
-        enum_type_.emplace_back();
-        enum_type_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 8 /* oneof_decl */:
-        oneof_decl_.emplace_back();
-        oneof_decl_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 9 /* reserved_range */:
-        reserved_range_.emplace_back();
-        reserved_range_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 10 /* reserved_name */:
-        reserved_name_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &reserved_name_.back());
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
+  // Don't remove the first block to avoid malloc/free calls when the root
+  // message is reset. Hitting the allocator all the times is a waste of time.
+  if (block->entries == 0 && std::next(blocks_.cbegin()) != blocks_.cend()) {
+    blocks_.pop_front();
   }
-  return !packed_error && !dec.bytes_left();
 }

-std::string DescriptorProto::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
+}  // namespace protozero
+// gen_amalgamated begin source: src/protozero/packed_repeated_fields.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+
+namespace protozero {
+
+void PackedBufferBase::GrowSlowpath() {
+  size_t write_off = static_cast<size_t>(write_ptr_ - storage_begin_);
+  size_t old_size = static_cast<size_t>(storage_end_ - storage_begin_);
+  size_t new_size = old_size < 65536 ? (old_size * 2) : (old_size * 3 / 2);
+  new_size = perfetto::base::AlignUp<4096>(new_size);
+  std::unique_ptr<uint8_t[]> new_buf(new uint8_t[new_size]);
+  memcpy(new_buf.get(), storage_begin_, old_size);
+  heap_buf_ = std::move(new_buf);
+  storage_begin_ = heap_buf_.get();
+  storage_end_ = storage_begin_ + new_size;
+  write_ptr_ = storage_begin_ + write_off;
 }

-std::vector<uint8_t> DescriptorProto::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+void PackedBufferBase::Reset() {
+  heap_buf_.reset();
+  storage_begin_ = reinterpret_cast<uint8_t*>(&stack_buf_[0]);
+  storage_end_ = reinterpret_cast<uint8_t*>(&stack_buf_[kOnStackStorageSize]);
+  write_ptr_ = storage_begin_;
 }

-void DescriptorProto::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
-  }
+}  // namespace protozero
+// gen_amalgamated begin source: src/protozero/proto_decoder.cc
+/*
+ * 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.
+ */

-  // Field 2: field
-  for (auto& it : field_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
-  }
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"

-  // Field 6: extension
-  for (auto& it : extension_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
-  }
+#include <string.h>

-  // Field 3: nested_type
-  for (auto& it : nested_type_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
-  }
+#include <cinttypes>
+#include <limits>
+#include <memory>

-  // Field 4: enum_type
-  for (auto& it : enum_type_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
-  }
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+// gen_amalgamated expanded: #include "perfetto/public/compiler.h"

-  // Field 8: oneof_decl
-  for (auto& it : oneof_decl_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
-  }
+namespace protozero {

-  // Field 9: reserved_range
-  for (auto& it : reserved_range_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(9));
-  }
+using namespace proto_utils;

-  // Field 10: reserved_name
-  for (auto& it : reserved_name_) {
-    ::protozero::internal::gen_helpers::SerializeString(10, it, msg);
-  }
+const Field TypedProtoDecoderBase::kInvalidField{};

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+namespace {

+struct ParseFieldResult {
+  enum ParseResult { kAbort, kSkip, kOk };
+  ParseResult parse_res;
+  const uint8_t* next;
+  Field field;
+};

-DescriptorProto_ReservedRange::DescriptorProto_ReservedRange() = default;
-DescriptorProto_ReservedRange::~DescriptorProto_ReservedRange() = default;
-DescriptorProto_ReservedRange::DescriptorProto_ReservedRange(const DescriptorProto_ReservedRange&) = default;
-DescriptorProto_ReservedRange& DescriptorProto_ReservedRange::operator=(const DescriptorProto_ReservedRange&) = default;
-DescriptorProto_ReservedRange::DescriptorProto_ReservedRange(DescriptorProto_ReservedRange&&) noexcept = default;
-DescriptorProto_ReservedRange& DescriptorProto_ReservedRange::operator=(DescriptorProto_ReservedRange&&) = default;
+// Parses one field and returns the field itself and a pointer to the next
+// field to parse. If parsing fails, the returned |next| == |buffer|.
+// Force-inlined so that, after inlining, the result struct is decomposed by
+// the compiler and does not round-trip through memory.
+PERFETTO_ALWAYS_INLINE ParseFieldResult
+ParseOneField(const uint8_t* const buffer, const uint8_t* const end) {
+  ParseFieldResult res{ParseFieldResult::kAbort, buffer, Field{}};

-bool DescriptorProto_ReservedRange::operator==(const DescriptorProto_ReservedRange& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(start_, other.start_)
-   && ::protozero::internal::gen_helpers::EqualsField(end_, other.end_);
-}
+  const uint8_t* pos = buffer;

-bool DescriptorProto_ReservedRange::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+  // If we've already hit the end, just return an invalid field.
+  if (PERFETTO_UNLIKELY(pos >= end))
+    return res;

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* start */:
-        field.get(&start_);
-        break;
-      case 2 /* end */:
-        field.get(&end_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
+  uint64_t preamble = 0;
+  if (PERFETTO_LIKELY(*pos < 0x80)) {  // Fastpath for fields with ID < 16.
+    preamble = *(pos++);
+  } else {
+    const uint8_t* next = ParseVarIntFast(pos, end, &preamble);
+    if (PERFETTO_UNLIKELY(!next))
+      return res;
+    pos = next;
   }
-  return !packed_error && !dec.bytes_left();
-}

-std::string DescriptorProto_ReservedRange::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+  uint32_t field_id =
+      static_cast<uint32_t>(proto_utils::GetTagFieldId(preamble));
+  if (field_id == 0 || pos >= end)
+    return res;

-std::vector<uint8_t> DescriptorProto_ReservedRange::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  auto field_type =
+      static_cast<uint8_t>(proto_utils::GetTagFieldType(preamble));
+  const uint8_t* new_pos = pos;
+  uint64_t int_value = 0;
+  uint64_t size = 0;

-void DescriptorProto_ReservedRange::Serialize(::protozero::Message* msg) const {
-  // Field 1: start
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, start_, msg);
-  }
+  // If-chain ordered by observed frequency: wire types within a message are
+  // near-constant, so these branches predict better than a switch jump.
+  if (PERFETTO_LIKELY(field_type ==
+                      static_cast<uint8_t>(ProtoWireType::kVarInt))) {
+    new_pos = ParseVarIntFast(pos, end, &int_value);

-  // Field 2: end
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, end_, msg);
+    // new_pos being null means ParseVarIntFast could not fully parse the
+    // number. This is because we are out of space in the buffer. Set the id
+    // to zero and return but don't update the offset so a future read can
+    // read this field.
+    if (PERFETTO_UNLIKELY(!new_pos))
+      return res;
+  } else if (PERFETTO_LIKELY(
+                 field_type ==
+                 static_cast<uint8_t>(ProtoWireType::kLengthDelimited))) {
+    uint64_t payload_length;
+    new_pos = ParseVarIntFast(pos, end, &payload_length);
+    if (PERFETTO_UNLIKELY(!new_pos))
+      return res;
+
+    // ParseVarIntFast guarantees that |new_pos| <= |end| when it succeeds;
+    if (payload_length > static_cast<uint64_t>(end - new_pos))
+      return res;
+
+    const uintptr_t payload_start = reinterpret_cast<uintptr_t>(new_pos);
+    int_value = payload_start;
+    size = payload_length;
+    new_pos += payload_length;
+  } else if (field_type == static_cast<uint8_t>(ProtoWireType::kFixed64)) {
+    new_pos = pos + sizeof(uint64_t);
+    if (PERFETTO_UNLIKELY(new_pos > end))
+      return res;
+    memcpy(&int_value, pos, sizeof(uint64_t));
+    int_value = perfetto::base::LE64ToHost(int_value);
+  } else if (field_type == static_cast<uint8_t>(ProtoWireType::kFixed32)) {
+    new_pos = pos + sizeof(uint32_t);
+    if (PERFETTO_UNLIKELY(new_pos > end))
+      return res;
+    uint32_t value32;
+    memcpy(&value32, pos, sizeof(uint32_t));
+    int_value = perfetto::base::LE32ToHost(value32);
+  } else {
+    PERFETTO_DLOG("Invalid proto field type: %u", field_type);
+    return res;
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+  res.next = new_pos;

+  if (PERFETTO_UNLIKELY(field_id > Field::kMaxId)) {
+    PERFETTO_DLOG("Skipping field %" PRIu32 " because its id > %" PRIu32,
+                  field_id, Field::kMaxId);
+    res.parse_res = ParseFieldResult::kSkip;
+    return res;
+  }

-FileDescriptorProto::FileDescriptorProto() = default;
-FileDescriptorProto::~FileDescriptorProto() = default;
-FileDescriptorProto::FileDescriptorProto(const FileDescriptorProto&) = default;
-FileDescriptorProto& FileDescriptorProto::operator=(const FileDescriptorProto&) = default;
-FileDescriptorProto::FileDescriptorProto(FileDescriptorProto&&) noexcept = default;
-FileDescriptorProto& FileDescriptorProto::operator=(FileDescriptorProto&&) = default;
+  if (PERFETTO_UNLIKELY(size > proto_utils::kMaxMessageLength)) {
+    PERFETTO_DLOG("Skipping field %" PRIu32 " because it's too big (%" PRIu64
+                  " KB)",
+                  field_id, size / 1024);
+    res.parse_res = ParseFieldResult::kSkip;
+    return res;
+  }

-bool FileDescriptorProto::operator==(const FileDescriptorProto& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(package_, other.package_)
-   && ::protozero::internal::gen_helpers::EqualsField(dependency_, other.dependency_)
-   && ::protozero::internal::gen_helpers::EqualsField(public_dependency_, other.public_dependency_)
-   && ::protozero::internal::gen_helpers::EqualsField(weak_dependency_, other.weak_dependency_)
-   && ::protozero::internal::gen_helpers::EqualsField(message_type_, other.message_type_)
-   && ::protozero::internal::gen_helpers::EqualsField(enum_type_, other.enum_type_)
-   && ::protozero::internal::gen_helpers::EqualsField(extension_, other.extension_);
+  res.parse_res = ParseFieldResult::kOk;
+  res.field.initialize(field_id, field_type, int_value,
+                       static_cast<uint32_t>(size));
+  return res;
 }

-int FileDescriptorProto::message_type_size() const { return static_cast<int>(message_type_.size()); }
-void FileDescriptorProto::clear_message_type() { message_type_.clear(); }
-DescriptorProto* FileDescriptorProto::add_message_type() { message_type_.emplace_back(); return &message_type_.back(); }
-int FileDescriptorProto::enum_type_size() const { return static_cast<int>(enum_type_.size()); }
-void FileDescriptorProto::clear_enum_type() { enum_type_.clear(); }
-EnumDescriptorProto* FileDescriptorProto::add_enum_type() { enum_type_.emplace_back(); return &enum_type_.back(); }
-int FileDescriptorProto::extension_size() const { return static_cast<int>(extension_.size()); }
-void FileDescriptorProto::clear_extension() { extension_.clear(); }
-FieldDescriptorProto* FileDescriptorProto::add_extension() { extension_.emplace_back(); return &extension_.back(); }
-bool FileDescriptorProto::ParseFromArray(const void* raw, size_t size) {
-  dependency_.clear();
-  public_dependency_.clear();
-  weak_dependency_.clear();
-  message_type_.clear();
-  enum_type_.clear();
-  extension_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
+// Grows the selective-decoding spill area once the inline slots are
+// exhausted, moving the spilled fields to the heap. The append itself is
+// open-coded in the decode loop; see the comment there.
+PERFETTO_NO_INLINE void ExpandSpill(Field** spill,
+                                    uint32_t* spill_capacity,
+                                    uint32_t spill_size,
+                                    std::unique_ptr<Field[]>* heap_spill) {
+  // Double the capacity, with a floor so that an exhausted spill that somehow
+  // starts from zero capacity (e.g. a moved-from state) doesn't keep doubling
+  // zero. The floor matches SelectiveTypedProtoDecoder::kSpillStackCapacity.
+  const uint32_t new_capacity = std::max(*spill_capacity * 2, 16u);
+  PERFETTO_CHECK(new_capacity > spill_size);
+  std::unique_ptr<Field[]> new_storage(new Field[new_capacity]);
+  if (spill_size > 0)
+    memcpy(&new_storage[0], *spill, sizeof(Field) * spill_size);
+  *heap_spill = std::move(new_storage);
+  *spill = &(*heap_spill)[0];
+  *spill_capacity = new_capacity;
+}

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 2 /* package */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &package_);
-        break;
-      case 3 /* dependency */:
-        dependency_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &dependency_.back());
-        break;
-      case 10 /* public_dependency */:
-        public_dependency_.emplace_back();
-        field.get(&public_dependency_.back());
-        break;
-      case 11 /* weak_dependency */:
-        weak_dependency_.emplace_back();
-        field.get(&weak_dependency_.back());
-        break;
-      case 4 /* message_type */:
-        message_type_.emplace_back();
-        message_type_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 5 /* enum_type */:
-        enum_type_.emplace_back();
-        enum_type_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 7 /* extension */:
-        extension_.emplace_back();
-        extension_.back().ParseFromArray(field.data(), field.size());
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
+}  // namespace
+
+Field ProtoDecoder::FindField(uint32_t field_id) {
+  Field res{};
+  auto old_position = read_ptr_;
+  read_ptr_ = begin_;
+  for (auto f = ReadField(); f.valid(); f = ReadField()) {
+    if (f.id() == field_id) {
+      res = f;
+      break;
     }
   }
-  return !packed_error && !dec.bytes_left();
+  read_ptr_ = old_position;
+  return res;
 }

-std::string FileDescriptorProto::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
+Field ProtoDecoder::ReadField() {
+  ParseFieldResult res;
+  do {
+    res = ParseOneField(read_ptr_, end_);
+    read_ptr_ = res.next;
+  } while (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kSkip));
+  return res.field;
 }

-std::vector<uint8_t> FileDescriptorProto::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+void TypedProtoDecoderBase::ParseAllFields() {
+  ParseAllFieldsImpl<false>(nullptr, nullptr, nullptr, nullptr, nullptr);
 }

-void FileDescriptorProto::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
-  }
+void TypedProtoDecoderBase::ParseAllFieldsSelective(
+    const uint64_t* dense_mask,
+    Field** spill,
+    uint32_t* spill_capacity,
+    uint32_t* spill_size,
+    std::unique_ptr<Field[]>* heap_spill) {
+  PERFETTO_DCHECK(dense_mask && spill && spill_capacity && spill_size &&
+                  heap_spill);
+  ParseAllFieldsImpl<true>(dense_mask, spill, spill_capacity, spill_size,
+                           heap_spill);
+}

-  // Field 2: package
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, package_, msg);
-  }
+// Force-inlined into the two thin entry points above: the plain
+// instantiation compiles to a loop with no trace of the mask support and
+// the wrappers' arguments fold away entirely.
+template <bool kSelective>
+PERFETTO_ALWAYS_INLINE void TypedProtoDecoderBase::ParseAllFieldsImpl(
+    const uint64_t* dense_mask,
+    Field** spill,
+    uint32_t* spill_capacity,
+    uint32_t* spill_size,
+    std::unique_ptr<Field[]>* heap_spill) {
+  const uint8_t* cur = begin_;
+  ParseFieldResult res;
+  for (;;) {
+    res = ParseOneField(cur, end_);
+    PERFETTO_DCHECK(res.parse_res != ParseFieldResult::kOk || res.next != cur);
+    cur = res.next;
+    if (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kSkip))
+      continue;
+    if (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kAbort))
+      break;

-  // Field 3: dependency
-  for (auto& it : dependency_) {
-    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
-  }
+    PERFETTO_DCHECK(res.parse_res == ParseFieldResult::kOk);
+    PERFETTO_DCHECK(res.field.valid());
+    auto field_id = res.field.id();
+    if constexpr (kSelective) {
+      // Selective decoding: only allowlisted in-range ids go to the dense
+      // storage; everything else (including ids beyond the in-tree range) is
+      // appended, in wire order, to the spill area. |num_fields_| is capped
+      // at the mask's extent by SelectiveTypedProtoDecoder, so this also
+      // bounds the mask read.
+      const bool dense =
+          field_id < num_fields_ &&
+          (dense_mask[field_id / 64] & (1ULL << (field_id % 64))) != 0;
+      if (!dense) {
+        // The spill append is open-coded from the field's scalar components:
+        // a function taking the whole Field (by value or reference) makes
+        // the compiler materialize the packed Field representation on every
+        // loop iteration, including for fields that are not spilled.
+        if (PERFETTO_UNLIKELY(*spill_size >= *spill_capacity))
+          ExpandSpill(spill, spill_capacity, *spill_size, heap_spill);
+        (*spill)[(*spill_size)++].initialize(
+            field_id, static_cast<uint8_t>(res.field.type()),
+            res.field.raw_int_value(), res.field.raw_size());
+        continue;
+      }
+    } else if (PERFETTO_UNLIKELY(field_id >= num_fields_)) {
+      // Fields with an id beyond the highest field id known in-tree at compile
+      // time are out-of-tree "extension" fields (e.g. carved out of
+      // TracePacket's `extensions 1000 to 1999` range). They are intentionally
+      // not stored here because fields_ is indexed directly by field id, and
+      // extension ids are sparse and potentially very high. Callers that need
+      // them should decode selectively and consume them from unknown_fields().
+      has_out_of_range_fields_ = true;
+      continue;
+    }

-  // Field 10: public_dependency
-  for (auto& it : public_dependency_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(10, it, msg);
-  }
+    // There are two reasons why we might want to expand the heap capacity:
+    // 1. We are writing a non-repeated field, which has an id >
+    //    INITIAL_STACK_CAPACITY. In this case ExpandHeapStorage() ensures to
+    //    allocate at least (num_fields_ + 1) slots.
+    // 2. We are writing a repeated field but ran out of capacity.
+    if (PERFETTO_UNLIKELY(field_id >= size_ || size_ >= capacity_))
+      ExpandHeapStorage();

-  // Field 11: weak_dependency
-  for (auto& it : weak_dependency_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(11, it, msg);
-  }
+    PERFETTO_DCHECK(field_id < size_);
+    Field* fld = &fields_[field_id];
+    if (PERFETTO_LIKELY(!HasField(field_id))) {
+      // This is the first time we see this field.
+      SetField(field_id);
+      *fld = std::move(res.field);
+    } else {
+      // Repeated field case.
+      // In this case we need to:
+      // 1. Append the last value of the field to end of the repeated field
+      //    storage.
+      // 2. Replace the default instance at offset |field_id| with the current
+      //    value. This is because in case of repeated field a call to Get(X) is
+      //    supposed to return the last value of X, not the first one.
+      // This is so that the RepeatedFieldIterator will iterate in the right
+      // order, see comments on RepeatedFieldIterator.
+      if (num_fields_ > size_) {
+        ExpandHeapStorage();
+        fld = &fields_[field_id];
+      }

-  // Field 4: message_type
-  for (auto& it : message_type_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
+      PERFETTO_DCHECK(size_ < capacity_);
+      fields_[size_++] = *fld;
+      *fld = std::move(res.field);
+    }
   }
+  read_ptr_ = res.next;
+}

-  // Field 5: enum_type
-  for (auto& it : enum_type_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
-  }
+void TypedProtoDecoderBase::ExpandHeapStorage() {
+  // When we expand the heap we must ensure that we have at very last capacity
+  // to deal with all known fields plus at least one repeated field. We go +2048
+  // here based on observations on a large 4GB android trace. This is to avoid
+  // trivial re-allocations when dealing with repeated fields of a message that
+  // has > INITIAL_STACK_CAPACITY fields.
+  const uint32_t min_capacity = num_fields_ + 2048;  // Any num >= +1 will do.
+  const uint32_t new_capacity = std::max(capacity_ * 2, min_capacity);
+  PERFETTO_CHECK(new_capacity > size_ && new_capacity > num_fields_);
+  std::unique_ptr<Field[]> new_storage(new Field[new_capacity]);

-  // Field 7: extension
-  for (auto& it : extension_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(7));
-  }
+  static_assert(std::is_trivially_constructible<Field>::value,
+                "Field must be trivially constructible");
+  static_assert(std::is_trivially_copyable<Field>::value,
+                "Field must be trivially copyable");

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+  // There is no need to initialize the slots for known field IDs: reads of
+  // them are gated on the presence bitmap. There is also no need to initialize
+  // the repeated slots, because they are written linearly with no gaps and are
+  // always initialized before incrementing |size_|.
+  const uint32_t new_size = std::max(size_, num_fields_);
+  memcpy(&new_storage[0], fields_, sizeof(Field) * size_);
+
+  heap_storage_ = std::move(new_storage);
+  fields_ = &heap_storage_[0];
+  capacity_ = new_capacity;
+  size_ = new_size;
+}
+
+}  // namespace protozero
+// gen_amalgamated begin source: src/protozero/scattered_heap_buffer.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+
+#include <algorithm>
+
+namespace protozero {
+
+ScatteredHeapBuffer::Slice::Slice()
+    : buffer_(nullptr), size_(0u), unused_bytes_(0u) {}
+
+ScatteredHeapBuffer::Slice::Slice(size_t size)
+    : buffer_(std::unique_ptr<uint8_t[]>(new uint8_t[size])),
+      size_(size),
+      unused_bytes_(size) {
+  PERFETTO_DCHECK(size);
+  Clear();
 }

+ScatteredHeapBuffer::Slice::Slice(Slice&& slice) noexcept = default;

-FileDescriptorSet::FileDescriptorSet() = default;
-FileDescriptorSet::~FileDescriptorSet() = default;
-FileDescriptorSet::FileDescriptorSet(const FileDescriptorSet&) = default;
-FileDescriptorSet& FileDescriptorSet::operator=(const FileDescriptorSet&) = default;
-FileDescriptorSet::FileDescriptorSet(FileDescriptorSet&&) noexcept = default;
-FileDescriptorSet& FileDescriptorSet::operator=(FileDescriptorSet&&) = default;
+ScatteredHeapBuffer::Slice::~Slice() = default;

-bool FileDescriptorSet::operator==(const FileDescriptorSet& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(file_, other.file_);
+ScatteredHeapBuffer::Slice& ScatteredHeapBuffer::Slice::operator=(Slice&&) =
+    default;
+
+void ScatteredHeapBuffer::Slice::Clear() {
+  unused_bytes_ = size_;
+#if PERFETTO_DCHECK_IS_ON()
+  memset(start(), 0xff, size_);
+#endif  // PERFETTO_DCHECK_IS_ON()
 }

-int FileDescriptorSet::file_size() const { return static_cast<int>(file_.size()); }
-void FileDescriptorSet::clear_file() { file_.clear(); }
-FileDescriptorProto* FileDescriptorSet::add_file() { file_.emplace_back(); return &file_.back(); }
-bool FileDescriptorSet::ParseFromArray(const void* raw, size_t size) {
-  file_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
+ScatteredHeapBuffer::ScatteredHeapBuffer(size_t initial_slice_size_bytes,
+                                         size_t maximum_slice_size_bytes)
+    : next_slice_size_(initial_slice_size_bytes),
+      maximum_slice_size_(maximum_slice_size_bytes) {
+  PERFETTO_DCHECK(next_slice_size_ && maximum_slice_size_);
+  PERFETTO_DCHECK(maximum_slice_size_ >= initial_slice_size_bytes);
+}

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* file */:
-        file_.emplace_back();
-        file_.back().ParseFromArray(field.data(), field.size());
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
+ScatteredHeapBuffer::~ScatteredHeapBuffer() = default;
+
+protozero::ContiguousMemoryRange ScatteredHeapBuffer::GetNewBuffer() {
+  PERFETTO_CHECK(writer_);
+  AdjustUsedSizeOfCurrentSlice();
+
+  if (cached_slice_.start()) {
+    slices_.push_back(std::move(cached_slice_));
+    PERFETTO_DCHECK(!cached_slice_.start());
+  } else {
+    slices_.emplace_back(next_slice_size_);
   }
-  return !packed_error && !dec.bytes_left();
+  next_slice_size_ = std::min(maximum_slice_size_, next_slice_size_ * 2);
+  return slices_.back().GetTotalRange();
 }

-std::string FileDescriptorSet::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
+const std::vector<ScatteredHeapBuffer::Slice>&
+ScatteredHeapBuffer::GetSlices() {
+  AdjustUsedSizeOfCurrentSlice();
+  return slices_;
 }

-std::vector<uint8_t> FileDescriptorSet::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
+std::vector<ScatteredHeapBuffer::Slice> ScatteredHeapBuffer::TakeSlices() {
+  AdjustUsedSizeOfCurrentSlice();
+  std::vector<Slice> slices = std::move(slices_);
+  slices_.clear();
+  if (writer_)
+    writer_->Reset(protozero::ContiguousMemoryRange{});
+  return slices;
 }

-void FileDescriptorSet::Serialize(::protozero::Message* msg) const {
-  // Field 1: file
-  for (auto& it : file_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
-  }
+std::vector<uint8_t> ScatteredHeapBuffer::StitchSlices() {
+  size_t stitched_size = 0u;
+  const auto& slices = GetSlices();
+  for (const auto& slice : slices)
+    stitched_size += slice.size() - slice.unused_bytes();

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+  std::vector<uint8_t> buffer;
+  buffer.reserve(stitched_size);
+  for (const auto& slice : slices) {
+    auto used_range = slice.GetUsedRange();
+    buffer.insert(buffer.end(), used_range.begin, used_range.end);
+  }
+  return buffer;
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
+std::pair<std::unique_ptr<uint8_t[]>, size_t>
+ScatteredHeapBuffer::StitchAsUniquePtr() {
+  size_t stitched_size = 0u;
+  const auto& slices = GetSlices();
+  for (const auto& slice : slices)
+    stitched_size += slice.size() - slice.unused_bytes();
+
+  std::unique_ptr<uint8_t[]> buffer(new uint8_t[stitched_size]);
+  uint8_t* ptr = buffer.get();
+  for (const auto& slice : slices) {
+    auto used_range = slice.GetUsedRange();
+    memcpy(ptr, used_range.begin, used_range.size());
+    ptr += used_range.size();
+  }
+
+  return std::make_pair(std::move(buffer), stitched_size);
+}
+
+std::vector<protozero::ContiguousMemoryRange> ScatteredHeapBuffer::GetRanges() {
+  std::vector<protozero::ContiguousMemoryRange> ranges;
+  for (const auto& slice : GetSlices())
+    ranges.push_back(slice.GetUsedRange());
+  return ranges;
+}
+
+void ScatteredHeapBuffer::AdjustUsedSizeOfCurrentSlice() {
+  if (!slices_.empty())
+    slices_.back().set_unused_bytes(writer_->bytes_available());
+}
+
+size_t ScatteredHeapBuffer::GetTotalSize() {
+  size_t total_size = 0;
+  for (auto& slice : slices_) {
+    total_size += slice.size();
+  }
+  return total_size;
+}
+
+void ScatteredHeapBuffer::Reset() {
+  if (slices_.empty())
+    return;
+  cached_slice_ = std::move(slices_.front());
+  cached_slice_.Clear();
+  slices_.clear();
+}
+
+}  // namespace protozero
+// gen_amalgamated begin source: src/protozero/scattered_stream_null_delegate.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_stream_null_delegate.h"
+
+namespace protozero {
+
+// An implementation of ScatteredStreamWriter::Delegate which always returns
+// the same piece of memory.
+// This is used when we need to no-op the writers (e.g. during teardown or in
+// case of resource exhaustion), avoiding that the clients have to deal with
+// nullptr checks.
+ScatteredStreamWriterNullDelegate::ScatteredStreamWriterNullDelegate(
+    size_t chunk_size)
+    : chunk_size_(chunk_size),
+      chunk_(std::unique_ptr<uint8_t[]>(new uint8_t[chunk_size_])) {}
+
+ScatteredStreamWriterNullDelegate::~ScatteredStreamWriterNullDelegate() {}
+
+ContiguousMemoryRange ScatteredStreamWriterNullDelegate::GetNewBuffer() {
+  return {chunk_.get(), chunk_.get() + chunk_size_};
+}
+
+}  // namespace protozero
+// gen_amalgamated begin source: src/protozero/scattered_stream_writer.cc
+/*
+ * 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_stream_writer.h"
+
+#include <algorithm>
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+
+namespace protozero {
+
+ScatteredStreamWriter::Delegate::~Delegate() {}
+
+uint8_t* ScatteredStreamWriter::Delegate::AnnotatePatch(uint8_t* patch_addr) {
+  // In most cases, a patch is transparent. The caller can write directly into
+  // `to_patch`, because its memory is not going away. TraceWriterImpl, however,
+  // requires a more complicated logic, because the chunks might be copied
+  // earlier.
+  return patch_addr;
+}
+
+ScatteredStreamWriter::ScatteredStreamWriter(Delegate* delegate)
+    : delegate_(delegate),
+      cur_range_({nullptr, nullptr}),
+      write_ptr_(nullptr) {}
+
+ScatteredStreamWriter::~ScatteredStreamWriter() {}
+
+void ScatteredStreamWriter::Reset(ContiguousMemoryRange range) {
+  written_previously_ += static_cast<uint64_t>(write_ptr_ - cur_range_.begin);
+  cur_range_ = range;
+  write_ptr_ = range.begin;
+  PERFETTO_DCHECK(!write_ptr_ || write_ptr_ < cur_range_.end);
+}
+
+void ScatteredStreamWriter::Extend() {
+  Reset(delegate_->GetNewBuffer());
+}
+
+void ScatteredStreamWriter::WriteBytesSlowPath(const uint8_t* src,
+                                               size_t size) {
+  size_t bytes_left = size;
+  while (bytes_left > 0) {
+    if (write_ptr_ >= cur_range_.end)
+      Extend();
+    const size_t burst_size = std::min(bytes_available(), bytes_left);
+    WriteBytesUnsafe(src, burst_size);
+    bytes_left -= burst_size;
+    src += burst_size;
+  }
+}
+
+// TODO(primiano): perf optimization: I suspect that at the end this will always
+// be called with |size| == 4, in which case we might just hardcode it.
+uint8_t* ScatteredStreamWriter::ReserveBytes(size_t size) {
+  PERFETTO_DCHECK(write_ptr_ <= cur_range_.end);
+  if (size > static_cast<size_t>(cur_range_.end - write_ptr_)) {
+    // Assume the reservations are always < Delegate::GetNewBuffer().size(),
+    // so that one single call to Extend() will definitely give enough headroom.
+    Extend();
+    PERFETTO_DCHECK(write_ptr_ <= cur_range_.end);
+    PERFETTO_DCHECK(size <= static_cast<size_t>(cur_range_.end - write_ptr_));
+  }
+  uint8_t* begin = write_ptr_;
+  write_ptr_ += size;
+#if PERFETTO_DCHECK_IS_ON()
+  // In the past, the service had a matching DCHECK in
+  // TraceBuffer::TryPatchChunkContents, which was assuming that service and all
+  // producers are built with matching DCHECK levels. This turned out to be a
+  // source of problems and was removed in b/197340286. This memset is useless
+  // these days and is here only to maintain ABI compatibility between producers
+  // that use a v20+ SDK and older versions of the service that were built in
+  // debug mode. At some point around 2023 it should be safe to remove it.
+  // (running a debug version of traced in production seems a bad idea
+  // regardless).
+  memset(begin, 0, size);
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/ftrace_descriptor.gen.cc
+  return begin;
+}
+
+}  // namespace protozero
+// gen_amalgamated begin source: src/protozero/static_buffer.cc
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/protozero/static_buffer.h"
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+
+namespace protozero {
+
+StaticBufferDelegate::~StaticBufferDelegate() = default;
+
+ContiguousMemoryRange StaticBufferDelegate::GetNewBuffer() {
+  if (get_new_buffer_called_once_) {
+    // This is the 2nd time GetNewBuffer is called. The estimate is wrong. We
+    // shouldn't try to grow the buffer after the initial call.
+    PERFETTO_FATAL("Static buffer too small");
+  }
+  get_new_buffer_called_once_ = true;
+  return range_;
+}
+
+}  // namespace protozero
+// gen_amalgamated begin source: src/protozero/virtual_destructors.cc
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/protozero/cpp_message_obj.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message_handle.h"
+
+namespace protozero {
+
+CppMessageObj::~CppMessageObj() = default;
+MessageFinalizationListener::~MessageFinalizationListener() = default;
+
+}  // namespace protozero
+// gen_amalgamated begin source: gen/protos/perfetto/protovm/vm_program.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -17917,29 +18192,24 @@ void FileDescriptorSet::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/ftrace_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/protovm/vm_program.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-FtraceDescriptor::FtraceDescriptor() = default;
-FtraceDescriptor::~FtraceDescriptor() = default;
-FtraceDescriptor::FtraceDescriptor(const FtraceDescriptor&) = default;
-FtraceDescriptor& FtraceDescriptor::operator=(const FtraceDescriptor&) = default;
-FtraceDescriptor::FtraceDescriptor(FtraceDescriptor&&) noexcept = default;
-FtraceDescriptor& FtraceDescriptor::operator=(FtraceDescriptor&&) = default;
+VmOpDel::VmOpDel() = default;
+VmOpDel::~VmOpDel() = default;
+VmOpDel::VmOpDel(const VmOpDel&) = default;
+VmOpDel& VmOpDel::operator=(const VmOpDel&) = default;
+VmOpDel::VmOpDel(VmOpDel&&) noexcept = default;
+VmOpDel& VmOpDel::operator=(VmOpDel&&) = default;

-bool FtraceDescriptor::operator==(const FtraceDescriptor& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(atrace_categories_, other.atrace_categories_);
+bool VmOpDel::operator==(const VmOpDel& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
 }

-int FtraceDescriptor::atrace_categories_size() const { return static_cast<int>(atrace_categories_.size()); }
-void FtraceDescriptor::clear_atrace_categories() { atrace_categories_.clear(); }
-FtraceDescriptor_AtraceCategory* FtraceDescriptor::add_atrace_categories() { atrace_categories_.emplace_back(); return &atrace_categories_.back(); }
-bool FtraceDescriptor::ParseFromArray(const void* raw, size_t size) {
-  atrace_categories_.clear();
+bool VmOpDel::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -17949,10 +18219,6 @@ bool FtraceDescriptor::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* atrace_categories */:
-        atrace_categories_.emplace_back();
-        atrace_categories_.back().ParseFromArray(field.data(), field.size());
-        break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
         break;
@@ -17961,42 +18227,35 @@ bool FtraceDescriptor::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string FtraceDescriptor::SerializeAsString() const {
+std::string VmOpDel::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> FtraceDescriptor::SerializeAsArray() const {
+std::vector<uint8_t> VmOpDel::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void FtraceDescriptor::Serialize(::protozero::Message* msg) const {
-  // Field 1: atrace_categories
-  for (auto& it : atrace_categories_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
-  }
-
+void VmOpDel::Serialize(::protozero::Message* msg) const {
   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-FtraceDescriptor_AtraceCategory::FtraceDescriptor_AtraceCategory() = default;
-FtraceDescriptor_AtraceCategory::~FtraceDescriptor_AtraceCategory() = default;
-FtraceDescriptor_AtraceCategory::FtraceDescriptor_AtraceCategory(const FtraceDescriptor_AtraceCategory&) = default;
-FtraceDescriptor_AtraceCategory& FtraceDescriptor_AtraceCategory::operator=(const FtraceDescriptor_AtraceCategory&) = default;
-FtraceDescriptor_AtraceCategory::FtraceDescriptor_AtraceCategory(FtraceDescriptor_AtraceCategory&&) noexcept = default;
-FtraceDescriptor_AtraceCategory& FtraceDescriptor_AtraceCategory::operator=(FtraceDescriptor_AtraceCategory&&) = default;
+VmOpSet::VmOpSet() = default;
+VmOpSet::~VmOpSet() = default;
+VmOpSet::VmOpSet(const VmOpSet&) = default;
+VmOpSet& VmOpSet::operator=(const VmOpSet&) = default;
+VmOpSet::VmOpSet(VmOpSet&&) noexcept = default;
+VmOpSet& VmOpSet::operator=(VmOpSet&&) = default;

-bool FtraceDescriptor_AtraceCategory::operator==(const FtraceDescriptor_AtraceCategory& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(description_, other.description_);
+bool VmOpSet::operator==(const VmOpSet& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
 }

-bool FtraceDescriptor_AtraceCategory::ParseFromArray(const void* raw, size_t size) {
+bool VmOpSet::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -18006,12 +18265,6 @@ bool FtraceDescriptor_AtraceCategory::ParseFromArray(const void* raw, size_t siz
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 2 /* description */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &description_);
-        break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
         break;
@@ -18020,87 +18273,37 @@ bool FtraceDescriptor_AtraceCategory::ParseFromArray(const void* raw, size_t siz
   return !packed_error && !dec.bytes_left();
 }

-std::string FtraceDescriptor_AtraceCategory::SerializeAsString() const {
+std::string VmOpSet::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> FtraceDescriptor_AtraceCategory::SerializeAsArray() const {
+std::vector<uint8_t> VmOpSet::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void FtraceDescriptor_AtraceCategory::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
-  }
-
-  // Field 2: description
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, description_, msg);
-  }
-
+void VmOpSet::Serialize(::protozero::Message* msg) const {
   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/gpu_counter_descriptor.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/gpu_counter_descriptor.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-GpuCounterDescriptor::GpuCounterDescriptor() = default;
-GpuCounterDescriptor::~GpuCounterDescriptor() = default;
-GpuCounterDescriptor::GpuCounterDescriptor(const GpuCounterDescriptor&) = default;
-GpuCounterDescriptor& GpuCounterDescriptor::operator=(const GpuCounterDescriptor&) = default;
-GpuCounterDescriptor::GpuCounterDescriptor(GpuCounterDescriptor&&) noexcept = default;
-GpuCounterDescriptor& GpuCounterDescriptor::operator=(GpuCounterDescriptor&&) = default;
+VmOpMerge::VmOpMerge() = default;
+VmOpMerge::~VmOpMerge() = default;
+VmOpMerge::VmOpMerge(const VmOpMerge&) = default;
+VmOpMerge& VmOpMerge::operator=(const VmOpMerge&) = default;
+VmOpMerge::VmOpMerge(VmOpMerge&&) noexcept = default;
+VmOpMerge& VmOpMerge::operator=(VmOpMerge&&) = default;

-bool GpuCounterDescriptor::operator==(const GpuCounterDescriptor& other) const {
+bool VmOpMerge::operator==(const VmOpMerge& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(specs_, other.specs_)
-   && ::protozero::internal::gen_helpers::EqualsField(blocks_, other.blocks_)
-   && ::protozero::internal::gen_helpers::EqualsField(counter_groups_, other.counter_groups_)
-   && ::protozero::internal::gen_helpers::EqualsField(min_sampling_period_ns_, other.min_sampling_period_ns_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_sampling_period_ns_, other.max_sampling_period_ns_)
-   && ::protozero::internal::gen_helpers::EqualsField(supports_instrumented_sampling_, other.supports_instrumented_sampling_)
-   && ::protozero::internal::gen_helpers::EqualsField(supports_counter_names_, other.supports_counter_names_)
-   && ::protozero::internal::gen_helpers::EqualsField(supports_counter_name_globs_, other.supports_counter_name_globs_);
+   && ::protozero::internal::gen_helpers::EqualsField(skip_submessages_, other.skip_submessages_)
+   && ::protozero::internal::gen_helpers::EqualsField(del_if_src_empty_, other.del_if_src_empty_);
 }

-int GpuCounterDescriptor::specs_size() const { return static_cast<int>(specs_.size()); }
-void GpuCounterDescriptor::clear_specs() { specs_.clear(); }
-GpuCounterDescriptor_GpuCounterSpec* GpuCounterDescriptor::add_specs() { specs_.emplace_back(); return &specs_.back(); }
-int GpuCounterDescriptor::blocks_size() const { return static_cast<int>(blocks_.size()); }
-void GpuCounterDescriptor::clear_blocks() { blocks_.clear(); }
-GpuCounterDescriptor_GpuCounterBlock* GpuCounterDescriptor::add_blocks() { blocks_.emplace_back(); return &blocks_.back(); }
-int GpuCounterDescriptor::counter_groups_size() const { return static_cast<int>(counter_groups_.size()); }
-void GpuCounterDescriptor::clear_counter_groups() { counter_groups_.clear(); }
-GpuCounterDescriptor_GpuCounterGroupSpec* GpuCounterDescriptor::add_counter_groups() { counter_groups_.emplace_back(); return &counter_groups_.back(); }
-bool GpuCounterDescriptor::ParseFromArray(const void* raw, size_t size) {
-  specs_.clear();
-  blocks_.clear();
-  counter_groups_.clear();
+bool VmOpMerge::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -18110,32 +18313,11 @@ bool GpuCounterDescriptor::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* specs */:
-        specs_.emplace_back();
-        specs_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 2 /* blocks */:
-        blocks_.emplace_back();
-        blocks_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 6 /* counter_groups */:
-        counter_groups_.emplace_back();
-        counter_groups_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 3 /* min_sampling_period_ns */:
-        field.get(&min_sampling_period_ns_);
-        break;
-      case 4 /* max_sampling_period_ns */:
-        field.get(&max_sampling_period_ns_);
-        break;
-      case 5 /* supports_instrumented_sampling */:
-        field.get(&supports_instrumented_sampling_);
-        break;
-      case 7 /* supports_counter_names */:
-        field.get(&supports_counter_names_);
+      case 1 /* skip_submessages */:
+        field.get(&skip_submessages_);
         break;
-      case 8 /* supports_counter_name_globs */:
-        field.get(&supports_counter_name_globs_);
+      case 2 /* del_if_src_empty */:
+        field.get(&del_if_src_empty_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -18145,80 +18327,47 @@ bool GpuCounterDescriptor::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string GpuCounterDescriptor::SerializeAsString() const {
+std::string VmOpMerge::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> GpuCounterDescriptor::SerializeAsArray() const {
+std::vector<uint8_t> VmOpMerge::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void GpuCounterDescriptor::Serialize(::protozero::Message* msg) const {
-  // Field 1: specs
-  for (auto& it : specs_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
-  }
-
-  // Field 2: blocks
-  for (auto& it : blocks_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
-  }
-
-  // Field 6: counter_groups
-  for (auto& it : counter_groups_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
-  }
-
-  // Field 3: min_sampling_period_ns
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, min_sampling_period_ns_, msg);
-  }
-
-  // Field 4: max_sampling_period_ns
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, max_sampling_period_ns_, msg);
-  }
-
-  // Field 5: supports_instrumented_sampling
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, supports_instrumented_sampling_, msg);
-  }
-
-  // Field 7: supports_counter_names
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, supports_counter_names_, msg);
+void VmOpMerge::Serialize(::protozero::Message* msg) const {
+  // Field 1: skip_submessages
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, skip_submessages_, msg);
   }

-  // Field 8: supports_counter_name_globs
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, supports_counter_name_globs_, msg);
+  // Field 2: del_if_src_empty
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, del_if_src_empty_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-GpuCounterDescriptor_GpuCounterGroupSpec::GpuCounterDescriptor_GpuCounterGroupSpec() = default;
-GpuCounterDescriptor_GpuCounterGroupSpec::~GpuCounterDescriptor_GpuCounterGroupSpec() = default;
-GpuCounterDescriptor_GpuCounterGroupSpec::GpuCounterDescriptor_GpuCounterGroupSpec(const GpuCounterDescriptor_GpuCounterGroupSpec&) = default;
-GpuCounterDescriptor_GpuCounterGroupSpec& GpuCounterDescriptor_GpuCounterGroupSpec::operator=(const GpuCounterDescriptor_GpuCounterGroupSpec&) = default;
-GpuCounterDescriptor_GpuCounterGroupSpec::GpuCounterDescriptor_GpuCounterGroupSpec(GpuCounterDescriptor_GpuCounterGroupSpec&&) noexcept = default;
-GpuCounterDescriptor_GpuCounterGroupSpec& GpuCounterDescriptor_GpuCounterGroupSpec::operator=(GpuCounterDescriptor_GpuCounterGroupSpec&&) = default;
+VmOpRegLoad::VmOpRegLoad() = default;
+VmOpRegLoad::~VmOpRegLoad() = default;
+VmOpRegLoad::VmOpRegLoad(const VmOpRegLoad&) = default;
+VmOpRegLoad& VmOpRegLoad::operator=(const VmOpRegLoad&) = default;
+VmOpRegLoad::VmOpRegLoad(VmOpRegLoad&&) noexcept = default;
+VmOpRegLoad& VmOpRegLoad::operator=(VmOpRegLoad&&) = default;

-bool GpuCounterDescriptor_GpuCounterGroupSpec::operator==(const GpuCounterDescriptor_GpuCounterGroupSpec& other) const {
+bool VmOpRegLoad::operator==(const VmOpRegLoad& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(group_id_, other.group_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(description_, other.description_)
-   && ::protozero::internal::gen_helpers::EqualsField(counter_ids_, other.counter_ids_);
+   && ::protozero::internal::gen_helpers::EqualsField(cursor_, other.cursor_)
+   && ::protozero::internal::gen_helpers::EqualsField(dst_register_, other.dst_register_);
 }

-bool GpuCounterDescriptor_GpuCounterGroupSpec::ParseFromArray(const void* raw, size_t size) {
-  counter_ids_.clear();
+bool VmOpRegLoad::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -18228,18 +18377,11 @@ bool GpuCounterDescriptor_GpuCounterGroupSpec::ParseFromArray(const void* raw, s
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* group_id */:
-        field.get(&group_id_);
-        break;
-      case 2 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 3 /* description */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &description_);
+      case 1 /* cursor */:
+        field.get(&cursor_);
         break;
-      case 4 /* counter_ids */:
-        counter_ids_.emplace_back();
-        field.get(&counter_ids_.back());
+      case 2 /* dst_register */:
+        field.get(&dst_register_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -18249,61 +18391,129 @@ bool GpuCounterDescriptor_GpuCounterGroupSpec::ParseFromArray(const void* raw, s
   return !packed_error && !dec.bytes_left();
 }

-std::string GpuCounterDescriptor_GpuCounterGroupSpec::SerializeAsString() const {
+std::string VmOpRegLoad::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> GpuCounterDescriptor_GpuCounterGroupSpec::SerializeAsArray() const {
+std::vector<uint8_t> VmOpRegLoad::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void GpuCounterDescriptor_GpuCounterGroupSpec::Serialize(::protozero::Message* msg) const {
-  // Field 1: group_id
+void VmOpRegLoad::Serialize(::protozero::Message* msg) const {
+  // Field 1: cursor
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, group_id_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, cursor_, msg);
   }

-  // Field 2: name
+  // Field 2: dst_register
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, dst_register_, msg);
   }

-  // Field 3: description
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeString(3, description_, msg);
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+VmOpSelect::VmOpSelect() = default;
+VmOpSelect::~VmOpSelect() = default;
+VmOpSelect::VmOpSelect(const VmOpSelect&) = default;
+VmOpSelect& VmOpSelect::operator=(const VmOpSelect&) = default;
+VmOpSelect::VmOpSelect(VmOpSelect&&) noexcept = default;
+VmOpSelect& VmOpSelect::operator=(VmOpSelect&&) = default;
+
+bool VmOpSelect::operator==(const VmOpSelect& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(cursor_, other.cursor_)
+   && ::protozero::internal::gen_helpers::EqualsField(relative_path_, other.relative_path_)
+   && ::protozero::internal::gen_helpers::EqualsField(create_if_not_exist_, other.create_if_not_exist_);
+}
+
+int VmOpSelect::relative_path_size() const { return static_cast<int>(relative_path_.size()); }
+void VmOpSelect::clear_relative_path() { relative_path_.clear(); }
+VmOpSelect_PathComponent* VmOpSelect::add_relative_path() { relative_path_.emplace_back(); return &relative_path_.back(); }
+bool VmOpSelect::ParseFromArray(const void* raw, size_t size) {
+  relative_path_.clear();
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* cursor */:
+        field.get(&cursor_);
+        break;
+      case 2 /* relative_path */:
+        relative_path_.emplace_back();
+        relative_path_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 3 /* create_if_not_exist */:
+        field.get(&create_if_not_exist_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 4: counter_ids
-  for (auto& it : counter_ids_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, it, msg);
+std::string VmOpSelect::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> VmOpSelect::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void VmOpSelect::Serialize(::protozero::Message* msg) const {
+  // Field 1: cursor
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, cursor_, msg);
+  }
+
+  // Field 2: relative_path
+  for (auto& it : relative_path_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+  }
+
+  // Field 3: create_if_not_exist
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, create_if_not_exist_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-GpuCounterDescriptor_GpuCounterBlock::GpuCounterDescriptor_GpuCounterBlock() = default;
-GpuCounterDescriptor_GpuCounterBlock::~GpuCounterDescriptor_GpuCounterBlock() = default;
-GpuCounterDescriptor_GpuCounterBlock::GpuCounterDescriptor_GpuCounterBlock(const GpuCounterDescriptor_GpuCounterBlock&) = default;
-GpuCounterDescriptor_GpuCounterBlock& GpuCounterDescriptor_GpuCounterBlock::operator=(const GpuCounterDescriptor_GpuCounterBlock&) = default;
-GpuCounterDescriptor_GpuCounterBlock::GpuCounterDescriptor_GpuCounterBlock(GpuCounterDescriptor_GpuCounterBlock&&) noexcept = default;
-GpuCounterDescriptor_GpuCounterBlock& GpuCounterDescriptor_GpuCounterBlock::operator=(GpuCounterDescriptor_GpuCounterBlock&&) = default;
+VmOpSelect_PathComponent::VmOpSelect_PathComponent() = default;
+VmOpSelect_PathComponent::~VmOpSelect_PathComponent() = default;
+VmOpSelect_PathComponent::VmOpSelect_PathComponent(const VmOpSelect_PathComponent&) = default;
+VmOpSelect_PathComponent& VmOpSelect_PathComponent::operator=(const VmOpSelect_PathComponent&) = default;
+VmOpSelect_PathComponent::VmOpSelect_PathComponent(VmOpSelect_PathComponent&&) noexcept = default;
+VmOpSelect_PathComponent& VmOpSelect_PathComponent::operator=(VmOpSelect_PathComponent&&) = default;

-bool GpuCounterDescriptor_GpuCounterBlock::operator==(const GpuCounterDescriptor_GpuCounterBlock& other) const {
+bool VmOpSelect_PathComponent::operator==(const VmOpSelect_PathComponent& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(block_id_, other.block_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(block_capacity_, other.block_capacity_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(description_, other.description_)
-   && ::protozero::internal::gen_helpers::EqualsField(counter_ids_, other.counter_ids_);
+   && ::protozero::internal::gen_helpers::EqualsField(field_id_, other.field_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(array_index_, other.array_index_)
+   && ::protozero::internal::gen_helpers::EqualsField(map_key_field_id_, other.map_key_field_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(is_repeated_, other.is_repeated_)
+   && ::protozero::internal::gen_helpers::EqualsField(register_to_match_, other.register_to_match_)
+   && ::protozero::internal::gen_helpers::EqualsField(store_foreach_index_into_register_, other.store_foreach_index_into_register_);
 }

-bool GpuCounterDescriptor_GpuCounterBlock::ParseFromArray(const void* raw, size_t size) {
-  counter_ids_.clear();
+bool VmOpSelect_PathComponent::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -18313,21 +18523,23 @@ bool GpuCounterDescriptor_GpuCounterBlock::ParseFromArray(const void* raw, size_
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* block_id */:
-        field.get(&block_id_);
+      case 1 /* field_id */:
+        field.get(&field_id_);
         break;
-      case 2 /* block_capacity */:
-        field.get(&block_capacity_);
+      case 2 /* array_index */:
+        field.get(&array_index_);
         break;
-      case 3 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+      case 3 /* map_key_field_id */:
+        field.get(&map_key_field_id_);
         break;
-      case 4 /* description */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &description_);
+      case 5 /* is_repeated */:
+        field.get(&is_repeated_);
         break;
-      case 5 /* counter_ids */:
-        counter_ids_.emplace_back();
-        field.get(&counter_ids_.back());
+      case 6 /* register_to_match */:
+        field.get(&register_to_match_);
+        break;
+      case 7 /* store_foreach_index_into_register */:
+        field.get(&store_foreach_index_into_register_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -18337,73 +18549,76 @@ bool GpuCounterDescriptor_GpuCounterBlock::ParseFromArray(const void* raw, size_
   return !packed_error && !dec.bytes_left();
 }

-std::string GpuCounterDescriptor_GpuCounterBlock::SerializeAsString() const {
+std::string VmOpSelect_PathComponent::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> GpuCounterDescriptor_GpuCounterBlock::SerializeAsArray() const {
+std::vector<uint8_t> VmOpSelect_PathComponent::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void GpuCounterDescriptor_GpuCounterBlock::Serialize(::protozero::Message* msg) const {
-  // Field 1: block_id
+void VmOpSelect_PathComponent::Serialize(::protozero::Message* msg) const {
+  // Field 1: field_id
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, block_id_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, field_id_, msg);
   }

-  // Field 2: block_capacity
+  // Field 2: array_index
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, block_capacity_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, array_index_, msg);
   }

-  // Field 3: name
+  // Field 3: map_key_field_id
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeString(3, name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, map_key_field_id_, msg);
   }

-  // Field 4: description
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeString(4, description_, msg);
+  // Field 5: is_repeated
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, is_repeated_, msg);
   }

-  // Field 5: counter_ids
-  for (auto& it : counter_ids_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, it, msg);
+  // Field 6: register_to_match
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, register_to_match_, msg);
+  }
+
+  // Field 7: store_foreach_index_into_register
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, store_foreach_index_into_register_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-GpuCounterDescriptor_GpuCounterSpec::GpuCounterDescriptor_GpuCounterSpec() = default;
-GpuCounterDescriptor_GpuCounterSpec::~GpuCounterDescriptor_GpuCounterSpec() = default;
-GpuCounterDescriptor_GpuCounterSpec::GpuCounterDescriptor_GpuCounterSpec(const GpuCounterDescriptor_GpuCounterSpec&) = default;
-GpuCounterDescriptor_GpuCounterSpec& GpuCounterDescriptor_GpuCounterSpec::operator=(const GpuCounterDescriptor_GpuCounterSpec&) = default;
-GpuCounterDescriptor_GpuCounterSpec::GpuCounterDescriptor_GpuCounterSpec(GpuCounterDescriptor_GpuCounterSpec&&) noexcept = default;
-GpuCounterDescriptor_GpuCounterSpec& GpuCounterDescriptor_GpuCounterSpec::operator=(GpuCounterDescriptor_GpuCounterSpec&&) = default;
+VmInstruction::VmInstruction() = default;
+VmInstruction::~VmInstruction() = default;
+VmInstruction::VmInstruction(const VmInstruction&) = default;
+VmInstruction& VmInstruction::operator=(const VmInstruction&) = default;
+VmInstruction::VmInstruction(VmInstruction&&) noexcept = default;
+VmInstruction& VmInstruction::operator=(VmInstruction&&) = default;

-bool GpuCounterDescriptor_GpuCounterSpec::operator==(const GpuCounterDescriptor_GpuCounterSpec& other) const {
+bool VmInstruction::operator==(const VmInstruction& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(counter_id_, other.counter_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(description_, other.description_)
-   && ::protozero::internal::gen_helpers::EqualsField(int_peak_value_, other.int_peak_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(double_peak_value_, other.double_peak_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(numerator_units_, other.numerator_units_)
-   && ::protozero::internal::gen_helpers::EqualsField(denominator_units_, other.denominator_units_)
-   && ::protozero::internal::gen_helpers::EqualsField(select_by_default_, other.select_by_default_)
-   && ::protozero::internal::gen_helpers::EqualsField(groups_, other.groups_)
-   && ::protozero::internal::gen_helpers::EqualsField(value_direction_, other.value_direction_);
+   && ::protozero::internal::gen_helpers::EqualsField(select_, other.select_)
+   && ::protozero::internal::gen_helpers::EqualsField(reg_load_, other.reg_load_)
+   && ::protozero::internal::gen_helpers::EqualsField(merge_, other.merge_)
+   && ::protozero::internal::gen_helpers::EqualsField(set_, other.set_)
+   && ::protozero::internal::gen_helpers::EqualsField(del_, other.del_)
+   && ::protozero::internal::gen_helpers::EqualsField(abort_level_, other.abort_level_)
+   && ::protozero::internal::gen_helpers::EqualsField(nested_instructions_, other.nested_instructions_);
 }

-bool GpuCounterDescriptor_GpuCounterSpec::ParseFromArray(const void* raw, size_t size) {
-  numerator_units_.clear();
-  denominator_units_.clear();
-  groups_.clear();
+int VmInstruction::nested_instructions_size() const { return static_cast<int>(nested_instructions_.size()); }
+void VmInstruction::clear_nested_instructions() { nested_instructions_.clear(); }
+VmInstruction* VmInstruction::add_nested_instructions() { nested_instructions_.emplace_back(); return &nested_instructions_.back(); }
+bool VmInstruction::ParseFromArray(const void* raw, size_t size) {
+  nested_instructions_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -18413,38 +18628,27 @@ bool GpuCounterDescriptor_GpuCounterSpec::ParseFromArray(const void* raw, size_t
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* counter_id */:
-        field.get(&counter_id_);
-        break;
-      case 2 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 3 /* description */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &description_);
-        break;
-      case 5 /* int_peak_value */:
-        field.get(&int_peak_value_);
+      case 1 /* select */:
+        (*select_).ParseFromArray(field.data(), field.size());
         break;
-      case 6 /* double_peak_value */:
-        field.get(&double_peak_value_);
+      case 2 /* reg_load */:
+        (*reg_load_).ParseFromArray(field.data(), field.size());
         break;
-      case 7 /* numerator_units */:
-        numerator_units_.emplace_back();
-        field.get(&numerator_units_.back());
+      case 3 /* merge */:
+        (*merge_).ParseFromArray(field.data(), field.size());
         break;
-      case 8 /* denominator_units */:
-        denominator_units_.emplace_back();
-        field.get(&denominator_units_.back());
+      case 4 /* set */:
+        (*set_).ParseFromArray(field.data(), field.size());
         break;
-      case 9 /* select_by_default */:
-        field.get(&select_by_default_);
+      case 5 /* del */:
+        (*del_).ParseFromArray(field.data(), field.size());
         break;
-      case 10 /* groups */:
-        groups_.emplace_back();
-        field.get(&groups_.back());
+      case 6 /* abort_level */:
+        field.get(&abort_level_);
         break;
-      case 11 /* value_direction */:
-        field.get(&value_direction_);
+      case 7 /* nested_instructions */:
+        nested_instructions_.emplace_back();
+        nested_instructions_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -18454,108 +18658,76 @@ bool GpuCounterDescriptor_GpuCounterSpec::ParseFromArray(const void* raw, size_t
   return !packed_error && !dec.bytes_left();
 }

-std::string GpuCounterDescriptor_GpuCounterSpec::SerializeAsString() const {
+std::string VmInstruction::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> GpuCounterDescriptor_GpuCounterSpec::SerializeAsArray() const {
+std::vector<uint8_t> VmInstruction::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void GpuCounterDescriptor_GpuCounterSpec::Serialize(::protozero::Message* msg) const {
-  // Field 1: counter_id
+void VmInstruction::Serialize(::protozero::Message* msg) const {
+  // Field 1: select
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, counter_id_, msg);
+    (*select_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 2: name
+  // Field 2: reg_load
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
+    (*reg_load_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

-  // Field 3: description
+  // Field 3: merge
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeString(3, description_, msg);
-  }
-
-  // Field 5: int_peak_value
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, int_peak_value_, msg);
-  }
-
-  // Field 6: double_peak_value
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(6, double_peak_value_, msg);
-  }
-
-  // Field 7: numerator_units
-  for (auto& it : numerator_units_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, it, msg);
+    (*merge_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
   }

-  // Field 8: denominator_units
-  for (auto& it : denominator_units_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, it, msg);
+  // Field 4: set
+  if (_has_field_[4]) {
+    (*set_).Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
   }

-  // Field 9: select_by_default
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, select_by_default_, msg);
+  // Field 5: del
+  if (_has_field_[5]) {
+    (*del_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
   }

-  // Field 10: groups
-  for (auto& it : groups_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(10, it, msg);
+  // Field 6: abort_level
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, abort_level_, msg);
   }

-  // Field 11: value_direction
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(11, value_direction_, msg);
+  // Field 7: nested_instructions
+  for (auto& it : nested_instructions_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(7));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/interceptor_descriptor.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/interceptor_descriptor.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-InterceptorDescriptor::InterceptorDescriptor() = default;
-InterceptorDescriptor::~InterceptorDescriptor() = default;
-InterceptorDescriptor::InterceptorDescriptor(const InterceptorDescriptor&) = default;
-InterceptorDescriptor& InterceptorDescriptor::operator=(const InterceptorDescriptor&) = default;
-InterceptorDescriptor::InterceptorDescriptor(InterceptorDescriptor&&) noexcept = default;
-InterceptorDescriptor& InterceptorDescriptor::operator=(InterceptorDescriptor&&) = default;
+VmProgram::VmProgram() = default;
+VmProgram::~VmProgram() = default;
+VmProgram::VmProgram(const VmProgram&) = default;
+VmProgram& VmProgram::operator=(const VmProgram&) = default;
+VmProgram::VmProgram(VmProgram&&) noexcept = default;
+VmProgram& VmProgram::operator=(VmProgram&&) = default;

-bool InterceptorDescriptor::operator==(const InterceptorDescriptor& other) const {
+bool VmProgram::operator==(const VmProgram& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+   && ::protozero::internal::gen_helpers::EqualsField(version_, other.version_)
+   && ::protozero::internal::gen_helpers::EqualsField(instructions_, other.instructions_);
 }

-bool InterceptorDescriptor::ParseFromArray(const void* raw, size_t size) {
+int VmProgram::instructions_size() const { return static_cast<int>(instructions_.size()); }
+void VmProgram::clear_instructions() { instructions_.clear(); }
+VmInstruction* VmProgram::add_instructions() { instructions_.emplace_back(); return &instructions_.back(); }
+bool VmProgram::ParseFromArray(const void* raw, size_t size) {
+  instructions_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -18565,8 +18737,12 @@ bool InterceptorDescriptor::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+      case 1 /* version */:
+        field.get(&version_);
+        break;
+      case 2 /* instructions */:
+        instructions_.emplace_back();
+        instructions_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -18576,22 +18752,27 @@ bool InterceptorDescriptor::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string InterceptorDescriptor::SerializeAsString() const {
+std::string VmProgram::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> InterceptorDescriptor::SerializeAsArray() const {
+std::vector<uint8_t> VmProgram::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void InterceptorDescriptor::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
+void VmProgram::Serialize(::protozero::Message* msg) const {
+  // Field 1: version
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, version_, msg);
+  }
+
+  // Field 2: instructions
+  for (auto& it : instructions_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -18603,7 +18784,7 @@ void InterceptorDescriptor::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/observable_events.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/common/android_energy_consumer_descriptor.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -18614,31 +18795,29 @@ void InterceptorDescriptor::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/observable_events.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/android_energy_consumer_descriptor.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-ObservableEvents::ObservableEvents() = default;
-ObservableEvents::~ObservableEvents() = default;
-ObservableEvents::ObservableEvents(const ObservableEvents&) = default;
-ObservableEvents& ObservableEvents::operator=(const ObservableEvents&) = default;
-ObservableEvents::ObservableEvents(ObservableEvents&&) noexcept = default;
-ObservableEvents& ObservableEvents::operator=(ObservableEvents&&) = default;
+AndroidEnergyConsumerDescriptor::AndroidEnergyConsumerDescriptor() = default;
+AndroidEnergyConsumerDescriptor::~AndroidEnergyConsumerDescriptor() = default;
+AndroidEnergyConsumerDescriptor::AndroidEnergyConsumerDescriptor(const AndroidEnergyConsumerDescriptor&) = default;
+AndroidEnergyConsumerDescriptor& AndroidEnergyConsumerDescriptor::operator=(const AndroidEnergyConsumerDescriptor&) = default;
+AndroidEnergyConsumerDescriptor::AndroidEnergyConsumerDescriptor(AndroidEnergyConsumerDescriptor&&) noexcept = default;
+AndroidEnergyConsumerDescriptor& AndroidEnergyConsumerDescriptor::operator=(AndroidEnergyConsumerDescriptor&&) = default;

-bool ObservableEvents::operator==(const ObservableEvents& other) const {
+bool AndroidEnergyConsumerDescriptor::operator==(const AndroidEnergyConsumerDescriptor& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(instance_state_changes_, other.instance_state_changes_)
-   && ::protozero::internal::gen_helpers::EqualsField(all_data_sources_started_, other.all_data_sources_started_)
-   && ::protozero::internal::gen_helpers::EqualsField(clone_trigger_hit_, other.clone_trigger_hit_);
+   && ::protozero::internal::gen_helpers::EqualsField(energy_consumers_, other.energy_consumers_);
 }

-int ObservableEvents::instance_state_changes_size() const { return static_cast<int>(instance_state_changes_.size()); }
-void ObservableEvents::clear_instance_state_changes() { instance_state_changes_.clear(); }
-ObservableEvents_DataSourceInstanceStateChange* ObservableEvents::add_instance_state_changes() { instance_state_changes_.emplace_back(); return &instance_state_changes_.back(); }
-bool ObservableEvents::ParseFromArray(const void* raw, size_t size) {
-  instance_state_changes_.clear();
+int AndroidEnergyConsumerDescriptor::energy_consumers_size() const { return static_cast<int>(energy_consumers_.size()); }
+void AndroidEnergyConsumerDescriptor::clear_energy_consumers() { energy_consumers_.clear(); }
+AndroidEnergyConsumer* AndroidEnergyConsumerDescriptor::add_energy_consumers() { energy_consumers_.emplace_back(); return &energy_consumers_.back(); }
+bool AndroidEnergyConsumerDescriptor::ParseFromArray(const void* raw, size_t size) {
+  energy_consumers_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -18648,15 +18827,9 @@ bool ObservableEvents::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* instance_state_changes */:
-        instance_state_changes_.emplace_back();
-        instance_state_changes_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 2 /* all_data_sources_started */:
-        field.get(&all_data_sources_started_);
-        break;
-      case 3 /* clone_trigger_hit */:
-        (*clone_trigger_hit_).ParseFromArray(field.data(), field.size());
+      case 1 /* energy_consumers */:
+        energy_consumers_.emplace_back();
+        energy_consumers_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -18666,56 +18839,44 @@ bool ObservableEvents::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ObservableEvents::SerializeAsString() const {
+std::string AndroidEnergyConsumerDescriptor::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ObservableEvents::SerializeAsArray() const {
+std::vector<uint8_t> AndroidEnergyConsumerDescriptor::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ObservableEvents::Serialize(::protozero::Message* msg) const {
-  // Field 1: instance_state_changes
-  for (auto& it : instance_state_changes_) {
+void AndroidEnergyConsumerDescriptor::Serialize(::protozero::Message* msg) const {
+  // Field 1: energy_consumers
+  for (auto& it : energy_consumers_) {
     it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 2: all_data_sources_started
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, all_data_sources_started_, msg);
-  }
-
-  // Field 3: clone_trigger_hit
-  if (_has_field_[3]) {
-    (*clone_trigger_hit_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
-  }
-
   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-ObservableEvents_CloneTriggerHit::ObservableEvents_CloneTriggerHit() = default;
-ObservableEvents_CloneTriggerHit::~ObservableEvents_CloneTriggerHit() = default;
-ObservableEvents_CloneTriggerHit::ObservableEvents_CloneTriggerHit(const ObservableEvents_CloneTriggerHit&) = default;
-ObservableEvents_CloneTriggerHit& ObservableEvents_CloneTriggerHit::operator=(const ObservableEvents_CloneTriggerHit&) = default;
-ObservableEvents_CloneTriggerHit::ObservableEvents_CloneTriggerHit(ObservableEvents_CloneTriggerHit&&) noexcept = default;
-ObservableEvents_CloneTriggerHit& ObservableEvents_CloneTriggerHit::operator=(ObservableEvents_CloneTriggerHit&&) = default;
+AndroidEnergyConsumer::AndroidEnergyConsumer() = default;
+AndroidEnergyConsumer::~AndroidEnergyConsumer() = default;
+AndroidEnergyConsumer::AndroidEnergyConsumer(const AndroidEnergyConsumer&) = default;
+AndroidEnergyConsumer& AndroidEnergyConsumer::operator=(const AndroidEnergyConsumer&) = default;
+AndroidEnergyConsumer::AndroidEnergyConsumer(AndroidEnergyConsumer&&) noexcept = default;
+AndroidEnergyConsumer& AndroidEnergyConsumer::operator=(AndroidEnergyConsumer&&) = default;

-bool ObservableEvents_CloneTriggerHit::operator==(const ObservableEvents_CloneTriggerHit& other) const {
+bool AndroidEnergyConsumer::operator==(const AndroidEnergyConsumer& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(tracing_session_id_, other.tracing_session_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(trigger_name_, other.trigger_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(producer_name_, other.producer_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(producer_uid_, other.producer_uid_)
-   && ::protozero::internal::gen_helpers::EqualsField(boot_time_ns_, other.boot_time_ns_)
-   && ::protozero::internal::gen_helpers::EqualsField(trigger_delay_ms_, other.trigger_delay_ms_);
+   && ::protozero::internal::gen_helpers::EqualsField(energy_consumer_id_, other.energy_consumer_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(ordinal_, other.ordinal_)
+   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
 }

-bool ObservableEvents_CloneTriggerHit::ParseFromArray(const void* raw, size_t size) {
+bool AndroidEnergyConsumer::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -18725,23 +18886,17 @@ bool ObservableEvents_CloneTriggerHit::ParseFromArray(const void* raw, size_t si
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* tracing_session_id */:
-        field.get(&tracing_session_id_);
-        break;
-      case 2 /* trigger_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &trigger_name_);
-        break;
-      case 3 /* producer_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &producer_name_);
+      case 1 /* energy_consumer_id */:
+        field.get(&energy_consumer_id_);
         break;
-      case 4 /* producer_uid */:
-        field.get(&producer_uid_);
+      case 2 /* ordinal */:
+        field.get(&ordinal_);
         break;
-      case 5 /* boot_time_ns */:
-        field.get(&boot_time_ns_);
+      case 3 /* type */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &type_);
         break;
-      case 6 /* trigger_delay_ms */:
-        field.get(&trigger_delay_ms_);
+      case 4 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -18751,132 +18906,93 @@ bool ObservableEvents_CloneTriggerHit::ParseFromArray(const void* raw, size_t si
   return !packed_error && !dec.bytes_left();
 }

-std::string ObservableEvents_CloneTriggerHit::SerializeAsString() const {
+std::string AndroidEnergyConsumer::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ObservableEvents_CloneTriggerHit::SerializeAsArray() const {
+std::vector<uint8_t> AndroidEnergyConsumer::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ObservableEvents_CloneTriggerHit::Serialize(::protozero::Message* msg) const {
-  // Field 1: tracing_session_id
+void AndroidEnergyConsumer::Serialize(::protozero::Message* msg) const {
+  // Field 1: energy_consumer_id
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, tracing_session_id_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, energy_consumer_id_, msg);
   }

-  // Field 2: trigger_name
+  // Field 2: ordinal
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, trigger_name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, ordinal_, msg);
   }

-  // Field 3: producer_name
+  // Field 3: type
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeString(3, producer_name_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(3, type_, msg);
   }

-  // Field 4: producer_uid
+  // Field 4: name
   if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, producer_uid_, msg);
-  }
-
-  // Field 5: boot_time_ns
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, boot_time_ns_, msg);
-  }
-
-  // Field 6: trigger_delay_ms
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, trigger_delay_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(4, name_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/android_log_constants.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/android_log_constants.gen.h"

-ObservableEvents_DataSourceInstanceStateChange::ObservableEvents_DataSourceInstanceStateChange() = default;
-ObservableEvents_DataSourceInstanceStateChange::~ObservableEvents_DataSourceInstanceStateChange() = default;
-ObservableEvents_DataSourceInstanceStateChange::ObservableEvents_DataSourceInstanceStateChange(const ObservableEvents_DataSourceInstanceStateChange&) = default;
-ObservableEvents_DataSourceInstanceStateChange& ObservableEvents_DataSourceInstanceStateChange::operator=(const ObservableEvents_DataSourceInstanceStateChange&) = default;
-ObservableEvents_DataSourceInstanceStateChange::ObservableEvents_DataSourceInstanceStateChange(ObservableEvents_DataSourceInstanceStateChange&&) noexcept = default;
-ObservableEvents_DataSourceInstanceStateChange& ObservableEvents_DataSourceInstanceStateChange::operator=(ObservableEvents_DataSourceInstanceStateChange&&) = default;
-
-bool ObservableEvents_DataSourceInstanceStateChange::operator==(const ObservableEvents_DataSourceInstanceStateChange& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(producer_name_, other.producer_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(data_source_name_, other.data_source_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(state_, other.state_);
-}
-
-bool ObservableEvents_DataSourceInstanceStateChange::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
-
-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* producer_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &producer_name_);
-        break;
-      case 2 /* data_source_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &data_source_name_);
-        break;
-      case 3 /* state */:
-        field.get(&state_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
-}
-
-std::string ObservableEvents_DataSourceInstanceStateChange::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
-
-std::vector<uint8_t> ObservableEvents_DataSourceInstanceStateChange::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
-
-void ObservableEvents_DataSourceInstanceStateChange::Serialize(::protozero::Message* msg) const {
-  // Field 1: producer_name
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, producer_name_, msg);
-  }
-
-  // Field 2: data_source_name
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, data_source_name_, msg);
-  }
-
-  // Field 3: state
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, state_, msg);
-  }
-
-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+namespace perfetto {
+namespace protos {
+namespace gen {
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/builtin_clock.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/builtin_clock.gen.h"

+namespace perfetto {
+namespace protos {
+namespace gen {
 }  // namespace perfetto
 }  // namespace protos
 }  // namespace gen
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/perf_events.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/common/commit_data_request.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -18887,30 +19003,35 @@ void ObservableEvents_DataSourceInstanceStateChange::Serialize(::protozero::Mess
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/perf_events.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/commit_data_request.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-FollowerEvent::FollowerEvent() = default;
-FollowerEvent::~FollowerEvent() = default;
-FollowerEvent::FollowerEvent(const FollowerEvent&) = default;
-FollowerEvent& FollowerEvent::operator=(const FollowerEvent&) = default;
-FollowerEvent::FollowerEvent(FollowerEvent&&) noexcept = default;
-FollowerEvent& FollowerEvent::operator=(FollowerEvent&&) = default;
+CommitDataRequest::CommitDataRequest() = default;
+CommitDataRequest::~CommitDataRequest() = default;
+CommitDataRequest::CommitDataRequest(const CommitDataRequest&) = default;
+CommitDataRequest& CommitDataRequest::operator=(const CommitDataRequest&) = default;
+CommitDataRequest::CommitDataRequest(CommitDataRequest&&) noexcept = default;
+CommitDataRequest& CommitDataRequest::operator=(CommitDataRequest&&) = default;

-bool FollowerEvent::operator==(const FollowerEvent& other) const {
+bool CommitDataRequest::operator==(const CommitDataRequest& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(counter_, other.counter_)
-   && ::protozero::internal::gen_helpers::EqualsField(tracepoint_, other.tracepoint_)
-   && ::protozero::internal::gen_helpers::EqualsField(raw_event_, other.raw_event_)
-   && ::protozero::internal::gen_helpers::EqualsField(modifiers_, other.modifiers_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+   && ::protozero::internal::gen_helpers::EqualsField(chunks_to_move_, other.chunks_to_move_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunks_to_patch_, other.chunks_to_patch_)
+   && ::protozero::internal::gen_helpers::EqualsField(flush_request_id_, other.flush_request_id_);
 }

-bool FollowerEvent::ParseFromArray(const void* raw, size_t size) {
-  modifiers_.clear();
+int CommitDataRequest::chunks_to_move_size() const { return static_cast<int>(chunks_to_move_.size()); }
+void CommitDataRequest::clear_chunks_to_move() { chunks_to_move_.clear(); }
+CommitDataRequest_ChunksToMove* CommitDataRequest::add_chunks_to_move() { chunks_to_move_.emplace_back(); return &chunks_to_move_.back(); }
+int CommitDataRequest::chunks_to_patch_size() const { return static_cast<int>(chunks_to_patch_.size()); }
+void CommitDataRequest::clear_chunks_to_patch() { chunks_to_patch_.clear(); }
+CommitDataRequest_ChunkToPatch* CommitDataRequest::add_chunks_to_patch() { chunks_to_patch_.emplace_back(); return &chunks_to_patch_.back(); }
+bool CommitDataRequest::ParseFromArray(const void* raw, size_t size) {
+  chunks_to_move_.clear();
+  chunks_to_patch_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -18920,21 +19041,16 @@ bool FollowerEvent::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* counter */:
-        field.get(&counter_);
-        break;
-      case 2 /* tracepoint */:
-        (*tracepoint_).ParseFromArray(field.data(), field.size());
-        break;
-      case 3 /* raw_event */:
-        (*raw_event_).ParseFromArray(field.data(), field.size());
+      case 1 /* chunks_to_move */:
+        chunks_to_move_.emplace_back();
+        chunks_to_move_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 5 /* modifiers */:
-        modifiers_.emplace_back();
-        field.get(&modifiers_.back());
+      case 2 /* chunks_to_patch */:
+        chunks_to_patch_.emplace_back();
+        chunks_to_patch_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 4 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+      case 3 /* flush_request_id */:
+        field.get(&flush_request_id_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -18944,65 +19060,59 @@ bool FollowerEvent::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string FollowerEvent::SerializeAsString() const {
+std::string CommitDataRequest::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> FollowerEvent::SerializeAsArray() const {
+std::vector<uint8_t> CommitDataRequest::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void FollowerEvent::Serialize(::protozero::Message* msg) const {
-  // Field 1: counter
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, counter_, msg);
+void CommitDataRequest::Serialize(::protozero::Message* msg) const {
+  // Field 1: chunks_to_move
+  for (auto& it : chunks_to_move_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 2: tracepoint
-  if (_has_field_[2]) {
-    (*tracepoint_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+  // Field 2: chunks_to_patch
+  for (auto& it : chunks_to_patch_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

-  // Field 3: raw_event
+  // Field 3: flush_request_id
   if (_has_field_[3]) {
-    (*raw_event_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
-  }
-
-  // Field 5: modifiers
-  for (auto& it : modifiers_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, it, msg);
-  }
-
-  // Field 4: name
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeString(4, name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, flush_request_id_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-PerfEvents_RawEvent::PerfEvents_RawEvent() = default;
-PerfEvents_RawEvent::~PerfEvents_RawEvent() = default;
-PerfEvents_RawEvent::PerfEvents_RawEvent(const PerfEvents_RawEvent&) = default;
-PerfEvents_RawEvent& PerfEvents_RawEvent::operator=(const PerfEvents_RawEvent&) = default;
-PerfEvents_RawEvent::PerfEvents_RawEvent(PerfEvents_RawEvent&&) noexcept = default;
-PerfEvents_RawEvent& PerfEvents_RawEvent::operator=(PerfEvents_RawEvent&&) = default;
+CommitDataRequest_ChunkToPatch::CommitDataRequest_ChunkToPatch() = default;
+CommitDataRequest_ChunkToPatch::~CommitDataRequest_ChunkToPatch() = default;
+CommitDataRequest_ChunkToPatch::CommitDataRequest_ChunkToPatch(const CommitDataRequest_ChunkToPatch&) = default;
+CommitDataRequest_ChunkToPatch& CommitDataRequest_ChunkToPatch::operator=(const CommitDataRequest_ChunkToPatch&) = default;
+CommitDataRequest_ChunkToPatch::CommitDataRequest_ChunkToPatch(CommitDataRequest_ChunkToPatch&&) noexcept = default;
+CommitDataRequest_ChunkToPatch& CommitDataRequest_ChunkToPatch::operator=(CommitDataRequest_ChunkToPatch&&) = default;

-bool PerfEvents_RawEvent::operator==(const PerfEvents_RawEvent& other) const {
+bool CommitDataRequest_ChunkToPatch::operator==(const CommitDataRequest_ChunkToPatch& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
-   && ::protozero::internal::gen_helpers::EqualsField(config_, other.config_)
-   && ::protozero::internal::gen_helpers::EqualsField(config1_, other.config1_)
-   && ::protozero::internal::gen_helpers::EqualsField(config2_, other.config2_)
-   && ::protozero::internal::gen_helpers::EqualsField(pmu_name_, other.pmu_name_);
+   && ::protozero::internal::gen_helpers::EqualsField(target_buffer_, other.target_buffer_)
+   && ::protozero::internal::gen_helpers::EqualsField(writer_id_, other.writer_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunk_id_, other.chunk_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(patches_, other.patches_)
+   && ::protozero::internal::gen_helpers::EqualsField(has_more_patches_, other.has_more_patches_);
 }

-bool PerfEvents_RawEvent::ParseFromArray(const void* raw, size_t size) {
+int CommitDataRequest_ChunkToPatch::patches_size() const { return static_cast<int>(patches_.size()); }
+void CommitDataRequest_ChunkToPatch::clear_patches() { patches_.clear(); }
+CommitDataRequest_ChunkToPatch_Patch* CommitDataRequest_ChunkToPatch::add_patches() { patches_.emplace_back(); return &patches_.back(); }
+bool CommitDataRequest_ChunkToPatch::ParseFromArray(const void* raw, size_t size) {
+  patches_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -19012,20 +19122,21 @@ bool PerfEvents_RawEvent::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* type */:
-        field.get(&type_);
+      case 1 /* target_buffer */:
+        field.get(&target_buffer_);
         break;
-      case 2 /* config */:
-        field.get(&config_);
+      case 2 /* writer_id */:
+        field.get(&writer_id_);
         break;
-      case 3 /* config1 */:
-        field.get(&config1_);
+      case 3 /* chunk_id */:
+        field.get(&chunk_id_);
         break;
-      case 4 /* config2 */:
-        field.get(&config2_);
+      case 4 /* patches */:
+        patches_.emplace_back();
+        patches_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 5 /* pmu_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &pmu_name_);
+      case 5 /* has_more_patches */:
+        field.get(&has_more_patches_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -19035,62 +19146,62 @@ bool PerfEvents_RawEvent::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string PerfEvents_RawEvent::SerializeAsString() const {
+std::string CommitDataRequest_ChunkToPatch::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> PerfEvents_RawEvent::SerializeAsArray() const {
+std::vector<uint8_t> CommitDataRequest_ChunkToPatch::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void PerfEvents_RawEvent::Serialize(::protozero::Message* msg) const {
-  // Field 1: type
+void CommitDataRequest_ChunkToPatch::Serialize(::protozero::Message* msg) const {
+  // Field 1: target_buffer
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, type_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, target_buffer_, msg);
   }

-  // Field 2: config
+  // Field 2: writer_id
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, config_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, writer_id_, msg);
   }

-  // Field 3: config1
+  // Field 3: chunk_id
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, config1_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, chunk_id_, msg);
   }

-  // Field 4: config2
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, config2_, msg);
+  // Field 4: patches
+  for (auto& it : patches_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
   }

-  // Field 5: pmu_name
+  // Field 5: has_more_patches
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeString(5, pmu_name_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, has_more_patches_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-PerfEvents_Tracepoint::PerfEvents_Tracepoint() = default;
-PerfEvents_Tracepoint::~PerfEvents_Tracepoint() = default;
-PerfEvents_Tracepoint::PerfEvents_Tracepoint(const PerfEvents_Tracepoint&) = default;
-PerfEvents_Tracepoint& PerfEvents_Tracepoint::operator=(const PerfEvents_Tracepoint&) = default;
-PerfEvents_Tracepoint::PerfEvents_Tracepoint(PerfEvents_Tracepoint&&) noexcept = default;
-PerfEvents_Tracepoint& PerfEvents_Tracepoint::operator=(PerfEvents_Tracepoint&&) = default;
+CommitDataRequest_ChunkToPatch_Patch::CommitDataRequest_ChunkToPatch_Patch() = default;
+CommitDataRequest_ChunkToPatch_Patch::~CommitDataRequest_ChunkToPatch_Patch() = default;
+CommitDataRequest_ChunkToPatch_Patch::CommitDataRequest_ChunkToPatch_Patch(const CommitDataRequest_ChunkToPatch_Patch&) = default;
+CommitDataRequest_ChunkToPatch_Patch& CommitDataRequest_ChunkToPatch_Patch::operator=(const CommitDataRequest_ChunkToPatch_Patch&) = default;
+CommitDataRequest_ChunkToPatch_Patch::CommitDataRequest_ChunkToPatch_Patch(CommitDataRequest_ChunkToPatch_Patch&&) noexcept = default;
+CommitDataRequest_ChunkToPatch_Patch& CommitDataRequest_ChunkToPatch_Patch::operator=(CommitDataRequest_ChunkToPatch_Patch&&) = default;

-bool PerfEvents_Tracepoint::operator==(const PerfEvents_Tracepoint& other) const {
+bool CommitDataRequest_ChunkToPatch_Patch::operator==(const CommitDataRequest_ChunkToPatch_Patch& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(filter_, other.filter_);
+   && ::protozero::internal::gen_helpers::EqualsField(offset_, other.offset_)
+   && ::protozero::internal::gen_helpers::EqualsField(data_, other.data_);
 }

-bool PerfEvents_Tracepoint::ParseFromArray(const void* raw, size_t size) {
+bool CommitDataRequest_ChunkToPatch_Patch::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -19100,11 +19211,11 @@ bool PerfEvents_Tracepoint::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+      case 1 /* offset */:
+        field.get(&offset_);
         break;
-      case 2 /* filter */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &filter_);
+      case 2 /* data */:
+        field.get(&data_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -19114,45 +19225,50 @@ bool PerfEvents_Tracepoint::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string PerfEvents_Tracepoint::SerializeAsString() const {
+std::string CommitDataRequest_ChunkToPatch_Patch::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> PerfEvents_Tracepoint::SerializeAsArray() const {
+std::vector<uint8_t> CommitDataRequest_ChunkToPatch_Patch::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void PerfEvents_Tracepoint::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
+void CommitDataRequest_ChunkToPatch_Patch::Serialize(::protozero::Message* msg) const {
+  // Field 1: offset
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, offset_, msg);
   }

-  // Field 2: filter
+  // Field 2: data
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, filter_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(2, data_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-PerfEvents::PerfEvents() = default;
-PerfEvents::~PerfEvents() = default;
-PerfEvents::PerfEvents(const PerfEvents&) = default;
-PerfEvents& PerfEvents::operator=(const PerfEvents&) = default;
-PerfEvents::PerfEvents(PerfEvents&&) noexcept = default;
-PerfEvents& PerfEvents::operator=(PerfEvents&&) = default;
+CommitDataRequest_ChunksToMove::CommitDataRequest_ChunksToMove() = default;
+CommitDataRequest_ChunksToMove::~CommitDataRequest_ChunksToMove() = default;
+CommitDataRequest_ChunksToMove::CommitDataRequest_ChunksToMove(const CommitDataRequest_ChunksToMove&) = default;
+CommitDataRequest_ChunksToMove& CommitDataRequest_ChunksToMove::operator=(const CommitDataRequest_ChunksToMove&) = default;
+CommitDataRequest_ChunksToMove::CommitDataRequest_ChunksToMove(CommitDataRequest_ChunksToMove&&) noexcept = default;
+CommitDataRequest_ChunksToMove& CommitDataRequest_ChunksToMove::operator=(CommitDataRequest_ChunksToMove&&) = default;

-bool PerfEvents::operator==(const PerfEvents& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
+bool CommitDataRequest_ChunksToMove::operator==(const CommitDataRequest_ChunksToMove& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(page_, other.page_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunk_, other.chunk_)
+   && ::protozero::internal::gen_helpers::EqualsField(target_buffer_, other.target_buffer_)
+   && ::protozero::internal::gen_helpers::EqualsField(data_, other.data_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunk_incomplete_, other.chunk_incomplete_);
 }

-bool PerfEvents::ParseFromArray(const void* raw, size_t size) {
+bool CommitDataRequest_ChunksToMove::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -19162,6 +19278,21 @@ bool PerfEvents::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
+      case 1 /* page */:
+        field.get(&page_);
+        break;
+      case 2 /* chunk */:
+        field.get(&chunk_);
+        break;
+      case 3 /* target_buffer */:
+        field.get(&target_buffer_);
+        break;
+      case 4 /* data */:
+        field.get(&data_);
+        break;
+      case 5 /* chunk_incomplete */:
+        field.get(&chunk_incomplete_);
+        break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
         break;
@@ -19170,45 +19301,92 @@ bool PerfEvents::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string PerfEvents::SerializeAsString() const {
+std::string CommitDataRequest_ChunksToMove::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> PerfEvents::SerializeAsArray() const {
+std::vector<uint8_t> CommitDataRequest_ChunksToMove::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void PerfEvents::Serialize(::protozero::Message* msg) const {
+void CommitDataRequest_ChunksToMove::Serialize(::protozero::Message* msg) const {
+  // Field 1: page
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, page_, msg);
+  }
+
+  // Field 2: chunk
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, chunk_, msg);
+  }
+
+  // Field 3: target_buffer
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, target_buffer_, msg);
+  }
+
+  // Field 4: data
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeString(4, data_, msg);
+  }
+
+  // Field 5: chunk_incomplete
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, chunk_incomplete_, msg);
+  }
+
   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/data_source_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/data_source_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-PerfEvents_Timebase::PerfEvents_Timebase() = default;
-PerfEvents_Timebase::~PerfEvents_Timebase() = default;
-PerfEvents_Timebase::PerfEvents_Timebase(const PerfEvents_Timebase&) = default;
-PerfEvents_Timebase& PerfEvents_Timebase::operator=(const PerfEvents_Timebase&) = default;
-PerfEvents_Timebase::PerfEvents_Timebase(PerfEvents_Timebase&&) noexcept = default;
-PerfEvents_Timebase& PerfEvents_Timebase::operator=(PerfEvents_Timebase&&) = default;
+DataSourceDescriptor::DataSourceDescriptor() = default;
+DataSourceDescriptor::~DataSourceDescriptor() = default;
+DataSourceDescriptor::DataSourceDescriptor(const DataSourceDescriptor&) = default;
+DataSourceDescriptor& DataSourceDescriptor::operator=(const DataSourceDescriptor&) = default;
+DataSourceDescriptor::DataSourceDescriptor(DataSourceDescriptor&&) noexcept = default;
+DataSourceDescriptor& DataSourceDescriptor::operator=(DataSourceDescriptor&&) = default;

-bool PerfEvents_Timebase::operator==(const PerfEvents_Timebase& other) const {
+bool DataSourceDescriptor::operator==(const DataSourceDescriptor& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(frequency_, other.frequency_)
-   && ::protozero::internal::gen_helpers::EqualsField(period_, other.period_)
-   && ::protozero::internal::gen_helpers::EqualsField(poll_period_ms_, other.poll_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(counter_, other.counter_)
-   && ::protozero::internal::gen_helpers::EqualsField(tracepoint_, other.tracepoint_)
-   && ::protozero::internal::gen_helpers::EqualsField(raw_event_, other.raw_event_)
-   && ::protozero::internal::gen_helpers::EqualsField(modifiers_, other.modifiers_)
-   && ::protozero::internal::gen_helpers::EqualsField(timestamp_clock_, other.timestamp_clock_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(id_, other.id_)
+   && ::protozero::internal::gen_helpers::EqualsField(will_notify_on_stop_, other.will_notify_on_stop_)
+   && ::protozero::internal::gen_helpers::EqualsField(will_notify_on_start_, other.will_notify_on_start_)
+   && ::protozero::internal::gen_helpers::EqualsField(handles_incremental_state_clear_, other.handles_incremental_state_clear_)
+   && ::protozero::internal::gen_helpers::EqualsField(no_flush_, other.no_flush_)
+   && ::protozero::internal::gen_helpers::EqualsField(protovm_program_, other.protovm_program_)
+   && ::protozero::internal::gen_helpers::EqualsField(gpu_counter_descriptor_, other.gpu_counter_descriptor_)
+   && ::protozero::internal::gen_helpers::EqualsField(track_event_descriptor_, other.track_event_descriptor_)
+   && ::protozero::internal::gen_helpers::EqualsField(ftrace_descriptor_, other.ftrace_descriptor_);
 }

-bool PerfEvents_Timebase::ParseFromArray(const void* raw, size_t size) {
-  modifiers_.clear();
+bool DataSourceDescriptor::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -19218,33 +19396,35 @@ bool PerfEvents_Timebase::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 2 /* frequency */:
-        field.get(&frequency_);
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 1 /* period */:
-        field.get(&period_);
+      case 7 /* id */:
+        field.get(&id_);
         break;
-      case 6 /* poll_period_ms */:
-        field.get(&poll_period_ms_);
+      case 2 /* will_notify_on_stop */:
+        field.get(&will_notify_on_stop_);
         break;
-      case 4 /* counter */:
-        field.get(&counter_);
+      case 3 /* will_notify_on_start */:
+        field.get(&will_notify_on_start_);
         break;
-      case 3 /* tracepoint */:
-        (*tracepoint_).ParseFromArray(field.data(), field.size());
+      case 4 /* handles_incremental_state_clear */:
+        field.get(&handles_incremental_state_clear_);
         break;
-      case 5 /* raw_event */:
-        (*raw_event_).ParseFromArray(field.data(), field.size());
+      case 9 /* no_flush */:
+        field.get(&no_flush_);
         break;
-      case 12 /* modifiers */:
-        modifiers_.emplace_back();
-        field.get(&modifiers_.back());
+      case 10 /* protovm_program */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &protovm_program_);
         break;
-      case 11 /* timestamp_clock */:
-        field.get(&timestamp_clock_);
+      case 5 /* gpu_counter_descriptor */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &gpu_counter_descriptor_);
         break;
-      case 10 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+      case 6 /* track_event_descriptor */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &track_event_descriptor_);
+        break;
+      case 8 /* ftrace_descriptor */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &ftrace_descriptor_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -19254,62 +19434,67 @@ bool PerfEvents_Timebase::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string PerfEvents_Timebase::SerializeAsString() const {
+std::string DataSourceDescriptor::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> PerfEvents_Timebase::SerializeAsArray() const {
+std::vector<uint8_t> DataSourceDescriptor::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void PerfEvents_Timebase::Serialize(::protozero::Message* msg) const {
-  // Field 2: frequency
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, frequency_, msg);
+void DataSourceDescriptor::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  // Field 1: period
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, period_, msg);
+  // Field 7: id
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, id_, msg);
   }

-  // Field 6: poll_period_ms
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, poll_period_ms_, msg);
+  // Field 2: will_notify_on_stop
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, will_notify_on_stop_, msg);
   }

-  // Field 4: counter
+  // Field 3: will_notify_on_start
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, will_notify_on_start_, msg);
+  }
+
+  // Field 4: handles_incremental_state_clear
   if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, counter_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, handles_incremental_state_clear_, msg);
   }

-  // Field 3: tracepoint
-  if (_has_field_[3]) {
-    (*tracepoint_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+  // Field 9: no_flush
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, no_flush_, msg);
   }

-  // Field 5: raw_event
-  if (_has_field_[5]) {
-    (*raw_event_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
+  // Field 10: protovm_program
+  if (_has_field_[10]) {
+    msg->AppendString(10, protovm_program_);
   }

-  // Field 12: modifiers
-  for (auto& it : modifiers_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(12, it, msg);
+  // Field 5: gpu_counter_descriptor
+  if (_has_field_[5]) {
+    msg->AppendString(5, gpu_counter_descriptor_);
   }

-  // Field 11: timestamp_clock
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(11, timestamp_clock_, msg);
+  // Field 6: track_event_descriptor
+  if (_has_field_[6]) {
+    msg->AppendString(6, track_event_descriptor_);
   }

-  // Field 10: name
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeString(10, name_, msg);
+  // Field 8: ftrace_descriptor
+  if (_has_field_[8]) {
+    msg->AppendString(8, ftrace_descriptor_);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -19321,29 +19506,7 @@ void PerfEvents_Timebase::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/sys_stats_counters.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/sys_stats_counters.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {
-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/system_info.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/common/descriptor.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -19354,41 +19517,24 @@ namespace gen {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/system_info.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/descriptor.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-SystemInfo::SystemInfo() = default;
-SystemInfo::~SystemInfo() = default;
-SystemInfo::SystemInfo(const SystemInfo&) = default;
-SystemInfo& SystemInfo::operator=(const SystemInfo&) = default;
-SystemInfo::SystemInfo(SystemInfo&&) noexcept = default;
-SystemInfo& SystemInfo::operator=(SystemInfo&&) = default;
+OneofOptions::OneofOptions() = default;
+OneofOptions::~OneofOptions() = default;
+OneofOptions::OneofOptions(const OneofOptions&) = default;
+OneofOptions& OneofOptions::operator=(const OneofOptions&) = default;
+OneofOptions::OneofOptions(OneofOptions&&) noexcept = default;
+OneofOptions& OneofOptions::operator=(OneofOptions&&) = default;

-bool SystemInfo::operator==(const SystemInfo& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(utsname_, other.utsname_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_build_fingerprint_, other.android_build_fingerprint_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_device_manufacturer_, other.android_device_manufacturer_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_soc_model_, other.android_soc_model_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_guest_soc_model_, other.android_guest_soc_model_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_hardware_revision_, other.android_hardware_revision_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_storage_model_, other.android_storage_model_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_ram_model_, other.android_ram_model_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_serial_console_, other.android_serial_console_)
-   && ::protozero::internal::gen_helpers::EqualsField(tracing_service_version_, other.tracing_service_version_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_sdk_version_, other.android_sdk_version_)
-   && ::protozero::internal::gen_helpers::EqualsField(page_size_, other.page_size_)
-   && ::protozero::internal::gen_helpers::EqualsField(num_cpus_, other.num_cpus_)
-   && ::protozero::internal::gen_helpers::EqualsField(timezone_off_mins_, other.timezone_off_mins_)
-   && ::protozero::internal::gen_helpers::EqualsField(hz_, other.hz_)
-   && ::protozero::internal::gen_helpers::EqualsField(system_ram_bytes_, other.system_ram_bytes_)
-   && ::protozero::internal::gen_helpers::EqualsField(machine_name_, other.machine_name_);
+bool OneofOptions::operator==(const OneofOptions& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
 }

-bool SystemInfo::ParseFromArray(const void* raw, size_t size) {
+bool OneofOptions::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -19398,57 +19544,6 @@ bool SystemInfo::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* utsname */:
-        (*utsname_).ParseFromArray(field.data(), field.size());
-        break;
-      case 2 /* android_build_fingerprint */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_build_fingerprint_);
-        break;
-      case 14 /* android_device_manufacturer */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_device_manufacturer_);
-        break;
-      case 9 /* android_soc_model */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_soc_model_);
-        break;
-      case 13 /* android_guest_soc_model */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_guest_soc_model_);
-        break;
-      case 10 /* android_hardware_revision */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_hardware_revision_);
-        break;
-      case 11 /* android_storage_model */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_storage_model_);
-        break;
-      case 12 /* android_ram_model */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_ram_model_);
-        break;
-      case 15 /* android_serial_console */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_serial_console_);
-        break;
-      case 4 /* tracing_service_version */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &tracing_service_version_);
-        break;
-      case 5 /* android_sdk_version */:
-        field.get(&android_sdk_version_);
-        break;
-      case 6 /* page_size */:
-        field.get(&page_size_);
-        break;
-      case 8 /* num_cpus */:
-        field.get(&num_cpus_);
-        break;
-      case 7 /* timezone_off_mins */:
-        field.get(&timezone_off_mins_);
-        break;
-      case 3 /* hz */:
-        field.get(&hz_);
-        break;
-      case 16 /* system_ram_bytes */:
-        field.get(&system_ram_bytes_);
-        break;
-      case 17 /* machine_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &machine_name_);
-        break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
         break;
@@ -19457,124 +19552,107 @@ bool SystemInfo::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string SystemInfo::SerializeAsString() const {
+std::string OneofOptions::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> SystemInfo::SerializeAsArray() const {
+std::vector<uint8_t> OneofOptions::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void SystemInfo::Serialize(::protozero::Message* msg) const {
-  // Field 1: utsname
-  if (_has_field_[1]) {
-    (*utsname_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
-  }
-
-  // Field 2: android_build_fingerprint
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, android_build_fingerprint_, msg);
-  }
-
-  // Field 14: android_device_manufacturer
-  if (_has_field_[14]) {
-    ::protozero::internal::gen_helpers::SerializeString(14, android_device_manufacturer_, msg);
-  }
-
-  // Field 9: android_soc_model
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeString(9, android_soc_model_, msg);
-  }
-
-  // Field 13: android_guest_soc_model
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeString(13, android_guest_soc_model_, msg);
-  }
-
-  // Field 10: android_hardware_revision
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeString(10, android_hardware_revision_, msg);
-  }
-
-  // Field 11: android_storage_model
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeString(11, android_storage_model_, msg);
-  }
-
-  // Field 12: android_ram_model
-  if (_has_field_[12]) {
-    ::protozero::internal::gen_helpers::SerializeString(12, android_ram_model_, msg);
-  }
+void OneofOptions::Serialize(::protozero::Message* msg) const {
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}

-  // Field 15: android_serial_console
-  if (_has_field_[15]) {
-    ::protozero::internal::gen_helpers::SerializeString(15, android_serial_console_, msg);
-  }

-  // Field 4: tracing_service_version
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeString(4, tracing_service_version_, msg);
-  }
+EnumValueDescriptorProto::EnumValueDescriptorProto() = default;
+EnumValueDescriptorProto::~EnumValueDescriptorProto() = default;
+EnumValueDescriptorProto::EnumValueDescriptorProto(const EnumValueDescriptorProto&) = default;
+EnumValueDescriptorProto& EnumValueDescriptorProto::operator=(const EnumValueDescriptorProto&) = default;
+EnumValueDescriptorProto::EnumValueDescriptorProto(EnumValueDescriptorProto&&) noexcept = default;
+EnumValueDescriptorProto& EnumValueDescriptorProto::operator=(EnumValueDescriptorProto&&) = default;

-  // Field 5: android_sdk_version
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, android_sdk_version_, msg);
-  }
+bool EnumValueDescriptorProto::operator==(const EnumValueDescriptorProto& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(number_, other.number_);
+}

-  // Field 6: page_size
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, page_size_, msg);
-  }
+bool EnumValueDescriptorProto::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;

-  // Field 8: num_cpus
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, num_cpus_, msg);
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      case 2 /* number */:
+        field.get(&number_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 7: timezone_off_mins
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, timezone_off_mins_, msg);
-  }
+std::string EnumValueDescriptorProto::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}

-  // Field 3: hz
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, hz_, msg);
-  }
+std::vector<uint8_t> EnumValueDescriptorProto::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}

-  // Field 16: system_ram_bytes
-  if (_has_field_[16]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(16, system_ram_bytes_, msg);
+void EnumValueDescriptorProto::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  // Field 17: machine_name
-  if (_has_field_[17]) {
-    ::protozero::internal::gen_helpers::SerializeString(17, machine_name_, msg);
+  // Field 2: number
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, number_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-Utsname::Utsname() = default;
-Utsname::~Utsname() = default;
-Utsname::Utsname(const Utsname&) = default;
-Utsname& Utsname::operator=(const Utsname&) = default;
-Utsname::Utsname(Utsname&&) noexcept = default;
-Utsname& Utsname::operator=(Utsname&&) = default;
+EnumDescriptorProto::EnumDescriptorProto() = default;
+EnumDescriptorProto::~EnumDescriptorProto() = default;
+EnumDescriptorProto::EnumDescriptorProto(const EnumDescriptorProto&) = default;
+EnumDescriptorProto& EnumDescriptorProto::operator=(const EnumDescriptorProto&) = default;
+EnumDescriptorProto::EnumDescriptorProto(EnumDescriptorProto&&) noexcept = default;
+EnumDescriptorProto& EnumDescriptorProto::operator=(EnumDescriptorProto&&) = default;

-bool Utsname::operator==(const Utsname& other) const {
+bool EnumDescriptorProto::operator==(const EnumDescriptorProto& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(sysname_, other.sysname_)
-   && ::protozero::internal::gen_helpers::EqualsField(version_, other.version_)
-   && ::protozero::internal::gen_helpers::EqualsField(release_, other.release_)
-   && ::protozero::internal::gen_helpers::EqualsField(machine_, other.machine_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(value_, other.value_)
+   && ::protozero::internal::gen_helpers::EqualsField(reserved_name_, other.reserved_name_);
 }

-bool Utsname::ParseFromArray(const void* raw, size_t size) {
+int EnumDescriptorProto::value_size() const { return static_cast<int>(value_.size()); }
+void EnumDescriptorProto::clear_value() { value_.clear(); }
+EnumValueDescriptorProto* EnumDescriptorProto::add_value() { value_.emplace_back(); return &value_.back(); }
+bool EnumDescriptorProto::ParseFromArray(const void* raw, size_t size) {
+  value_.clear();
+  reserved_name_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -19584,17 +19662,16 @@ bool Utsname::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* sysname */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &sysname_);
-        break;
-      case 2 /* version */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &version_);
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 3 /* release */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &release_);
+      case 2 /* value */:
+        value_.emplace_back();
+        value_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 4 /* machine */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &machine_);
+      case 5 /* reserved_name */:
+        reserved_name_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &reserved_name_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -19604,82 +19681,52 @@ bool Utsname::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string Utsname::SerializeAsString() const {
+std::string EnumDescriptorProto::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> Utsname::SerializeAsArray() const {
+std::vector<uint8_t> EnumDescriptorProto::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void Utsname::Serialize(::protozero::Message* msg) const {
-  // Field 1: sysname
+void EnumDescriptorProto::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, sysname_, msg);
-  }
-
-  // Field 2: version
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, version_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  // Field 3: release
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeString(3, release_, msg);
+  // Field 2: value
+  for (auto& it : value_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

-  // Field 4: machine
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeString(4, machine_, msg);
+  // Field 5: reserved_name
+  for (auto& it : reserved_name_) {
+    ::protozero::internal::gen_helpers::SerializeString(5, it, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/trace_attributes.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/trace_attributes.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-TraceAttributes::TraceAttributes() = default;
-TraceAttributes::~TraceAttributes() = default;
-TraceAttributes::TraceAttributes(const TraceAttributes&) = default;
-TraceAttributes& TraceAttributes::operator=(const TraceAttributes&) = default;
-TraceAttributes::TraceAttributes(TraceAttributes&&) noexcept = default;
-TraceAttributes& TraceAttributes::operator=(TraceAttributes&&) = default;
+OneofDescriptorProto::OneofDescriptorProto() = default;
+OneofDescriptorProto::~OneofDescriptorProto() = default;
+OneofDescriptorProto::OneofDescriptorProto(const OneofDescriptorProto&) = default;
+OneofDescriptorProto& OneofDescriptorProto::operator=(const OneofDescriptorProto&) = default;
+OneofDescriptorProto::OneofDescriptorProto(OneofDescriptorProto&&) noexcept = default;
+OneofDescriptorProto& OneofDescriptorProto::operator=(OneofDescriptorProto&&) = default;

-bool TraceAttributes::operator==(const TraceAttributes& other) const {
+bool OneofDescriptorProto::operator==(const OneofDescriptorProto& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(attribute_, other.attribute_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(options_, other.options_);
 }

-int TraceAttributes::attribute_size() const { return static_cast<int>(attribute_.size()); }
-void TraceAttributes::clear_attribute() { attribute_.clear(); }
-TraceAttributes_Attribute* TraceAttributes::add_attribute() { attribute_.emplace_back(); return &attribute_.back(); }
-bool TraceAttributes::ParseFromArray(const void* raw, size_t size) {
-  attribute_.clear();
+bool OneofDescriptorProto::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -19689,9 +19736,11 @@ bool TraceAttributes::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* attribute */:
-        attribute_.emplace_back();
-        attribute_.back().ParseFromArray(field.data(), field.size());
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      case 2 /* options */:
+        (*options_).ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -19701,43 +19750,54 @@ bool TraceAttributes::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceAttributes::SerializeAsString() const {
+std::string OneofDescriptorProto::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceAttributes::SerializeAsArray() const {
+std::vector<uint8_t> OneofDescriptorProto::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceAttributes::Serialize(::protozero::Message* msg) const {
-  // Field 1: attribute
-  for (auto& it : attribute_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+void OneofDescriptorProto::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+  }
+
+  // Field 2: options
+  if (_has_field_[2]) {
+    (*options_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TraceAttributes_Attribute::TraceAttributes_Attribute() = default;
-TraceAttributes_Attribute::~TraceAttributes_Attribute() = default;
-TraceAttributes_Attribute::TraceAttributes_Attribute(const TraceAttributes_Attribute&) = default;
-TraceAttributes_Attribute& TraceAttributes_Attribute::operator=(const TraceAttributes_Attribute&) = default;
-TraceAttributes_Attribute::TraceAttributes_Attribute(TraceAttributes_Attribute&&) noexcept = default;
-TraceAttributes_Attribute& TraceAttributes_Attribute::operator=(TraceAttributes_Attribute&&) = default;
+FieldDescriptorProto::FieldDescriptorProto() = default;
+FieldDescriptorProto::~FieldDescriptorProto() = default;
+FieldDescriptorProto::FieldDescriptorProto(const FieldDescriptorProto&) = default;
+FieldDescriptorProto& FieldDescriptorProto::operator=(const FieldDescriptorProto&) = default;
+FieldDescriptorProto::FieldDescriptorProto(FieldDescriptorProto&&) noexcept = default;
+FieldDescriptorProto& FieldDescriptorProto::operator=(FieldDescriptorProto&&) = default;

-bool TraceAttributes_Attribute::operator==(const TraceAttributes_Attribute& other) const {
+bool FieldDescriptorProto::operator==(const FieldDescriptorProto& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(key_, other.key_)
-   && ::protozero::internal::gen_helpers::EqualsField(long_value_, other.long_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(string_value_, other.string_value_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(number_, other.number_)
+   && ::protozero::internal::gen_helpers::EqualsField(label_, other.label_)
+   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
+   && ::protozero::internal::gen_helpers::EqualsField(type_name_, other.type_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(extendee_, other.extendee_)
+   && ::protozero::internal::gen_helpers::EqualsField(default_value_, other.default_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(options_, other.options_)
+   && ::protozero::internal::gen_helpers::EqualsField(oneof_index_, other.oneof_index_);
 }

-bool TraceAttributes_Attribute::ParseFromArray(const void* raw, size_t size) {
+bool FieldDescriptorProto::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -19747,14 +19807,32 @@ bool TraceAttributes_Attribute::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* key */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &key_);
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 2 /* long_value */:
-        field.get(&long_value_);
+      case 3 /* number */:
+        field.get(&number_);
         break;
-      case 3 /* string_value */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &string_value_);
+      case 4 /* label */:
+        field.get(&label_);
+        break;
+      case 5 /* type */:
+        field.get(&type_);
+        break;
+      case 6 /* type_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &type_name_);
+        break;
+      case 2 /* extendee */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &extendee_);
+        break;
+      case 7 /* default_value */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &default_value_);
+        break;
+      case 8 /* options */:
+        (*options_).ParseFromArray(field.data(), field.size());
+        break;
+      case 9 /* oneof_index */:
+        field.get(&oneof_index_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -19764,98 +19842,86 @@ bool TraceAttributes_Attribute::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceAttributes_Attribute::SerializeAsString() const {
+std::string FieldDescriptorProto::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceAttributes_Attribute::SerializeAsArray() const {
+std::vector<uint8_t> FieldDescriptorProto::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceAttributes_Attribute::Serialize(::protozero::Message* msg) const {
-  // Field 1: key
+void FieldDescriptorProto::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, key_, msg);
-  }
-
-  // Field 2: long_value
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, long_value_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  // Field 3: string_value
+  // Field 3: number
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeString(3, string_value_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, number_, msg);
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+  // Field 4: label
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, label_, msg);
+  }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/trace_stats.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/trace_stats.gen.h"
+  // Field 5: type
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, type_, msg);
+  }

-namespace perfetto {
-namespace protos {
-namespace gen {
+  // Field 6: type_name
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeString(6, type_name_, msg);
+  }

-TraceStats::TraceStats() = default;
-TraceStats::~TraceStats() = default;
-TraceStats::TraceStats(const TraceStats&) = default;
-TraceStats& TraceStats::operator=(const TraceStats&) = default;
-TraceStats::TraceStats(TraceStats&&) noexcept = default;
-TraceStats& TraceStats::operator=(TraceStats&&) = default;
+  // Field 2: extendee
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, extendee_, msg);
+  }

-bool TraceStats::operator==(const TraceStats& other) const {
+  // Field 7: default_value
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeString(7, default_value_, msg);
+  }
+
+  // Field 8: options
+  if (_has_field_[8]) {
+    (*options_).Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
+  }
+
+  // Field 9: oneof_index
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(9, oneof_index_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+FieldOptions::FieldOptions() = default;
+FieldOptions::~FieldOptions() = default;
+FieldOptions::FieldOptions(const FieldOptions&) = default;
+FieldOptions& FieldOptions::operator=(const FieldOptions&) = default;
+FieldOptions::FieldOptions(FieldOptions&&) noexcept = default;
+FieldOptions& FieldOptions::operator=(FieldOptions&&) = default;
+
+bool FieldOptions::operator==(const FieldOptions& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(buffer_stats_, other.buffer_stats_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunk_payload_histogram_def_, other.chunk_payload_histogram_def_)
-   && ::protozero::internal::gen_helpers::EqualsField(writer_stats_, other.writer_stats_)
-   && ::protozero::internal::gen_helpers::EqualsField(producers_connected_, other.producers_connected_)
-   && ::protozero::internal::gen_helpers::EqualsField(producers_seen_, other.producers_seen_)
-   && ::protozero::internal::gen_helpers::EqualsField(data_sources_registered_, other.data_sources_registered_)
-   && ::protozero::internal::gen_helpers::EqualsField(data_sources_seen_, other.data_sources_seen_)
-   && ::protozero::internal::gen_helpers::EqualsField(tracing_sessions_, other.tracing_sessions_)
-   && ::protozero::internal::gen_helpers::EqualsField(total_buffers_, other.total_buffers_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunks_discarded_, other.chunks_discarded_)
-   && ::protozero::internal::gen_helpers::EqualsField(patches_discarded_, other.patches_discarded_)
-   && ::protozero::internal::gen_helpers::EqualsField(invalid_packets_, other.invalid_packets_)
-   && ::protozero::internal::gen_helpers::EqualsField(filter_stats_, other.filter_stats_)
-   && ::protozero::internal::gen_helpers::EqualsField(flushes_requested_, other.flushes_requested_)
-   && ::protozero::internal::gen_helpers::EqualsField(flushes_succeeded_, other.flushes_succeeded_)
-   && ::protozero::internal::gen_helpers::EqualsField(flushes_failed_, other.flushes_failed_)
-   && ::protozero::internal::gen_helpers::EqualsField(final_flush_outcome_, other.final_flush_outcome_);
+   && ::protozero::internal::gen_helpers::EqualsField(packed_, other.packed_)
+   && ::protozero::internal::gen_helpers::EqualsField(uninterpreted_option_, other.uninterpreted_option_);
 }

-int TraceStats::buffer_stats_size() const { return static_cast<int>(buffer_stats_.size()); }
-void TraceStats::clear_buffer_stats() { buffer_stats_.clear(); }
-TraceStats_BufferStats* TraceStats::add_buffer_stats() { buffer_stats_.emplace_back(); return &buffer_stats_.back(); }
-int TraceStats::writer_stats_size() const { return static_cast<int>(writer_stats_.size()); }
-void TraceStats::clear_writer_stats() { writer_stats_.clear(); }
-TraceStats_WriterStats* TraceStats::add_writer_stats() { writer_stats_.emplace_back(); return &writer_stats_.back(); }
-bool TraceStats::ParseFromArray(const void* raw, size_t size) {
-  buffer_stats_.clear();
-  chunk_payload_histogram_def_.clear();
-  writer_stats_.clear();
+int FieldOptions::uninterpreted_option_size() const { return static_cast<int>(uninterpreted_option_.size()); }
+void FieldOptions::clear_uninterpreted_option() { uninterpreted_option_.clear(); }
+UninterpretedOption* FieldOptions::add_uninterpreted_option() { uninterpreted_option_.emplace_back(); return &uninterpreted_option_.back(); }
+bool FieldOptions::ParseFromArray(const void* raw, size_t size) {
+  uninterpreted_option_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -19865,59 +19931,12 @@ bool TraceStats::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* buffer_stats */:
-        buffer_stats_.emplace_back();
-        buffer_stats_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 17 /* chunk_payload_histogram_def */:
-        chunk_payload_histogram_def_.emplace_back();
-        field.get(&chunk_payload_histogram_def_.back());
-        break;
-      case 18 /* writer_stats */:
-        writer_stats_.emplace_back();
-        writer_stats_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 2 /* producers_connected */:
-        field.get(&producers_connected_);
-        break;
-      case 3 /* producers_seen */:
-        field.get(&producers_seen_);
-        break;
-      case 4 /* data_sources_registered */:
-        field.get(&data_sources_registered_);
-        break;
-      case 5 /* data_sources_seen */:
-        field.get(&data_sources_seen_);
-        break;
-      case 6 /* tracing_sessions */:
-        field.get(&tracing_sessions_);
-        break;
-      case 7 /* total_buffers */:
-        field.get(&total_buffers_);
-        break;
-      case 8 /* chunks_discarded */:
-        field.get(&chunks_discarded_);
-        break;
-      case 9 /* patches_discarded */:
-        field.get(&patches_discarded_);
-        break;
-      case 10 /* invalid_packets */:
-        field.get(&invalid_packets_);
-        break;
-      case 11 /* filter_stats */:
-        (*filter_stats_).ParseFromArray(field.data(), field.size());
-        break;
-      case 12 /* flushes_requested */:
-        field.get(&flushes_requested_);
-        break;
-      case 13 /* flushes_succeeded */:
-        field.get(&flushes_succeeded_);
-        break;
-      case 14 /* flushes_failed */:
-        field.get(&flushes_failed_);
+      case 2 /* packed */:
+        field.get(&packed_);
         break;
-      case 15 /* final_flush_outcome */:
-        field.get(&final_flush_outcome_);
+      case 999 /* uninterpreted_option */:
+        uninterpreted_option_.emplace_back();
+        uninterpreted_option_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -19927,127 +19946,56 @@ bool TraceStats::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceStats::SerializeAsString() const {
+std::string FieldOptions::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceStats::SerializeAsArray() const {
+std::vector<uint8_t> FieldOptions::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceStats::Serialize(::protozero::Message* msg) const {
-  // Field 1: buffer_stats
-  for (auto& it : buffer_stats_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
-  }
-
-  // Field 17: chunk_payload_histogram_def
-  for (auto& it : chunk_payload_histogram_def_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(17, it, msg);
-  }
-
-  // Field 18: writer_stats
-  for (auto& it : writer_stats_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(18));
-  }
-
-  // Field 2: producers_connected
+void FieldOptions::Serialize(::protozero::Message* msg) const {
+  // Field 2: packed
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, producers_connected_, msg);
-  }
-
-  // Field 3: producers_seen
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, producers_seen_, msg);
-  }
-
-  // Field 4: data_sources_registered
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, data_sources_registered_, msg);
-  }
-
-  // Field 5: data_sources_seen
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, data_sources_seen_, msg);
-  }
-
-  // Field 6: tracing_sessions
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, tracing_sessions_, msg);
-  }
-
-  // Field 7: total_buffers
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, total_buffers_, msg);
-  }
-
-  // Field 8: chunks_discarded
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, chunks_discarded_, msg);
-  }
-
-  // Field 9: patches_discarded
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(9, patches_discarded_, msg);
-  }
-
-  // Field 10: invalid_packets
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(10, invalid_packets_, msg);
-  }
-
-  // Field 11: filter_stats
-  if (_has_field_[11]) {
-    (*filter_stats_).Serialize(msg->BeginNestedMessage<::protozero::Message>(11));
-  }
-
-  // Field 12: flushes_requested
-  if (_has_field_[12]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(12, flushes_requested_, msg);
-  }
-
-  // Field 13: flushes_succeeded
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(13, flushes_succeeded_, msg);
-  }
-
-  // Field 14: flushes_failed
-  if (_has_field_[14]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(14, flushes_failed_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, packed_, msg);
   }

-  // Field 15: final_flush_outcome
-  if (_has_field_[15]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(15, final_flush_outcome_, msg);
+  // Field 999: uninterpreted_option
+  for (auto& it : uninterpreted_option_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(999));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TraceStats_FilterStats::TraceStats_FilterStats() = default;
-TraceStats_FilterStats::~TraceStats_FilterStats() = default;
-TraceStats_FilterStats::TraceStats_FilterStats(const TraceStats_FilterStats&) = default;
-TraceStats_FilterStats& TraceStats_FilterStats::operator=(const TraceStats_FilterStats&) = default;
-TraceStats_FilterStats::TraceStats_FilterStats(TraceStats_FilterStats&&) noexcept = default;
-TraceStats_FilterStats& TraceStats_FilterStats::operator=(TraceStats_FilterStats&&) = default;
+UninterpretedOption::UninterpretedOption() = default;
+UninterpretedOption::~UninterpretedOption() = default;
+UninterpretedOption::UninterpretedOption(const UninterpretedOption&) = default;
+UninterpretedOption& UninterpretedOption::operator=(const UninterpretedOption&) = default;
+UninterpretedOption::UninterpretedOption(UninterpretedOption&&) noexcept = default;
+UninterpretedOption& UninterpretedOption::operator=(UninterpretedOption&&) = default;

-bool TraceStats_FilterStats::operator==(const TraceStats_FilterStats& other) const {
+bool UninterpretedOption::operator==(const UninterpretedOption& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(input_packets_, other.input_packets_)
-   && ::protozero::internal::gen_helpers::EqualsField(input_bytes_, other.input_bytes_)
-   && ::protozero::internal::gen_helpers::EqualsField(output_bytes_, other.output_bytes_)
-   && ::protozero::internal::gen_helpers::EqualsField(errors_, other.errors_)
-   && ::protozero::internal::gen_helpers::EqualsField(time_taken_ns_, other.time_taken_ns_)
-   && ::protozero::internal::gen_helpers::EqualsField(bytes_discarded_per_buffer_, other.bytes_discarded_per_buffer_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(identifier_value_, other.identifier_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(positive_int_value_, other.positive_int_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(negative_int_value_, other.negative_int_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(double_value_, other.double_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(string_value_, other.string_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(aggregate_value_, other.aggregate_value_);
 }

-bool TraceStats_FilterStats::ParseFromArray(const void* raw, size_t size) {
-  bytes_discarded_per_buffer_.clear();
+int UninterpretedOption::name_size() const { return static_cast<int>(name_.size()); }
+void UninterpretedOption::clear_name() { name_.clear(); }
+UninterpretedOption_NamePart* UninterpretedOption::add_name() { name_.emplace_back(); return &name_.back(); }
+bool UninterpretedOption::ParseFromArray(const void* raw, size_t size) {
+  name_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -20057,24 +20005,27 @@ bool TraceStats_FilterStats::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* input_packets */:
-        field.get(&input_packets_);
+      case 2 /* name */:
+        name_.emplace_back();
+        name_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 2 /* input_bytes */:
-        field.get(&input_bytes_);
+      case 3 /* identifier_value */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &identifier_value_);
         break;
-      case 3 /* output_bytes */:
-        field.get(&output_bytes_);
+      case 4 /* positive_int_value */:
+        field.get(&positive_int_value_);
         break;
-      case 4 /* errors */:
-        field.get(&errors_);
+      case 5 /* negative_int_value */:
+        field.get(&negative_int_value_);
         break;
-      case 5 /* time_taken_ns */:
-        field.get(&time_taken_ns_);
+      case 6 /* double_value */:
+        field.get(&double_value_);
         break;
-      case 20 /* bytes_discarded_per_buffer */:
-        bytes_discarded_per_buffer_.emplace_back();
-        field.get(&bytes_discarded_per_buffer_.back());
+      case 7 /* string_value */:
+        field.get(&string_value_);
+        break;
+      case 8 /* aggregate_value */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &aggregate_value_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -20084,71 +20035,72 @@ bool TraceStats_FilterStats::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceStats_FilterStats::SerializeAsString() const {
+std::string UninterpretedOption::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceStats_FilterStats::SerializeAsArray() const {
+std::vector<uint8_t> UninterpretedOption::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceStats_FilterStats::Serialize(::protozero::Message* msg) const {
-  // Field 1: input_packets
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, input_packets_, msg);
-  }
-
-  // Field 2: input_bytes
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, input_bytes_, msg);
+void UninterpretedOption::Serialize(::protozero::Message* msg) const {
+  // Field 2: name
+  for (auto& it : name_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

-  // Field 3: output_bytes
+  // Field 3: identifier_value
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, output_bytes_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(3, identifier_value_, msg);
   }

-  // Field 4: errors
+  // Field 4: positive_int_value
   if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, errors_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, positive_int_value_, msg);
   }

-  // Field 5: time_taken_ns
+  // Field 5: negative_int_value
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, time_taken_ns_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, negative_int_value_, msg);
   }

-  // Field 20: bytes_discarded_per_buffer
-  for (auto& it : bytes_discarded_per_buffer_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(20, it, msg);
+  // Field 6: double_value
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeFixed(6, double_value_, msg);
+  }
+
+  // Field 7: string_value
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeString(7, string_value_, msg);
+  }
+
+  // Field 8: aggregate_value
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeString(8, aggregate_value_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TraceStats_WriterStats::TraceStats_WriterStats() = default;
-TraceStats_WriterStats::~TraceStats_WriterStats() = default;
-TraceStats_WriterStats::TraceStats_WriterStats(const TraceStats_WriterStats&) = default;
-TraceStats_WriterStats& TraceStats_WriterStats::operator=(const TraceStats_WriterStats&) = default;
-TraceStats_WriterStats::TraceStats_WriterStats(TraceStats_WriterStats&&) noexcept = default;
-TraceStats_WriterStats& TraceStats_WriterStats::operator=(TraceStats_WriterStats&&) = default;
+UninterpretedOption_NamePart::UninterpretedOption_NamePart() = default;
+UninterpretedOption_NamePart::~UninterpretedOption_NamePart() = default;
+UninterpretedOption_NamePart::UninterpretedOption_NamePart(const UninterpretedOption_NamePart&) = default;
+UninterpretedOption_NamePart& UninterpretedOption_NamePart::operator=(const UninterpretedOption_NamePart&) = default;
+UninterpretedOption_NamePart::UninterpretedOption_NamePart(UninterpretedOption_NamePart&&) noexcept = default;
+UninterpretedOption_NamePart& UninterpretedOption_NamePart::operator=(UninterpretedOption_NamePart&&) = default;

-bool TraceStats_WriterStats::operator==(const TraceStats_WriterStats& other) const {
+bool UninterpretedOption_NamePart::operator==(const UninterpretedOption_NamePart& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(sequence_id_, other.sequence_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(buffer_, other.buffer_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunk_payload_histogram_counts_, other.chunk_payload_histogram_counts_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunk_payload_histogram_sum_, other.chunk_payload_histogram_sum_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_part_, other.name_part_)
+   && ::protozero::internal::gen_helpers::EqualsField(is_extension_, other.is_extension_);
 }

-bool TraceStats_WriterStats::ParseFromArray(const void* raw, size_t size) {
-  chunk_payload_histogram_counts_.clear();
-  chunk_payload_histogram_sum_.clear();
+bool UninterpretedOption_NamePart::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -20158,19 +20110,11 @@ bool TraceStats_WriterStats::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* sequence_id */:
-        field.get(&sequence_id_);
-        break;
-      case 4 /* buffer */:
-        field.get(&buffer_);
-        break;
-      case 2 /* chunk_payload_histogram_counts */:
-        if (!::protozero::internal::gen_helpers::DeserializePackedRepeated<::protozero::proto_utils::ProtoWireType::kVarInt, uint64_t>(field, &chunk_payload_histogram_counts_)) {
-          packed_error = true;}
+      case 1 /* name_part */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_part_);
         break;
-      case 3 /* chunk_payload_histogram_sum */:
-        if (!::protozero::internal::gen_helpers::DeserializePackedRepeated<::protozero::proto_utils::ProtoWireType::kVarInt, int64_t>(field, &chunk_payload_histogram_sum_)) {
-          packed_error = true;}
+      case 2 /* is_extension */:
+        field.get(&is_extension_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -20180,81 +20124,78 @@ bool TraceStats_WriterStats::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceStats_WriterStats::SerializeAsString() const {
+std::string UninterpretedOption_NamePart::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceStats_WriterStats::SerializeAsArray() const {
+std::vector<uint8_t> UninterpretedOption_NamePart::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceStats_WriterStats::Serialize(::protozero::Message* msg) const {
-  // Field 1: sequence_id
+void UninterpretedOption_NamePart::Serialize(::protozero::Message* msg) const {
+  // Field 1: name_part
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, sequence_id_, msg);
-  }
-
-  // Field 4: buffer
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, buffer_, msg);
-  }
-
-  // Field 2: chunk_payload_histogram_counts
-  {
-    ::protozero::PackedVarInt pack;
-    for (auto& it : chunk_payload_histogram_counts_)
-      pack.Append(it);
-    msg->AppendBytes(2, pack.data(), pack.size());
+    ::protozero::internal::gen_helpers::SerializeString(1, name_part_, msg);
   }

-  // Field 3: chunk_payload_histogram_sum
-  {
-    ::protozero::PackedVarInt pack;
-    for (auto& it : chunk_payload_histogram_sum_)
-      pack.Append(it);
-    msg->AppendBytes(3, pack.data(), pack.size());
+  // Field 2: is_extension
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, is_extension_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TraceStats_BufferStats::TraceStats_BufferStats() = default;
-TraceStats_BufferStats::~TraceStats_BufferStats() = default;
-TraceStats_BufferStats::TraceStats_BufferStats(const TraceStats_BufferStats&) = default;
-TraceStats_BufferStats& TraceStats_BufferStats::operator=(const TraceStats_BufferStats&) = default;
-TraceStats_BufferStats::TraceStats_BufferStats(TraceStats_BufferStats&&) noexcept = default;
-TraceStats_BufferStats& TraceStats_BufferStats::operator=(TraceStats_BufferStats&&) = default;
+DescriptorProto::DescriptorProto() = default;
+DescriptorProto::~DescriptorProto() = default;
+DescriptorProto::DescriptorProto(const DescriptorProto&) = default;
+DescriptorProto& DescriptorProto::operator=(const DescriptorProto&) = default;
+DescriptorProto::DescriptorProto(DescriptorProto&&) noexcept = default;
+DescriptorProto& DescriptorProto::operator=(DescriptorProto&&) = default;

-bool TraceStats_BufferStats::operator==(const TraceStats_BufferStats& other) const {
+bool DescriptorProto::operator==(const DescriptorProto& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(buffer_size_, other.buffer_size_)
-   && ::protozero::internal::gen_helpers::EqualsField(bytes_written_, other.bytes_written_)
-   && ::protozero::internal::gen_helpers::EqualsField(bytes_overwritten_, other.bytes_overwritten_)
-   && ::protozero::internal::gen_helpers::EqualsField(bytes_read_, other.bytes_read_)
-   && ::protozero::internal::gen_helpers::EqualsField(padding_bytes_written_, other.padding_bytes_written_)
-   && ::protozero::internal::gen_helpers::EqualsField(padding_bytes_cleared_, other.padding_bytes_cleared_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunks_written_, other.chunks_written_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunks_rewritten_, other.chunks_rewritten_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunks_relocated_, other.chunks_relocated_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunks_overwritten_, other.chunks_overwritten_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunks_discarded_, other.chunks_discarded_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunks_read_, other.chunks_read_)
-   && ::protozero::internal::gen_helpers::EqualsField(chunks_committed_out_of_order_, other.chunks_committed_out_of_order_)
-   && ::protozero::internal::gen_helpers::EqualsField(write_wrap_count_, other.write_wrap_count_)
-   && ::protozero::internal::gen_helpers::EqualsField(patches_succeeded_, other.patches_succeeded_)
-   && ::protozero::internal::gen_helpers::EqualsField(patches_failed_, other.patches_failed_)
-   && ::protozero::internal::gen_helpers::EqualsField(readaheads_succeeded_, other.readaheads_succeeded_)
-   && ::protozero::internal::gen_helpers::EqualsField(readaheads_failed_, other.readaheads_failed_)
-   && ::protozero::internal::gen_helpers::EqualsField(abi_violations_, other.abi_violations_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_writer_packet_loss_, other.trace_writer_packet_loss_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(field_, other.field_)
+   && ::protozero::internal::gen_helpers::EqualsField(extension_, other.extension_)
+   && ::protozero::internal::gen_helpers::EqualsField(nested_type_, other.nested_type_)
+   && ::protozero::internal::gen_helpers::EqualsField(enum_type_, other.enum_type_)
+   && ::protozero::internal::gen_helpers::EqualsField(oneof_decl_, other.oneof_decl_)
+   && ::protozero::internal::gen_helpers::EqualsField(reserved_range_, other.reserved_range_)
+   && ::protozero::internal::gen_helpers::EqualsField(reserved_name_, other.reserved_name_);
 }

-bool TraceStats_BufferStats::ParseFromArray(const void* raw, size_t size) {
+int DescriptorProto::field_size() const { return static_cast<int>(field_.size()); }
+void DescriptorProto::clear_field() { field_.clear(); }
+FieldDescriptorProto* DescriptorProto::add_field() { field_.emplace_back(); return &field_.back(); }
+int DescriptorProto::extension_size() const { return static_cast<int>(extension_.size()); }
+void DescriptorProto::clear_extension() { extension_.clear(); }
+FieldDescriptorProto* DescriptorProto::add_extension() { extension_.emplace_back(); return &extension_.back(); }
+int DescriptorProto::nested_type_size() const { return static_cast<int>(nested_type_.size()); }
+void DescriptorProto::clear_nested_type() { nested_type_.clear(); }
+DescriptorProto* DescriptorProto::add_nested_type() { nested_type_.emplace_back(); return &nested_type_.back(); }
+int DescriptorProto::enum_type_size() const { return static_cast<int>(enum_type_.size()); }
+void DescriptorProto::clear_enum_type() { enum_type_.clear(); }
+EnumDescriptorProto* DescriptorProto::add_enum_type() { enum_type_.emplace_back(); return &enum_type_.back(); }
+int DescriptorProto::oneof_decl_size() const { return static_cast<int>(oneof_decl_.size()); }
+void DescriptorProto::clear_oneof_decl() { oneof_decl_.clear(); }
+OneofDescriptorProto* DescriptorProto::add_oneof_decl() { oneof_decl_.emplace_back(); return &oneof_decl_.back(); }
+int DescriptorProto::reserved_range_size() const { return static_cast<int>(reserved_range_.size()); }
+void DescriptorProto::clear_reserved_range() { reserved_range_.clear(); }
+DescriptorProto_ReservedRange* DescriptorProto::add_reserved_range() { reserved_range_.emplace_back(); return &reserved_range_.back(); }
+bool DescriptorProto::ParseFromArray(const void* raw, size_t size) {
+  field_.clear();
+  extension_.clear();
+  nested_type_.clear();
+  enum_type_.clear();
+  oneof_decl_.clear();
+  reserved_range_.clear();
+  reserved_name_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -20264,65 +20205,36 @@ bool TraceStats_BufferStats::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 12 /* buffer_size */:
-        field.get(&buffer_size_);
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 1 /* bytes_written */:
-        field.get(&bytes_written_);
+      case 2 /* field */:
+        field_.emplace_back();
+        field_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 13 /* bytes_overwritten */:
-        field.get(&bytes_overwritten_);
-        break;
-      case 14 /* bytes_read */:
-        field.get(&bytes_read_);
-        break;
-      case 15 /* padding_bytes_written */:
-        field.get(&padding_bytes_written_);
-        break;
-      case 16 /* padding_bytes_cleared */:
-        field.get(&padding_bytes_cleared_);
-        break;
-      case 2 /* chunks_written */:
-        field.get(&chunks_written_);
-        break;
-      case 10 /* chunks_rewritten */:
-        field.get(&chunks_rewritten_);
-        break;
-      case 20 /* chunks_relocated */:
-        field.get(&chunks_relocated_);
-        break;
-      case 3 /* chunks_overwritten */:
-        field.get(&chunks_overwritten_);
-        break;
-      case 18 /* chunks_discarded */:
-        field.get(&chunks_discarded_);
-        break;
-      case 17 /* chunks_read */:
-        field.get(&chunks_read_);
-        break;
-      case 11 /* chunks_committed_out_of_order */:
-        field.get(&chunks_committed_out_of_order_);
-        break;
-      case 4 /* write_wrap_count */:
-        field.get(&write_wrap_count_);
-        break;
-      case 5 /* patches_succeeded */:
-        field.get(&patches_succeeded_);
+      case 6 /* extension */:
+        extension_.emplace_back();
+        extension_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 6 /* patches_failed */:
-        field.get(&patches_failed_);
+      case 3 /* nested_type */:
+        nested_type_.emplace_back();
+        nested_type_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 7 /* readaheads_succeeded */:
-        field.get(&readaheads_succeeded_);
+      case 4 /* enum_type */:
+        enum_type_.emplace_back();
+        enum_type_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 8 /* readaheads_failed */:
-        field.get(&readaheads_failed_);
+      case 8 /* oneof_decl */:
+        oneof_decl_.emplace_back();
+        oneof_decl_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 9 /* abi_violations */:
-        field.get(&abi_violations_);
+      case 9 /* reserved_range */:
+        reserved_range_.emplace_back();
+        reserved_range_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 19 /* trace_writer_packet_loss */:
-        field.get(&trace_writer_packet_loss_);
+      case 10 /* reserved_name */:
+        reserved_name_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &reserved_name_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -20332,163 +20244,77 @@ bool TraceStats_BufferStats::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceStats_BufferStats::SerializeAsString() const {
+std::string DescriptorProto::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceStats_BufferStats::SerializeAsArray() const {
+std::vector<uint8_t> DescriptorProto::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceStats_BufferStats::Serialize(::protozero::Message* msg) const {
-  // Field 12: buffer_size
-  if (_has_field_[12]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(12, buffer_size_, msg);
-  }
-
-  // Field 1: bytes_written
+void DescriptorProto::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, bytes_written_, msg);
-  }
-
-  // Field 13: bytes_overwritten
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(13, bytes_overwritten_, msg);
-  }
-
-  // Field 14: bytes_read
-  if (_has_field_[14]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(14, bytes_read_, msg);
-  }
-
-  // Field 15: padding_bytes_written
-  if (_has_field_[15]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(15, padding_bytes_written_, msg);
-  }
-
-  // Field 16: padding_bytes_cleared
-  if (_has_field_[16]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(16, padding_bytes_cleared_, msg);
-  }
-
-  // Field 2: chunks_written
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, chunks_written_, msg);
-  }
-
-  // Field 10: chunks_rewritten
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(10, chunks_rewritten_, msg);
-  }
-
-  // Field 20: chunks_relocated
-  if (_has_field_[20]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(20, chunks_relocated_, msg);
-  }
-
-  // Field 3: chunks_overwritten
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, chunks_overwritten_, msg);
-  }
-
-  // Field 18: chunks_discarded
-  if (_has_field_[18]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(18, chunks_discarded_, msg);
-  }
-
-  // Field 17: chunks_read
-  if (_has_field_[17]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(17, chunks_read_, msg);
-  }
-
-  // Field 11: chunks_committed_out_of_order
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(11, chunks_committed_out_of_order_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  // Field 4: write_wrap_count
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, write_wrap_count_, msg);
+  // Field 2: field
+  for (auto& it : field_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

-  // Field 5: patches_succeeded
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, patches_succeeded_, msg);
+  // Field 6: extension
+  for (auto& it : extension_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
   }

-  // Field 6: patches_failed
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, patches_failed_, msg);
+  // Field 3: nested_type
+  for (auto& it : nested_type_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
   }

-  // Field 7: readaheads_succeeded
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, readaheads_succeeded_, msg);
+  // Field 4: enum_type
+  for (auto& it : enum_type_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
   }

-  // Field 8: readaheads_failed
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, readaheads_failed_, msg);
+  // Field 8: oneof_decl
+  for (auto& it : oneof_decl_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
   }

-  // Field 9: abi_violations
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(9, abi_violations_, msg);
+  // Field 9: reserved_range
+  for (auto& it : reserved_range_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(9));
   }

-  // Field 19: trace_writer_packet_loss
-  if (_has_field_[19]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(19, trace_writer_packet_loss_, msg);
+  // Field 10: reserved_name
+  for (auto& it : reserved_name_) {
+    ::protozero::internal::gen_helpers::SerializeString(10, it, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/tracing_service_capabilities.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/tracing_service_capabilities.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/observable_events.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-TracingServiceCapabilities::TracingServiceCapabilities() = default;
-TracingServiceCapabilities::~TracingServiceCapabilities() = default;
-TracingServiceCapabilities::TracingServiceCapabilities(const TracingServiceCapabilities&) = default;
-TracingServiceCapabilities& TracingServiceCapabilities::operator=(const TracingServiceCapabilities&) = default;
-TracingServiceCapabilities::TracingServiceCapabilities(TracingServiceCapabilities&&) noexcept = default;
-TracingServiceCapabilities& TracingServiceCapabilities::operator=(TracingServiceCapabilities&&) = default;
+DescriptorProto_ReservedRange::DescriptorProto_ReservedRange() = default;
+DescriptorProto_ReservedRange::~DescriptorProto_ReservedRange() = default;
+DescriptorProto_ReservedRange::DescriptorProto_ReservedRange(const DescriptorProto_ReservedRange&) = default;
+DescriptorProto_ReservedRange& DescriptorProto_ReservedRange::operator=(const DescriptorProto_ReservedRange&) = default;
+DescriptorProto_ReservedRange::DescriptorProto_ReservedRange(DescriptorProto_ReservedRange&&) noexcept = default;
+DescriptorProto_ReservedRange& DescriptorProto_ReservedRange::operator=(DescriptorProto_ReservedRange&&) = default;

-bool TracingServiceCapabilities::operator==(const TracingServiceCapabilities& other) const {
+bool DescriptorProto_ReservedRange::operator==(const DescriptorProto_ReservedRange& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(has_query_capabilities_, other.has_query_capabilities_)
-   && ::protozero::internal::gen_helpers::EqualsField(observable_events_, other.observable_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(has_trace_config_output_path_, other.has_trace_config_output_path_)
-   && ::protozero::internal::gen_helpers::EqualsField(has_clone_session_, other.has_clone_session_);
+   && ::protozero::internal::gen_helpers::EqualsField(start_, other.start_)
+   && ::protozero::internal::gen_helpers::EqualsField(end_, other.end_);
 }

-bool TracingServiceCapabilities::ParseFromArray(const void* raw, size_t size) {
-  observable_events_.clear();
+bool DescriptorProto_ReservedRange::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -20498,18 +20324,11 @@ bool TracingServiceCapabilities::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* has_query_capabilities */:
-        field.get(&has_query_capabilities_);
-        break;
-      case 2 /* observable_events */:
-        observable_events_.emplace_back();
-        field.get(&observable_events_.back());
-        break;
-      case 3 /* has_trace_config_output_path */:
-        field.get(&has_trace_config_output_path_);
+      case 1 /* start */:
+        field.get(&start_);
         break;
-      case 4 /* has_clone_session */:
-        field.get(&has_clone_session_);
+      case 2 /* end */:
+        field.get(&end_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -20519,101 +20338,68 @@ bool TracingServiceCapabilities::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TracingServiceCapabilities::SerializeAsString() const {
+std::string DescriptorProto_ReservedRange::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TracingServiceCapabilities::SerializeAsArray() const {
+std::vector<uint8_t> DescriptorProto_ReservedRange::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TracingServiceCapabilities::Serialize(::protozero::Message* msg) const {
-  // Field 1: has_query_capabilities
+void DescriptorProto_ReservedRange::Serialize(::protozero::Message* msg) const {
+  // Field 1: start
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, has_query_capabilities_, msg);
-  }
-
-  // Field 2: observable_events
-  for (auto& it : observable_events_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
-  }
-
-  // Field 3: has_trace_config_output_path
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, has_trace_config_output_path_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, start_, msg);
   }

-  // Field 4: has_clone_session
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, has_clone_session_, msg);
+  // Field 2: end
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, end_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/tracing_service_state.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/tracing_service_state.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/data_source_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/protovm/vm_program.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/track_event_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/gpu_counter_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/ftrace_descriptor.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-TracingServiceState::TracingServiceState() = default;
-TracingServiceState::~TracingServiceState() = default;
-TracingServiceState::TracingServiceState(const TracingServiceState&) = default;
-TracingServiceState& TracingServiceState::operator=(const TracingServiceState&) = default;
-TracingServiceState::TracingServiceState(TracingServiceState&&) noexcept = default;
-TracingServiceState& TracingServiceState::operator=(TracingServiceState&&) = default;
+FileDescriptorProto::FileDescriptorProto() = default;
+FileDescriptorProto::~FileDescriptorProto() = default;
+FileDescriptorProto::FileDescriptorProto(const FileDescriptorProto&) = default;
+FileDescriptorProto& FileDescriptorProto::operator=(const FileDescriptorProto&) = default;
+FileDescriptorProto::FileDescriptorProto(FileDescriptorProto&&) noexcept = default;
+FileDescriptorProto& FileDescriptorProto::operator=(FileDescriptorProto&&) = default;

-bool TracingServiceState::operator==(const TracingServiceState& other) const {
+bool FileDescriptorProto::operator==(const FileDescriptorProto& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(producers_, other.producers_)
-   && ::protozero::internal::gen_helpers::EqualsField(data_sources_, other.data_sources_)
-   && ::protozero::internal::gen_helpers::EqualsField(tracing_sessions_, other.tracing_sessions_)
-   && ::protozero::internal::gen_helpers::EqualsField(supports_tracing_sessions_, other.supports_tracing_sessions_)
-   && ::protozero::internal::gen_helpers::EqualsField(num_sessions_, other.num_sessions_)
-   && ::protozero::internal::gen_helpers::EqualsField(num_sessions_started_, other.num_sessions_started_)
-   && ::protozero::internal::gen_helpers::EqualsField(tracing_service_version_, other.tracing_service_version_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(package_, other.package_)
+   && ::protozero::internal::gen_helpers::EqualsField(dependency_, other.dependency_)
+   && ::protozero::internal::gen_helpers::EqualsField(public_dependency_, other.public_dependency_)
+   && ::protozero::internal::gen_helpers::EqualsField(weak_dependency_, other.weak_dependency_)
+   && ::protozero::internal::gen_helpers::EqualsField(message_type_, other.message_type_)
+   && ::protozero::internal::gen_helpers::EqualsField(enum_type_, other.enum_type_)
+   && ::protozero::internal::gen_helpers::EqualsField(extension_, other.extension_);
 }

-int TracingServiceState::producers_size() const { return static_cast<int>(producers_.size()); }
-void TracingServiceState::clear_producers() { producers_.clear(); }
-TracingServiceState_Producer* TracingServiceState::add_producers() { producers_.emplace_back(); return &producers_.back(); }
-int TracingServiceState::data_sources_size() const { return static_cast<int>(data_sources_.size()); }
-void TracingServiceState::clear_data_sources() { data_sources_.clear(); }
-TracingServiceState_DataSource* TracingServiceState::add_data_sources() { data_sources_.emplace_back(); return &data_sources_.back(); }
-int TracingServiceState::tracing_sessions_size() const { return static_cast<int>(tracing_sessions_.size()); }
-void TracingServiceState::clear_tracing_sessions() { tracing_sessions_.clear(); }
-TracingServiceState_TracingSession* TracingServiceState::add_tracing_sessions() { tracing_sessions_.emplace_back(); return &tracing_sessions_.back(); }
-bool TracingServiceState::ParseFromArray(const void* raw, size_t size) {
-  producers_.clear();
-  data_sources_.clear();
-  tracing_sessions_.clear();
+int FileDescriptorProto::message_type_size() const { return static_cast<int>(message_type_.size()); }
+void FileDescriptorProto::clear_message_type() { message_type_.clear(); }
+DescriptorProto* FileDescriptorProto::add_message_type() { message_type_.emplace_back(); return &message_type_.back(); }
+int FileDescriptorProto::enum_type_size() const { return static_cast<int>(enum_type_.size()); }
+void FileDescriptorProto::clear_enum_type() { enum_type_.clear(); }
+EnumDescriptorProto* FileDescriptorProto::add_enum_type() { enum_type_.emplace_back(); return &enum_type_.back(); }
+int FileDescriptorProto::extension_size() const { return static_cast<int>(extension_.size()); }
+void FileDescriptorProto::clear_extension() { extension_.clear(); }
+FieldDescriptorProto* FileDescriptorProto::add_extension() { extension_.emplace_back(); return &extension_.back(); }
+bool FileDescriptorProto::ParseFromArray(const void* raw, size_t size) {
+  dependency_.clear();
+  public_dependency_.clear();
+  weak_dependency_.clear();
+  message_type_.clear();
+  enum_type_.clear();
+  extension_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -20623,29 +20409,35 @@ bool TracingServiceState::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* producers */:
-        producers_.emplace_back();
-        producers_.back().ParseFromArray(field.data(), field.size());
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 2 /* data_sources */:
-        data_sources_.emplace_back();
-        data_sources_.back().ParseFromArray(field.data(), field.size());
+      case 2 /* package */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &package_);
         break;
-      case 6 /* tracing_sessions */:
-        tracing_sessions_.emplace_back();
-        tracing_sessions_.back().ParseFromArray(field.data(), field.size());
+      case 3 /* dependency */:
+        dependency_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &dependency_.back());
         break;
-      case 7 /* supports_tracing_sessions */:
-        field.get(&supports_tracing_sessions_);
+      case 10 /* public_dependency */:
+        public_dependency_.emplace_back();
+        field.get(&public_dependency_.back());
         break;
-      case 3 /* num_sessions */:
-        field.get(&num_sessions_);
+      case 11 /* weak_dependency */:
+        weak_dependency_.emplace_back();
+        field.get(&weak_dependency_.back());
         break;
-      case 4 /* num_sessions_started */:
-        field.get(&num_sessions_started_);
+      case 4 /* message_type */:
+        message_type_.emplace_back();
+        message_type_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 5 /* tracing_service_version */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &tracing_service_version_);
+      case 5 /* enum_type */:
+        enum_type_.emplace_back();
+        enum_type_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 7 /* extension */:
+        extension_.emplace_back();
+        extension_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -20655,82 +20447,80 @@ bool TracingServiceState::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TracingServiceState::SerializeAsString() const {
+std::string FileDescriptorProto::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TracingServiceState::SerializeAsArray() const {
+std::vector<uint8_t> FileDescriptorProto::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TracingServiceState::Serialize(::protozero::Message* msg) const {
-  // Field 1: producers
-  for (auto& it : producers_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+void FileDescriptorProto::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  // Field 2: data_sources
-  for (auto& it : data_sources_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+  // Field 2: package
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, package_, msg);
   }

-  // Field 6: tracing_sessions
-  for (auto& it : tracing_sessions_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
+  // Field 3: dependency
+  for (auto& it : dependency_) {
+    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
   }

-  // Field 7: supports_tracing_sessions
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, supports_tracing_sessions_, msg);
+  // Field 10: public_dependency
+  for (auto& it : public_dependency_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(10, it, msg);
   }

-  // Field 3: num_sessions
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, num_sessions_, msg);
+  // Field 11: weak_dependency
+  for (auto& it : weak_dependency_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(11, it, msg);
   }

-  // Field 4: num_sessions_started
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, num_sessions_started_, msg);
+  // Field 4: message_type
+  for (auto& it : message_type_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
   }

-  // Field 5: tracing_service_version
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeString(5, tracing_service_version_, msg);
+  // Field 5: enum_type
+  for (auto& it : enum_type_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
+  }
+
+  // Field 7: extension
+  for (auto& it : extension_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(7));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TracingServiceState_TracingSession::TracingServiceState_TracingSession() = default;
-TracingServiceState_TracingSession::~TracingServiceState_TracingSession() = default;
-TracingServiceState_TracingSession::TracingServiceState_TracingSession(const TracingServiceState_TracingSession&) = default;
-TracingServiceState_TracingSession& TracingServiceState_TracingSession::operator=(const TracingServiceState_TracingSession&) = default;
-TracingServiceState_TracingSession::TracingServiceState_TracingSession(TracingServiceState_TracingSession&&) noexcept = default;
-TracingServiceState_TracingSession& TracingServiceState_TracingSession::operator=(TracingServiceState_TracingSession&&) = default;
+FileDescriptorSet::FileDescriptorSet() = default;
+FileDescriptorSet::~FileDescriptorSet() = default;
+FileDescriptorSet::FileDescriptorSet(const FileDescriptorSet&) = default;
+FileDescriptorSet& FileDescriptorSet::operator=(const FileDescriptorSet&) = default;
+FileDescriptorSet::FileDescriptorSet(FileDescriptorSet&&) noexcept = default;
+FileDescriptorSet& FileDescriptorSet::operator=(FileDescriptorSet&&) = default;

-bool TracingServiceState_TracingSession::operator==(const TracingServiceState_TracingSession& other) const {
+bool FileDescriptorSet::operator==(const FileDescriptorSet& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(id_, other.id_)
-   && ::protozero::internal::gen_helpers::EqualsField(consumer_uid_, other.consumer_uid_)
-   && ::protozero::internal::gen_helpers::EqualsField(state_, other.state_)
-   && ::protozero::internal::gen_helpers::EqualsField(unique_session_name_, other.unique_session_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(buffer_size_kb_, other.buffer_size_kb_)
-   && ::protozero::internal::gen_helpers::EqualsField(duration_ms_, other.duration_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(num_data_sources_, other.num_data_sources_)
-   && ::protozero::internal::gen_helpers::EqualsField(start_realtime_ns_, other.start_realtime_ns_)
-   && ::protozero::internal::gen_helpers::EqualsField(bugreport_score_, other.bugreport_score_)
-   && ::protozero::internal::gen_helpers::EqualsField(bugreport_filename_, other.bugreport_filename_)
-   && ::protozero::internal::gen_helpers::EqualsField(is_started_, other.is_started_);
+   && ::protozero::internal::gen_helpers::EqualsField(file_, other.file_);
 }

-bool TracingServiceState_TracingSession::ParseFromArray(const void* raw, size_t size) {
-  buffer_size_kb_.clear();
+int FileDescriptorSet::file_size() const { return static_cast<int>(file_.size()); }
+void FileDescriptorSet::clear_file() { file_.clear(); }
+FileDescriptorProto* FileDescriptorSet::add_file() { file_.emplace_back(); return &file_.back(); }
+bool FileDescriptorSet::ParseFromArray(const void* raw, size_t size) {
+  file_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -20740,39 +20530,9 @@ bool TracingServiceState_TracingSession::ParseFromArray(const void* raw, size_t
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* id */:
-        field.get(&id_);
-        break;
-      case 2 /* consumer_uid */:
-        field.get(&consumer_uid_);
-        break;
-      case 3 /* state */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &state_);
-        break;
-      case 4 /* unique_session_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &unique_session_name_);
-        break;
-      case 5 /* buffer_size_kb */:
-        buffer_size_kb_.emplace_back();
-        field.get(&buffer_size_kb_.back());
-        break;
-      case 6 /* duration_ms */:
-        field.get(&duration_ms_);
-        break;
-      case 7 /* num_data_sources */:
-        field.get(&num_data_sources_);
-        break;
-      case 8 /* start_realtime_ns */:
-        field.get(&start_realtime_ns_);
-        break;
-      case 9 /* bugreport_score */:
-        field.get(&bugreport_score_);
-        break;
-      case 10 /* bugreport_filename */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &bugreport_filename_);
-        break;
-      case 11 /* is_started */:
-        field.get(&is_started_);
+      case 1 /* file */:
+        file_.emplace_back();
+        file_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -20782,92 +20542,67 @@ bool TracingServiceState_TracingSession::ParseFromArray(const void* raw, size_t
   return !packed_error && !dec.bytes_left();
 }

-std::string TracingServiceState_TracingSession::SerializeAsString() const {
+std::string FileDescriptorSet::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TracingServiceState_TracingSession::SerializeAsArray() const {
+std::vector<uint8_t> FileDescriptorSet::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TracingServiceState_TracingSession::Serialize(::protozero::Message* msg) const {
-  // Field 1: id
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, id_, msg);
-  }
-
-  // Field 2: consumer_uid
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, consumer_uid_, msg);
-  }
-
-  // Field 3: state
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeString(3, state_, msg);
-  }
-
-  // Field 4: unique_session_name
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeString(4, unique_session_name_, msg);
-  }
-
-  // Field 5: buffer_size_kb
-  for (auto& it : buffer_size_kb_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, it, msg);
-  }
-
-  // Field 6: duration_ms
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, duration_ms_, msg);
-  }
-
-  // Field 7: num_data_sources
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, num_data_sources_, msg);
-  }
-
-  // Field 8: start_realtime_ns
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, start_realtime_ns_, msg);
-  }
-
-  // Field 9: bugreport_score
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(9, bugreport_score_, msg);
-  }
-
-  // Field 10: bugreport_filename
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeString(10, bugreport_filename_, msg);
-  }
-
-  // Field 11: is_started
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(11, is_started_, msg);
+void FileDescriptorSet::Serialize(::protozero::Message* msg) const {
+  // Field 1: file
+  for (auto& it : file_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/ftrace_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/ftrace_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-TracingServiceState_DataSource::TracingServiceState_DataSource() = default;
-TracingServiceState_DataSource::~TracingServiceState_DataSource() = default;
-TracingServiceState_DataSource::TracingServiceState_DataSource(const TracingServiceState_DataSource&) = default;
-TracingServiceState_DataSource& TracingServiceState_DataSource::operator=(const TracingServiceState_DataSource&) = default;
-TracingServiceState_DataSource::TracingServiceState_DataSource(TracingServiceState_DataSource&&) noexcept = default;
-TracingServiceState_DataSource& TracingServiceState_DataSource::operator=(TracingServiceState_DataSource&&) = default;
+FtraceDescriptor::FtraceDescriptor() = default;
+FtraceDescriptor::~FtraceDescriptor() = default;
+FtraceDescriptor::FtraceDescriptor(const FtraceDescriptor&) = default;
+FtraceDescriptor& FtraceDescriptor::operator=(const FtraceDescriptor&) = default;
+FtraceDescriptor::FtraceDescriptor(FtraceDescriptor&&) noexcept = default;
+FtraceDescriptor& FtraceDescriptor::operator=(FtraceDescriptor&&) = default;

-bool TracingServiceState_DataSource::operator==(const TracingServiceState_DataSource& other) const {
+bool FtraceDescriptor::operator==(const FtraceDescriptor& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(ds_descriptor_, other.ds_descriptor_)
-   && ::protozero::internal::gen_helpers::EqualsField(producer_id_, other.producer_id_);
+   && ::protozero::internal::gen_helpers::EqualsField(atrace_categories_, other.atrace_categories_);
 }

-bool TracingServiceState_DataSource::ParseFromArray(const void* raw, size_t size) {
+int FtraceDescriptor::atrace_categories_size() const { return static_cast<int>(atrace_categories_.size()); }
+void FtraceDescriptor::clear_atrace_categories() { atrace_categories_.clear(); }
+FtraceDescriptor_AtraceCategory* FtraceDescriptor::add_atrace_categories() { atrace_categories_.emplace_back(); return &atrace_categories_.back(); }
+bool FtraceDescriptor::ParseFromArray(const void* raw, size_t size) {
+  atrace_categories_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -20877,11 +20612,9 @@ bool TracingServiceState_DataSource::ParseFromArray(const void* raw, size_t size
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* ds_descriptor */:
-        (*ds_descriptor_).ParseFromArray(field.data(), field.size());
-        break;
-      case 2 /* producer_id */:
-        field.get(&producer_id_);
+      case 1 /* atrace_categories */:
+        atrace_categories_.emplace_back();
+        atrace_categories_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -20891,53 +20624,42 @@ bool TracingServiceState_DataSource::ParseFromArray(const void* raw, size_t size
   return !packed_error && !dec.bytes_left();
 }

-std::string TracingServiceState_DataSource::SerializeAsString() const {
+std::string FtraceDescriptor::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TracingServiceState_DataSource::SerializeAsArray() const {
+std::vector<uint8_t> FtraceDescriptor::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TracingServiceState_DataSource::Serialize(::protozero::Message* msg) const {
-  // Field 1: ds_descriptor
-  if (_has_field_[1]) {
-    (*ds_descriptor_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
-  }
-
-  // Field 2: producer_id
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, producer_id_, msg);
+void FtraceDescriptor::Serialize(::protozero::Message* msg) const {
+  // Field 1: atrace_categories
+  for (auto& it : atrace_categories_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TracingServiceState_Producer::TracingServiceState_Producer() = default;
-TracingServiceState_Producer::~TracingServiceState_Producer() = default;
-TracingServiceState_Producer::TracingServiceState_Producer(const TracingServiceState_Producer&) = default;
-TracingServiceState_Producer& TracingServiceState_Producer::operator=(const TracingServiceState_Producer&) = default;
-TracingServiceState_Producer::TracingServiceState_Producer(TracingServiceState_Producer&&) noexcept = default;
-TracingServiceState_Producer& TracingServiceState_Producer::operator=(TracingServiceState_Producer&&) = default;
+FtraceDescriptor_AtraceCategory::FtraceDescriptor_AtraceCategory() = default;
+FtraceDescriptor_AtraceCategory::~FtraceDescriptor_AtraceCategory() = default;
+FtraceDescriptor_AtraceCategory::FtraceDescriptor_AtraceCategory(const FtraceDescriptor_AtraceCategory&) = default;
+FtraceDescriptor_AtraceCategory& FtraceDescriptor_AtraceCategory::operator=(const FtraceDescriptor_AtraceCategory&) = default;
+FtraceDescriptor_AtraceCategory::FtraceDescriptor_AtraceCategory(FtraceDescriptor_AtraceCategory&&) noexcept = default;
+FtraceDescriptor_AtraceCategory& FtraceDescriptor_AtraceCategory::operator=(FtraceDescriptor_AtraceCategory&&) = default;

-bool TracingServiceState_Producer::operator==(const TracingServiceState_Producer& other) const {
+bool FtraceDescriptor_AtraceCategory::operator==(const FtraceDescriptor_AtraceCategory& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(id_, other.id_)
    && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(pid_, other.pid_)
-   && ::protozero::internal::gen_helpers::EqualsField(uid_, other.uid_)
-   && ::protozero::internal::gen_helpers::EqualsField(sdk_version_, other.sdk_version_)
-   && ::protozero::internal::gen_helpers::EqualsField(frozen_, other.frozen_)
-   && ::protozero::internal::gen_helpers::EqualsField(machine_id_, other.machine_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(machine_name_, other.machine_name_);
+   && ::protozero::internal::gen_helpers::EqualsField(description_, other.description_);
 }

-bool TracingServiceState_Producer::ParseFromArray(const void* raw, size_t size) {
+bool FtraceDescriptor_AtraceCategory::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -20947,29 +20669,11 @@ bool TracingServiceState_Producer::ParseFromArray(const void* raw, size_t size)
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* id */:
-        field.get(&id_);
-        break;
-      case 2 /* name */:
+      case 1 /* name */:
         ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 5 /* pid */:
-        field.get(&pid_);
-        break;
-      case 3 /* uid */:
-        field.get(&uid_);
-        break;
-      case 4 /* sdk_version */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &sdk_version_);
-        break;
-      case 6 /* frozen */:
-        field.get(&frozen_);
-        break;
-      case 7 /* machine_id */:
-        field.get(&machine_id_);
-        break;
-      case 8 /* machine_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &machine_name_);
+      case 2 /* description */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &description_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -20979,57 +20683,27 @@ bool TracingServiceState_Producer::ParseFromArray(const void* raw, size_t size)
   return !packed_error && !dec.bytes_left();
 }

-std::string TracingServiceState_Producer::SerializeAsString() const {
+std::string FtraceDescriptor_AtraceCategory::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TracingServiceState_Producer::SerializeAsArray() const {
+std::vector<uint8_t> FtraceDescriptor_AtraceCategory::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TracingServiceState_Producer::Serialize(::protozero::Message* msg) const {
-  // Field 1: id
+void FtraceDescriptor_AtraceCategory::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, id_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  // Field 2: name
+  // Field 2: description
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
-  }
-
-  // Field 5: pid
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, pid_, msg);
-  }
-
-  // Field 3: uid
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, uid_, msg);
-  }
-
-  // Field 4: sdk_version
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeString(4, sdk_version_, msg);
-  }
-
-  // Field 6: frozen
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(6, frozen_, msg);
-  }
-
-  // Field 7: machine_id
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, machine_id_, msg);
-  }
-
-  // Field 8: machine_name
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeString(8, machine_name_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(2, description_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -21041,7 +20715,7 @@ void TracingServiceState_Producer::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/track_event_descriptor.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/common/gpu_counter_descriptor.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -21052,29 +20726,44 @@ void TracingServiceState_Producer::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/track_event_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/gpu_counter_descriptor.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-TrackEventDescriptor::TrackEventDescriptor() = default;
-TrackEventDescriptor::~TrackEventDescriptor() = default;
-TrackEventDescriptor::TrackEventDescriptor(const TrackEventDescriptor&) = default;
-TrackEventDescriptor& TrackEventDescriptor::operator=(const TrackEventDescriptor&) = default;
-TrackEventDescriptor::TrackEventDescriptor(TrackEventDescriptor&&) noexcept = default;
-TrackEventDescriptor& TrackEventDescriptor::operator=(TrackEventDescriptor&&) = default;
+GpuCounterDescriptor::GpuCounterDescriptor() = default;
+GpuCounterDescriptor::~GpuCounterDescriptor() = default;
+GpuCounterDescriptor::GpuCounterDescriptor(const GpuCounterDescriptor&) = default;
+GpuCounterDescriptor& GpuCounterDescriptor::operator=(const GpuCounterDescriptor&) = default;
+GpuCounterDescriptor::GpuCounterDescriptor(GpuCounterDescriptor&&) noexcept = default;
+GpuCounterDescriptor& GpuCounterDescriptor::operator=(GpuCounterDescriptor&&) = default;

-bool TrackEventDescriptor::operator==(const TrackEventDescriptor& other) const {
+bool GpuCounterDescriptor::operator==(const GpuCounterDescriptor& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(available_categories_, other.available_categories_);
+   && ::protozero::internal::gen_helpers::EqualsField(specs_, other.specs_)
+   && ::protozero::internal::gen_helpers::EqualsField(blocks_, other.blocks_)
+   && ::protozero::internal::gen_helpers::EqualsField(counter_groups_, other.counter_groups_)
+   && ::protozero::internal::gen_helpers::EqualsField(min_sampling_period_ns_, other.min_sampling_period_ns_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_sampling_period_ns_, other.max_sampling_period_ns_)
+   && ::protozero::internal::gen_helpers::EqualsField(supports_instrumented_sampling_, other.supports_instrumented_sampling_)
+   && ::protozero::internal::gen_helpers::EqualsField(supports_counter_names_, other.supports_counter_names_)
+   && ::protozero::internal::gen_helpers::EqualsField(supports_counter_name_globs_, other.supports_counter_name_globs_);
 }

-int TrackEventDescriptor::available_categories_size() const { return static_cast<int>(available_categories_.size()); }
-void TrackEventDescriptor::clear_available_categories() { available_categories_.clear(); }
-TrackEventCategory* TrackEventDescriptor::add_available_categories() { available_categories_.emplace_back(); return &available_categories_.back(); }
-bool TrackEventDescriptor::ParseFromArray(const void* raw, size_t size) {
-  available_categories_.clear();
+int GpuCounterDescriptor::specs_size() const { return static_cast<int>(specs_.size()); }
+void GpuCounterDescriptor::clear_specs() { specs_.clear(); }
+GpuCounterDescriptor_GpuCounterSpec* GpuCounterDescriptor::add_specs() { specs_.emplace_back(); return &specs_.back(); }
+int GpuCounterDescriptor::blocks_size() const { return static_cast<int>(blocks_.size()); }
+void GpuCounterDescriptor::clear_blocks() { blocks_.clear(); }
+GpuCounterDescriptor_GpuCounterBlock* GpuCounterDescriptor::add_blocks() { blocks_.emplace_back(); return &blocks_.back(); }
+int GpuCounterDescriptor::counter_groups_size() const { return static_cast<int>(counter_groups_.size()); }
+void GpuCounterDescriptor::clear_counter_groups() { counter_groups_.clear(); }
+GpuCounterDescriptor_GpuCounterGroupSpec* GpuCounterDescriptor::add_counter_groups() { counter_groups_.emplace_back(); return &counter_groups_.back(); }
+bool GpuCounterDescriptor::ParseFromArray(const void* raw, size_t size) {
+  specs_.clear();
+  blocks_.clear();
+  counter_groups_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -21084,9 +20773,32 @@ bool TrackEventDescriptor::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* available_categories */:
-        available_categories_.emplace_back();
-        available_categories_.back().ParseFromArray(field.data(), field.size());
+      case 1 /* specs */:
+        specs_.emplace_back();
+        specs_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 2 /* blocks */:
+        blocks_.emplace_back();
+        blocks_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 6 /* counter_groups */:
+        counter_groups_.emplace_back();
+        counter_groups_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 3 /* min_sampling_period_ns */:
+        field.get(&min_sampling_period_ns_);
+        break;
+      case 4 /* max_sampling_period_ns */:
+        field.get(&max_sampling_period_ns_);
+        break;
+      case 5 /* supports_instrumented_sampling */:
+        field.get(&supports_instrumented_sampling_);
+        break;
+      case 7 /* supports_counter_names */:
+        field.get(&supports_counter_names_);
+        break;
+      case 8 /* supports_counter_name_globs */:
+        field.get(&supports_counter_name_globs_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -21096,44 +20808,80 @@ bool TrackEventDescriptor::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TrackEventDescriptor::SerializeAsString() const {
+std::string GpuCounterDescriptor::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TrackEventDescriptor::SerializeAsArray() const {
+std::vector<uint8_t> GpuCounterDescriptor::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TrackEventDescriptor::Serialize(::protozero::Message* msg) const {
-  // Field 1: available_categories
-  for (auto& it : available_categories_) {
+void GpuCounterDescriptor::Serialize(::protozero::Message* msg) const {
+  // Field 1: specs
+  for (auto& it : specs_) {
     it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

+  // Field 2: blocks
+  for (auto& it : blocks_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+  }
+
+  // Field 6: counter_groups
+  for (auto& it : counter_groups_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
+  }
+
+  // Field 3: min_sampling_period_ns
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, min_sampling_period_ns_, msg);
+  }
+
+  // Field 4: max_sampling_period_ns
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, max_sampling_period_ns_, msg);
+  }
+
+  // Field 5: supports_instrumented_sampling
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, supports_instrumented_sampling_, msg);
+  }
+
+  // Field 7: supports_counter_names
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, supports_counter_names_, msg);
+  }
+
+  // Field 8: supports_counter_name_globs
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, supports_counter_name_globs_, msg);
+  }
+
   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TrackEventCategory::TrackEventCategory() = default;
-TrackEventCategory::~TrackEventCategory() = default;
-TrackEventCategory::TrackEventCategory(const TrackEventCategory&) = default;
-TrackEventCategory& TrackEventCategory::operator=(const TrackEventCategory&) = default;
-TrackEventCategory::TrackEventCategory(TrackEventCategory&&) noexcept = default;
-TrackEventCategory& TrackEventCategory::operator=(TrackEventCategory&&) = default;
+GpuCounterDescriptor_GpuCounterGroupSpec::GpuCounterDescriptor_GpuCounterGroupSpec() = default;
+GpuCounterDescriptor_GpuCounterGroupSpec::~GpuCounterDescriptor_GpuCounterGroupSpec() = default;
+GpuCounterDescriptor_GpuCounterGroupSpec::GpuCounterDescriptor_GpuCounterGroupSpec(const GpuCounterDescriptor_GpuCounterGroupSpec&) = default;
+GpuCounterDescriptor_GpuCounterGroupSpec& GpuCounterDescriptor_GpuCounterGroupSpec::operator=(const GpuCounterDescriptor_GpuCounterGroupSpec&) = default;
+GpuCounterDescriptor_GpuCounterGroupSpec::GpuCounterDescriptor_GpuCounterGroupSpec(GpuCounterDescriptor_GpuCounterGroupSpec&&) noexcept = default;
+GpuCounterDescriptor_GpuCounterGroupSpec& GpuCounterDescriptor_GpuCounterGroupSpec::operator=(GpuCounterDescriptor_GpuCounterGroupSpec&&) = default;

-bool TrackEventCategory::operator==(const TrackEventCategory& other) const {
+bool GpuCounterDescriptor_GpuCounterGroupSpec::operator==(const GpuCounterDescriptor_GpuCounterGroupSpec& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(group_id_, other.group_id_)
    && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
    && ::protozero::internal::gen_helpers::EqualsField(description_, other.description_)
-   && ::protozero::internal::gen_helpers::EqualsField(tags_, other.tags_);
+   && ::protozero::internal::gen_helpers::EqualsField(counter_ids_, other.counter_ids_);
 }

-bool TrackEventCategory::ParseFromArray(const void* raw, size_t size) {
-  tags_.clear();
+bool GpuCounterDescriptor_GpuCounterGroupSpec::ParseFromArray(const void* raw, size_t size) {
+  counter_ids_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -21143,15 +20891,18 @@ bool TrackEventCategory::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* name */:
+      case 1 /* group_id */:
+        field.get(&group_id_);
+        break;
+      case 2 /* name */:
         ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 2 /* description */:
+      case 3 /* description */:
         ::protozero::internal::gen_helpers::DeserializeString(field, &description_);
         break;
-      case 3 /* tags */:
-        tags_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &tags_.back());
+      case 4 /* counter_ids */:
+        counter_ids_.emplace_back();
+        field.get(&counter_ids_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -21161,95 +20912,61 @@ bool TrackEventCategory::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TrackEventCategory::SerializeAsString() const {
+std::string GpuCounterDescriptor_GpuCounterGroupSpec::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TrackEventCategory::SerializeAsArray() const {
+std::vector<uint8_t> GpuCounterDescriptor_GpuCounterGroupSpec::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TrackEventCategory::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
+void GpuCounterDescriptor_GpuCounterGroupSpec::Serialize(::protozero::Message* msg) const {
+  // Field 1: group_id
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, group_id_, msg);
   }

-  // Field 2: description
+  // Field 2: name
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, description_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
   }

-  // Field 3: tags
-  for (auto& it : tags_) {
-    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
+  // Field 3: description
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeString(3, description_, msg);
+  }
+
+  // Field 4: counter_ids
+  for (auto& it : counter_ids_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, it, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/semantic_type.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/common/semantic_type.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {
-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_aflags_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_aflags_config.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-AndroidAflagsConfig::AndroidAflagsConfig() = default;
-AndroidAflagsConfig::~AndroidAflagsConfig() = default;
-AndroidAflagsConfig::AndroidAflagsConfig(const AndroidAflagsConfig&) = default;
-AndroidAflagsConfig& AndroidAflagsConfig::operator=(const AndroidAflagsConfig&) = default;
-AndroidAflagsConfig::AndroidAflagsConfig(AndroidAflagsConfig&&) noexcept = default;
-AndroidAflagsConfig& AndroidAflagsConfig::operator=(AndroidAflagsConfig&&) = default;
+GpuCounterDescriptor_GpuCounterBlock::GpuCounterDescriptor_GpuCounterBlock() = default;
+GpuCounterDescriptor_GpuCounterBlock::~GpuCounterDescriptor_GpuCounterBlock() = default;
+GpuCounterDescriptor_GpuCounterBlock::GpuCounterDescriptor_GpuCounterBlock(const GpuCounterDescriptor_GpuCounterBlock&) = default;
+GpuCounterDescriptor_GpuCounterBlock& GpuCounterDescriptor_GpuCounterBlock::operator=(const GpuCounterDescriptor_GpuCounterBlock&) = default;
+GpuCounterDescriptor_GpuCounterBlock::GpuCounterDescriptor_GpuCounterBlock(GpuCounterDescriptor_GpuCounterBlock&&) noexcept = default;
+GpuCounterDescriptor_GpuCounterBlock& GpuCounterDescriptor_GpuCounterBlock::operator=(GpuCounterDescriptor_GpuCounterBlock&&) = default;

-bool AndroidAflagsConfig::operator==(const AndroidAflagsConfig& other) const {
+bool GpuCounterDescriptor_GpuCounterBlock::operator==(const GpuCounterDescriptor_GpuCounterBlock& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(poll_ms_, other.poll_ms_);
+   && ::protozero::internal::gen_helpers::EqualsField(block_id_, other.block_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(block_capacity_, other.block_capacity_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(description_, other.description_)
+   && ::protozero::internal::gen_helpers::EqualsField(counter_ids_, other.counter_ids_);
 }

-bool AndroidAflagsConfig::ParseFromArray(const void* raw, size_t size) {
+bool GpuCounterDescriptor_GpuCounterBlock::ParseFromArray(const void* raw, size_t size) {
+  counter_ids_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -21259,8 +20976,21 @@ bool AndroidAflagsConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* poll_ms */:
-        field.get(&poll_ms_);
+      case 1 /* block_id */:
+        field.get(&block_id_);
+        break;
+      case 2 /* block_capacity */:
+        field.get(&block_capacity_);
+        break;
+      case 3 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      case 4 /* description */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &description_);
+        break;
+      case 5 /* counter_ids */:
+        counter_ids_.emplace_back();
+        field.get(&counter_ids_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -21270,64 +21000,225 @@ bool AndroidAflagsConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string AndroidAflagsConfig::SerializeAsString() const {
+std::string GpuCounterDescriptor_GpuCounterBlock::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> AndroidAflagsConfig::SerializeAsArray() const {
+std::vector<uint8_t> GpuCounterDescriptor_GpuCounterBlock::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void AndroidAflagsConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: poll_ms
+void GpuCounterDescriptor_GpuCounterBlock::Serialize(::protozero::Message* msg) const {
+  // Field 1: block_id
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, poll_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, block_id_, msg);
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+  // Field 2: block_capacity
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, block_capacity_, msg);
+  }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_game_intervention_list_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+  // Field 3: name
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeString(3, name_, msg);
+  }
+
+  // Field 4: description
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeString(4, description_, msg);
+  }
+
+  // Field 5: counter_ids
+  for (auto& it : counter_ids_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, it, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+GpuCounterDescriptor_GpuCounterSpec::GpuCounterDescriptor_GpuCounterSpec() = default;
+GpuCounterDescriptor_GpuCounterSpec::~GpuCounterDescriptor_GpuCounterSpec() = default;
+GpuCounterDescriptor_GpuCounterSpec::GpuCounterDescriptor_GpuCounterSpec(const GpuCounterDescriptor_GpuCounterSpec&) = default;
+GpuCounterDescriptor_GpuCounterSpec& GpuCounterDescriptor_GpuCounterSpec::operator=(const GpuCounterDescriptor_GpuCounterSpec&) = default;
+GpuCounterDescriptor_GpuCounterSpec::GpuCounterDescriptor_GpuCounterSpec(GpuCounterDescriptor_GpuCounterSpec&&) noexcept = default;
+GpuCounterDescriptor_GpuCounterSpec& GpuCounterDescriptor_GpuCounterSpec::operator=(GpuCounterDescriptor_GpuCounterSpec&&) = default;
+
+bool GpuCounterDescriptor_GpuCounterSpec::operator==(const GpuCounterDescriptor_GpuCounterSpec& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(counter_id_, other.counter_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(description_, other.description_)
+   && ::protozero::internal::gen_helpers::EqualsField(int_peak_value_, other.int_peak_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(double_peak_value_, other.double_peak_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(numerator_units_, other.numerator_units_)
+   && ::protozero::internal::gen_helpers::EqualsField(denominator_units_, other.denominator_units_)
+   && ::protozero::internal::gen_helpers::EqualsField(select_by_default_, other.select_by_default_)
+   && ::protozero::internal::gen_helpers::EqualsField(groups_, other.groups_)
+   && ::protozero::internal::gen_helpers::EqualsField(value_direction_, other.value_direction_);
+}
+
+bool GpuCounterDescriptor_GpuCounterSpec::ParseFromArray(const void* raw, size_t size) {
+  numerator_units_.clear();
+  denominator_units_.clear();
+  groups_.clear();
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* counter_id */:
+        field.get(&counter_id_);
+        break;
+      case 2 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      case 3 /* description */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &description_);
+        break;
+      case 5 /* int_peak_value */:
+        field.get(&int_peak_value_);
+        break;
+      case 6 /* double_peak_value */:
+        field.get(&double_peak_value_);
+        break;
+      case 7 /* numerator_units */:
+        numerator_units_.emplace_back();
+        field.get(&numerator_units_.back());
+        break;
+      case 8 /* denominator_units */:
+        denominator_units_.emplace_back();
+        field.get(&denominator_units_.back());
+        break;
+      case 9 /* select_by_default */:
+        field.get(&select_by_default_);
+        break;
+      case 10 /* groups */:
+        groups_.emplace_back();
+        field.get(&groups_.back());
+        break;
+      case 11 /* value_direction */:
+        field.get(&value_direction_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string GpuCounterDescriptor_GpuCounterSpec::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> GpuCounterDescriptor_GpuCounterSpec::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void GpuCounterDescriptor_GpuCounterSpec::Serialize(::protozero::Message* msg) const {
+  // Field 1: counter_id
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, counter_id_, msg);
+  }
+
+  // Field 2: name
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
+  }
+
+  // Field 3: description
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeString(3, description_, msg);
+  }
+
+  // Field 5: int_peak_value
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, int_peak_value_, msg);
+  }
+
+  // Field 6: double_peak_value
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeFixed(6, double_peak_value_, msg);
+  }
+
+  // Field 7: numerator_units
+  for (auto& it : numerator_units_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, it, msg);
+  }
+
+  // Field 8: denominator_units
+  for (auto& it : denominator_units_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, it, msg);
+  }
+
+  // Field 9: select_by_default
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, select_by_default_, msg);
+  }
+
+  // Field 10: groups
+  for (auto& it : groups_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(10, it, msg);
+  }
+
+  // Field 11: value_direction
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(11, value_direction_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/interceptor_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
 // DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_game_intervention_list_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/interceptor_descriptor.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-AndroidGameInterventionListConfig::AndroidGameInterventionListConfig() = default;
-AndroidGameInterventionListConfig::~AndroidGameInterventionListConfig() = default;
-AndroidGameInterventionListConfig::AndroidGameInterventionListConfig(const AndroidGameInterventionListConfig&) = default;
-AndroidGameInterventionListConfig& AndroidGameInterventionListConfig::operator=(const AndroidGameInterventionListConfig&) = default;
-AndroidGameInterventionListConfig::AndroidGameInterventionListConfig(AndroidGameInterventionListConfig&&) noexcept = default;
-AndroidGameInterventionListConfig& AndroidGameInterventionListConfig::operator=(AndroidGameInterventionListConfig&&) = default;
+InterceptorDescriptor::InterceptorDescriptor() = default;
+InterceptorDescriptor::~InterceptorDescriptor() = default;
+InterceptorDescriptor::InterceptorDescriptor(const InterceptorDescriptor&) = default;
+InterceptorDescriptor& InterceptorDescriptor::operator=(const InterceptorDescriptor&) = default;
+InterceptorDescriptor::InterceptorDescriptor(InterceptorDescriptor&&) noexcept = default;
+InterceptorDescriptor& InterceptorDescriptor::operator=(InterceptorDescriptor&&) = default;

-bool AndroidGameInterventionListConfig::operator==(const AndroidGameInterventionListConfig& other) const {
+bool InterceptorDescriptor::operator==(const InterceptorDescriptor& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(package_name_filter_, other.package_name_filter_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
 }

-bool AndroidGameInterventionListConfig::ParseFromArray(const void* raw, size_t size) {
-  package_name_filter_.clear();
+bool InterceptorDescriptor::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -21337,9 +21228,8 @@ bool AndroidGameInterventionListConfig::ParseFromArray(const void* raw, size_t s
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* package_name_filter */:
-        package_name_filter_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &package_name_filter_.back());
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -21349,22 +21239,22 @@ bool AndroidGameInterventionListConfig::ParseFromArray(const void* raw, size_t s
   return !packed_error && !dec.bytes_left();
 }

-std::string AndroidGameInterventionListConfig::SerializeAsString() const {
+std::string InterceptorDescriptor::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> AndroidGameInterventionListConfig::SerializeAsArray() const {
+std::vector<uint8_t> InterceptorDescriptor::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void AndroidGameInterventionListConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: package_name_filter
-  for (auto& it : package_name_filter_) {
-    ::protozero::internal::gen_helpers::SerializeString(1, it, msg);
+void InterceptorDescriptor::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -21376,7 +21266,7 @@ void AndroidGameInterventionListConfig::Serialize(::protozero::Message* msg) con
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_input_event_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/common/observable_events.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -21387,33 +21277,31 @@ void AndroidGameInterventionListConfig::Serialize(::protozero::Message* msg) con
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_input_event_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/observable_events.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-AndroidInputEventConfig::AndroidInputEventConfig() = default;
-AndroidInputEventConfig::~AndroidInputEventConfig() = default;
-AndroidInputEventConfig::AndroidInputEventConfig(const AndroidInputEventConfig&) = default;
-AndroidInputEventConfig& AndroidInputEventConfig::operator=(const AndroidInputEventConfig&) = default;
-AndroidInputEventConfig::AndroidInputEventConfig(AndroidInputEventConfig&&) noexcept = default;
-AndroidInputEventConfig& AndroidInputEventConfig::operator=(AndroidInputEventConfig&&) = default;
+ObservableEvents::ObservableEvents() = default;
+ObservableEvents::~ObservableEvents() = default;
+ObservableEvents::ObservableEvents(const ObservableEvents&) = default;
+ObservableEvents& ObservableEvents::operator=(const ObservableEvents&) = default;
+ObservableEvents::ObservableEvents(ObservableEvents&&) noexcept = default;
+ObservableEvents& ObservableEvents::operator=(ObservableEvents&&) = default;

-bool AndroidInputEventConfig::operator==(const AndroidInputEventConfig& other) const {
+bool ObservableEvents::operator==(const ObservableEvents& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(mode_, other.mode_)
-   && ::protozero::internal::gen_helpers::EqualsField(rules_, other.rules_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_dispatcher_input_events_, other.trace_dispatcher_input_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_dispatcher_window_dispatch_, other.trace_dispatcher_window_dispatch_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_evdev_events_, other.trace_evdev_events_);
+   && ::protozero::internal::gen_helpers::EqualsField(instance_state_changes_, other.instance_state_changes_)
+   && ::protozero::internal::gen_helpers::EqualsField(all_data_sources_started_, other.all_data_sources_started_)
+   && ::protozero::internal::gen_helpers::EqualsField(clone_trigger_hit_, other.clone_trigger_hit_);
 }

-int AndroidInputEventConfig::rules_size() const { return static_cast<int>(rules_.size()); }
-void AndroidInputEventConfig::clear_rules() { rules_.clear(); }
-AndroidInputEventConfig_TraceRule* AndroidInputEventConfig::add_rules() { rules_.emplace_back(); return &rules_.back(); }
-bool AndroidInputEventConfig::ParseFromArray(const void* raw, size_t size) {
-  rules_.clear();
+int ObservableEvents::instance_state_changes_size() const { return static_cast<int>(instance_state_changes_.size()); }
+void ObservableEvents::clear_instance_state_changes() { instance_state_changes_.clear(); }
+ObservableEvents_DataSourceInstanceStateChange* ObservableEvents::add_instance_state_changes() { instance_state_changes_.emplace_back(); return &instance_state_changes_.back(); }
+bool ObservableEvents::ParseFromArray(const void* raw, size_t size) {
+  instance_state_changes_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -21423,21 +21311,15 @@ bool AndroidInputEventConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* mode */:
-        field.get(&mode_);
-        break;
-      case 2 /* rules */:
-        rules_.emplace_back();
-        rules_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 3 /* trace_dispatcher_input_events */:
-        field.get(&trace_dispatcher_input_events_);
+      case 1 /* instance_state_changes */:
+        instance_state_changes_.emplace_back();
+        instance_state_changes_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 4 /* trace_dispatcher_window_dispatch */:
-        field.get(&trace_dispatcher_window_dispatch_);
+      case 2 /* all_data_sources_started */:
+        field.get(&all_data_sources_started_);
         break;
-      case 5 /* trace_evdev_events */:
-        field.get(&trace_evdev_events_);
+      case 3 /* clone_trigger_hit */:
+        (*clone_trigger_hit_).ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -21447,67 +21329,56 @@ bool AndroidInputEventConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string AndroidInputEventConfig::SerializeAsString() const {
+std::string ObservableEvents::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> AndroidInputEventConfig::SerializeAsArray() const {
+std::vector<uint8_t> ObservableEvents::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void AndroidInputEventConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: mode
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, mode_, msg);
+void ObservableEvents::Serialize(::protozero::Message* msg) const {
+  // Field 1: instance_state_changes
+  for (auto& it : instance_state_changes_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 2: rules
-  for (auto& it : rules_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+  // Field 2: all_data_sources_started
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, all_data_sources_started_, msg);
   }

-  // Field 3: trace_dispatcher_input_events
+  // Field 3: clone_trigger_hit
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, trace_dispatcher_input_events_, msg);
-  }
-
-  // Field 4: trace_dispatcher_window_dispatch
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, trace_dispatcher_window_dispatch_, msg);
-  }
-
-  // Field 5: trace_evdev_events
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, trace_evdev_events_, msg);
+    (*clone_trigger_hit_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-AndroidInputEventConfig_TraceRule::AndroidInputEventConfig_TraceRule() = default;
-AndroidInputEventConfig_TraceRule::~AndroidInputEventConfig_TraceRule() = default;
-AndroidInputEventConfig_TraceRule::AndroidInputEventConfig_TraceRule(const AndroidInputEventConfig_TraceRule&) = default;
-AndroidInputEventConfig_TraceRule& AndroidInputEventConfig_TraceRule::operator=(const AndroidInputEventConfig_TraceRule&) = default;
-AndroidInputEventConfig_TraceRule::AndroidInputEventConfig_TraceRule(AndroidInputEventConfig_TraceRule&&) noexcept = default;
-AndroidInputEventConfig_TraceRule& AndroidInputEventConfig_TraceRule::operator=(AndroidInputEventConfig_TraceRule&&) = default;
+ObservableEvents_CloneTriggerHit::ObservableEvents_CloneTriggerHit() = default;
+ObservableEvents_CloneTriggerHit::~ObservableEvents_CloneTriggerHit() = default;
+ObservableEvents_CloneTriggerHit::ObservableEvents_CloneTriggerHit(const ObservableEvents_CloneTriggerHit&) = default;
+ObservableEvents_CloneTriggerHit& ObservableEvents_CloneTriggerHit::operator=(const ObservableEvents_CloneTriggerHit&) = default;
+ObservableEvents_CloneTriggerHit::ObservableEvents_CloneTriggerHit(ObservableEvents_CloneTriggerHit&&) noexcept = default;
+ObservableEvents_CloneTriggerHit& ObservableEvents_CloneTriggerHit::operator=(ObservableEvents_CloneTriggerHit&&) = default;

-bool AndroidInputEventConfig_TraceRule::operator==(const AndroidInputEventConfig_TraceRule& other) const {
+bool ObservableEvents_CloneTriggerHit::operator==(const ObservableEvents_CloneTriggerHit& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_level_, other.trace_level_)
-   && ::protozero::internal::gen_helpers::EqualsField(match_all_packages_, other.match_all_packages_)
-   && ::protozero::internal::gen_helpers::EqualsField(match_any_packages_, other.match_any_packages_)
-   && ::protozero::internal::gen_helpers::EqualsField(match_secure_, other.match_secure_)
-   && ::protozero::internal::gen_helpers::EqualsField(match_ime_connection_active_, other.match_ime_connection_active_);
+   && ::protozero::internal::gen_helpers::EqualsField(tracing_session_id_, other.tracing_session_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(trigger_name_, other.trigger_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(producer_name_, other.producer_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(producer_uid_, other.producer_uid_)
+   && ::protozero::internal::gen_helpers::EqualsField(boot_time_ns_, other.boot_time_ns_)
+   && ::protozero::internal::gen_helpers::EqualsField(trigger_delay_ms_, other.trigger_delay_ms_);
 }

-bool AndroidInputEventConfig_TraceRule::ParseFromArray(const void* raw, size_t size) {
-  match_all_packages_.clear();
-  match_any_packages_.clear();
+bool ObservableEvents_CloneTriggerHit::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -21517,22 +21388,23 @@ bool AndroidInputEventConfig_TraceRule::ParseFromArray(const void* raw, size_t s
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* trace_level */:
-        field.get(&trace_level_);
+      case 1 /* tracing_session_id */:
+        field.get(&tracing_session_id_);
         break;
-      case 2 /* match_all_packages */:
-        match_all_packages_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &match_all_packages_.back());
+      case 2 /* trigger_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &trigger_name_);
         break;
-      case 3 /* match_any_packages */:
-        match_any_packages_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &match_any_packages_.back());
+      case 3 /* producer_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &producer_name_);
         break;
-      case 4 /* match_secure */:
-        field.get(&match_secure_);
+      case 4 /* producer_uid */:
+        field.get(&producer_uid_);
         break;
-      case 5 /* match_ime_connection_active */:
-        field.get(&match_ime_connection_active_);
+      case 5 /* boot_time_ns */:
+        field.get(&boot_time_ns_);
+        break;
+      case 6 /* trigger_delay_ms */:
+        field.get(&trigger_delay_ms_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -21542,89 +21414,68 @@ bool AndroidInputEventConfig_TraceRule::ParseFromArray(const void* raw, size_t s
   return !packed_error && !dec.bytes_left();
 }

-std::string AndroidInputEventConfig_TraceRule::SerializeAsString() const {
+std::string ObservableEvents_CloneTriggerHit::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> AndroidInputEventConfig_TraceRule::SerializeAsArray() const {
+std::vector<uint8_t> ObservableEvents_CloneTriggerHit::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void AndroidInputEventConfig_TraceRule::Serialize(::protozero::Message* msg) const {
-  // Field 1: trace_level
+void ObservableEvents_CloneTriggerHit::Serialize(::protozero::Message* msg) const {
+  // Field 1: tracing_session_id
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, trace_level_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, tracing_session_id_, msg);
   }

-  // Field 2: match_all_packages
-  for (auto& it : match_all_packages_) {
-    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
+  // Field 2: trigger_name
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, trigger_name_, msg);
   }

-  // Field 3: match_any_packages
-  for (auto& it : match_any_packages_) {
-    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
+  // Field 3: producer_name
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeString(3, producer_name_, msg);
   }

-  // Field 4: match_secure
+  // Field 4: producer_uid
   if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, match_secure_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, producer_uid_, msg);
   }

-  // Field 5: match_ime_connection_active
+  // Field 5: boot_time_ns
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, match_ime_connection_active_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, boot_time_ns_, msg);
+  }
+
+  // Field 6: trigger_delay_ms
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, trigger_delay_ms_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_log_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_log_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/android_log_constants.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-AndroidLogConfig::AndroidLogConfig() = default;
-AndroidLogConfig::~AndroidLogConfig() = default;
-AndroidLogConfig::AndroidLogConfig(const AndroidLogConfig&) = default;
-AndroidLogConfig& AndroidLogConfig::operator=(const AndroidLogConfig&) = default;
-AndroidLogConfig::AndroidLogConfig(AndroidLogConfig&&) noexcept = default;
-AndroidLogConfig& AndroidLogConfig::operator=(AndroidLogConfig&&) = default;
+ObservableEvents_DataSourceInstanceStateChange::ObservableEvents_DataSourceInstanceStateChange() = default;
+ObservableEvents_DataSourceInstanceStateChange::~ObservableEvents_DataSourceInstanceStateChange() = default;
+ObservableEvents_DataSourceInstanceStateChange::ObservableEvents_DataSourceInstanceStateChange(const ObservableEvents_DataSourceInstanceStateChange&) = default;
+ObservableEvents_DataSourceInstanceStateChange& ObservableEvents_DataSourceInstanceStateChange::operator=(const ObservableEvents_DataSourceInstanceStateChange&) = default;
+ObservableEvents_DataSourceInstanceStateChange::ObservableEvents_DataSourceInstanceStateChange(ObservableEvents_DataSourceInstanceStateChange&&) noexcept = default;
+ObservableEvents_DataSourceInstanceStateChange& ObservableEvents_DataSourceInstanceStateChange::operator=(ObservableEvents_DataSourceInstanceStateChange&&) = default;

-bool AndroidLogConfig::operator==(const AndroidLogConfig& other) const {
+bool ObservableEvents_DataSourceInstanceStateChange::operator==(const ObservableEvents_DataSourceInstanceStateChange& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(log_ids_, other.log_ids_)
-   && ::protozero::internal::gen_helpers::EqualsField(min_prio_, other.min_prio_)
-   && ::protozero::internal::gen_helpers::EqualsField(filter_tags_, other.filter_tags_)
-   && ::protozero::internal::gen_helpers::EqualsField(preserve_log_buffer_, other.preserve_log_buffer_);
+   && ::protozero::internal::gen_helpers::EqualsField(producer_name_, other.producer_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(data_source_name_, other.data_source_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(state_, other.state_);
 }

-bool AndroidLogConfig::ParseFromArray(const void* raw, size_t size) {
-  log_ids_.clear();
-  filter_tags_.clear();
+bool ObservableEvents_DataSourceInstanceStateChange::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -21634,19 +21485,14 @@ bool AndroidLogConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* log_ids */:
-        log_ids_.emplace_back();
-        field.get(&log_ids_.back());
-        break;
-      case 3 /* min_prio */:
-        field.get(&min_prio_);
+      case 1 /* producer_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &producer_name_);
         break;
-      case 4 /* filter_tags */:
-        filter_tags_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &filter_tags_.back());
+      case 2 /* data_source_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &data_source_name_);
         break;
-      case 5 /* preserve_log_buffer */:
-        field.get(&preserve_log_buffer_);
+      case 3 /* state */:
+        field.get(&state_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -21656,37 +21502,32 @@ bool AndroidLogConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string AndroidLogConfig::SerializeAsString() const {
+std::string ObservableEvents_DataSourceInstanceStateChange::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> AndroidLogConfig::SerializeAsArray() const {
+std::vector<uint8_t> ObservableEvents_DataSourceInstanceStateChange::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void AndroidLogConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: log_ids
-  for (auto& it : log_ids_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
-  }
-
-  // Field 3: min_prio
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, min_prio_, msg);
+void ObservableEvents_DataSourceInstanceStateChange::Serialize(::protozero::Message* msg) const {
+  // Field 1: producer_name
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, producer_name_, msg);
   }

-  // Field 4: filter_tags
-  for (auto& it : filter_tags_) {
-    ::protozero::internal::gen_helpers::SerializeString(4, it, msg);
+  // Field 2: data_source_name
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, data_source_name_, msg);
   }

-  // Field 5: preserve_log_buffer
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, preserve_log_buffer_, msg);
+  // Field 3: state
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, state_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -21698,7 +21539,7 @@ void AndroidLogConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_polled_state_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/common/perf_events.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -21709,25 +21550,30 @@ void AndroidLogConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_polled_state_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/perf_events.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-AndroidPolledStateConfig::AndroidPolledStateConfig() = default;
-AndroidPolledStateConfig::~AndroidPolledStateConfig() = default;
-AndroidPolledStateConfig::AndroidPolledStateConfig(const AndroidPolledStateConfig&) = default;
-AndroidPolledStateConfig& AndroidPolledStateConfig::operator=(const AndroidPolledStateConfig&) = default;
-AndroidPolledStateConfig::AndroidPolledStateConfig(AndroidPolledStateConfig&&) noexcept = default;
-AndroidPolledStateConfig& AndroidPolledStateConfig::operator=(AndroidPolledStateConfig&&) = default;
+FollowerEvent::FollowerEvent() = default;
+FollowerEvent::~FollowerEvent() = default;
+FollowerEvent::FollowerEvent(const FollowerEvent&) = default;
+FollowerEvent& FollowerEvent::operator=(const FollowerEvent&) = default;
+FollowerEvent::FollowerEvent(FollowerEvent&&) noexcept = default;
+FollowerEvent& FollowerEvent::operator=(FollowerEvent&&) = default;

-bool AndroidPolledStateConfig::operator==(const AndroidPolledStateConfig& other) const {
+bool FollowerEvent::operator==(const FollowerEvent& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(poll_ms_, other.poll_ms_);
+   && ::protozero::internal::gen_helpers::EqualsField(counter_, other.counter_)
+   && ::protozero::internal::gen_helpers::EqualsField(tracepoint_, other.tracepoint_)
+   && ::protozero::internal::gen_helpers::EqualsField(raw_event_, other.raw_event_)
+   && ::protozero::internal::gen_helpers::EqualsField(modifiers_, other.modifiers_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
 }

-bool AndroidPolledStateConfig::ParseFromArray(const void* raw, size_t size) {
+bool FollowerEvent::ParseFromArray(const void* raw, size_t size) {
+  modifiers_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -21737,8 +21583,21 @@ bool AndroidPolledStateConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* poll_ms */:
-        field.get(&poll_ms_);
+      case 1 /* counter */:
+        field.get(&counter_);
+        break;
+      case 2 /* tracepoint */:
+        (*tracepoint_).ParseFromArray(field.data(), field.size());
+        break;
+      case 3 /* raw_event */:
+        (*raw_event_).ParseFromArray(field.data(), field.size());
+        break;
+      case 5 /* modifiers */:
+        modifiers_.emplace_back();
+        field.get(&modifiers_.back());
+        break;
+      case 4 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -21748,66 +21607,65 @@ bool AndroidPolledStateConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string AndroidPolledStateConfig::SerializeAsString() const {
+std::string FollowerEvent::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> AndroidPolledStateConfig::SerializeAsArray() const {
+std::vector<uint8_t> FollowerEvent::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void AndroidPolledStateConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: poll_ms
+void FollowerEvent::Serialize(::protozero::Message* msg) const {
+  // Field 1: counter
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, poll_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, counter_, msg);
+  }
+
+  // Field 2: tracepoint
+  if (_has_field_[2]) {
+    (*tracepoint_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+  }
+
+  // Field 3: raw_event
+  if (_has_field_[3]) {
+    (*raw_event_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+  }
+
+  // Field 5: modifiers
+  for (auto& it : modifiers_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, it, msg);
+  }
+
+  // Field 4: name
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeString(4, name_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_sdk_sysprop_guard_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_sdk_sysprop_guard_config.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-AndroidSdkSyspropGuardConfig::AndroidSdkSyspropGuardConfig() = default;
-AndroidSdkSyspropGuardConfig::~AndroidSdkSyspropGuardConfig() = default;
-AndroidSdkSyspropGuardConfig::AndroidSdkSyspropGuardConfig(const AndroidSdkSyspropGuardConfig&) = default;
-AndroidSdkSyspropGuardConfig& AndroidSdkSyspropGuardConfig::operator=(const AndroidSdkSyspropGuardConfig&) = default;
-AndroidSdkSyspropGuardConfig::AndroidSdkSyspropGuardConfig(AndroidSdkSyspropGuardConfig&&) noexcept = default;
-AndroidSdkSyspropGuardConfig& AndroidSdkSyspropGuardConfig::operator=(AndroidSdkSyspropGuardConfig&&) = default;
+PerfEvents_RawEvent::PerfEvents_RawEvent() = default;
+PerfEvents_RawEvent::~PerfEvents_RawEvent() = default;
+PerfEvents_RawEvent::PerfEvents_RawEvent(const PerfEvents_RawEvent&) = default;
+PerfEvents_RawEvent& PerfEvents_RawEvent::operator=(const PerfEvents_RawEvent&) = default;
+PerfEvents_RawEvent::PerfEvents_RawEvent(PerfEvents_RawEvent&&) noexcept = default;
+PerfEvents_RawEvent& PerfEvents_RawEvent::operator=(PerfEvents_RawEvent&&) = default;

-bool AndroidSdkSyspropGuardConfig::operator==(const AndroidSdkSyspropGuardConfig& other) const {
+bool PerfEvents_RawEvent::operator==(const PerfEvents_RawEvent& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(surfaceflinger_skia_track_events_, other.surfaceflinger_skia_track_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(hwui_skia_track_events_, other.hwui_skia_track_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(hwui_package_name_filter_, other.hwui_package_name_filter_);
+   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
+   && ::protozero::internal::gen_helpers::EqualsField(config_, other.config_)
+   && ::protozero::internal::gen_helpers::EqualsField(config1_, other.config1_)
+   && ::protozero::internal::gen_helpers::EqualsField(config2_, other.config2_)
+   && ::protozero::internal::gen_helpers::EqualsField(pmu_name_, other.pmu_name_);
 }

-bool AndroidSdkSyspropGuardConfig::ParseFromArray(const void* raw, size_t size) {
-  hwui_package_name_filter_.clear();
+bool PerfEvents_RawEvent::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -21817,15 +21675,20 @@ bool AndroidSdkSyspropGuardConfig::ParseFromArray(const void* raw, size_t size)
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* surfaceflinger_skia_track_events */:
-        field.get(&surfaceflinger_skia_track_events_);
+      case 1 /* type */:
+        field.get(&type_);
         break;
-      case 2 /* hwui_skia_track_events */:
-        field.get(&hwui_skia_track_events_);
+      case 2 /* config */:
+        field.get(&config_);
         break;
-      case 3 /* hwui_package_name_filter */:
-        hwui_package_name_filter_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &hwui_package_name_filter_.back());
+      case 3 /* config1 */:
+        field.get(&config1_);
+        break;
+      case 4 /* config2 */:
+        field.get(&config2_);
+        break;
+      case 5 /* pmu_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &pmu_name_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -21835,75 +21698,62 @@ bool AndroidSdkSyspropGuardConfig::ParseFromArray(const void* raw, size_t size)
   return !packed_error && !dec.bytes_left();
 }

-std::string AndroidSdkSyspropGuardConfig::SerializeAsString() const {
+std::string PerfEvents_RawEvent::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> AndroidSdkSyspropGuardConfig::SerializeAsArray() const {
+std::vector<uint8_t> PerfEvents_RawEvent::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void AndroidSdkSyspropGuardConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: surfaceflinger_skia_track_events
+void PerfEvents_RawEvent::Serialize(::protozero::Message* msg) const {
+  // Field 1: type
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, surfaceflinger_skia_track_events_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, type_, msg);
   }

-  // Field 2: hwui_skia_track_events
+  // Field 2: config
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, hwui_skia_track_events_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, config_, msg);
   }

-  // Field 3: hwui_package_name_filter
-  for (auto& it : hwui_package_name_filter_) {
-    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
+  // Field 3: config1
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, config1_, msg);
+  }
+
+  // Field 4: config2
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, config2_, msg);
+  }
+
+  // Field 5: pmu_name
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeString(5, pmu_name_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_system_property_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_system_property_config.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-AndroidSystemPropertyConfig::AndroidSystemPropertyConfig() = default;
-AndroidSystemPropertyConfig::~AndroidSystemPropertyConfig() = default;
-AndroidSystemPropertyConfig::AndroidSystemPropertyConfig(const AndroidSystemPropertyConfig&) = default;
-AndroidSystemPropertyConfig& AndroidSystemPropertyConfig::operator=(const AndroidSystemPropertyConfig&) = default;
-AndroidSystemPropertyConfig::AndroidSystemPropertyConfig(AndroidSystemPropertyConfig&&) noexcept = default;
-AndroidSystemPropertyConfig& AndroidSystemPropertyConfig::operator=(AndroidSystemPropertyConfig&&) = default;
+PerfEvents_Tracepoint::PerfEvents_Tracepoint() = default;
+PerfEvents_Tracepoint::~PerfEvents_Tracepoint() = default;
+PerfEvents_Tracepoint::PerfEvents_Tracepoint(const PerfEvents_Tracepoint&) = default;
+PerfEvents_Tracepoint& PerfEvents_Tracepoint::operator=(const PerfEvents_Tracepoint&) = default;
+PerfEvents_Tracepoint::PerfEvents_Tracepoint(PerfEvents_Tracepoint&&) noexcept = default;
+PerfEvents_Tracepoint& PerfEvents_Tracepoint::operator=(PerfEvents_Tracepoint&&) = default;

-bool AndroidSystemPropertyConfig::operator==(const AndroidSystemPropertyConfig& other) const {
+bool PerfEvents_Tracepoint::operator==(const PerfEvents_Tracepoint& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(poll_ms_, other.poll_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(property_name_, other.property_name_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(filter_, other.filter_);
 }

-bool AndroidSystemPropertyConfig::ParseFromArray(const void* raw, size_t size) {
-  property_name_.clear();
+bool PerfEvents_Tracepoint::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -21913,12 +21763,11 @@ bool AndroidSystemPropertyConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* poll_ms */:
-        field.get(&poll_ms_);
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 2 /* property_name */:
-        property_name_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &property_name_.back());
+      case 2 /* filter */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &filter_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -21928,70 +21777,45 @@ bool AndroidSystemPropertyConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string AndroidSystemPropertyConfig::SerializeAsString() const {
+std::string PerfEvents_Tracepoint::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> AndroidSystemPropertyConfig::SerializeAsArray() const {
+std::vector<uint8_t> PerfEvents_Tracepoint::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void AndroidSystemPropertyConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: poll_ms
+void PerfEvents_Tracepoint::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, poll_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  // Field 2: property_name
-  for (auto& it : property_name_) {
-    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
+  // Field 2: filter
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, filter_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/app_wakelock_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/app_wakelock_config.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-AppWakelocksConfig::AppWakelocksConfig() = default;
-AppWakelocksConfig::~AppWakelocksConfig() = default;
-AppWakelocksConfig::AppWakelocksConfig(const AppWakelocksConfig&) = default;
-AppWakelocksConfig& AppWakelocksConfig::operator=(const AppWakelocksConfig&) = default;
-AppWakelocksConfig::AppWakelocksConfig(AppWakelocksConfig&&) noexcept = default;
-AppWakelocksConfig& AppWakelocksConfig::operator=(AppWakelocksConfig&&) = default;
+PerfEvents::PerfEvents() = default;
+PerfEvents::~PerfEvents() = default;
+PerfEvents::PerfEvents(const PerfEvents&) = default;
+PerfEvents& PerfEvents::operator=(const PerfEvents&) = default;
+PerfEvents::PerfEvents(PerfEvents&&) noexcept = default;
+PerfEvents& PerfEvents::operator=(PerfEvents&&) = default;

-bool AppWakelocksConfig::operator==(const AppWakelocksConfig& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(write_delay_ms_, other.write_delay_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(filter_duration_below_ms_, other.filter_duration_below_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(drop_owner_pid_, other.drop_owner_pid_);
+bool PerfEvents::operator==(const PerfEvents& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
 }

-bool AppWakelocksConfig::ParseFromArray(const void* raw, size_t size) {
+bool PerfEvents::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -22001,15 +21825,6 @@ bool AppWakelocksConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* write_delay_ms */:
-        field.get(&write_delay_ms_);
-        break;
-      case 2 /* filter_duration_below_ms */:
-        field.get(&filter_duration_below_ms_);
-        break;
-      case 3 /* drop_owner_pid */:
-        field.get(&drop_owner_pid_);
-        break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
         break;
@@ -22018,73 +21833,45 @@ bool AppWakelocksConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string AppWakelocksConfig::SerializeAsString() const {
+std::string PerfEvents::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> AppWakelocksConfig::SerializeAsArray() const {
+std::vector<uint8_t> PerfEvents::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void AppWakelocksConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: write_delay_ms
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, write_delay_ms_, msg);
-  }
-
-  // Field 2: filter_duration_below_ms
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, filter_duration_below_ms_, msg);
-  }
-
-  // Field 3: drop_owner_pid
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, drop_owner_pid_, msg);
-  }
-
+void PerfEvents::Serialize(::protozero::Message* msg) const {
   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/cpu_per_uid_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/cpu_per_uid_config.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-CpuPerUidConfig::CpuPerUidConfig() = default;
-CpuPerUidConfig::~CpuPerUidConfig() = default;
-CpuPerUidConfig::CpuPerUidConfig(const CpuPerUidConfig&) = default;
-CpuPerUidConfig& CpuPerUidConfig::operator=(const CpuPerUidConfig&) = default;
-CpuPerUidConfig::CpuPerUidConfig(CpuPerUidConfig&&) noexcept = default;
-CpuPerUidConfig& CpuPerUidConfig::operator=(CpuPerUidConfig&&) = default;
+PerfEvents_Timebase::PerfEvents_Timebase() = default;
+PerfEvents_Timebase::~PerfEvents_Timebase() = default;
+PerfEvents_Timebase::PerfEvents_Timebase(const PerfEvents_Timebase&) = default;
+PerfEvents_Timebase& PerfEvents_Timebase::operator=(const PerfEvents_Timebase&) = default;
+PerfEvents_Timebase::PerfEvents_Timebase(PerfEvents_Timebase&&) noexcept = default;
+PerfEvents_Timebase& PerfEvents_Timebase::operator=(PerfEvents_Timebase&&) = default;

-bool CpuPerUidConfig::operator==(const CpuPerUidConfig& other) const {
+bool PerfEvents_Timebase::operator==(const PerfEvents_Timebase& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(poll_ms_, other.poll_ms_);
+   && ::protozero::internal::gen_helpers::EqualsField(frequency_, other.frequency_)
+   && ::protozero::internal::gen_helpers::EqualsField(period_, other.period_)
+   && ::protozero::internal::gen_helpers::EqualsField(poll_period_ms_, other.poll_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(counter_, other.counter_)
+   && ::protozero::internal::gen_helpers::EqualsField(tracepoint_, other.tracepoint_)
+   && ::protozero::internal::gen_helpers::EqualsField(raw_event_, other.raw_event_)
+   && ::protozero::internal::gen_helpers::EqualsField(modifiers_, other.modifiers_)
+   && ::protozero::internal::gen_helpers::EqualsField(timestamp_clock_, other.timestamp_clock_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
 }

-bool CpuPerUidConfig::ParseFromArray(const void* raw, size_t size) {
+bool PerfEvents_Timebase::ParseFromArray(const void* raw, size_t size) {
+  modifiers_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -22094,8 +21881,33 @@ bool CpuPerUidConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* poll_ms */:
-        field.get(&poll_ms_);
+      case 2 /* frequency */:
+        field.get(&frequency_);
+        break;
+      case 1 /* period */:
+        field.get(&period_);
+        break;
+      case 6 /* poll_period_ms */:
+        field.get(&poll_period_ms_);
+        break;
+      case 4 /* counter */:
+        field.get(&counter_);
+        break;
+      case 3 /* tracepoint */:
+        (*tracepoint_).ParseFromArray(field.data(), field.size());
+        break;
+      case 5 /* raw_event */:
+        (*raw_event_).ParseFromArray(field.data(), field.size());
+        break;
+      case 12 /* modifiers */:
+        modifiers_.emplace_back();
+        field.get(&modifiers_.back());
+        break;
+      case 11 /* timestamp_clock */:
+        field.get(&timestamp_clock_);
+        break;
+      case 10 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -22105,22 +21917,62 @@ bool CpuPerUidConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string CpuPerUidConfig::SerializeAsString() const {
+std::string PerfEvents_Timebase::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> CpuPerUidConfig::SerializeAsArray() const {
+std::vector<uint8_t> PerfEvents_Timebase::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void CpuPerUidConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: poll_ms
+void PerfEvents_Timebase::Serialize(::protozero::Message* msg) const {
+  // Field 2: frequency
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, frequency_, msg);
+  }
+
+  // Field 1: period
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, poll_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, period_, msg);
+  }
+
+  // Field 6: poll_period_ms
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, poll_period_ms_, msg);
+  }
+
+  // Field 4: counter
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, counter_, msg);
+  }
+
+  // Field 3: tracepoint
+  if (_has_field_[3]) {
+    (*tracepoint_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+  }
+
+  // Field 5: raw_event
+  if (_has_field_[5]) {
+    (*raw_event_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
+  }
+
+  // Field 12: modifiers
+  for (auto& it : modifiers_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(12, it, msg);
+  }
+
+  // Field 11: timestamp_clock
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(11, timestamp_clock_, msg);
+  }
+
+  // Field 10: name
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeString(10, name_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -22132,7 +21984,7 @@ void CpuPerUidConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/display_video_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/common/sys_stats_counters.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -22143,109 +21995,18 @@ void CpuPerUidConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/display_video_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/sys_stats_counters.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {
-
-DisplayVideoConfig::DisplayVideoConfig() = default;
-DisplayVideoConfig::~DisplayVideoConfig() = default;
-DisplayVideoConfig::DisplayVideoConfig(const DisplayVideoConfig&) = default;
-DisplayVideoConfig& DisplayVideoConfig::operator=(const DisplayVideoConfig&) = default;
-DisplayVideoConfig::DisplayVideoConfig(DisplayVideoConfig&&) noexcept = default;
-DisplayVideoConfig& DisplayVideoConfig::operator=(DisplayVideoConfig&&) = default;
-
-bool DisplayVideoConfig::operator==(const DisplayVideoConfig& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(scale_, other.scale_)
-   && ::protozero::internal::gen_helpers::EqualsField(format_, other.format_)
-   && ::protozero::internal::gen_helpers::EqualsField(key_frame_interval_secs_, other.key_frame_interval_secs_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_stream_size_bytes_, other.max_stream_size_bytes_)
-   && ::protozero::internal::gen_helpers::EqualsField(bitrate_bps_, other.bitrate_bps_);
-}
-
-bool DisplayVideoConfig::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
-
-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* scale */:
-        field.get(&scale_);
-        break;
-      case 2 /* format */:
-        field.get(&format_);
-        break;
-      case 3 /* key_frame_interval_secs */:
-        field.get(&key_frame_interval_secs_);
-        break;
-      case 4 /* max_stream_size_bytes */:
-        field.get(&max_stream_size_bytes_);
-        break;
-      case 5 /* bitrate_bps */:
-        field.get(&bitrate_bps_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
-}
-
-std::string DisplayVideoConfig::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
-
-std::vector<uint8_t> DisplayVideoConfig::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
-
-void DisplayVideoConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: scale
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(1, scale_, msg);
-  }
-
-  // Field 2: format
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, format_, msg);
-  }
-
-  // Field 3: key_frame_interval_secs
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, key_frame_interval_secs_, msg);
-  }
-
-  // Field 4: max_stream_size_bytes
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, max_stream_size_bytes_, msg);
-  }
-
-  // Field 5: bitrate_bps
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, bitrate_bps_, msg);
-  }
-
-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
-
 }  // namespace perfetto
 }  // namespace protos
 }  // namespace gen
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/inputmethod_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/common/system_info.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -22256,27 +22017,43 @@ void DisplayVideoConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/inputmethod_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/system_info.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-InputMethodConfig::InputMethodConfig() = default;
-InputMethodConfig::~InputMethodConfig() = default;
-InputMethodConfig::InputMethodConfig(const InputMethodConfig&) = default;
-InputMethodConfig& InputMethodConfig::operator=(const InputMethodConfig&) = default;
-InputMethodConfig::InputMethodConfig(InputMethodConfig&&) noexcept = default;
-InputMethodConfig& InputMethodConfig::operator=(InputMethodConfig&&) = default;
+SystemInfo::SystemInfo() = default;
+SystemInfo::~SystemInfo() = default;
+SystemInfo::SystemInfo(const SystemInfo&) = default;
+SystemInfo& SystemInfo::operator=(const SystemInfo&) = default;
+SystemInfo::SystemInfo(SystemInfo&&) noexcept = default;
+SystemInfo& SystemInfo::operator=(SystemInfo&&) = default;

-bool InputMethodConfig::operator==(const InputMethodConfig& other) const {
+bool SystemInfo::operator==(const SystemInfo& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(client_, other.client_)
-   && ::protozero::internal::gen_helpers::EqualsField(service_, other.service_)
-   && ::protozero::internal::gen_helpers::EqualsField(manager_service_, other.manager_service_);
+   && ::protozero::internal::gen_helpers::EqualsField(utsname_, other.utsname_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_build_fingerprint_, other.android_build_fingerprint_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_device_manufacturer_, other.android_device_manufacturer_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_soc_model_, other.android_soc_model_)
+   && ::protozero::internal::gen_helpers::EqualsField(device_tree_compatibles_, other.device_tree_compatibles_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_guest_soc_model_, other.android_guest_soc_model_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_hardware_revision_, other.android_hardware_revision_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_storage_model_, other.android_storage_model_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_ram_model_, other.android_ram_model_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_serial_console_, other.android_serial_console_)
+   && ::protozero::internal::gen_helpers::EqualsField(tracing_service_version_, other.tracing_service_version_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_sdk_version_, other.android_sdk_version_)
+   && ::protozero::internal::gen_helpers::EqualsField(page_size_, other.page_size_)
+   && ::protozero::internal::gen_helpers::EqualsField(num_cpus_, other.num_cpus_)
+   && ::protozero::internal::gen_helpers::EqualsField(timezone_off_mins_, other.timezone_off_mins_)
+   && ::protozero::internal::gen_helpers::EqualsField(hz_, other.hz_)
+   && ::protozero::internal::gen_helpers::EqualsField(system_ram_bytes_, other.system_ram_bytes_)
+   && ::protozero::internal::gen_helpers::EqualsField(machine_name_, other.machine_name_);
 }

-bool InputMethodConfig::ParseFromArray(const void* raw, size_t size) {
+bool SystemInfo::ParseFromArray(const void* raw, size_t size) {
+  device_tree_compatibles_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -22286,14 +22063,60 @@ bool InputMethodConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* client */:
-        field.get(&client_);
+      case 1 /* utsname */:
+        (*utsname_).ParseFromArray(field.data(), field.size());
         break;
-      case 2 /* service */:
-        field.get(&service_);
+      case 2 /* android_build_fingerprint */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_build_fingerprint_);
         break;
-      case 3 /* manager_service */:
-        field.get(&manager_service_);
+      case 14 /* android_device_manufacturer */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_device_manufacturer_);
+        break;
+      case 9 /* android_soc_model */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_soc_model_);
+        break;
+      case 18 /* device_tree_compatibles */:
+        device_tree_compatibles_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &device_tree_compatibles_.back());
+        break;
+      case 13 /* android_guest_soc_model */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_guest_soc_model_);
+        break;
+      case 10 /* android_hardware_revision */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_hardware_revision_);
+        break;
+      case 11 /* android_storage_model */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_storage_model_);
+        break;
+      case 12 /* android_ram_model */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_ram_model_);
+        break;
+      case 15 /* android_serial_console */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_serial_console_);
+        break;
+      case 4 /* tracing_service_version */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &tracing_service_version_);
+        break;
+      case 5 /* android_sdk_version */:
+        field.get(&android_sdk_version_);
+        break;
+      case 6 /* page_size */:
+        field.get(&page_size_);
+        break;
+      case 8 /* num_cpus */:
+        field.get(&num_cpus_);
+        break;
+      case 7 /* timezone_off_mins */:
+        field.get(&timezone_off_mins_);
+        break;
+      case 3 /* hz */:
+        field.get(&hz_);
+        break;
+      case 16 /* system_ram_bytes */:
+        field.get(&system_ram_bytes_);
+        break;
+      case 17 /* machine_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &machine_name_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -22303,155 +22126,129 @@ bool InputMethodConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string InputMethodConfig::SerializeAsString() const {
+std::string SystemInfo::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> InputMethodConfig::SerializeAsArray() const {
+std::vector<uint8_t> SystemInfo::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void InputMethodConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: client
+void SystemInfo::Serialize(::protozero::Message* msg) const {
+  // Field 1: utsname
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, client_, msg);
+    (*utsname_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 2: service
+  // Field 2: android_build_fingerprint
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, service_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(2, android_build_fingerprint_, msg);
   }

-  // Field 3: manager_service
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, manager_service_, msg);
+  // Field 14: android_device_manufacturer
+  if (_has_field_[14]) {
+    ::protozero::internal::gen_helpers::SerializeString(14, android_device_manufacturer_, msg);
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+  // Field 9: android_soc_model
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeString(9, android_soc_model_, msg);
+  }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/kernel_wakelocks_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/kernel_wakelocks_config.gen.h"
+  // Field 18: device_tree_compatibles
+  for (auto& it : device_tree_compatibles_) {
+    ::protozero::internal::gen_helpers::SerializeString(18, it, msg);
+  }

-namespace perfetto {
-namespace protos {
-namespace gen {
+  // Field 13: android_guest_soc_model
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeString(13, android_guest_soc_model_, msg);
+  }

-KernelWakelocksConfig::KernelWakelocksConfig() = default;
-KernelWakelocksConfig::~KernelWakelocksConfig() = default;
-KernelWakelocksConfig::KernelWakelocksConfig(const KernelWakelocksConfig&) = default;
-KernelWakelocksConfig& KernelWakelocksConfig::operator=(const KernelWakelocksConfig&) = default;
-KernelWakelocksConfig::KernelWakelocksConfig(KernelWakelocksConfig&&) noexcept = default;
-KernelWakelocksConfig& KernelWakelocksConfig::operator=(KernelWakelocksConfig&&) = default;
+  // Field 10: android_hardware_revision
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeString(10, android_hardware_revision_, msg);
+  }

-bool KernelWakelocksConfig::operator==(const KernelWakelocksConfig& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(poll_ms_, other.poll_ms_);
-}
+  // Field 11: android_storage_model
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeString(11, android_storage_model_, msg);
+  }

-bool KernelWakelocksConfig::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+  // Field 12: android_ram_model
+  if (_has_field_[12]) {
+    ::protozero::internal::gen_helpers::SerializeString(12, android_ram_model_, msg);
+  }

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* poll_ms */:
-        field.get(&poll_ms_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
+  // Field 15: android_serial_console
+  if (_has_field_[15]) {
+    ::protozero::internal::gen_helpers::SerializeString(15, android_serial_console_, msg);
   }
-  return !packed_error && !dec.bytes_left();
-}

-std::string KernelWakelocksConfig::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+  // Field 4: tracing_service_version
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeString(4, tracing_service_version_, msg);
+  }

-std::vector<uint8_t> KernelWakelocksConfig::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  // Field 5: android_sdk_version
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, android_sdk_version_, msg);
+  }

-void KernelWakelocksConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: poll_ms
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, poll_ms_, msg);
+  // Field 6: page_size
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, page_size_, msg);
+  }
+
+  // Field 8: num_cpus
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, num_cpus_, msg);
+  }
+
+  // Field 7: timezone_off_mins
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, timezone_off_mins_, msg);
+  }
+
+  // Field 3: hz
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, hz_, msg);
+  }
+
+  // Field 16: system_ram_bytes
+  if (_has_field_[16]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(16, system_ram_bytes_, msg);
+  }
+
+  // Field 17: machine_name
+  if (_has_field_[17]) {
+    ::protozero::internal::gen_helpers::SerializeString(17, machine_name_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/network_trace_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/network_trace_config.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-NetworkPacketTraceConfig::NetworkPacketTraceConfig() = default;
-NetworkPacketTraceConfig::~NetworkPacketTraceConfig() = default;
-NetworkPacketTraceConfig::NetworkPacketTraceConfig(const NetworkPacketTraceConfig&) = default;
-NetworkPacketTraceConfig& NetworkPacketTraceConfig::operator=(const NetworkPacketTraceConfig&) = default;
-NetworkPacketTraceConfig::NetworkPacketTraceConfig(NetworkPacketTraceConfig&&) noexcept = default;
-NetworkPacketTraceConfig& NetworkPacketTraceConfig::operator=(NetworkPacketTraceConfig&&) = default;
+Utsname::Utsname() = default;
+Utsname::~Utsname() = default;
+Utsname::Utsname(const Utsname&) = default;
+Utsname& Utsname::operator=(const Utsname&) = default;
+Utsname::Utsname(Utsname&&) noexcept = default;
+Utsname& Utsname::operator=(Utsname&&) = default;

-bool NetworkPacketTraceConfig::operator==(const NetworkPacketTraceConfig& other) const {
+bool Utsname::operator==(const Utsname& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(poll_ms_, other.poll_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(aggregation_threshold_, other.aggregation_threshold_)
-   && ::protozero::internal::gen_helpers::EqualsField(intern_limit_, other.intern_limit_)
-   && ::protozero::internal::gen_helpers::EqualsField(drop_local_port_, other.drop_local_port_)
-   && ::protozero::internal::gen_helpers::EqualsField(drop_remote_port_, other.drop_remote_port_)
-   && ::protozero::internal::gen_helpers::EqualsField(drop_tcp_flags_, other.drop_tcp_flags_);
+   && ::protozero::internal::gen_helpers::EqualsField(sysname_, other.sysname_)
+   && ::protozero::internal::gen_helpers::EqualsField(version_, other.version_)
+   && ::protozero::internal::gen_helpers::EqualsField(release_, other.release_)
+   && ::protozero::internal::gen_helpers::EqualsField(machine_, other.machine_);
 }

-bool NetworkPacketTraceConfig::ParseFromArray(const void* raw, size_t size) {
+bool Utsname::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -22461,23 +22258,17 @@ bool NetworkPacketTraceConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* poll_ms */:
-        field.get(&poll_ms_);
-        break;
-      case 2 /* aggregation_threshold */:
-        field.get(&aggregation_threshold_);
-        break;
-      case 3 /* intern_limit */:
-        field.get(&intern_limit_);
+      case 1 /* sysname */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &sysname_);
         break;
-      case 4 /* drop_local_port */:
-        field.get(&drop_local_port_);
+      case 2 /* version */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &version_);
         break;
-      case 5 /* drop_remote_port */:
-        field.get(&drop_remote_port_);
+      case 3 /* release */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &release_);
         break;
-      case 6 /* drop_tcp_flags */:
-        field.get(&drop_tcp_flags_);
+      case 4 /* machine */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &machine_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -22487,47 +22278,37 @@ bool NetworkPacketTraceConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string NetworkPacketTraceConfig::SerializeAsString() const {
+std::string Utsname::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> NetworkPacketTraceConfig::SerializeAsArray() const {
+std::vector<uint8_t> Utsname::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void NetworkPacketTraceConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: poll_ms
+void Utsname::Serialize(::protozero::Message* msg) const {
+  // Field 1: sysname
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, poll_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, sysname_, msg);
   }

-  // Field 2: aggregation_threshold
+  // Field 2: version
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, aggregation_threshold_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(2, version_, msg);
   }

-  // Field 3: intern_limit
+  // Field 3: release
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, intern_limit_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(3, release_, msg);
   }

-  // Field 4: drop_local_port
+  // Field 4: machine
   if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, drop_local_port_, msg);
-  }
-
-  // Field 5: drop_remote_port
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, drop_remote_port_, msg);
-  }
-
-  // Field 6: drop_tcp_flags
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(6, drop_tcp_flags_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(4, machine_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -22539,7 +22320,7 @@ void NetworkPacketTraceConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/packages_list_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/common/trace_attributes.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -22550,29 +22331,29 @@ void NetworkPacketTraceConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/packages_list_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/trace_attributes.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-PackagesListConfig::PackagesListConfig() = default;
-PackagesListConfig::~PackagesListConfig() = default;
-PackagesListConfig::PackagesListConfig(const PackagesListConfig&) = default;
-PackagesListConfig& PackagesListConfig::operator=(const PackagesListConfig&) = default;
-PackagesListConfig::PackagesListConfig(PackagesListConfig&&) noexcept = default;
-PackagesListConfig& PackagesListConfig::operator=(PackagesListConfig&&) = default;
+TraceAttributes::TraceAttributes() = default;
+TraceAttributes::~TraceAttributes() = default;
+TraceAttributes::TraceAttributes(const TraceAttributes&) = default;
+TraceAttributes& TraceAttributes::operator=(const TraceAttributes&) = default;
+TraceAttributes::TraceAttributes(TraceAttributes&&) noexcept = default;
+TraceAttributes& TraceAttributes::operator=(TraceAttributes&&) = default;

-bool PackagesListConfig::operator==(const PackagesListConfig& other) const {
+bool TraceAttributes::operator==(const TraceAttributes& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(package_name_filter_, other.package_name_filter_)
-   && ::protozero::internal::gen_helpers::EqualsField(package_name_regex_filter_, other.package_name_regex_filter_)
-   && ::protozero::internal::gen_helpers::EqualsField(only_write_on_cpu_use_every_ms_, other.only_write_on_cpu_use_every_ms_);
+   && ::protozero::internal::gen_helpers::EqualsField(attribute_, other.attribute_);
 }

-bool PackagesListConfig::ParseFromArray(const void* raw, size_t size) {
-  package_name_filter_.clear();
-  package_name_regex_filter_.clear();
+int TraceAttributes::attribute_size() const { return static_cast<int>(attribute_.size()); }
+void TraceAttributes::clear_attribute() { attribute_.clear(); }
+TraceAttributes_Attribute* TraceAttributes::add_attribute() { attribute_.emplace_back(); return &attribute_.back(); }
+bool TraceAttributes::ParseFromArray(const void* raw, size_t size) {
+  attribute_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -22582,16 +22363,9 @@ bool PackagesListConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* package_name_filter */:
-        package_name_filter_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &package_name_filter_.back());
-        break;
-      case 3 /* package_name_regex_filter */:
-        package_name_regex_filter_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &package_name_regex_filter_.back());
-        break;
-      case 2 /* only_write_on_cpu_use_every_ms */:
-        field.get(&only_write_on_cpu_use_every_ms_);
+      case 1 /* attribute */:
+        attribute_.emplace_back();
+        attribute_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -22601,77 +22375,43 @@ bool PackagesListConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string PackagesListConfig::SerializeAsString() const {
+std::string TraceAttributes::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> PackagesListConfig::SerializeAsArray() const {
+std::vector<uint8_t> TraceAttributes::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void PackagesListConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: package_name_filter
-  for (auto& it : package_name_filter_) {
-    ::protozero::internal::gen_helpers::SerializeString(1, it, msg);
-  }
-
-  // Field 3: package_name_regex_filter
-  for (auto& it : package_name_regex_filter_) {
-    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
-  }
-
-  // Field 2: only_write_on_cpu_use_every_ms
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, only_write_on_cpu_use_every_ms_, msg);
+void TraceAttributes::Serialize(::protozero::Message* msg) const {
+  // Field 1: attribute
+  for (auto& it : attribute_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/pixel_modem_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/pixel_modem_config.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-PixelModemConfig::PixelModemConfig() = default;
-PixelModemConfig::~PixelModemConfig() = default;
-PixelModemConfig::PixelModemConfig(const PixelModemConfig&) = default;
-PixelModemConfig& PixelModemConfig::operator=(const PixelModemConfig&) = default;
-PixelModemConfig::PixelModemConfig(PixelModemConfig&&) noexcept = default;
-PixelModemConfig& PixelModemConfig::operator=(PixelModemConfig&&) = default;
+TraceAttributes_Attribute::TraceAttributes_Attribute() = default;
+TraceAttributes_Attribute::~TraceAttributes_Attribute() = default;
+TraceAttributes_Attribute::TraceAttributes_Attribute(const TraceAttributes_Attribute&) = default;
+TraceAttributes_Attribute& TraceAttributes_Attribute::operator=(const TraceAttributes_Attribute&) = default;
+TraceAttributes_Attribute::TraceAttributes_Attribute(TraceAttributes_Attribute&&) noexcept = default;
+TraceAttributes_Attribute& TraceAttributes_Attribute::operator=(TraceAttributes_Attribute&&) = default;

-bool PixelModemConfig::operator==(const PixelModemConfig& other) const {
+bool TraceAttributes_Attribute::operator==(const TraceAttributes_Attribute& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(event_group_, other.event_group_)
-   && ::protozero::internal::gen_helpers::EqualsField(pigweed_hash_allow_list_, other.pigweed_hash_allow_list_)
-   && ::protozero::internal::gen_helpers::EqualsField(pigweed_hash_deny_list_, other.pigweed_hash_deny_list_);
+   && ::protozero::internal::gen_helpers::EqualsField(key_, other.key_)
+   && ::protozero::internal::gen_helpers::EqualsField(long_value_, other.long_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(string_value_, other.string_value_);
 }

-bool PixelModemConfig::ParseFromArray(const void* raw, size_t size) {
-  pigweed_hash_allow_list_.clear();
-  pigweed_hash_deny_list_.clear();
+bool TraceAttributes_Attribute::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -22681,16 +22421,14 @@ bool PixelModemConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* event_group */:
-        field.get(&event_group_);
+      case 1 /* key */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &key_);
         break;
-      case 2 /* pigweed_hash_allow_list */:
-        pigweed_hash_allow_list_.emplace_back();
-        field.get(&pigweed_hash_allow_list_.back());
+      case 2 /* long_value */:
+        field.get(&long_value_);
         break;
-      case 3 /* pigweed_hash_deny_list */:
-        pigweed_hash_deny_list_.emplace_back();
-        field.get(&pigweed_hash_deny_list_.back());
+      case 3 /* string_value */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &string_value_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -22700,32 +22438,32 @@ bool PixelModemConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string PixelModemConfig::SerializeAsString() const {
+std::string TraceAttributes_Attribute::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> PixelModemConfig::SerializeAsArray() const {
+std::vector<uint8_t> TraceAttributes_Attribute::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void PixelModemConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: event_group
+void TraceAttributes_Attribute::Serialize(::protozero::Message* msg) const {
+  // Field 1: key
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, event_group_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, key_, msg);
   }

-  // Field 2: pigweed_hash_allow_list
-  for (auto& it : pigweed_hash_allow_list_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
+  // Field 2: long_value
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, long_value_, msg);
   }

-  // Field 3: pigweed_hash_deny_list
-  for (auto& it : pigweed_hash_deny_list_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, it, msg);
+  // Field 3: string_value
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeString(3, string_value_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -22737,7 +22475,7 @@ void PixelModemConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/protolog_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/common/trace_stats.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -22748,44 +22486,112 @@ void PixelModemConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/protolog_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/trace_stats.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-ProtoLogGroup::ProtoLogGroup() = default;
-ProtoLogGroup::~ProtoLogGroup() = default;
-ProtoLogGroup::ProtoLogGroup(const ProtoLogGroup&) = default;
-ProtoLogGroup& ProtoLogGroup::operator=(const ProtoLogGroup&) = default;
-ProtoLogGroup::ProtoLogGroup(ProtoLogGroup&&) noexcept = default;
-ProtoLogGroup& ProtoLogGroup::operator=(ProtoLogGroup&&) = default;
+TraceStats::TraceStats() = default;
+TraceStats::~TraceStats() = default;
+TraceStats::TraceStats(const TraceStats&) = default;
+TraceStats& TraceStats::operator=(const TraceStats&) = default;
+TraceStats::TraceStats(TraceStats&&) noexcept = default;
+TraceStats& TraceStats::operator=(TraceStats&&) = default;

-bool ProtoLogGroup::operator==(const ProtoLogGroup& other) const {
+bool TraceStats::operator==(const TraceStats& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(group_name_, other.group_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(log_from_, other.log_from_)
-   && ::protozero::internal::gen_helpers::EqualsField(collect_stacktrace_, other.collect_stacktrace_);
-}
-
-bool ProtoLogGroup::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
-
-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
+   && ::protozero::internal::gen_helpers::EqualsField(buffer_stats_, other.buffer_stats_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunk_payload_histogram_def_, other.chunk_payload_histogram_def_)
+   && ::protozero::internal::gen_helpers::EqualsField(writer_stats_, other.writer_stats_)
+   && ::protozero::internal::gen_helpers::EqualsField(producers_connected_, other.producers_connected_)
+   && ::protozero::internal::gen_helpers::EqualsField(producers_seen_, other.producers_seen_)
+   && ::protozero::internal::gen_helpers::EqualsField(data_sources_registered_, other.data_sources_registered_)
+   && ::protozero::internal::gen_helpers::EqualsField(data_sources_seen_, other.data_sources_seen_)
+   && ::protozero::internal::gen_helpers::EqualsField(tracing_sessions_, other.tracing_sessions_)
+   && ::protozero::internal::gen_helpers::EqualsField(total_buffers_, other.total_buffers_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunks_discarded_, other.chunks_discarded_)
+   && ::protozero::internal::gen_helpers::EqualsField(patches_discarded_, other.patches_discarded_)
+   && ::protozero::internal::gen_helpers::EqualsField(invalid_packets_, other.invalid_packets_)
+   && ::protozero::internal::gen_helpers::EqualsField(filter_stats_, other.filter_stats_)
+   && ::protozero::internal::gen_helpers::EqualsField(flushes_requested_, other.flushes_requested_)
+   && ::protozero::internal::gen_helpers::EqualsField(flushes_succeeded_, other.flushes_succeeded_)
+   && ::protozero::internal::gen_helpers::EqualsField(flushes_failed_, other.flushes_failed_)
+   && ::protozero::internal::gen_helpers::EqualsField(final_flush_outcome_, other.final_flush_outcome_);
+}
+
+int TraceStats::buffer_stats_size() const { return static_cast<int>(buffer_stats_.size()); }
+void TraceStats::clear_buffer_stats() { buffer_stats_.clear(); }
+TraceStats_BufferStats* TraceStats::add_buffer_stats() { buffer_stats_.emplace_back(); return &buffer_stats_.back(); }
+int TraceStats::writer_stats_size() const { return static_cast<int>(writer_stats_.size()); }
+void TraceStats::clear_writer_stats() { writer_stats_.clear(); }
+TraceStats_WriterStats* TraceStats::add_writer_stats() { writer_stats_.emplace_back(); return &writer_stats_.back(); }
+bool TraceStats::ParseFromArray(const void* raw, size_t size) {
+  buffer_stats_.clear();
+  chunk_payload_histogram_def_.clear();
+  writer_stats_.clear();
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* group_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &group_name_);
+      case 1 /* buffer_stats */:
+        buffer_stats_.emplace_back();
+        buffer_stats_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 2 /* log_from */:
-        field.get(&log_from_);
+      case 17 /* chunk_payload_histogram_def */:
+        chunk_payload_histogram_def_.emplace_back();
+        field.get(&chunk_payload_histogram_def_.back());
         break;
-      case 3 /* collect_stacktrace */:
-        field.get(&collect_stacktrace_);
+      case 18 /* writer_stats */:
+        writer_stats_.emplace_back();
+        writer_stats_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 2 /* producers_connected */:
+        field.get(&producers_connected_);
+        break;
+      case 3 /* producers_seen */:
+        field.get(&producers_seen_);
+        break;
+      case 4 /* data_sources_registered */:
+        field.get(&data_sources_registered_);
+        break;
+      case 5 /* data_sources_seen */:
+        field.get(&data_sources_seen_);
+        break;
+      case 6 /* tracing_sessions */:
+        field.get(&tracing_sessions_);
+        break;
+      case 7 /* total_buffers */:
+        field.get(&total_buffers_);
+        break;
+      case 8 /* chunks_discarded */:
+        field.get(&chunks_discarded_);
+        break;
+      case 9 /* patches_discarded */:
+        field.get(&patches_discarded_);
+        break;
+      case 10 /* invalid_packets */:
+        field.get(&invalid_packets_);
+        break;
+      case 11 /* filter_stats */:
+        (*filter_stats_).ParseFromArray(field.data(), field.size());
+        break;
+      case 12 /* flushes_requested */:
+        field.get(&flushes_requested_);
+        break;
+      case 13 /* flushes_succeeded */:
+        field.get(&flushes_succeeded_);
+        break;
+      case 14 /* flushes_failed */:
+        field.get(&flushes_failed_);
+        break;
+      case 15 /* final_flush_outcome */:
+        field.get(&final_flush_outcome_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -22795,57 +22601,127 @@ bool ProtoLogGroup::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ProtoLogGroup::SerializeAsString() const {
+std::string TraceStats::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ProtoLogGroup::SerializeAsArray() const {
+std::vector<uint8_t> TraceStats::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ProtoLogGroup::Serialize(::protozero::Message* msg) const {
-  // Field 1: group_name
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, group_name_, msg);
+void TraceStats::Serialize(::protozero::Message* msg) const {
+  // Field 1: buffer_stats
+  for (auto& it : buffer_stats_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 2: log_from
+  // Field 17: chunk_payload_histogram_def
+  for (auto& it : chunk_payload_histogram_def_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(17, it, msg);
+  }
+
+  // Field 18: writer_stats
+  for (auto& it : writer_stats_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(18));
+  }
+
+  // Field 2: producers_connected
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, log_from_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, producers_connected_, msg);
   }

-  // Field 3: collect_stacktrace
+  // Field 3: producers_seen
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, collect_stacktrace_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, producers_seen_, msg);
+  }
+
+  // Field 4: data_sources_registered
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, data_sources_registered_, msg);
+  }
+
+  // Field 5: data_sources_seen
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, data_sources_seen_, msg);
+  }
+
+  // Field 6: tracing_sessions
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, tracing_sessions_, msg);
+  }
+
+  // Field 7: total_buffers
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, total_buffers_, msg);
+  }
+
+  // Field 8: chunks_discarded
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, chunks_discarded_, msg);
+  }
+
+  // Field 9: patches_discarded
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(9, patches_discarded_, msg);
+  }
+
+  // Field 10: invalid_packets
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(10, invalid_packets_, msg);
+  }
+
+  // Field 11: filter_stats
+  if (_has_field_[11]) {
+    (*filter_stats_).Serialize(msg->BeginNestedMessage<::protozero::Message>(11));
+  }
+
+  // Field 12: flushes_requested
+  if (_has_field_[12]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(12, flushes_requested_, msg);
+  }
+
+  // Field 13: flushes_succeeded
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(13, flushes_succeeded_, msg);
+  }
+
+  // Field 14: flushes_failed
+  if (_has_field_[14]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(14, flushes_failed_, msg);
+  }
+
+  // Field 15: final_flush_outcome
+  if (_has_field_[15]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(15, final_flush_outcome_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-ProtoLogConfig::ProtoLogConfig() = default;
-ProtoLogConfig::~ProtoLogConfig() = default;
-ProtoLogConfig::ProtoLogConfig(const ProtoLogConfig&) = default;
-ProtoLogConfig& ProtoLogConfig::operator=(const ProtoLogConfig&) = default;
-ProtoLogConfig::ProtoLogConfig(ProtoLogConfig&&) noexcept = default;
-ProtoLogConfig& ProtoLogConfig::operator=(ProtoLogConfig&&) = default;
+TraceStats_FilterStats::TraceStats_FilterStats() = default;
+TraceStats_FilterStats::~TraceStats_FilterStats() = default;
+TraceStats_FilterStats::TraceStats_FilterStats(const TraceStats_FilterStats&) = default;
+TraceStats_FilterStats& TraceStats_FilterStats::operator=(const TraceStats_FilterStats&) = default;
+TraceStats_FilterStats::TraceStats_FilterStats(TraceStats_FilterStats&&) noexcept = default;
+TraceStats_FilterStats& TraceStats_FilterStats::operator=(TraceStats_FilterStats&&) = default;

-bool ProtoLogConfig::operator==(const ProtoLogConfig& other) const {
+bool TraceStats_FilterStats::operator==(const TraceStats_FilterStats& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(group_overrides_, other.group_overrides_)
-   && ::protozero::internal::gen_helpers::EqualsField(tracing_mode_, other.tracing_mode_)
-   && ::protozero::internal::gen_helpers::EqualsField(default_log_from_level_, other.default_log_from_level_);
+   && ::protozero::internal::gen_helpers::EqualsField(input_packets_, other.input_packets_)
+   && ::protozero::internal::gen_helpers::EqualsField(input_bytes_, other.input_bytes_)
+   && ::protozero::internal::gen_helpers::EqualsField(output_bytes_, other.output_bytes_)
+   && ::protozero::internal::gen_helpers::EqualsField(errors_, other.errors_)
+   && ::protozero::internal::gen_helpers::EqualsField(time_taken_ns_, other.time_taken_ns_)
+   && ::protozero::internal::gen_helpers::EqualsField(bytes_discarded_per_buffer_, other.bytes_discarded_per_buffer_);
 }

-int ProtoLogConfig::group_overrides_size() const { return static_cast<int>(group_overrides_.size()); }
-void ProtoLogConfig::clear_group_overrides() { group_overrides_.clear(); }
-ProtoLogGroup* ProtoLogConfig::add_group_overrides() { group_overrides_.emplace_back(); return &group_overrides_.back(); }
-bool ProtoLogConfig::ParseFromArray(const void* raw, size_t size) {
-  group_overrides_.clear();
+bool TraceStats_FilterStats::ParseFromArray(const void* raw, size_t size) {
+  bytes_discarded_per_buffer_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -22855,15 +22731,24 @@ bool ProtoLogConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* group_overrides */:
-        group_overrides_.emplace_back();
-        group_overrides_.back().ParseFromArray(field.data(), field.size());
+      case 1 /* input_packets */:
+        field.get(&input_packets_);
         break;
-      case 2 /* tracing_mode */:
-        field.get(&tracing_mode_);
+      case 2 /* input_bytes */:
+        field.get(&input_bytes_);
         break;
-      case 3 /* default_log_from_level */:
-        field.get(&default_log_from_level_);
+      case 3 /* output_bytes */:
+        field.get(&output_bytes_);
+        break;
+      case 4 /* errors */:
+        field.get(&errors_);
+        break;
+      case 5 /* time_taken_ns */:
+        field.get(&time_taken_ns_);
+        break;
+      case 20 /* bytes_discarded_per_buffer */:
+        bytes_discarded_per_buffer_.emplace_back();
+        field.get(&bytes_discarded_per_buffer_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -22873,75 +22758,71 @@ bool ProtoLogConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ProtoLogConfig::SerializeAsString() const {
+std::string TraceStats_FilterStats::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ProtoLogConfig::SerializeAsArray() const {
+std::vector<uint8_t> TraceStats_FilterStats::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ProtoLogConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: group_overrides
-  for (auto& it : group_overrides_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+void TraceStats_FilterStats::Serialize(::protozero::Message* msg) const {
+  // Field 1: input_packets
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, input_packets_, msg);
   }

-  // Field 2: tracing_mode
+  // Field 2: input_bytes
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, tracing_mode_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, input_bytes_, msg);
   }

-  // Field 3: default_log_from_level
+  // Field 3: output_bytes
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, default_log_from_level_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, output_bytes_, msg);
+  }
+
+  // Field 4: errors
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, errors_, msg);
+  }
+
+  // Field 5: time_taken_ns
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, time_taken_ns_, msg);
+  }
+
+  // Field 20: bytes_discarded_per_buffer
+  for (auto& it : bytes_discarded_per_buffer_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(20, it, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/surfaceflinger_layers_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_layers_config.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-SurfaceFlingerLayersConfig::SurfaceFlingerLayersConfig() = default;
-SurfaceFlingerLayersConfig::~SurfaceFlingerLayersConfig() = default;
-SurfaceFlingerLayersConfig::SurfaceFlingerLayersConfig(const SurfaceFlingerLayersConfig&) = default;
-SurfaceFlingerLayersConfig& SurfaceFlingerLayersConfig::operator=(const SurfaceFlingerLayersConfig&) = default;
-SurfaceFlingerLayersConfig::SurfaceFlingerLayersConfig(SurfaceFlingerLayersConfig&&) noexcept = default;
-SurfaceFlingerLayersConfig& SurfaceFlingerLayersConfig::operator=(SurfaceFlingerLayersConfig&&) = default;
+TraceStats_WriterStats::TraceStats_WriterStats() = default;
+TraceStats_WriterStats::~TraceStats_WriterStats() = default;
+TraceStats_WriterStats::TraceStats_WriterStats(const TraceStats_WriterStats&) = default;
+TraceStats_WriterStats& TraceStats_WriterStats::operator=(const TraceStats_WriterStats&) = default;
+TraceStats_WriterStats::TraceStats_WriterStats(TraceStats_WriterStats&&) noexcept = default;
+TraceStats_WriterStats& TraceStats_WriterStats::operator=(TraceStats_WriterStats&&) = default;

-bool SurfaceFlingerLayersConfig::operator==(const SurfaceFlingerLayersConfig& other) const {
+bool TraceStats_WriterStats::operator==(const TraceStats_WriterStats& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(mode_, other.mode_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_flags_, other.trace_flags_);
+   && ::protozero::internal::gen_helpers::EqualsField(sequence_id_, other.sequence_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(buffer_, other.buffer_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunk_payload_histogram_counts_, other.chunk_payload_histogram_counts_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunk_payload_histogram_sum_, other.chunk_payload_histogram_sum_);
 }

-bool SurfaceFlingerLayersConfig::ParseFromArray(const void* raw, size_t size) {
-  trace_flags_.clear();
+bool TraceStats_WriterStats::ParseFromArray(const void* raw, size_t size) {
+  chunk_payload_histogram_counts_.clear();
+  chunk_payload_histogram_sum_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -22951,12 +22832,19 @@ bool SurfaceFlingerLayersConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* mode */:
-        field.get(&mode_);
+      case 1 /* sequence_id */:
+        field.get(&sequence_id_);
         break;
-      case 2 /* trace_flags */:
-        trace_flags_.emplace_back();
-        field.get(&trace_flags_.back());
+      case 4 /* buffer */:
+        field.get(&buffer_);
+        break;
+      case 2 /* chunk_payload_histogram_counts */:
+        if (!::protozero::internal::gen_helpers::DeserializePackedRepeated<::protozero::proto_utils::ProtoWireType::kVarInt, uint64_t>(field, &chunk_payload_histogram_counts_)) {
+          packed_error = true;}
+        break;
+      case 3 /* chunk_payload_histogram_sum */:
+        if (!::protozero::internal::gen_helpers::DeserializePackedRepeated<::protozero::proto_utils::ProtoWireType::kVarInt, int64_t>(field, &chunk_payload_histogram_sum_)) {
+          packed_error = true;}
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -22966,68 +22854,84 @@ bool SurfaceFlingerLayersConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string SurfaceFlingerLayersConfig::SerializeAsString() const {
+std::string TraceStats_WriterStats::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> SurfaceFlingerLayersConfig::SerializeAsArray() const {
+std::vector<uint8_t> TraceStats_WriterStats::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void SurfaceFlingerLayersConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: mode
+void TraceStats_WriterStats::Serialize(::protozero::Message* msg) const {
+  // Field 1: sequence_id
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, mode_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, sequence_id_, msg);
   }

-  // Field 2: trace_flags
-  for (auto& it : trace_flags_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
+  // Field 4: buffer
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, buffer_, msg);
+  }
+
+  // Field 2: chunk_payload_histogram_counts
+  {
+    ::protozero::PackedVarInt pack;
+    for (auto& it : chunk_payload_histogram_counts_)
+      pack.Append(it);
+    msg->AppendBytes(2, pack.data(), pack.size());
+  }
+
+  // Field 3: chunk_payload_histogram_sum
+  {
+    ::protozero::PackedVarInt pack;
+    for (auto& it : chunk_payload_histogram_sum_)
+      pack.Append(it);
+    msg->AppendBytes(3, pack.data(), pack.size());
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/surfaceflinger_transactions_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_transactions_config.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-SurfaceFlingerTransactionsConfig::SurfaceFlingerTransactionsConfig() = default;
-SurfaceFlingerTransactionsConfig::~SurfaceFlingerTransactionsConfig() = default;
-SurfaceFlingerTransactionsConfig::SurfaceFlingerTransactionsConfig(const SurfaceFlingerTransactionsConfig&) = default;
-SurfaceFlingerTransactionsConfig& SurfaceFlingerTransactionsConfig::operator=(const SurfaceFlingerTransactionsConfig&) = default;
-SurfaceFlingerTransactionsConfig::SurfaceFlingerTransactionsConfig(SurfaceFlingerTransactionsConfig&&) noexcept = default;
-SurfaceFlingerTransactionsConfig& SurfaceFlingerTransactionsConfig::operator=(SurfaceFlingerTransactionsConfig&&) = default;
+TraceStats_BufferStats::TraceStats_BufferStats() = default;
+TraceStats_BufferStats::~TraceStats_BufferStats() = default;
+TraceStats_BufferStats::TraceStats_BufferStats(const TraceStats_BufferStats&) = default;
+TraceStats_BufferStats& TraceStats_BufferStats::operator=(const TraceStats_BufferStats&) = default;
+TraceStats_BufferStats::TraceStats_BufferStats(TraceStats_BufferStats&&) noexcept = default;
+TraceStats_BufferStats& TraceStats_BufferStats::operator=(TraceStats_BufferStats&&) = default;

-bool SurfaceFlingerTransactionsConfig::operator==(const SurfaceFlingerTransactionsConfig& other) const {
+bool TraceStats_BufferStats::operator==(const TraceStats_BufferStats& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(mode_, other.mode_);
+   && ::protozero::internal::gen_helpers::EqualsField(buffer_size_, other.buffer_size_)
+   && ::protozero::internal::gen_helpers::EqualsField(bytes_written_, other.bytes_written_)
+   && ::protozero::internal::gen_helpers::EqualsField(bytes_overwritten_, other.bytes_overwritten_)
+   && ::protozero::internal::gen_helpers::EqualsField(bytes_read_, other.bytes_read_)
+   && ::protozero::internal::gen_helpers::EqualsField(padding_bytes_written_, other.padding_bytes_written_)
+   && ::protozero::internal::gen_helpers::EqualsField(padding_bytes_cleared_, other.padding_bytes_cleared_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunks_written_, other.chunks_written_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunks_rewritten_, other.chunks_rewritten_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunks_relocated_, other.chunks_relocated_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunks_overwritten_, other.chunks_overwritten_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunks_discarded_, other.chunks_discarded_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunks_read_, other.chunks_read_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunks_committed_out_of_order_, other.chunks_committed_out_of_order_)
+   && ::protozero::internal::gen_helpers::EqualsField(write_wrap_count_, other.write_wrap_count_)
+   && ::protozero::internal::gen_helpers::EqualsField(compactions_, other.compactions_)
+   && ::protozero::internal::gen_helpers::EqualsField(compactions_skipped_, other.compactions_skipped_)
+   && ::protozero::internal::gen_helpers::EqualsField(patches_succeeded_, other.patches_succeeded_)
+   && ::protozero::internal::gen_helpers::EqualsField(patches_failed_, other.patches_failed_)
+   && ::protozero::internal::gen_helpers::EqualsField(readaheads_succeeded_, other.readaheads_succeeded_)
+   && ::protozero::internal::gen_helpers::EqualsField(readaheads_failed_, other.readaheads_failed_)
+   && ::protozero::internal::gen_helpers::EqualsField(abi_violations_, other.abi_violations_)
+   && ::protozero::internal::gen_helpers::EqualsField(trace_writer_packet_loss_, other.trace_writer_packet_loss_)
+   && ::protozero::internal::gen_helpers::EqualsField(oversized_packets_dropped_, other.oversized_packets_dropped_);
 }

-bool SurfaceFlingerTransactionsConfig::ParseFromArray(const void* raw, size_t size) {
+bool TraceStats_BufferStats::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -23037,8 +22941,74 @@ bool SurfaceFlingerTransactionsConfig::ParseFromArray(const void* raw, size_t si
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* mode */:
-        field.get(&mode_);
+      case 12 /* buffer_size */:
+        field.get(&buffer_size_);
+        break;
+      case 1 /* bytes_written */:
+        field.get(&bytes_written_);
+        break;
+      case 13 /* bytes_overwritten */:
+        field.get(&bytes_overwritten_);
+        break;
+      case 14 /* bytes_read */:
+        field.get(&bytes_read_);
+        break;
+      case 15 /* padding_bytes_written */:
+        field.get(&padding_bytes_written_);
+        break;
+      case 16 /* padding_bytes_cleared */:
+        field.get(&padding_bytes_cleared_);
+        break;
+      case 2 /* chunks_written */:
+        field.get(&chunks_written_);
+        break;
+      case 10 /* chunks_rewritten */:
+        field.get(&chunks_rewritten_);
+        break;
+      case 20 /* chunks_relocated */:
+        field.get(&chunks_relocated_);
+        break;
+      case 3 /* chunks_overwritten */:
+        field.get(&chunks_overwritten_);
+        break;
+      case 18 /* chunks_discarded */:
+        field.get(&chunks_discarded_);
+        break;
+      case 17 /* chunks_read */:
+        field.get(&chunks_read_);
+        break;
+      case 11 /* chunks_committed_out_of_order */:
+        field.get(&chunks_committed_out_of_order_);
+        break;
+      case 4 /* write_wrap_count */:
+        field.get(&write_wrap_count_);
+        break;
+      case 23 /* compactions */:
+        field.get(&compactions_);
+        break;
+      case 24 /* compactions_skipped */:
+        field.get(&compactions_skipped_);
+        break;
+      case 5 /* patches_succeeded */:
+        field.get(&patches_succeeded_);
+        break;
+      case 6 /* patches_failed */:
+        field.get(&patches_failed_);
+        break;
+      case 7 /* readaheads_succeeded */:
+        field.get(&readaheads_succeeded_);
+        break;
+      case 8 /* readaheads_failed */:
+        field.get(&readaheads_failed_);
+        break;
+      case 9 /* abi_violations */:
+        field.get(&abi_violations_);
+        break;
+      case 19 /* trace_writer_packet_loss */:
+        field.get(&trace_writer_packet_loss_);
+        break;
+      case 22 /* oversized_packets_dropped */:
+        field.get(&oversized_packets_dropped_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -23048,101 +23018,132 @@ bool SurfaceFlingerTransactionsConfig::ParseFromArray(const void* raw, size_t si
   return !packed_error && !dec.bytes_left();
 }

-std::string SurfaceFlingerTransactionsConfig::SerializeAsString() const {
+std::string TraceStats_BufferStats::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> SurfaceFlingerTransactionsConfig::SerializeAsArray() const {
+std::vector<uint8_t> TraceStats_BufferStats::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void SurfaceFlingerTransactionsConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: mode
+void TraceStats_BufferStats::Serialize(::protozero::Message* msg) const {
+  // Field 12: buffer_size
+  if (_has_field_[12]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(12, buffer_size_, msg);
+  }
+
+  // Field 1: bytes_written
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, mode_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, bytes_written_, msg);
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+  // Field 13: bytes_overwritten
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(13, bytes_overwritten_, msg);
+  }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/user_list_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/user_list_config.gen.h"
+  // Field 14: bytes_read
+  if (_has_field_[14]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(14, bytes_read_, msg);
+  }

-namespace perfetto {
-namespace protos {
-namespace gen {
+  // Field 15: padding_bytes_written
+  if (_has_field_[15]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(15, padding_bytes_written_, msg);
+  }

-AndroidUserListConfig::AndroidUserListConfig() = default;
-AndroidUserListConfig::~AndroidUserListConfig() = default;
-AndroidUserListConfig::AndroidUserListConfig(const AndroidUserListConfig&) = default;
-AndroidUserListConfig& AndroidUserListConfig::operator=(const AndroidUserListConfig&) = default;
-AndroidUserListConfig::AndroidUserListConfig(AndroidUserListConfig&&) noexcept = default;
-AndroidUserListConfig& AndroidUserListConfig::operator=(AndroidUserListConfig&&) = default;
+  // Field 16: padding_bytes_cleared
+  if (_has_field_[16]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(16, padding_bytes_cleared_, msg);
+  }

-bool AndroidUserListConfig::operator==(const AndroidUserListConfig& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(user_type_filter_, other.user_type_filter_);
-}
+  // Field 2: chunks_written
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, chunks_written_, msg);
+  }

-bool AndroidUserListConfig::ParseFromArray(const void* raw, size_t size) {
-  user_type_filter_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
+  // Field 10: chunks_rewritten
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(10, chunks_rewritten_, msg);
+  }

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* user_type_filter */:
-        user_type_filter_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &user_type_filter_.back());
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
+  // Field 20: chunks_relocated
+  if (_has_field_[20]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(20, chunks_relocated_, msg);
   }
-  return !packed_error && !dec.bytes_left();
-}

-std::string AndroidUserListConfig::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+  // Field 3: chunks_overwritten
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, chunks_overwritten_, msg);
+  }

-std::vector<uint8_t> AndroidUserListConfig::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  // Field 18: chunks_discarded
+  if (_has_field_[18]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(18, chunks_discarded_, msg);
+  }

-void AndroidUserListConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: user_type_filter
-  for (auto& it : user_type_filter_) {
-    ::protozero::internal::gen_helpers::SerializeString(1, it, msg);
+  // Field 17: chunks_read
+  if (_has_field_[17]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(17, chunks_read_, msg);
+  }
+
+  // Field 11: chunks_committed_out_of_order
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(11, chunks_committed_out_of_order_, msg);
+  }
+
+  // Field 4: write_wrap_count
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, write_wrap_count_, msg);
+  }
+
+  // Field 23: compactions
+  if (_has_field_[23]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(23, compactions_, msg);
+  }
+
+  // Field 24: compactions_skipped
+  if (_has_field_[24]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(24, compactions_skipped_, msg);
+  }
+
+  // Field 5: patches_succeeded
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, patches_succeeded_, msg);
+  }
+
+  // Field 6: patches_failed
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, patches_failed_, msg);
+  }
+
+  // Field 7: readaheads_succeeded
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, readaheads_succeeded_, msg);
+  }
+
+  // Field 8: readaheads_failed
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, readaheads_failed_, msg);
+  }
+
+  // Field 9: abi_violations
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(9, abi_violations_, msg);
+  }
+
+  // Field 19: trace_writer_packet_loss
+  if (_has_field_[19]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(19, trace_writer_packet_loss_, msg);
+  }
+
+  // Field 22: oversized_packets_dropped
+  if (_has_field_[22]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(22, oversized_packets_dropped_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -23154,7 +23155,7 @@ void AndroidUserListConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/windowmanager_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/common/tracing_service_capabilities.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -23165,27 +23166,30 @@ void AndroidUserListConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/windowmanager_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/tracing_service_capabilities.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/observable_events.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-WindowManagerConfig::WindowManagerConfig() = default;
-WindowManagerConfig::~WindowManagerConfig() = default;
-WindowManagerConfig::WindowManagerConfig(const WindowManagerConfig&) = default;
-WindowManagerConfig& WindowManagerConfig::operator=(const WindowManagerConfig&) = default;
-WindowManagerConfig::WindowManagerConfig(WindowManagerConfig&&) noexcept = default;
-WindowManagerConfig& WindowManagerConfig::operator=(WindowManagerConfig&&) = default;
+TracingServiceCapabilities::TracingServiceCapabilities() = default;
+TracingServiceCapabilities::~TracingServiceCapabilities() = default;
+TracingServiceCapabilities::TracingServiceCapabilities(const TracingServiceCapabilities&) = default;
+TracingServiceCapabilities& TracingServiceCapabilities::operator=(const TracingServiceCapabilities&) = default;
+TracingServiceCapabilities::TracingServiceCapabilities(TracingServiceCapabilities&&) noexcept = default;
+TracingServiceCapabilities& TracingServiceCapabilities::operator=(TracingServiceCapabilities&&) = default;

-bool WindowManagerConfig::operator==(const WindowManagerConfig& other) const {
+bool TracingServiceCapabilities::operator==(const TracingServiceCapabilities& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(log_frequency_, other.log_frequency_)
-   && ::protozero::internal::gen_helpers::EqualsField(log_level_, other.log_level_)
-   && ::protozero::internal::gen_helpers::EqualsField(log_mode_, other.log_mode_);
+   && ::protozero::internal::gen_helpers::EqualsField(has_query_capabilities_, other.has_query_capabilities_)
+   && ::protozero::internal::gen_helpers::EqualsField(observable_events_, other.observable_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(has_trace_config_output_path_, other.has_trace_config_output_path_)
+   && ::protozero::internal::gen_helpers::EqualsField(has_clone_session_, other.has_clone_session_);
 }

-bool WindowManagerConfig::ParseFromArray(const void* raw, size_t size) {
+bool TracingServiceCapabilities::ParseFromArray(const void* raw, size_t size) {
+  observable_events_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -23195,14 +23199,18 @@ bool WindowManagerConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* log_frequency */:
-        field.get(&log_frequency_);
+      case 1 /* has_query_capabilities */:
+        field.get(&has_query_capabilities_);
         break;
-      case 2 /* log_level */:
-        field.get(&log_level_);
+      case 2 /* observable_events */:
+        observable_events_.emplace_back();
+        field.get(&observable_events_.back());
         break;
-      case 3 /* log_mode */:
-        field.get(&log_mode_);
+      case 3 /* has_trace_config_output_path */:
+        field.get(&has_trace_config_output_path_);
+        break;
+      case 4 /* has_clone_session */:
+        field.get(&has_clone_session_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -23212,32 +23220,37 @@ bool WindowManagerConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string WindowManagerConfig::SerializeAsString() const {
+std::string TracingServiceCapabilities::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> WindowManagerConfig::SerializeAsArray() const {
+std::vector<uint8_t> TracingServiceCapabilities::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void WindowManagerConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: log_frequency
+void TracingServiceCapabilities::Serialize(::protozero::Message* msg) const {
+  // Field 1: has_query_capabilities
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, log_frequency_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, has_query_capabilities_, msg);
   }

-  // Field 2: log_level
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, log_level_, msg);
+  // Field 2: observable_events
+  for (auto& it : observable_events_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
   }

-  // Field 3: log_mode
+  // Field 3: has_trace_config_output_path
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, log_mode_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, has_trace_config_output_path_, msg);
+  }
+
+  // Field 4: has_clone_session
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, has_clone_session_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -23249,7 +23262,7 @@ void WindowManagerConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/ftrace/frozen_ftrace_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/common/tracing_service_state.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -23260,25 +23273,48 @@ void WindowManagerConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/frozen_ftrace_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/tracing_service_state.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/data_source_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/protovm/vm_program.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/track_event_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/gpu_counter_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/ftrace_descriptor.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-FrozenFtraceConfig::FrozenFtraceConfig() = default;
-FrozenFtraceConfig::~FrozenFtraceConfig() = default;
-FrozenFtraceConfig::FrozenFtraceConfig(const FrozenFtraceConfig&) = default;
-FrozenFtraceConfig& FrozenFtraceConfig::operator=(const FrozenFtraceConfig&) = default;
-FrozenFtraceConfig::FrozenFtraceConfig(FrozenFtraceConfig&&) noexcept = default;
-FrozenFtraceConfig& FrozenFtraceConfig::operator=(FrozenFtraceConfig&&) = default;
+TracingServiceState::TracingServiceState() = default;
+TracingServiceState::~TracingServiceState() = default;
+TracingServiceState::TracingServiceState(const TracingServiceState&) = default;
+TracingServiceState& TracingServiceState::operator=(const TracingServiceState&) = default;
+TracingServiceState::TracingServiceState(TracingServiceState&&) noexcept = default;
+TracingServiceState& TracingServiceState::operator=(TracingServiceState&&) = default;

-bool FrozenFtraceConfig::operator==(const FrozenFtraceConfig& other) const {
+bool TracingServiceState::operator==(const TracingServiceState& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(instance_name_, other.instance_name_);
+   && ::protozero::internal::gen_helpers::EqualsField(producers_, other.producers_)
+   && ::protozero::internal::gen_helpers::EqualsField(data_sources_, other.data_sources_)
+   && ::protozero::internal::gen_helpers::EqualsField(tracing_sessions_, other.tracing_sessions_)
+   && ::protozero::internal::gen_helpers::EqualsField(supports_tracing_sessions_, other.supports_tracing_sessions_)
+   && ::protozero::internal::gen_helpers::EqualsField(num_sessions_, other.num_sessions_)
+   && ::protozero::internal::gen_helpers::EqualsField(num_sessions_started_, other.num_sessions_started_)
+   && ::protozero::internal::gen_helpers::EqualsField(tracing_service_version_, other.tracing_service_version_);
 }

-bool FrozenFtraceConfig::ParseFromArray(const void* raw, size_t size) {
+int TracingServiceState::producers_size() const { return static_cast<int>(producers_.size()); }
+void TracingServiceState::clear_producers() { producers_.clear(); }
+TracingServiceState_Producer* TracingServiceState::add_producers() { producers_.emplace_back(); return &producers_.back(); }
+int TracingServiceState::data_sources_size() const { return static_cast<int>(data_sources_.size()); }
+void TracingServiceState::clear_data_sources() { data_sources_.clear(); }
+TracingServiceState_DataSource* TracingServiceState::add_data_sources() { data_sources_.emplace_back(); return &data_sources_.back(); }
+int TracingServiceState::tracing_sessions_size() const { return static_cast<int>(tracing_sessions_.size()); }
+void TracingServiceState::clear_tracing_sessions() { tracing_sessions_.clear(); }
+TracingServiceState_TracingSession* TracingServiceState::add_tracing_sessions() { tracing_sessions_.emplace_back(); return &tracing_sessions_.back(); }
+bool TracingServiceState::ParseFromArray(const void* raw, size_t size) {
+  producers_.clear();
+  data_sources_.clear();
+  tracing_sessions_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -23288,8 +23324,29 @@ bool FrozenFtraceConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* instance_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &instance_name_);
+      case 1 /* producers */:
+        producers_.emplace_back();
+        producers_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 2 /* data_sources */:
+        data_sources_.emplace_back();
+        data_sources_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 6 /* tracing_sessions */:
+        tracing_sessions_.emplace_back();
+        tracing_sessions_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 7 /* supports_tracing_sessions */:
+        field.get(&supports_tracing_sessions_);
+        break;
+      case 3 /* num_sessions */:
+        field.get(&num_sessions_);
+        break;
+      case 4 /* num_sessions_started */:
+        field.get(&num_sessions_started_);
+        break;
+      case 5 /* tracing_service_version */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &tracing_service_version_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -23299,108 +23356,82 @@ bool FrozenFtraceConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string FrozenFtraceConfig::SerializeAsString() const {
+std::string TracingServiceState::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> FrozenFtraceConfig::SerializeAsArray() const {
+std::vector<uint8_t> TracingServiceState::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void FrozenFtraceConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: instance_name
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, instance_name_, msg);
+void TracingServiceState::Serialize(::protozero::Message* msg) const {
+  // Field 1: producers
+  for (auto& it : producers_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+  }
+
+  // Field 2: data_sources
+  for (auto& it : data_sources_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+  }
+
+  // Field 6: tracing_sessions
+  for (auto& it : tracing_sessions_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
+  }
+
+  // Field 7: supports_tracing_sessions
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, supports_tracing_sessions_, msg);
+  }
+
+  // Field 3: num_sessions
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, num_sessions_, msg);
+  }
+
+  // Field 4: num_sessions_started
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, num_sessions_started_, msg);
+  }
+
+  // Field 5: tracing_service_version
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeString(5, tracing_service_version_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/ftrace/ftrace_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-FtraceConfig::FtraceConfig() = default;
-FtraceConfig::~FtraceConfig() = default;
-FtraceConfig::FtraceConfig(const FtraceConfig&) = default;
-FtraceConfig& FtraceConfig::operator=(const FtraceConfig&) = default;
-FtraceConfig::FtraceConfig(FtraceConfig&&) noexcept = default;
-FtraceConfig& FtraceConfig::operator=(FtraceConfig&&) = default;
+TracingServiceState_TracingSession::TracingServiceState_TracingSession() = default;
+TracingServiceState_TracingSession::~TracingServiceState_TracingSession() = default;
+TracingServiceState_TracingSession::TracingServiceState_TracingSession(const TracingServiceState_TracingSession&) = default;
+TracingServiceState_TracingSession& TracingServiceState_TracingSession::operator=(const TracingServiceState_TracingSession&) = default;
+TracingServiceState_TracingSession::TracingServiceState_TracingSession(TracingServiceState_TracingSession&&) noexcept = default;
+TracingServiceState_TracingSession& TracingServiceState_TracingSession::operator=(TracingServiceState_TracingSession&&) = default;

-bool FtraceConfig::operator==(const FtraceConfig& other) const {
+bool TracingServiceState_TracingSession::operator==(const TracingServiceState_TracingSession& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(ftrace_events_, other.ftrace_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(atrace_categories_, other.atrace_categories_)
-   && ::protozero::internal::gen_helpers::EqualsField(atrace_apps_, other.atrace_apps_)
-   && ::protozero::internal::gen_helpers::EqualsField(atrace_categories_prefer_sdk_, other.atrace_categories_prefer_sdk_)
-   && ::protozero::internal::gen_helpers::EqualsField(atrace_userspace_only_, other.atrace_userspace_only_)
+   && ::protozero::internal::gen_helpers::EqualsField(id_, other.id_)
+   && ::protozero::internal::gen_helpers::EqualsField(consumer_uid_, other.consumer_uid_)
+   && ::protozero::internal::gen_helpers::EqualsField(state_, other.state_)
+   && ::protozero::internal::gen_helpers::EqualsField(unique_session_name_, other.unique_session_name_)
    && ::protozero::internal::gen_helpers::EqualsField(buffer_size_kb_, other.buffer_size_kb_)
-   && ::protozero::internal::gen_helpers::EqualsField(buffer_size_lower_bound_, other.buffer_size_lower_bound_)
-   && ::protozero::internal::gen_helpers::EqualsField(drain_period_ms_, other.drain_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(drain_buffer_percent_, other.drain_buffer_percent_)
-   && ::protozero::internal::gen_helpers::EqualsField(compact_sched_, other.compact_sched_)
-   && ::protozero::internal::gen_helpers::EqualsField(print_filter_, other.print_filter_)
-   && ::protozero::internal::gen_helpers::EqualsField(symbolize_ksyms_, other.symbolize_ksyms_)
-   && ::protozero::internal::gen_helpers::EqualsField(ksyms_mem_policy_, other.ksyms_mem_policy_)
-   && ::protozero::internal::gen_helpers::EqualsField(throttle_rss_stat_, other.throttle_rss_stat_)
-   && ::protozero::internal::gen_helpers::EqualsField(denser_generic_event_encoding_, other.denser_generic_event_encoding_)
-   && ::protozero::internal::gen_helpers::EqualsField(disable_generic_events_, other.disable_generic_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(syscall_events_, other.syscall_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(enable_function_graph_, other.enable_function_graph_)
-   && ::protozero::internal::gen_helpers::EqualsField(function_filters_, other.function_filters_)
-   && ::protozero::internal::gen_helpers::EqualsField(function_graph_roots_, other.function_graph_roots_)
-   && ::protozero::internal::gen_helpers::EqualsField(function_graph_max_depth_, other.function_graph_max_depth_)
-   && ::protozero::internal::gen_helpers::EqualsField(kprobe_events_, other.kprobe_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(preserve_ftrace_buffer_, other.preserve_ftrace_buffer_)
-   && ::protozero::internal::gen_helpers::EqualsField(use_monotonic_raw_clock_, other.use_monotonic_raw_clock_)
-   && ::protozero::internal::gen_helpers::EqualsField(instance_name_, other.instance_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(debug_ftrace_abi_, other.debug_ftrace_abi_)
-   && ::protozero::internal::gen_helpers::EqualsField(tids_to_trace_, other.tids_to_trace_)
-   && ::protozero::internal::gen_helpers::EqualsField(tracefs_options_, other.tracefs_options_)
-   && ::protozero::internal::gen_helpers::EqualsField(tracing_cpumask_, other.tracing_cpumask_)
-   && ::protozero::internal::gen_helpers::EqualsField(initialize_ksyms_synchronously_for_testing_, other.initialize_ksyms_synchronously_for_testing_);
+   && ::protozero::internal::gen_helpers::EqualsField(duration_ms_, other.duration_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(num_data_sources_, other.num_data_sources_)
+   && ::protozero::internal::gen_helpers::EqualsField(start_realtime_ns_, other.start_realtime_ns_)
+   && ::protozero::internal::gen_helpers::EqualsField(bugreport_score_, other.bugreport_score_)
+   && ::protozero::internal::gen_helpers::EqualsField(bugreport_filename_, other.bugreport_filename_)
+   && ::protozero::internal::gen_helpers::EqualsField(is_started_, other.is_started_);
 }

-int FtraceConfig::kprobe_events_size() const { return static_cast<int>(kprobe_events_.size()); }
-void FtraceConfig::clear_kprobe_events() { kprobe_events_.clear(); }
-FtraceConfig_KprobeEvent* FtraceConfig::add_kprobe_events() { kprobe_events_.emplace_back(); return &kprobe_events_.back(); }
-int FtraceConfig::tracefs_options_size() const { return static_cast<int>(tracefs_options_.size()); }
-void FtraceConfig::clear_tracefs_options() { tracefs_options_.clear(); }
-FtraceConfig_TracefsOption* FtraceConfig::add_tracefs_options() { tracefs_options_.emplace_back(); return &tracefs_options_.back(); }
-bool FtraceConfig::ParseFromArray(const void* raw, size_t size) {
-  ftrace_events_.clear();
-  atrace_categories_.clear();
-  atrace_apps_.clear();
-  atrace_categories_prefer_sdk_.clear();
-  syscall_events_.clear();
-  function_filters_.clear();
-  function_graph_roots_.clear();
-  kprobe_events_.clear();
-  tids_to_trace_.clear();
-  tracefs_options_.clear();
+bool TracingServiceState_TracingSession::ParseFromArray(const void* raw, size_t size) {
+  buffer_size_kb_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -23410,295 +23441,134 @@ bool FtraceConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* ftrace_events */:
-        ftrace_events_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &ftrace_events_.back());
-        break;
-      case 2 /* atrace_categories */:
-        atrace_categories_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &atrace_categories_.back());
-        break;
-      case 3 /* atrace_apps */:
-        atrace_apps_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &atrace_apps_.back());
-        break;
-      case 28 /* atrace_categories_prefer_sdk */:
-        atrace_categories_prefer_sdk_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &atrace_categories_prefer_sdk_.back());
-        break;
-      case 34 /* atrace_userspace_only */:
-        field.get(&atrace_userspace_only_);
-        break;
-      case 10 /* buffer_size_kb */:
-        field.get(&buffer_size_kb_);
-        break;
-      case 27 /* buffer_size_lower_bound */:
-        field.get(&buffer_size_lower_bound_);
-        break;
-      case 11 /* drain_period_ms */:
-        field.get(&drain_period_ms_);
-        break;
-      case 29 /* drain_buffer_percent */:
-        field.get(&drain_buffer_percent_);
-        break;
-      case 12 /* compact_sched */:
-        (*compact_sched_).ParseFromArray(field.data(), field.size());
-        break;
-      case 22 /* print_filter */:
-        (*print_filter_).ParseFromArray(field.data(), field.size());
-        break;
-      case 13 /* symbolize_ksyms */:
-        field.get(&symbolize_ksyms_);
+      case 1 /* id */:
+        field.get(&id_);
         break;
-      case 17 /* ksyms_mem_policy */:
-        field.get(&ksyms_mem_policy_);
+      case 2 /* consumer_uid */:
+        field.get(&consumer_uid_);
         break;
-      case 15 /* throttle_rss_stat */:
-        field.get(&throttle_rss_stat_);
+      case 3 /* state */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &state_);
         break;
-      case 32 /* denser_generic_event_encoding */:
-        field.get(&denser_generic_event_encoding_);
+      case 4 /* unique_session_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &unique_session_name_);
         break;
-      case 16 /* disable_generic_events */:
-        field.get(&disable_generic_events_);
+      case 5 /* buffer_size_kb */:
+        buffer_size_kb_.emplace_back();
+        field.get(&buffer_size_kb_.back());
         break;
-      case 18 /* syscall_events */:
-        syscall_events_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &syscall_events_.back());
+      case 6 /* duration_ms */:
+        field.get(&duration_ms_);
         break;
-      case 19 /* enable_function_graph */:
-        field.get(&enable_function_graph_);
+      case 7 /* num_data_sources */:
+        field.get(&num_data_sources_);
         break;
-      case 20 /* function_filters */:
-        function_filters_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &function_filters_.back());
+      case 8 /* start_realtime_ns */:
+        field.get(&start_realtime_ns_);
         break;
-      case 21 /* function_graph_roots */:
-        function_graph_roots_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &function_graph_roots_.back());
+      case 9 /* bugreport_score */:
+        field.get(&bugreport_score_);
         break;
-      case 33 /* function_graph_max_depth */:
-        field.get(&function_graph_max_depth_);
+      case 10 /* bugreport_filename */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &bugreport_filename_);
         break;
-      case 30 /* kprobe_events */:
-        kprobe_events_.emplace_back();
-        kprobe_events_.back().ParseFromArray(field.data(), field.size());
+      case 11 /* is_started */:
+        field.get(&is_started_);
         break;
-      case 23 /* preserve_ftrace_buffer */:
-        field.get(&preserve_ftrace_buffer_);
-        break;
-      case 24 /* use_monotonic_raw_clock */:
-        field.get(&use_monotonic_raw_clock_);
-        break;
-      case 25 /* instance_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &instance_name_);
-        break;
-      case 31 /* debug_ftrace_abi */:
-        field.get(&debug_ftrace_abi_);
-        break;
-      case 35 /* tids_to_trace */:
-        tids_to_trace_.emplace_back();
-        field.get(&tids_to_trace_.back());
-        break;
-      case 36 /* tracefs_options */:
-        tracefs_options_.emplace_back();
-        tracefs_options_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 37 /* tracing_cpumask */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &tracing_cpumask_);
-        break;
-      case 14 /* initialize_ksyms_synchronously_for_testing */:
-        field.get(&initialize_ksyms_synchronously_for_testing_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
         break;
     }
   }
   return !packed_error && !dec.bytes_left();
 }

-std::string FtraceConfig::SerializeAsString() const {
+std::string TracingServiceState_TracingSession::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> FtraceConfig::SerializeAsArray() const {
+std::vector<uint8_t> TracingServiceState_TracingSession::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void FtraceConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: ftrace_events
-  for (auto& it : ftrace_events_) {
-    ::protozero::internal::gen_helpers::SerializeString(1, it, msg);
-  }
-
-  // Field 2: atrace_categories
-  for (auto& it : atrace_categories_) {
-    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
-  }
-
-  // Field 3: atrace_apps
-  for (auto& it : atrace_apps_) {
-    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
-  }
-
-  // Field 28: atrace_categories_prefer_sdk
-  for (auto& it : atrace_categories_prefer_sdk_) {
-    ::protozero::internal::gen_helpers::SerializeString(28, it, msg);
-  }
-
-  // Field 34: atrace_userspace_only
-  if (_has_field_[34]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(34, atrace_userspace_only_, msg);
-  }
-
-  // Field 10: buffer_size_kb
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(10, buffer_size_kb_, msg);
-  }
-
-  // Field 27: buffer_size_lower_bound
-  if (_has_field_[27]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(27, buffer_size_lower_bound_, msg);
-  }
-
-  // Field 11: drain_period_ms
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(11, drain_period_ms_, msg);
-  }
-
-  // Field 29: drain_buffer_percent
-  if (_has_field_[29]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(29, drain_buffer_percent_, msg);
-  }
-
-  // Field 12: compact_sched
-  if (_has_field_[12]) {
-    (*compact_sched_).Serialize(msg->BeginNestedMessage<::protozero::Message>(12));
-  }
-
-  // Field 22: print_filter
-  if (_has_field_[22]) {
-    (*print_filter_).Serialize(msg->BeginNestedMessage<::protozero::Message>(22));
-  }
-
-  // Field 13: symbolize_ksyms
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(13, symbolize_ksyms_, msg);
-  }
-
-  // Field 17: ksyms_mem_policy
-  if (_has_field_[17]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(17, ksyms_mem_policy_, msg);
-  }
-
-  // Field 15: throttle_rss_stat
-  if (_has_field_[15]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(15, throttle_rss_stat_, msg);
-  }
-
-  // Field 32: denser_generic_event_encoding
-  if (_has_field_[32]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(32, denser_generic_event_encoding_, msg);
-  }
-
-  // Field 16: disable_generic_events
-  if (_has_field_[16]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(16, disable_generic_events_, msg);
-  }
-
-  // Field 18: syscall_events
-  for (auto& it : syscall_events_) {
-    ::protozero::internal::gen_helpers::SerializeString(18, it, msg);
-  }
-
-  // Field 19: enable_function_graph
-  if (_has_field_[19]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(19, enable_function_graph_, msg);
-  }
-
-  // Field 20: function_filters
-  for (auto& it : function_filters_) {
-    ::protozero::internal::gen_helpers::SerializeString(20, it, msg);
-  }
-
-  // Field 21: function_graph_roots
-  for (auto& it : function_graph_roots_) {
-    ::protozero::internal::gen_helpers::SerializeString(21, it, msg);
+void TracingServiceState_TracingSession::Serialize(::protozero::Message* msg) const {
+  // Field 1: id
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, id_, msg);
   }

-  // Field 33: function_graph_max_depth
-  if (_has_field_[33]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(33, function_graph_max_depth_, msg);
+  // Field 2: consumer_uid
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, consumer_uid_, msg);
   }

-  // Field 30: kprobe_events
-  for (auto& it : kprobe_events_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(30));
+  // Field 3: state
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeString(3, state_, msg);
   }

-  // Field 23: preserve_ftrace_buffer
-  if (_has_field_[23]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(23, preserve_ftrace_buffer_, msg);
+  // Field 4: unique_session_name
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeString(4, unique_session_name_, msg);
   }

-  // Field 24: use_monotonic_raw_clock
-  if (_has_field_[24]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(24, use_monotonic_raw_clock_, msg);
+  // Field 5: buffer_size_kb
+  for (auto& it : buffer_size_kb_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, it, msg);
   }

-  // Field 25: instance_name
-  if (_has_field_[25]) {
-    ::protozero::internal::gen_helpers::SerializeString(25, instance_name_, msg);
+  // Field 6: duration_ms
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, duration_ms_, msg);
   }

-  // Field 31: debug_ftrace_abi
-  if (_has_field_[31]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(31, debug_ftrace_abi_, msg);
+  // Field 7: num_data_sources
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, num_data_sources_, msg);
   }

-  // Field 35: tids_to_trace
-  for (auto& it : tids_to_trace_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(35, it, msg);
+  // Field 8: start_realtime_ns
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, start_realtime_ns_, msg);
   }

-  // Field 36: tracefs_options
-  for (auto& it : tracefs_options_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(36));
+  // Field 9: bugreport_score
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(9, bugreport_score_, msg);
   }

-  // Field 37: tracing_cpumask
-  if (_has_field_[37]) {
-    ::protozero::internal::gen_helpers::SerializeString(37, tracing_cpumask_, msg);
+  // Field 10: bugreport_filename
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeString(10, bugreport_filename_, msg);
   }

-  // Field 14: initialize_ksyms_synchronously_for_testing
-  if (_has_field_[14]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(14, initialize_ksyms_synchronously_for_testing_, msg);
+  // Field 11: is_started
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(11, is_started_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-FtraceConfig_TracefsOption::FtraceConfig_TracefsOption() = default;
-FtraceConfig_TracefsOption::~FtraceConfig_TracefsOption() = default;
-FtraceConfig_TracefsOption::FtraceConfig_TracefsOption(const FtraceConfig_TracefsOption&) = default;
-FtraceConfig_TracefsOption& FtraceConfig_TracefsOption::operator=(const FtraceConfig_TracefsOption&) = default;
-FtraceConfig_TracefsOption::FtraceConfig_TracefsOption(FtraceConfig_TracefsOption&&) noexcept = default;
-FtraceConfig_TracefsOption& FtraceConfig_TracefsOption::operator=(FtraceConfig_TracefsOption&&) = default;
+TracingServiceState_DataSource::TracingServiceState_DataSource() = default;
+TracingServiceState_DataSource::~TracingServiceState_DataSource() = default;
+TracingServiceState_DataSource::TracingServiceState_DataSource(const TracingServiceState_DataSource&) = default;
+TracingServiceState_DataSource& TracingServiceState_DataSource::operator=(const TracingServiceState_DataSource&) = default;
+TracingServiceState_DataSource::TracingServiceState_DataSource(TracingServiceState_DataSource&&) noexcept = default;
+TracingServiceState_DataSource& TracingServiceState_DataSource::operator=(TracingServiceState_DataSource&&) = default;

-bool FtraceConfig_TracefsOption::operator==(const FtraceConfig_TracefsOption& other) const {
+bool TracingServiceState_DataSource::operator==(const TracingServiceState_DataSource& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(state_, other.state_);
+   && ::protozero::internal::gen_helpers::EqualsField(ds_descriptor_, other.ds_descriptor_)
+   && ::protozero::internal::gen_helpers::EqualsField(producer_id_, other.producer_id_);
 }

-bool FtraceConfig_TracefsOption::ParseFromArray(const void* raw, size_t size) {
+bool TracingServiceState_DataSource::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -23708,11 +23578,11 @@ bool FtraceConfig_TracefsOption::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+      case 1 /* ds_descriptor */:
+        (*ds_descriptor_).ParseFromArray(field.data(), field.size());
         break;
-      case 2 /* state */:
-        field.get(&state_);
+      case 2 /* producer_id */:
+        field.get(&producer_id_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -23722,47 +23592,53 @@ bool FtraceConfig_TracefsOption::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string FtraceConfig_TracefsOption::SerializeAsString() const {
+std::string TracingServiceState_DataSource::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> FtraceConfig_TracefsOption::SerializeAsArray() const {
+std::vector<uint8_t> TracingServiceState_DataSource::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void FtraceConfig_TracefsOption::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
+void TracingServiceState_DataSource::Serialize(::protozero::Message* msg) const {
+  // Field 1: ds_descriptor
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+    (*ds_descriptor_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 2: state
+  // Field 2: producer_id
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, state_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, producer_id_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-FtraceConfig_KprobeEvent::FtraceConfig_KprobeEvent() = default;
-FtraceConfig_KprobeEvent::~FtraceConfig_KprobeEvent() = default;
-FtraceConfig_KprobeEvent::FtraceConfig_KprobeEvent(const FtraceConfig_KprobeEvent&) = default;
-FtraceConfig_KprobeEvent& FtraceConfig_KprobeEvent::operator=(const FtraceConfig_KprobeEvent&) = default;
-FtraceConfig_KprobeEvent::FtraceConfig_KprobeEvent(FtraceConfig_KprobeEvent&&) noexcept = default;
-FtraceConfig_KprobeEvent& FtraceConfig_KprobeEvent::operator=(FtraceConfig_KprobeEvent&&) = default;
+TracingServiceState_Producer::TracingServiceState_Producer() = default;
+TracingServiceState_Producer::~TracingServiceState_Producer() = default;
+TracingServiceState_Producer::TracingServiceState_Producer(const TracingServiceState_Producer&) = default;
+TracingServiceState_Producer& TracingServiceState_Producer::operator=(const TracingServiceState_Producer&) = default;
+TracingServiceState_Producer::TracingServiceState_Producer(TracingServiceState_Producer&&) noexcept = default;
+TracingServiceState_Producer& TracingServiceState_Producer::operator=(TracingServiceState_Producer&&) = default;

-bool FtraceConfig_KprobeEvent::operator==(const FtraceConfig_KprobeEvent& other) const {
+bool TracingServiceState_Producer::operator==(const TracingServiceState_Producer& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(probe_, other.probe_)
-   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_);
+   && ::protozero::internal::gen_helpers::EqualsField(id_, other.id_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(pid_, other.pid_)
+   && ::protozero::internal::gen_helpers::EqualsField(uid_, other.uid_)
+   && ::protozero::internal::gen_helpers::EqualsField(sdk_version_, other.sdk_version_)
+   && ::protozero::internal::gen_helpers::EqualsField(frozen_, other.frozen_)
+   && ::protozero::internal::gen_helpers::EqualsField(machine_id_, other.machine_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(machine_name_, other.machine_name_);
 }

-bool FtraceConfig_KprobeEvent::ParseFromArray(const void* raw, size_t size) {
+bool TracingServiceState_Producer::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -23772,11 +23648,29 @@ bool FtraceConfig_KprobeEvent::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* probe */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &probe_);
+      case 1 /* id */:
+        field.get(&id_);
         break;
-      case 2 /* type */:
-        field.get(&type_);
+      case 2 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      case 5 /* pid */:
+        field.get(&pid_);
+        break;
+      case 3 /* uid */:
+        field.get(&uid_);
+        break;
+      case 4 /* sdk_version */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &sdk_version_);
+        break;
+      case 6 /* frozen */:
+        field.get(&frozen_);
+        break;
+      case 7 /* machine_id */:
+        field.get(&machine_id_);
+        break;
+      case 8 /* machine_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &machine_name_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -23786,108 +23680,102 @@ bool FtraceConfig_KprobeEvent::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string FtraceConfig_KprobeEvent::SerializeAsString() const {
+std::string TracingServiceState_Producer::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> FtraceConfig_KprobeEvent::SerializeAsArray() const {
+std::vector<uint8_t> TracingServiceState_Producer::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void FtraceConfig_KprobeEvent::Serialize(::protozero::Message* msg) const {
-  // Field 1: probe
+void TracingServiceState_Producer::Serialize(::protozero::Message* msg) const {
+  // Field 1: id
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, probe_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, id_, msg);
   }

-  // Field 2: type
+  // Field 2: name
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, type_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
-
-
-FtraceConfig_PrintFilter::FtraceConfig_PrintFilter() = default;
-FtraceConfig_PrintFilter::~FtraceConfig_PrintFilter() = default;
-FtraceConfig_PrintFilter::FtraceConfig_PrintFilter(const FtraceConfig_PrintFilter&) = default;
-FtraceConfig_PrintFilter& FtraceConfig_PrintFilter::operator=(const FtraceConfig_PrintFilter&) = default;
-FtraceConfig_PrintFilter::FtraceConfig_PrintFilter(FtraceConfig_PrintFilter&&) noexcept = default;
-FtraceConfig_PrintFilter& FtraceConfig_PrintFilter::operator=(FtraceConfig_PrintFilter&&) = default;
-
-bool FtraceConfig_PrintFilter::operator==(const FtraceConfig_PrintFilter& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(rules_, other.rules_);
-}
+  // Field 5: pid
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, pid_, msg);
+  }

-int FtraceConfig_PrintFilter::rules_size() const { return static_cast<int>(rules_.size()); }
-void FtraceConfig_PrintFilter::clear_rules() { rules_.clear(); }
-FtraceConfig_PrintFilter_Rule* FtraceConfig_PrintFilter::add_rules() { rules_.emplace_back(); return &rules_.back(); }
-bool FtraceConfig_PrintFilter::ParseFromArray(const void* raw, size_t size) {
-  rules_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
+  // Field 3: uid
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, uid_, msg);
+  }

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* rules */:
-        rules_.emplace_back();
-        rules_.back().ParseFromArray(field.data(), field.size());
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
+  // Field 4: sdk_version
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeString(4, sdk_version_, msg);
   }
-  return !packed_error && !dec.bytes_left();
-}

-std::string FtraceConfig_PrintFilter::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+  // Field 6: frozen
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(6, frozen_, msg);
+  }

-std::vector<uint8_t> FtraceConfig_PrintFilter::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  // Field 7: machine_id
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, machine_id_, msg);
+  }

-void FtraceConfig_PrintFilter::Serialize(::protozero::Message* msg) const {
-  // Field 1: rules
-  for (auto& it : rules_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+  // Field 8: machine_name
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeString(8, machine_name_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/track_event_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/track_event_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-FtraceConfig_PrintFilter_Rule::FtraceConfig_PrintFilter_Rule() = default;
-FtraceConfig_PrintFilter_Rule::~FtraceConfig_PrintFilter_Rule() = default;
-FtraceConfig_PrintFilter_Rule::FtraceConfig_PrintFilter_Rule(const FtraceConfig_PrintFilter_Rule&) = default;
-FtraceConfig_PrintFilter_Rule& FtraceConfig_PrintFilter_Rule::operator=(const FtraceConfig_PrintFilter_Rule&) = default;
-FtraceConfig_PrintFilter_Rule::FtraceConfig_PrintFilter_Rule(FtraceConfig_PrintFilter_Rule&&) noexcept = default;
-FtraceConfig_PrintFilter_Rule& FtraceConfig_PrintFilter_Rule::operator=(FtraceConfig_PrintFilter_Rule&&) = default;
+TrackEventDescriptor::TrackEventDescriptor() = default;
+TrackEventDescriptor::~TrackEventDescriptor() = default;
+TrackEventDescriptor::TrackEventDescriptor(const TrackEventDescriptor&) = default;
+TrackEventDescriptor& TrackEventDescriptor::operator=(const TrackEventDescriptor&) = default;
+TrackEventDescriptor::TrackEventDescriptor(TrackEventDescriptor&&) noexcept = default;
+TrackEventDescriptor& TrackEventDescriptor::operator=(TrackEventDescriptor&&) = default;

-bool FtraceConfig_PrintFilter_Rule::operator==(const FtraceConfig_PrintFilter_Rule& other) const {
+bool TrackEventDescriptor::operator==(const TrackEventDescriptor& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(prefix_, other.prefix_)
-   && ::protozero::internal::gen_helpers::EqualsField(atrace_msg_, other.atrace_msg_)
-   && ::protozero::internal::gen_helpers::EqualsField(allow_, other.allow_);
+   && ::protozero::internal::gen_helpers::EqualsField(available_categories_, other.available_categories_);
 }

-bool FtraceConfig_PrintFilter_Rule::ParseFromArray(const void* raw, size_t size) {
+int TrackEventDescriptor::available_categories_size() const { return static_cast<int>(available_categories_.size()); }
+void TrackEventDescriptor::clear_available_categories() { available_categories_.clear(); }
+TrackEventCategory* TrackEventDescriptor::add_available_categories() { available_categories_.emplace_back(); return &available_categories_.back(); }
+bool TrackEventDescriptor::ParseFromArray(const void* raw, size_t size) {
+  available_categories_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -23897,14 +23785,9 @@ bool FtraceConfig_PrintFilter_Rule::ParseFromArray(const void* raw, size_t size)
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* prefix */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &prefix_);
-        break;
-      case 3 /* atrace_msg */:
-        (*atrace_msg_).ParseFromArray(field.data(), field.size());
-        break;
-      case 2 /* allow */:
-        field.get(&allow_);
+      case 1 /* available_categories */:
+        available_categories_.emplace_back();
+        available_categories_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -23914,52 +23797,44 @@ bool FtraceConfig_PrintFilter_Rule::ParseFromArray(const void* raw, size_t size)
   return !packed_error && !dec.bytes_left();
 }

-std::string FtraceConfig_PrintFilter_Rule::SerializeAsString() const {
+std::string TrackEventDescriptor::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> FtraceConfig_PrintFilter_Rule::SerializeAsArray() const {
+std::vector<uint8_t> TrackEventDescriptor::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void FtraceConfig_PrintFilter_Rule::Serialize(::protozero::Message* msg) const {
-  // Field 1: prefix
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, prefix_, msg);
-  }
-
-  // Field 3: atrace_msg
-  if (_has_field_[3]) {
-    (*atrace_msg_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
-  }
-
-  // Field 2: allow
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, allow_, msg);
+void TrackEventDescriptor::Serialize(::protozero::Message* msg) const {
+  // Field 1: available_categories
+  for (auto& it : available_categories_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-FtraceConfig_PrintFilter_Rule_AtraceMessage::FtraceConfig_PrintFilter_Rule_AtraceMessage() = default;
-FtraceConfig_PrintFilter_Rule_AtraceMessage::~FtraceConfig_PrintFilter_Rule_AtraceMessage() = default;
-FtraceConfig_PrintFilter_Rule_AtraceMessage::FtraceConfig_PrintFilter_Rule_AtraceMessage(const FtraceConfig_PrintFilter_Rule_AtraceMessage&) = default;
-FtraceConfig_PrintFilter_Rule_AtraceMessage& FtraceConfig_PrintFilter_Rule_AtraceMessage::operator=(const FtraceConfig_PrintFilter_Rule_AtraceMessage&) = default;
-FtraceConfig_PrintFilter_Rule_AtraceMessage::FtraceConfig_PrintFilter_Rule_AtraceMessage(FtraceConfig_PrintFilter_Rule_AtraceMessage&&) noexcept = default;
-FtraceConfig_PrintFilter_Rule_AtraceMessage& FtraceConfig_PrintFilter_Rule_AtraceMessage::operator=(FtraceConfig_PrintFilter_Rule_AtraceMessage&&) = default;
+TrackEventCategory::TrackEventCategory() = default;
+TrackEventCategory::~TrackEventCategory() = default;
+TrackEventCategory::TrackEventCategory(const TrackEventCategory&) = default;
+TrackEventCategory& TrackEventCategory::operator=(const TrackEventCategory&) = default;
+TrackEventCategory::TrackEventCategory(TrackEventCategory&&) noexcept = default;
+TrackEventCategory& TrackEventCategory::operator=(TrackEventCategory&&) = default;

-bool FtraceConfig_PrintFilter_Rule_AtraceMessage::operator==(const FtraceConfig_PrintFilter_Rule_AtraceMessage& other) const {
+bool TrackEventCategory::operator==(const TrackEventCategory& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
-   && ::protozero::internal::gen_helpers::EqualsField(prefix_, other.prefix_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(description_, other.description_)
+   && ::protozero::internal::gen_helpers::EqualsField(tags_, other.tags_);
 }

-bool FtraceConfig_PrintFilter_Rule_AtraceMessage::ParseFromArray(const void* raw, size_t size) {
+bool TrackEventCategory::ParseFromArray(const void* raw, size_t size) {
+  tags_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -23969,11 +23844,15 @@ bool FtraceConfig_PrintFilter_Rule_AtraceMessage::ParseFromArray(const void* raw
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* type */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &type_);
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 2 /* prefix */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &prefix_);
+      case 2 /* description */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &description_);
+        break;
+      case 3 /* tags */:
+        tags_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &tags_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -23983,46 +23862,95 @@ bool FtraceConfig_PrintFilter_Rule_AtraceMessage::ParseFromArray(const void* raw
   return !packed_error && !dec.bytes_left();
 }

-std::string FtraceConfig_PrintFilter_Rule_AtraceMessage::SerializeAsString() const {
+std::string TrackEventCategory::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> FtraceConfig_PrintFilter_Rule_AtraceMessage::SerializeAsArray() const {
+std::vector<uint8_t> TrackEventCategory::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void FtraceConfig_PrintFilter_Rule_AtraceMessage::Serialize(::protozero::Message* msg) const {
-  // Field 1: type
+void TrackEventCategory::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, type_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  // Field 2: prefix
+  // Field 2: description
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, prefix_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(2, description_, msg);
+  }
+
+  // Field 3: tags
+  for (auto& it : tags_) {
+    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/semantic_type.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/common/semantic_type.gen.h"

-FtraceConfig_CompactSchedConfig::FtraceConfig_CompactSchedConfig() = default;
-FtraceConfig_CompactSchedConfig::~FtraceConfig_CompactSchedConfig() = default;
-FtraceConfig_CompactSchedConfig::FtraceConfig_CompactSchedConfig(const FtraceConfig_CompactSchedConfig&) = default;
-FtraceConfig_CompactSchedConfig& FtraceConfig_CompactSchedConfig::operator=(const FtraceConfig_CompactSchedConfig&) = default;
-FtraceConfig_CompactSchedConfig::FtraceConfig_CompactSchedConfig(FtraceConfig_CompactSchedConfig&&) noexcept = default;
-FtraceConfig_CompactSchedConfig& FtraceConfig_CompactSchedConfig::operator=(FtraceConfig_CompactSchedConfig&&) = default;
-
-bool FtraceConfig_CompactSchedConfig::operator==(const FtraceConfig_CompactSchedConfig& other) const {
+namespace perfetto {
+namespace protos {
+namespace gen {
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_aflags_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_aflags_config.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+AndroidAflagsConfig::AndroidAflagsConfig() = default;
+AndroidAflagsConfig::~AndroidAflagsConfig() = default;
+AndroidAflagsConfig::AndroidAflagsConfig(const AndroidAflagsConfig&) = default;
+AndroidAflagsConfig& AndroidAflagsConfig::operator=(const AndroidAflagsConfig&) = default;
+AndroidAflagsConfig::AndroidAflagsConfig(AndroidAflagsConfig&&) noexcept = default;
+AndroidAflagsConfig& AndroidAflagsConfig::operator=(AndroidAflagsConfig&&) = default;
+
+bool AndroidAflagsConfig::operator==(const AndroidAflagsConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(enabled_, other.enabled_);
+   && ::protozero::internal::gen_helpers::EqualsField(poll_ms_, other.poll_ms_);
 }

-bool FtraceConfig_CompactSchedConfig::ParseFromArray(const void* raw, size_t size) {
+bool AndroidAflagsConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -24032,8 +23960,8 @@ bool FtraceConfig_CompactSchedConfig::ParseFromArray(const void* raw, size_t siz
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* enabled */:
-        field.get(&enabled_);
+      case 1 /* poll_ms */:
+        field.get(&poll_ms_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -24043,22 +23971,22 @@ bool FtraceConfig_CompactSchedConfig::ParseFromArray(const void* raw, size_t siz
   return !packed_error && !dec.bytes_left();
 }

-std::string FtraceConfig_CompactSchedConfig::SerializeAsString() const {
+std::string AndroidAflagsConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> FtraceConfig_CompactSchedConfig::SerializeAsArray() const {
+std::vector<uint8_t> AndroidAflagsConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void FtraceConfig_CompactSchedConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: enabled
+void AndroidAflagsConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: poll_ms
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, enabled_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, poll_ms_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -24070,7 +23998,7 @@ void FtraceConfig_CompactSchedConfig::Serialize(::protozero::Message* msg) const
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/gpu/gpu_counter_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_game_intervention_list_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -24081,32 +24009,26 @@ void FtraceConfig_CompactSchedConfig::Serialize(::protozero::Message* msg) const
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_counter_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_game_intervention_list_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-GpuCounterConfig::GpuCounterConfig() = default;
-GpuCounterConfig::~GpuCounterConfig() = default;
-GpuCounterConfig::GpuCounterConfig(const GpuCounterConfig&) = default;
-GpuCounterConfig& GpuCounterConfig::operator=(const GpuCounterConfig&) = default;
-GpuCounterConfig::GpuCounterConfig(GpuCounterConfig&&) noexcept = default;
-GpuCounterConfig& GpuCounterConfig::operator=(GpuCounterConfig&&) = default;
+AndroidGameInterventionListConfig::AndroidGameInterventionListConfig() = default;
+AndroidGameInterventionListConfig::~AndroidGameInterventionListConfig() = default;
+AndroidGameInterventionListConfig::AndroidGameInterventionListConfig(const AndroidGameInterventionListConfig&) = default;
+AndroidGameInterventionListConfig& AndroidGameInterventionListConfig::operator=(const AndroidGameInterventionListConfig&) = default;
+AndroidGameInterventionListConfig::AndroidGameInterventionListConfig(AndroidGameInterventionListConfig&&) noexcept = default;
+AndroidGameInterventionListConfig& AndroidGameInterventionListConfig::operator=(AndroidGameInterventionListConfig&&) = default;

-bool GpuCounterConfig::operator==(const GpuCounterConfig& other) const {
+bool AndroidGameInterventionListConfig::operator==(const AndroidGameInterventionListConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(counter_period_ns_, other.counter_period_ns_)
-   && ::protozero::internal::gen_helpers::EqualsField(counter_ids_, other.counter_ids_)
-   && ::protozero::internal::gen_helpers::EqualsField(counter_names_, other.counter_names_)
-   && ::protozero::internal::gen_helpers::EqualsField(instrumented_sampling_, other.instrumented_sampling_)
-   && ::protozero::internal::gen_helpers::EqualsField(instrumented_sampling_config_, other.instrumented_sampling_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(fix_gpu_clock_, other.fix_gpu_clock_);
+   && ::protozero::internal::gen_helpers::EqualsField(package_name_filter_, other.package_name_filter_);
 }

-bool GpuCounterConfig::ParseFromArray(const void* raw, size_t size) {
-  counter_ids_.clear();
-  counter_names_.clear();
+bool AndroidGameInterventionListConfig::ParseFromArray(const void* raw, size_t size) {
+  package_name_filter_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -24116,25 +24038,9 @@ bool GpuCounterConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* counter_period_ns */:
-        field.get(&counter_period_ns_);
-        break;
-      case 2 /* counter_ids */:
-        counter_ids_.emplace_back();
-        field.get(&counter_ids_.back());
-        break;
-      case 6 /* counter_names */:
-        counter_names_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &counter_names_.back());
-        break;
-      case 3 /* instrumented_sampling */:
-        field.get(&instrumented_sampling_);
-        break;
-      case 5 /* instrumented_sampling_config */:
-        (*instrumented_sampling_config_).ParseFromArray(field.data(), field.size());
-        break;
-      case 4 /* fix_gpu_clock */:
-        field.get(&fix_gpu_clock_);
+      case 1 /* package_name_filter */:
+        package_name_filter_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &package_name_filter_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -24144,79 +24050,71 @@ bool GpuCounterConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string GpuCounterConfig::SerializeAsString() const {
+std::string AndroidGameInterventionListConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> GpuCounterConfig::SerializeAsArray() const {
+std::vector<uint8_t> AndroidGameInterventionListConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void GpuCounterConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: counter_period_ns
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, counter_period_ns_, msg);
-  }
-
-  // Field 2: counter_ids
-  for (auto& it : counter_ids_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
-  }
-
-  // Field 6: counter_names
-  for (auto& it : counter_names_) {
-    ::protozero::internal::gen_helpers::SerializeString(6, it, msg);
-  }
-
-  // Field 3: instrumented_sampling
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, instrumented_sampling_, msg);
-  }
-
-  // Field 5: instrumented_sampling_config
-  if (_has_field_[5]) {
-    (*instrumented_sampling_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
-  }
-
-  // Field 4: fix_gpu_clock
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, fix_gpu_clock_, msg);
+void AndroidGameInterventionListConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: package_name_filter
+  for (auto& it : package_name_filter_) {
+    ::protozero::internal::gen_helpers::SerializeString(1, it, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_input_event_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_input_event_config.gen.h"

-GpuCounterConfig_InstrumentedSamplingConfig::GpuCounterConfig_InstrumentedSamplingConfig() = default;
-GpuCounterConfig_InstrumentedSamplingConfig::~GpuCounterConfig_InstrumentedSamplingConfig() = default;
-GpuCounterConfig_InstrumentedSamplingConfig::GpuCounterConfig_InstrumentedSamplingConfig(const GpuCounterConfig_InstrumentedSamplingConfig&) = default;
-GpuCounterConfig_InstrumentedSamplingConfig& GpuCounterConfig_InstrumentedSamplingConfig::operator=(const GpuCounterConfig_InstrumentedSamplingConfig&) = default;
-GpuCounterConfig_InstrumentedSamplingConfig::GpuCounterConfig_InstrumentedSamplingConfig(GpuCounterConfig_InstrumentedSamplingConfig&&) noexcept = default;
-GpuCounterConfig_InstrumentedSamplingConfig& GpuCounterConfig_InstrumentedSamplingConfig::operator=(GpuCounterConfig_InstrumentedSamplingConfig&&) = default;
+namespace perfetto {
+namespace protos {
+namespace gen {

-bool GpuCounterConfig_InstrumentedSamplingConfig::operator==(const GpuCounterConfig_InstrumentedSamplingConfig& other) const {
+AndroidInputEventConfig::AndroidInputEventConfig() = default;
+AndroidInputEventConfig::~AndroidInputEventConfig() = default;
+AndroidInputEventConfig::AndroidInputEventConfig(const AndroidInputEventConfig&) = default;
+AndroidInputEventConfig& AndroidInputEventConfig::operator=(const AndroidInputEventConfig&) = default;
+AndroidInputEventConfig::AndroidInputEventConfig(AndroidInputEventConfig&&) noexcept = default;
+AndroidInputEventConfig& AndroidInputEventConfig::operator=(AndroidInputEventConfig&&) = default;
+
+bool AndroidInputEventConfig::operator==(const AndroidInputEventConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(activity_name_filters_, other.activity_name_filters_)
-   && ::protozero::internal::gen_helpers::EqualsField(activity_tx_include_globs_, other.activity_tx_include_globs_)
-   && ::protozero::internal::gen_helpers::EqualsField(activity_tx_exclude_globs_, other.activity_tx_exclude_globs_)
-   && ::protozero::internal::gen_helpers::EqualsField(activity_ranges_, other.activity_ranges_);
+   && ::protozero::internal::gen_helpers::EqualsField(mode_, other.mode_)
+   && ::protozero::internal::gen_helpers::EqualsField(rules_, other.rules_)
+   && ::protozero::internal::gen_helpers::EqualsField(trace_dispatcher_input_events_, other.trace_dispatcher_input_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(trace_dispatcher_window_dispatch_, other.trace_dispatcher_window_dispatch_)
+   && ::protozero::internal::gen_helpers::EqualsField(trace_evdev_events_, other.trace_evdev_events_);
 }

-int GpuCounterConfig_InstrumentedSamplingConfig::activity_name_filters_size() const { return static_cast<int>(activity_name_filters_.size()); }
-void GpuCounterConfig_InstrumentedSamplingConfig::clear_activity_name_filters() { activity_name_filters_.clear(); }
-GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter* GpuCounterConfig_InstrumentedSamplingConfig::add_activity_name_filters() { activity_name_filters_.emplace_back(); return &activity_name_filters_.back(); }
-int GpuCounterConfig_InstrumentedSamplingConfig::activity_ranges_size() const { return static_cast<int>(activity_ranges_.size()); }
-void GpuCounterConfig_InstrumentedSamplingConfig::clear_activity_ranges() { activity_ranges_.clear(); }
-GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange* GpuCounterConfig_InstrumentedSamplingConfig::add_activity_ranges() { activity_ranges_.emplace_back(); return &activity_ranges_.back(); }
-bool GpuCounterConfig_InstrumentedSamplingConfig::ParseFromArray(const void* raw, size_t size) {
-  activity_name_filters_.clear();
-  activity_tx_include_globs_.clear();
-  activity_tx_exclude_globs_.clear();
-  activity_ranges_.clear();
+int AndroidInputEventConfig::rules_size() const { return static_cast<int>(rules_.size()); }
+void AndroidInputEventConfig::clear_rules() { rules_.clear(); }
+AndroidInputEventConfig_TraceRule* AndroidInputEventConfig::add_rules() { rules_.emplace_back(); return &rules_.back(); }
+bool AndroidInputEventConfig::ParseFromArray(const void* raw, size_t size) {
+  rules_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -24226,21 +24124,21 @@ bool GpuCounterConfig_InstrumentedSamplingConfig::ParseFromArray(const void* raw
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 3 /* activity_name_filters */:
-        activity_name_filters_.emplace_back();
-        activity_name_filters_.back().ParseFromArray(field.data(), field.size());
+      case 1 /* mode */:
+        field.get(&mode_);
         break;
-      case 6 /* activity_tx_include_globs */:
-        activity_tx_include_globs_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &activity_tx_include_globs_.back());
+      case 2 /* rules */:
+        rules_.emplace_back();
+        rules_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 7 /* activity_tx_exclude_globs */:
-        activity_tx_exclude_globs_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &activity_tx_exclude_globs_.back());
+      case 3 /* trace_dispatcher_input_events */:
+        field.get(&trace_dispatcher_input_events_);
         break;
-      case 5 /* activity_ranges */:
-        activity_ranges_.emplace_back();
-        activity_ranges_.back().ParseFromArray(field.data(), field.size());
+      case 4 /* trace_dispatcher_window_dispatch */:
+        field.get(&trace_dispatcher_window_dispatch_);
+        break;
+      case 5 /* trace_evdev_events */:
+        field.get(&trace_evdev_events_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -24250,57 +24148,67 @@ bool GpuCounterConfig_InstrumentedSamplingConfig::ParseFromArray(const void* raw
   return !packed_error && !dec.bytes_left();
 }

-std::string GpuCounterConfig_InstrumentedSamplingConfig::SerializeAsString() const {
+std::string AndroidInputEventConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> GpuCounterConfig_InstrumentedSamplingConfig::SerializeAsArray() const {
+std::vector<uint8_t> AndroidInputEventConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void GpuCounterConfig_InstrumentedSamplingConfig::Serialize(::protozero::Message* msg) const {
-  // Field 3: activity_name_filters
-  for (auto& it : activity_name_filters_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+void AndroidInputEventConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: mode
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, mode_, msg);
   }

-  // Field 6: activity_tx_include_globs
-  for (auto& it : activity_tx_include_globs_) {
-    ::protozero::internal::gen_helpers::SerializeString(6, it, msg);
+  // Field 2: rules
+  for (auto& it : rules_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

-  // Field 7: activity_tx_exclude_globs
-  for (auto& it : activity_tx_exclude_globs_) {
-    ::protozero::internal::gen_helpers::SerializeString(7, it, msg);
+  // Field 3: trace_dispatcher_input_events
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, trace_dispatcher_input_events_, msg);
   }

-  // Field 5: activity_ranges
-  for (auto& it : activity_ranges_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
+  // Field 4: trace_dispatcher_window_dispatch
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, trace_dispatcher_window_dispatch_, msg);
+  }
+
+  // Field 5: trace_evdev_events
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, trace_evdev_events_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange() = default;
-GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::~GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange() = default;
-GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange(const GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange&) = default;
-GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange& GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::operator=(const GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange&) = default;
-GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange(GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange&&) noexcept = default;
-GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange& GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::operator=(GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange&&) = default;
+AndroidInputEventConfig_TraceRule::AndroidInputEventConfig_TraceRule() = default;
+AndroidInputEventConfig_TraceRule::~AndroidInputEventConfig_TraceRule() = default;
+AndroidInputEventConfig_TraceRule::AndroidInputEventConfig_TraceRule(const AndroidInputEventConfig_TraceRule&) = default;
+AndroidInputEventConfig_TraceRule& AndroidInputEventConfig_TraceRule::operator=(const AndroidInputEventConfig_TraceRule&) = default;
+AndroidInputEventConfig_TraceRule::AndroidInputEventConfig_TraceRule(AndroidInputEventConfig_TraceRule&&) noexcept = default;
+AndroidInputEventConfig_TraceRule& AndroidInputEventConfig_TraceRule::operator=(AndroidInputEventConfig_TraceRule&&) = default;

-bool GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::operator==(const GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange& other) const {
+bool AndroidInputEventConfig_TraceRule::operator==(const AndroidInputEventConfig_TraceRule& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(skip_, other.skip_)
-   && ::protozero::internal::gen_helpers::EqualsField(count_, other.count_);
+   && ::protozero::internal::gen_helpers::EqualsField(trace_level_, other.trace_level_)
+   && ::protozero::internal::gen_helpers::EqualsField(match_all_packages_, other.match_all_packages_)
+   && ::protozero::internal::gen_helpers::EqualsField(match_any_packages_, other.match_any_packages_)
+   && ::protozero::internal::gen_helpers::EqualsField(match_secure_, other.match_secure_)
+   && ::protozero::internal::gen_helpers::EqualsField(match_ime_connection_active_, other.match_ime_connection_active_);
 }

-bool GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::ParseFromArray(const void* raw, size_t size) {
+bool AndroidInputEventConfig_TraceRule::ParseFromArray(const void* raw, size_t size) {
+  match_all_packages_.clear();
+  match_any_packages_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -24310,11 +24218,22 @@ bool GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::ParseFromArray(c
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* skip */:
-        field.get(&skip_);
+      case 1 /* trace_level */:
+        field.get(&trace_level_);
         break;
-      case 2 /* count */:
-        field.get(&count_);
+      case 2 /* match_all_packages */:
+        match_all_packages_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &match_all_packages_.back());
+        break;
+      case 3 /* match_any_packages */:
+        match_any_packages_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &match_any_packages_.back());
+        break;
+      case 4 /* match_secure */:
+        field.get(&match_secure_);
+        break;
+      case 5 /* match_ime_connection_active */:
+        field.get(&match_ime_connection_active_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -24324,47 +24243,89 @@ bool GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::ParseFromArray(c
   return !packed_error && !dec.bytes_left();
 }

-std::string GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::SerializeAsString() const {
+std::string AndroidInputEventConfig_TraceRule::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::SerializeAsArray() const {
+std::vector<uint8_t> AndroidInputEventConfig_TraceRule::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::Serialize(::protozero::Message* msg) const {
-  // Field 1: skip
+void AndroidInputEventConfig_TraceRule::Serialize(::protozero::Message* msg) const {
+  // Field 1: trace_level
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, skip_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, trace_level_, msg);
   }

-  // Field 2: count
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, count_, msg);
+  // Field 2: match_all_packages
+  for (auto& it : match_all_packages_) {
+    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
+  }
+
+  // Field 3: match_any_packages
+  for (auto& it : match_any_packages_) {
+    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
+  }
+
+  // Field 4: match_secure
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, match_secure_, msg);
+  }
+
+  // Field 5: match_ime_connection_active
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, match_ime_connection_active_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_log_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_log_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/android_log_constants.gen.h"

-GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter() = default;
-GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::~GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter() = default;
-GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter(const GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter&) = default;
-GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter& GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::operator=(const GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter&) = default;
-GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter(GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter&&) noexcept = default;
-GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter& GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::operator=(GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter&&) = default;
+namespace perfetto {
+namespace protos {
+namespace gen {

-bool GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::operator==(const GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter& other) const {
+AndroidLogConfig::AndroidLogConfig() = default;
+AndroidLogConfig::~AndroidLogConfig() = default;
+AndroidLogConfig::AndroidLogConfig(const AndroidLogConfig&) = default;
+AndroidLogConfig& AndroidLogConfig::operator=(const AndroidLogConfig&) = default;
+AndroidLogConfig::AndroidLogConfig(AndroidLogConfig&&) noexcept = default;
+AndroidLogConfig& AndroidLogConfig::operator=(AndroidLogConfig&&) = default;
+
+bool AndroidLogConfig::operator==(const AndroidLogConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_glob_, other.name_glob_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_base_, other.name_base_);
+   && ::protozero::internal::gen_helpers::EqualsField(log_ids_, other.log_ids_)
+   && ::protozero::internal::gen_helpers::EqualsField(min_prio_, other.min_prio_)
+   && ::protozero::internal::gen_helpers::EqualsField(filter_tags_, other.filter_tags_)
+   && ::protozero::internal::gen_helpers::EqualsField(preserve_log_buffer_, other.preserve_log_buffer_);
 }

-bool GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::ParseFromArray(const void* raw, size_t size) {
+bool AndroidLogConfig::ParseFromArray(const void* raw, size_t size) {
+  log_ids_.clear();
+  filter_tags_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -24374,11 +24335,19 @@ bool GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::ParseFromAr
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* name_glob */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_glob_);
+      case 1 /* log_ids */:
+        log_ids_.emplace_back();
+        field.get(&log_ids_.back());
         break;
-      case 2 /* name_base */:
-        field.get(&name_base_);
+      case 3 /* min_prio */:
+        field.get(&min_prio_);
+        break;
+      case 4 /* filter_tags */:
+        filter_tags_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &filter_tags_.back());
+        break;
+      case 5 /* preserve_log_buffer */:
+        field.get(&preserve_log_buffer_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -24388,27 +24357,37 @@ bool GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::ParseFromAr
   return !packed_error && !dec.bytes_left();
 }

-std::string GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::SerializeAsString() const {
+std::string AndroidLogConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::SerializeAsArray() const {
+std::vector<uint8_t> AndroidLogConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::Serialize(::protozero::Message* msg) const {
-  // Field 1: name_glob
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_glob_, msg);
+void AndroidLogConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: log_ids
+  for (auto& it : log_ids_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
   }

-  // Field 2: name_base
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, name_base_, msg);
+  // Field 3: min_prio
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, min_prio_, msg);
+  }
+
+  // Field 4: filter_tags
+  for (auto& it : filter_tags_) {
+    ::protozero::internal::gen_helpers::SerializeString(4, it, msg);
+  }
+
+  // Field 5: preserve_log_buffer
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, preserve_log_buffer_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -24420,7 +24399,7 @@ void GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::Serialize(:
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/gpu/gpu_renderstages_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_polled_state_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -24431,28 +24410,25 @@ void GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::Serialize(:
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_renderstages_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_polled_state_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-GpuRenderStagesConfig::GpuRenderStagesConfig() = default;
-GpuRenderStagesConfig::~GpuRenderStagesConfig() = default;
-GpuRenderStagesConfig::GpuRenderStagesConfig(const GpuRenderStagesConfig&) = default;
-GpuRenderStagesConfig& GpuRenderStagesConfig::operator=(const GpuRenderStagesConfig&) = default;
-GpuRenderStagesConfig::GpuRenderStagesConfig(GpuRenderStagesConfig&&) noexcept = default;
-GpuRenderStagesConfig& GpuRenderStagesConfig::operator=(GpuRenderStagesConfig&&) = default;
+AndroidPolledStateConfig::AndroidPolledStateConfig() = default;
+AndroidPolledStateConfig::~AndroidPolledStateConfig() = default;
+AndroidPolledStateConfig::AndroidPolledStateConfig(const AndroidPolledStateConfig&) = default;
+AndroidPolledStateConfig& AndroidPolledStateConfig::operator=(const AndroidPolledStateConfig&) = default;
+AndroidPolledStateConfig::AndroidPolledStateConfig(AndroidPolledStateConfig&&) noexcept = default;
+AndroidPolledStateConfig& AndroidPolledStateConfig::operator=(AndroidPolledStateConfig&&) = default;

-bool GpuRenderStagesConfig::operator==(const GpuRenderStagesConfig& other) const {
+bool AndroidPolledStateConfig::operator==(const AndroidPolledStateConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(full_loadstore_, other.full_loadstore_)
-   && ::protozero::internal::gen_helpers::EqualsField(low_overhead_, other.low_overhead_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_metrics_, other.trace_metrics_);
+   && ::protozero::internal::gen_helpers::EqualsField(poll_ms_, other.poll_ms_);
 }

-bool GpuRenderStagesConfig::ParseFromArray(const void* raw, size_t size) {
-  trace_metrics_.clear();
+bool AndroidPolledStateConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -24462,15 +24438,8 @@ bool GpuRenderStagesConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* full_loadstore */:
-        field.get(&full_loadstore_);
-        break;
-      case 2 /* low_overhead */:
-        field.get(&low_overhead_);
-        break;
-      case 3 /* trace_metrics */:
-        trace_metrics_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &trace_metrics_.back());
+      case 1 /* poll_ms */:
+        field.get(&poll_ms_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -24480,32 +24449,22 @@ bool GpuRenderStagesConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string GpuRenderStagesConfig::SerializeAsString() const {
+std::string AndroidPolledStateConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> GpuRenderStagesConfig::SerializeAsArray() const {
+std::vector<uint8_t> AndroidPolledStateConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void GpuRenderStagesConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: full_loadstore
+void AndroidPolledStateConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: poll_ms
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, full_loadstore_, msg);
-  }
-
-  // Field 2: low_overhead
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, low_overhead_, msg);
-  }
-
-  // Field 3: trace_metrics
-  for (auto& it : trace_metrics_) {
-    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, poll_ms_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -24517,7 +24476,7 @@ void GpuRenderStagesConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/gpu/vulkan_memory_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_process_state_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -24528,26 +24487,25 @@ void GpuRenderStagesConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/vulkan_memory_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_process_state_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-VulkanMemoryConfig::VulkanMemoryConfig() = default;
-VulkanMemoryConfig::~VulkanMemoryConfig() = default;
-VulkanMemoryConfig::VulkanMemoryConfig(const VulkanMemoryConfig&) = default;
-VulkanMemoryConfig& VulkanMemoryConfig::operator=(const VulkanMemoryConfig&) = default;
-VulkanMemoryConfig::VulkanMemoryConfig(VulkanMemoryConfig&&) noexcept = default;
-VulkanMemoryConfig& VulkanMemoryConfig::operator=(VulkanMemoryConfig&&) = default;
+AndroidProcessStateConfig::AndroidProcessStateConfig() = default;
+AndroidProcessStateConfig::~AndroidProcessStateConfig() = default;
+AndroidProcessStateConfig::AndroidProcessStateConfig(const AndroidProcessStateConfig&) = default;
+AndroidProcessStateConfig& AndroidProcessStateConfig::operator=(const AndroidProcessStateConfig&) = default;
+AndroidProcessStateConfig::AndroidProcessStateConfig(AndroidProcessStateConfig&&) noexcept = default;
+AndroidProcessStateConfig& AndroidProcessStateConfig::operator=(AndroidProcessStateConfig&&) = default;

-bool VulkanMemoryConfig::operator==(const VulkanMemoryConfig& other) const {
+bool AndroidProcessStateConfig::operator==(const AndroidProcessStateConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(track_driver_memory_usage_, other.track_driver_memory_usage_)
-   && ::protozero::internal::gen_helpers::EqualsField(track_device_memory_usage_, other.track_device_memory_usage_);
+   && ::protozero::internal::gen_helpers::EqualsField(dump_process_metadata_, other.dump_process_metadata_);
 }

-bool VulkanMemoryConfig::ParseFromArray(const void* raw, size_t size) {
+bool AndroidProcessStateConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -24557,11 +24515,8 @@ bool VulkanMemoryConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* track_driver_memory_usage */:
-        field.get(&track_driver_memory_usage_);
-        break;
-      case 2 /* track_device_memory_usage */:
-        field.get(&track_device_memory_usage_);
+      case 1 /* dump_process_metadata */:
+        field.get(&dump_process_metadata_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -24571,27 +24526,22 @@ bool VulkanMemoryConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string VulkanMemoryConfig::SerializeAsString() const {
+std::string AndroidProcessStateConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> VulkanMemoryConfig::SerializeAsArray() const {
+std::vector<uint8_t> AndroidProcessStateConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void VulkanMemoryConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: track_driver_memory_usage
+void AndroidProcessStateConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: dump_process_metadata
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, track_driver_memory_usage_, msg);
-  }
-
-  // Field 2: track_device_memory_usage
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, track_device_memory_usage_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, dump_process_metadata_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -24603,7 +24553,7 @@ void VulkanMemoryConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/inode_file/inode_file_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_sdk_sysprop_guard_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -24614,35 +24564,28 @@ void VulkanMemoryConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/inode_file/inode_file_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_sdk_sysprop_guard_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-InodeFileConfig::InodeFileConfig() = default;
-InodeFileConfig::~InodeFileConfig() = default;
-InodeFileConfig::InodeFileConfig(const InodeFileConfig&) = default;
-InodeFileConfig& InodeFileConfig::operator=(const InodeFileConfig&) = default;
-InodeFileConfig::InodeFileConfig(InodeFileConfig&&) noexcept = default;
-InodeFileConfig& InodeFileConfig::operator=(InodeFileConfig&&) = default;
+AndroidSdkSyspropGuardConfig::AndroidSdkSyspropGuardConfig() = default;
+AndroidSdkSyspropGuardConfig::~AndroidSdkSyspropGuardConfig() = default;
+AndroidSdkSyspropGuardConfig::AndroidSdkSyspropGuardConfig(const AndroidSdkSyspropGuardConfig&) = default;
+AndroidSdkSyspropGuardConfig& AndroidSdkSyspropGuardConfig::operator=(const AndroidSdkSyspropGuardConfig&) = default;
+AndroidSdkSyspropGuardConfig::AndroidSdkSyspropGuardConfig(AndroidSdkSyspropGuardConfig&&) noexcept = default;
+AndroidSdkSyspropGuardConfig& AndroidSdkSyspropGuardConfig::operator=(AndroidSdkSyspropGuardConfig&&) = default;

-bool InodeFileConfig::operator==(const InodeFileConfig& other) const {
+bool AndroidSdkSyspropGuardConfig::operator==(const AndroidSdkSyspropGuardConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(scan_interval_ms_, other.scan_interval_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(scan_delay_ms_, other.scan_delay_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(scan_batch_size_, other.scan_batch_size_)
-   && ::protozero::internal::gen_helpers::EqualsField(do_not_scan_, other.do_not_scan_)
-   && ::protozero::internal::gen_helpers::EqualsField(scan_mount_points_, other.scan_mount_points_)
-   && ::protozero::internal::gen_helpers::EqualsField(mount_point_mapping_, other.mount_point_mapping_);
+   && ::protozero::internal::gen_helpers::EqualsField(surfaceflinger_skia_track_events_, other.surfaceflinger_skia_track_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(hwui_skia_track_events_, other.hwui_skia_track_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(hwui_package_name_filter_, other.hwui_package_name_filter_);
 }

-int InodeFileConfig::mount_point_mapping_size() const { return static_cast<int>(mount_point_mapping_.size()); }
-void InodeFileConfig::clear_mount_point_mapping() { mount_point_mapping_.clear(); }
-InodeFileConfig_MountPointMappingEntry* InodeFileConfig::add_mount_point_mapping() { mount_point_mapping_.emplace_back(); return &mount_point_mapping_.back(); }
-bool InodeFileConfig::ParseFromArray(const void* raw, size_t size) {
-  scan_mount_points_.clear();
-  mount_point_mapping_.clear();
+bool AndroidSdkSyspropGuardConfig::ParseFromArray(const void* raw, size_t size) {
+  hwui_package_name_filter_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -24652,25 +24595,15 @@ bool InodeFileConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* scan_interval_ms */:
-        field.get(&scan_interval_ms_);
-        break;
-      case 2 /* scan_delay_ms */:
-        field.get(&scan_delay_ms_);
-        break;
-      case 3 /* scan_batch_size */:
-        field.get(&scan_batch_size_);
-        break;
-      case 4 /* do_not_scan */:
-        field.get(&do_not_scan_);
+      case 1 /* surfaceflinger_skia_track_events */:
+        field.get(&surfaceflinger_skia_track_events_);
         break;
-      case 5 /* scan_mount_points */:
-        scan_mount_points_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &scan_mount_points_.back());
+      case 2 /* hwui_skia_track_events */:
+        field.get(&hwui_skia_track_events_);
         break;
-      case 6 /* mount_point_mapping */:
-        mount_point_mapping_.emplace_back();
-        mount_point_mapping_.back().ParseFromArray(field.data(), field.size());
+      case 3 /* hwui_package_name_filter */:
+        hwui_package_name_filter_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &hwui_package_name_filter_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -24680,68 +24613,75 @@ bool InodeFileConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string InodeFileConfig::SerializeAsString() const {
+std::string AndroidSdkSyspropGuardConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> InodeFileConfig::SerializeAsArray() const {
+std::vector<uint8_t> AndroidSdkSyspropGuardConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void InodeFileConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: scan_interval_ms
+void AndroidSdkSyspropGuardConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: surfaceflinger_skia_track_events
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, scan_interval_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, surfaceflinger_skia_track_events_, msg);
   }

-  // Field 2: scan_delay_ms
+  // Field 2: hwui_skia_track_events
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, scan_delay_ms_, msg);
-  }
-
-  // Field 3: scan_batch_size
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, scan_batch_size_, msg);
-  }
-
-  // Field 4: do_not_scan
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, do_not_scan_, msg);
-  }
-
-  // Field 5: scan_mount_points
-  for (auto& it : scan_mount_points_) {
-    ::protozero::internal::gen_helpers::SerializeString(5, it, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, hwui_skia_track_events_, msg);
   }

-  // Field 6: mount_point_mapping
-  for (auto& it : mount_point_mapping_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
+  // Field 3: hwui_package_name_filter
+  for (auto& it : hwui_package_name_filter_) {
+    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_system_property_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_system_property_config.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-InodeFileConfig_MountPointMappingEntry::InodeFileConfig_MountPointMappingEntry() = default;
-InodeFileConfig_MountPointMappingEntry::~InodeFileConfig_MountPointMappingEntry() = default;
-InodeFileConfig_MountPointMappingEntry::InodeFileConfig_MountPointMappingEntry(const InodeFileConfig_MountPointMappingEntry&) = default;
-InodeFileConfig_MountPointMappingEntry& InodeFileConfig_MountPointMappingEntry::operator=(const InodeFileConfig_MountPointMappingEntry&) = default;
-InodeFileConfig_MountPointMappingEntry::InodeFileConfig_MountPointMappingEntry(InodeFileConfig_MountPointMappingEntry&&) noexcept = default;
-InodeFileConfig_MountPointMappingEntry& InodeFileConfig_MountPointMappingEntry::operator=(InodeFileConfig_MountPointMappingEntry&&) = default;
+AndroidSystemPropertyConfig::AndroidSystemPropertyConfig() = default;
+AndroidSystemPropertyConfig::~AndroidSystemPropertyConfig() = default;
+AndroidSystemPropertyConfig::AndroidSystemPropertyConfig(const AndroidSystemPropertyConfig&) = default;
+AndroidSystemPropertyConfig& AndroidSystemPropertyConfig::operator=(const AndroidSystemPropertyConfig&) = default;
+AndroidSystemPropertyConfig::AndroidSystemPropertyConfig(AndroidSystemPropertyConfig&&) noexcept = default;
+AndroidSystemPropertyConfig& AndroidSystemPropertyConfig::operator=(AndroidSystemPropertyConfig&&) = default;

-bool InodeFileConfig_MountPointMappingEntry::operator==(const InodeFileConfig_MountPointMappingEntry& other) const {
+bool AndroidSystemPropertyConfig::operator==(const AndroidSystemPropertyConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(mountpoint_, other.mountpoint_)
-   && ::protozero::internal::gen_helpers::EqualsField(scan_roots_, other.scan_roots_);
+   && ::protozero::internal::gen_helpers::EqualsField(poll_ms_, other.poll_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(property_name_, other.property_name_);
 }

-bool InodeFileConfig_MountPointMappingEntry::ParseFromArray(const void* raw, size_t size) {
-  scan_roots_.clear();
+bool AndroidSystemPropertyConfig::ParseFromArray(const void* raw, size_t size) {
+  property_name_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -24751,12 +24691,12 @@ bool InodeFileConfig_MountPointMappingEntry::ParseFromArray(const void* raw, siz
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* mountpoint */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &mountpoint_);
+      case 1 /* poll_ms */:
+        field.get(&poll_ms_);
         break;
-      case 2 /* scan_roots */:
-        scan_roots_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &scan_roots_.back());
+      case 2 /* property_name */:
+        property_name_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &property_name_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -24766,26 +24706,26 @@ bool InodeFileConfig_MountPointMappingEntry::ParseFromArray(const void* raw, siz
   return !packed_error && !dec.bytes_left();
 }

-std::string InodeFileConfig_MountPointMappingEntry::SerializeAsString() const {
+std::string AndroidSystemPropertyConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> InodeFileConfig_MountPointMappingEntry::SerializeAsArray() const {
+std::vector<uint8_t> AndroidSystemPropertyConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void InodeFileConfig_MountPointMappingEntry::Serialize(::protozero::Message* msg) const {
-  // Field 1: mountpoint
+void AndroidSystemPropertyConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: poll_ms
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, mountpoint_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, poll_ms_, msg);
   }

-  // Field 2: scan_roots
-  for (auto& it : scan_roots_) {
+  // Field 2: property_name
+  for (auto& it : property_name_) {
     ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
   }

@@ -24798,7 +24738,7 @@ void InodeFileConfig_MountPointMappingEntry::Serialize(::protozero::Message* msg
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/interceptors/console_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/app_wakelock_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -24809,26 +24749,27 @@ void InodeFileConfig_MountPointMappingEntry::Serialize(::protozero::Message* msg
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/interceptors/console_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/app_wakelock_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-ConsoleConfig::ConsoleConfig() = default;
-ConsoleConfig::~ConsoleConfig() = default;
-ConsoleConfig::ConsoleConfig(const ConsoleConfig&) = default;
-ConsoleConfig& ConsoleConfig::operator=(const ConsoleConfig&) = default;
-ConsoleConfig::ConsoleConfig(ConsoleConfig&&) noexcept = default;
-ConsoleConfig& ConsoleConfig::operator=(ConsoleConfig&&) = default;
+AppWakelocksConfig::AppWakelocksConfig() = default;
+AppWakelocksConfig::~AppWakelocksConfig() = default;
+AppWakelocksConfig::AppWakelocksConfig(const AppWakelocksConfig&) = default;
+AppWakelocksConfig& AppWakelocksConfig::operator=(const AppWakelocksConfig&) = default;
+AppWakelocksConfig::AppWakelocksConfig(AppWakelocksConfig&&) noexcept = default;
+AppWakelocksConfig& AppWakelocksConfig::operator=(AppWakelocksConfig&&) = default;

-bool ConsoleConfig::operator==(const ConsoleConfig& other) const {
+bool AppWakelocksConfig::operator==(const AppWakelocksConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(output_, other.output_)
-   && ::protozero::internal::gen_helpers::EqualsField(enable_colors_, other.enable_colors_);
+   && ::protozero::internal::gen_helpers::EqualsField(write_delay_ms_, other.write_delay_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(filter_duration_below_ms_, other.filter_duration_below_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(drop_owner_pid_, other.drop_owner_pid_);
 }

-bool ConsoleConfig::ParseFromArray(const void* raw, size_t size) {
+bool AppWakelocksConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -24838,11 +24779,14 @@ bool ConsoleConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* output */:
-        field.get(&output_);
+      case 1 /* write_delay_ms */:
+        field.get(&write_delay_ms_);
         break;
-      case 2 /* enable_colors */:
-        field.get(&enable_colors_);
+      case 2 /* filter_duration_below_ms */:
+        field.get(&filter_duration_below_ms_);
+        break;
+      case 3 /* drop_owner_pid */:
+        field.get(&drop_owner_pid_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -24852,27 +24796,32 @@ bool ConsoleConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ConsoleConfig::SerializeAsString() const {
+std::string AppWakelocksConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ConsoleConfig::SerializeAsArray() const {
+std::vector<uint8_t> AppWakelocksConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ConsoleConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: output
+void AppWakelocksConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: write_delay_ms
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, output_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, write_delay_ms_, msg);
   }

-  // Field 2: enable_colors
+  // Field 2: filter_duration_below_ms
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, enable_colors_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, filter_duration_below_ms_, msg);
+  }
+
+  // Field 3: drop_owner_pid
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, drop_owner_pid_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -24884,7 +24833,7 @@ void ConsoleConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/linux/journald_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/cpu_per_uid_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -24895,29 +24844,25 @@ void ConsoleConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/linux/journald_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/cpu_per_uid_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-SystemdJournaldConfig::SystemdJournaldConfig() = default;
-SystemdJournaldConfig::~SystemdJournaldConfig() = default;
-SystemdJournaldConfig::SystemdJournaldConfig(const SystemdJournaldConfig&) = default;
-SystemdJournaldConfig& SystemdJournaldConfig::operator=(const SystemdJournaldConfig&) = default;
-SystemdJournaldConfig::SystemdJournaldConfig(SystemdJournaldConfig&&) noexcept = default;
-SystemdJournaldConfig& SystemdJournaldConfig::operator=(SystemdJournaldConfig&&) = default;
+CpuPerUidConfig::CpuPerUidConfig() = default;
+CpuPerUidConfig::~CpuPerUidConfig() = default;
+CpuPerUidConfig::CpuPerUidConfig(const CpuPerUidConfig&) = default;
+CpuPerUidConfig& CpuPerUidConfig::operator=(const CpuPerUidConfig&) = default;
+CpuPerUidConfig::CpuPerUidConfig(CpuPerUidConfig&&) noexcept = default;
+CpuPerUidConfig& CpuPerUidConfig::operator=(CpuPerUidConfig&&) = default;

-bool SystemdJournaldConfig::operator==(const SystemdJournaldConfig& other) const {
+bool CpuPerUidConfig::operator==(const CpuPerUidConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(min_prio_, other.min_prio_)
-   && ::protozero::internal::gen_helpers::EqualsField(filter_identifiers_, other.filter_identifiers_)
-   && ::protozero::internal::gen_helpers::EqualsField(filter_units_, other.filter_units_);
+   && ::protozero::internal::gen_helpers::EqualsField(poll_ms_, other.poll_ms_);
 }

-bool SystemdJournaldConfig::ParseFromArray(const void* raw, size_t size) {
-  filter_identifiers_.clear();
-  filter_units_.clear();
+bool CpuPerUidConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -24927,16 +24872,8 @@ bool SystemdJournaldConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* min_prio */:
-        field.get(&min_prio_);
-        break;
-      case 2 /* filter_identifiers */:
-        filter_identifiers_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &filter_identifiers_.back());
-        break;
-      case 3 /* filter_units */:
-        filter_units_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &filter_units_.back());
+      case 1 /* poll_ms */:
+        field.get(&poll_ms_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -24946,32 +24883,22 @@ bool SystemdJournaldConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string SystemdJournaldConfig::SerializeAsString() const {
+std::string CpuPerUidConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> SystemdJournaldConfig::SerializeAsArray() const {
+std::vector<uint8_t> CpuPerUidConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void SystemdJournaldConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: min_prio
+void CpuPerUidConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: poll_ms
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, min_prio_, msg);
-  }
-
-  // Field 2: filter_identifiers
-  for (auto& it : filter_identifiers_) {
-    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
-  }
-
-  // Field 3: filter_units
-  for (auto& it : filter_units_) {
-    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, poll_ms_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -24983,7 +24910,7 @@ void SystemdJournaldConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/power/android_power_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/display_video_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -24994,30 +24921,29 @@ void SystemdJournaldConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/power/android_power_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/display_video_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-AndroidPowerConfig::AndroidPowerConfig() = default;
-AndroidPowerConfig::~AndroidPowerConfig() = default;
-AndroidPowerConfig::AndroidPowerConfig(const AndroidPowerConfig&) = default;
-AndroidPowerConfig& AndroidPowerConfig::operator=(const AndroidPowerConfig&) = default;
-AndroidPowerConfig::AndroidPowerConfig(AndroidPowerConfig&&) noexcept = default;
-AndroidPowerConfig& AndroidPowerConfig::operator=(AndroidPowerConfig&&) = default;
+DisplayVideoConfig::DisplayVideoConfig() = default;
+DisplayVideoConfig::~DisplayVideoConfig() = default;
+DisplayVideoConfig::DisplayVideoConfig(const DisplayVideoConfig&) = default;
+DisplayVideoConfig& DisplayVideoConfig::operator=(const DisplayVideoConfig&) = default;
+DisplayVideoConfig::DisplayVideoConfig(DisplayVideoConfig&&) noexcept = default;
+DisplayVideoConfig& DisplayVideoConfig::operator=(DisplayVideoConfig&&) = default;

-bool AndroidPowerConfig::operator==(const AndroidPowerConfig& other) const {
+bool DisplayVideoConfig::operator==(const DisplayVideoConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(battery_poll_ms_, other.battery_poll_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(battery_counters_, other.battery_counters_)
-   && ::protozero::internal::gen_helpers::EqualsField(collect_power_rails_, other.collect_power_rails_)
-   && ::protozero::internal::gen_helpers::EqualsField(collect_energy_estimation_breakdown_, other.collect_energy_estimation_breakdown_)
-   && ::protozero::internal::gen_helpers::EqualsField(collect_entity_state_residency_, other.collect_entity_state_residency_);
+   && ::protozero::internal::gen_helpers::EqualsField(scale_, other.scale_)
+   && ::protozero::internal::gen_helpers::EqualsField(format_, other.format_)
+   && ::protozero::internal::gen_helpers::EqualsField(key_frame_interval_secs_, other.key_frame_interval_secs_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_stream_size_bytes_, other.max_stream_size_bytes_)
+   && ::protozero::internal::gen_helpers::EqualsField(bitrate_bps_, other.bitrate_bps_);
 }

-bool AndroidPowerConfig::ParseFromArray(const void* raw, size_t size) {
-  battery_counters_.clear();
+bool DisplayVideoConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -25027,21 +24953,20 @@ bool AndroidPowerConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* battery_poll_ms */:
-        field.get(&battery_poll_ms_);
+      case 1 /* scale */:
+        field.get(&scale_);
         break;
-      case 2 /* battery_counters */:
-        battery_counters_.emplace_back();
-        field.get(&battery_counters_.back());
+      case 2 /* format */:
+        field.get(&format_);
         break;
-      case 3 /* collect_power_rails */:
-        field.get(&collect_power_rails_);
+      case 3 /* key_frame_interval_secs */:
+        field.get(&key_frame_interval_secs_);
         break;
-      case 4 /* collect_energy_estimation_breakdown */:
-        field.get(&collect_energy_estimation_breakdown_);
+      case 4 /* max_stream_size_bytes */:
+        field.get(&max_stream_size_bytes_);
         break;
-      case 5 /* collect_entity_state_residency */:
-        field.get(&collect_entity_state_residency_);
+      case 5 /* bitrate_bps */:
+        field.get(&bitrate_bps_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -25051,42 +24976,42 @@ bool AndroidPowerConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string AndroidPowerConfig::SerializeAsString() const {
+std::string DisplayVideoConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> AndroidPowerConfig::SerializeAsArray() const {
+std::vector<uint8_t> DisplayVideoConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void AndroidPowerConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: battery_poll_ms
+void DisplayVideoConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: scale
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, battery_poll_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeFixed(1, scale_, msg);
   }

-  // Field 2: battery_counters
-  for (auto& it : battery_counters_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
+  // Field 2: format
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, format_, msg);
   }

-  // Field 3: collect_power_rails
+  // Field 3: key_frame_interval_secs
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, collect_power_rails_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, key_frame_interval_secs_, msg);
   }

-  // Field 4: collect_energy_estimation_breakdown
+  // Field 4: max_stream_size_bytes
   if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, collect_energy_estimation_breakdown_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, max_stream_size_bytes_, msg);
   }

-  // Field 5: collect_entity_state_residency
+  // Field 5: bitrate_bps
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, collect_entity_state_residency_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, bitrate_bps_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -25098,7 +25023,7 @@ void AndroidPowerConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/priority_boost/priority_boost_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/inputmethod_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -25109,26 +25034,27 @@ void AndroidPowerConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/priority_boost/priority_boost_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/inputmethod_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-PriorityBoostConfig::PriorityBoostConfig() = default;
-PriorityBoostConfig::~PriorityBoostConfig() = default;
-PriorityBoostConfig::PriorityBoostConfig(const PriorityBoostConfig&) = default;
-PriorityBoostConfig& PriorityBoostConfig::operator=(const PriorityBoostConfig&) = default;
-PriorityBoostConfig::PriorityBoostConfig(PriorityBoostConfig&&) noexcept = default;
-PriorityBoostConfig& PriorityBoostConfig::operator=(PriorityBoostConfig&&) = default;
+InputMethodConfig::InputMethodConfig() = default;
+InputMethodConfig::~InputMethodConfig() = default;
+InputMethodConfig::InputMethodConfig(const InputMethodConfig&) = default;
+InputMethodConfig& InputMethodConfig::operator=(const InputMethodConfig&) = default;
+InputMethodConfig::InputMethodConfig(InputMethodConfig&&) noexcept = default;
+InputMethodConfig& InputMethodConfig::operator=(InputMethodConfig&&) = default;

-bool PriorityBoostConfig::operator==(const PriorityBoostConfig& other) const {
+bool InputMethodConfig::operator==(const InputMethodConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(policy_, other.policy_)
-   && ::protozero::internal::gen_helpers::EqualsField(priority_, other.priority_);
+   && ::protozero::internal::gen_helpers::EqualsField(client_, other.client_)
+   && ::protozero::internal::gen_helpers::EqualsField(service_, other.service_)
+   && ::protozero::internal::gen_helpers::EqualsField(manager_service_, other.manager_service_);
 }

-bool PriorityBoostConfig::ParseFromArray(const void* raw, size_t size) {
+bool InputMethodConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -25138,11 +25064,14 @@ bool PriorityBoostConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* policy */:
-        field.get(&policy_);
+      case 1 /* client */:
+        field.get(&client_);
         break;
-      case 2 /* priority */:
-        field.get(&priority_);
+      case 2 /* service */:
+        field.get(&service_);
+        break;
+      case 3 /* manager_service */:
+        field.get(&manager_service_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -25152,27 +25081,32 @@ bool PriorityBoostConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string PriorityBoostConfig::SerializeAsString() const {
+std::string InputMethodConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> PriorityBoostConfig::SerializeAsArray() const {
+std::vector<uint8_t> InputMethodConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void PriorityBoostConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: policy
+void InputMethodConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: client
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, policy_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, client_, msg);
   }

-  // Field 2: priority
+  // Field 2: service
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, priority_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, service_, msg);
+  }
+
+  // Field 3: manager_service
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, manager_service_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -25184,7 +25118,7 @@ void PriorityBoostConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/process_stats/process_stats_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/kernel_wakelocks_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -25195,36 +25129,25 @@ void PriorityBoostConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/process_stats/process_stats_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/kernel_wakelocks_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-ProcessStatsConfig::ProcessStatsConfig() = default;
-ProcessStatsConfig::~ProcessStatsConfig() = default;
-ProcessStatsConfig::ProcessStatsConfig(const ProcessStatsConfig&) = default;
-ProcessStatsConfig& ProcessStatsConfig::operator=(const ProcessStatsConfig&) = default;
-ProcessStatsConfig::ProcessStatsConfig(ProcessStatsConfig&&) noexcept = default;
-ProcessStatsConfig& ProcessStatsConfig::operator=(ProcessStatsConfig&&) = default;
+KernelWakelocksConfig::KernelWakelocksConfig() = default;
+KernelWakelocksConfig::~KernelWakelocksConfig() = default;
+KernelWakelocksConfig::KernelWakelocksConfig(const KernelWakelocksConfig&) = default;
+KernelWakelocksConfig& KernelWakelocksConfig::operator=(const KernelWakelocksConfig&) = default;
+KernelWakelocksConfig::KernelWakelocksConfig(KernelWakelocksConfig&&) noexcept = default;
+KernelWakelocksConfig& KernelWakelocksConfig::operator=(KernelWakelocksConfig&&) = default;

-bool ProcessStatsConfig::operator==(const ProcessStatsConfig& other) const {
+bool KernelWakelocksConfig::operator==(const KernelWakelocksConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(quirks_, other.quirks_)
-   && ::protozero::internal::gen_helpers::EqualsField(scan_all_processes_on_start_, other.scan_all_processes_on_start_)
-   && ::protozero::internal::gen_helpers::EqualsField(record_thread_names_, other.record_thread_names_)
-   && ::protozero::internal::gen_helpers::EqualsField(proc_stats_poll_ms_, other.proc_stats_poll_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(proc_stats_cache_ttl_ms_, other.proc_stats_cache_ttl_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(scan_smaps_rollup_, other.scan_smaps_rollup_)
-   && ::protozero::internal::gen_helpers::EqualsField(record_process_age_, other.record_process_age_)
-   && ::protozero::internal::gen_helpers::EqualsField(record_process_runtime_, other.record_process_runtime_)
-   && ::protozero::internal::gen_helpers::EqualsField(record_process_dmabuf_rss_, other.record_process_dmabuf_rss_)
-   && ::protozero::internal::gen_helpers::EqualsField(resolve_process_fds_, other.resolve_process_fds_)
-   && ::protozero::internal::gen_helpers::EqualsField(skip_main_thread_message_, other.skip_main_thread_message_);
+   && ::protozero::internal::gen_helpers::EqualsField(poll_ms_, other.poll_ms_);
 }

-bool ProcessStatsConfig::ParseFromArray(const void* raw, size_t size) {
-  quirks_.clear();
+bool KernelWakelocksConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -25234,39 +25157,8 @@ bool ProcessStatsConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* quirks */:
-        quirks_.emplace_back();
-        field.get(&quirks_.back());
-        break;
-      case 2 /* scan_all_processes_on_start */:
-        field.get(&scan_all_processes_on_start_);
-        break;
-      case 3 /* record_thread_names */:
-        field.get(&record_thread_names_);
-        break;
-      case 4 /* proc_stats_poll_ms */:
-        field.get(&proc_stats_poll_ms_);
-        break;
-      case 6 /* proc_stats_cache_ttl_ms */:
-        field.get(&proc_stats_cache_ttl_ms_);
-        break;
-      case 10 /* scan_smaps_rollup */:
-        field.get(&scan_smaps_rollup_);
-        break;
-      case 11 /* record_process_age */:
-        field.get(&record_process_age_);
-        break;
-      case 12 /* record_process_runtime */:
-        field.get(&record_process_runtime_);
-        break;
-      case 13 /* record_process_dmabuf_rss */:
-        field.get(&record_process_dmabuf_rss_);
-        break;
-      case 9 /* resolve_process_fds */:
-        field.get(&resolve_process_fds_);
-        break;
-      case 14 /* skip_main_thread_message */:
-        field.get(&skip_main_thread_message_);
+      case 1 /* poll_ms */:
+        field.get(&poll_ms_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -25276,72 +25168,22 @@ bool ProcessStatsConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ProcessStatsConfig::SerializeAsString() const {
+std::string KernelWakelocksConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ProcessStatsConfig::SerializeAsArray() const {
+std::vector<uint8_t> KernelWakelocksConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ProcessStatsConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: quirks
-  for (auto& it : quirks_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
-  }
-
-  // Field 2: scan_all_processes_on_start
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, scan_all_processes_on_start_, msg);
-  }
-
-  // Field 3: record_thread_names
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, record_thread_names_, msg);
-  }
-
-  // Field 4: proc_stats_poll_ms
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, proc_stats_poll_ms_, msg);
-  }
-
-  // Field 6: proc_stats_cache_ttl_ms
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, proc_stats_cache_ttl_ms_, msg);
-  }
-
-  // Field 10: scan_smaps_rollup
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(10, scan_smaps_rollup_, msg);
-  }
-
-  // Field 11: record_process_age
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(11, record_process_age_, msg);
-  }
-
-  // Field 12: record_process_runtime
-  if (_has_field_[12]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(12, record_process_runtime_, msg);
-  }
-
-  // Field 13: record_process_dmabuf_rss
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(13, record_process_dmabuf_rss_, msg);
-  }
-
-  // Field 9: resolve_process_fds
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, resolve_process_fds_, msg);
-  }
-
-  // Field 14: skip_main_thread_message
-  if (_has_field_[14]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(14, skip_main_thread_message_, msg);
+void KernelWakelocksConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: poll_ms
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, poll_ms_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -25353,7 +25195,7 @@ void ProcessStatsConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/profiling/heapprofd_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/network_trace_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -25364,56 +25206,30 @@ void ProcessStatsConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/heapprofd_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/network_trace_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-HeapprofdConfig::HeapprofdConfig() = default;
-HeapprofdConfig::~HeapprofdConfig() = default;
-HeapprofdConfig::HeapprofdConfig(const HeapprofdConfig&) = default;
-HeapprofdConfig& HeapprofdConfig::operator=(const HeapprofdConfig&) = default;
-HeapprofdConfig::HeapprofdConfig(HeapprofdConfig&&) noexcept = default;
-HeapprofdConfig& HeapprofdConfig::operator=(HeapprofdConfig&&) = default;
+NetworkPacketTraceConfig::NetworkPacketTraceConfig() = default;
+NetworkPacketTraceConfig::~NetworkPacketTraceConfig() = default;
+NetworkPacketTraceConfig::NetworkPacketTraceConfig(const NetworkPacketTraceConfig&) = default;
+NetworkPacketTraceConfig& NetworkPacketTraceConfig::operator=(const NetworkPacketTraceConfig&) = default;
+NetworkPacketTraceConfig::NetworkPacketTraceConfig(NetworkPacketTraceConfig&&) noexcept = default;
+NetworkPacketTraceConfig& NetworkPacketTraceConfig::operator=(NetworkPacketTraceConfig&&) = default;

-bool HeapprofdConfig::operator==(const HeapprofdConfig& other) const {
+bool NetworkPacketTraceConfig::operator==(const NetworkPacketTraceConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(sampling_interval_bytes_, other.sampling_interval_bytes_)
-   && ::protozero::internal::gen_helpers::EqualsField(adaptive_sampling_shmem_threshold_, other.adaptive_sampling_shmem_threshold_)
-   && ::protozero::internal::gen_helpers::EqualsField(adaptive_sampling_max_sampling_interval_bytes_, other.adaptive_sampling_max_sampling_interval_bytes_)
-   && ::protozero::internal::gen_helpers::EqualsField(process_cmdline_, other.process_cmdline_)
-   && ::protozero::internal::gen_helpers::EqualsField(pid_, other.pid_)
-   && ::protozero::internal::gen_helpers::EqualsField(target_installed_by_, other.target_installed_by_)
-   && ::protozero::internal::gen_helpers::EqualsField(heaps_, other.heaps_)
-   && ::protozero::internal::gen_helpers::EqualsField(exclude_heaps_, other.exclude_heaps_)
-   && ::protozero::internal::gen_helpers::EqualsField(stream_allocations_, other.stream_allocations_)
-   && ::protozero::internal::gen_helpers::EqualsField(heap_sampling_intervals_, other.heap_sampling_intervals_)
-   && ::protozero::internal::gen_helpers::EqualsField(all_heaps_, other.all_heaps_)
-   && ::protozero::internal::gen_helpers::EqualsField(all_, other.all_)
-   && ::protozero::internal::gen_helpers::EqualsField(min_anonymous_memory_kb_, other.min_anonymous_memory_kb_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_heapprofd_memory_kb_, other.max_heapprofd_memory_kb_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_heapprofd_cpu_secs_, other.max_heapprofd_cpu_secs_)
-   && ::protozero::internal::gen_helpers::EqualsField(skip_symbol_prefix_, other.skip_symbol_prefix_)
-   && ::protozero::internal::gen_helpers::EqualsField(continuous_dump_config_, other.continuous_dump_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(shmem_size_bytes_, other.shmem_size_bytes_)
-   && ::protozero::internal::gen_helpers::EqualsField(block_client_, other.block_client_)
-   && ::protozero::internal::gen_helpers::EqualsField(block_client_timeout_us_, other.block_client_timeout_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(no_startup_, other.no_startup_)
-   && ::protozero::internal::gen_helpers::EqualsField(no_running_, other.no_running_)
-   && ::protozero::internal::gen_helpers::EqualsField(dump_at_max_, other.dump_at_max_)
-   && ::protozero::internal::gen_helpers::EqualsField(disable_fork_teardown_, other.disable_fork_teardown_)
-   && ::protozero::internal::gen_helpers::EqualsField(disable_vfork_detection_, other.disable_vfork_detection_);
+   && ::protozero::internal::gen_helpers::EqualsField(poll_ms_, other.poll_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(aggregation_threshold_, other.aggregation_threshold_)
+   && ::protozero::internal::gen_helpers::EqualsField(intern_limit_, other.intern_limit_)
+   && ::protozero::internal::gen_helpers::EqualsField(drop_local_port_, other.drop_local_port_)
+   && ::protozero::internal::gen_helpers::EqualsField(drop_remote_port_, other.drop_remote_port_)
+   && ::protozero::internal::gen_helpers::EqualsField(drop_tcp_flags_, other.drop_tcp_flags_);
 }

-bool HeapprofdConfig::ParseFromArray(const void* raw, size_t size) {
-  process_cmdline_.clear();
-  pid_.clear();
-  target_installed_by_.clear();
-  heaps_.clear();
-  exclude_heaps_.clear();
-  heap_sampling_intervals_.clear();
-  skip_symbol_prefix_.clear();
+bool NetworkPacketTraceConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -25423,87 +25239,23 @@ bool HeapprofdConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* sampling_interval_bytes */:
-        field.get(&sampling_interval_bytes_);
-        break;
-      case 24 /* adaptive_sampling_shmem_threshold */:
-        field.get(&adaptive_sampling_shmem_threshold_);
-        break;
-      case 25 /* adaptive_sampling_max_sampling_interval_bytes */:
-        field.get(&adaptive_sampling_max_sampling_interval_bytes_);
-        break;
-      case 2 /* process_cmdline */:
-        process_cmdline_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &process_cmdline_.back());
-        break;
-      case 4 /* pid */:
-        pid_.emplace_back();
-        field.get(&pid_.back());
-        break;
-      case 26 /* target_installed_by */:
-        target_installed_by_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &target_installed_by_.back());
-        break;
-      case 20 /* heaps */:
-        heaps_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &heaps_.back());
-        break;
-      case 27 /* exclude_heaps */:
-        exclude_heaps_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &exclude_heaps_.back());
-        break;
-      case 23 /* stream_allocations */:
-        field.get(&stream_allocations_);
-        break;
-      case 22 /* heap_sampling_intervals */:
-        heap_sampling_intervals_.emplace_back();
-        field.get(&heap_sampling_intervals_.back());
-        break;
-      case 21 /* all_heaps */:
-        field.get(&all_heaps_);
-        break;
-      case 5 /* all */:
-        field.get(&all_);
-        break;
-      case 15 /* min_anonymous_memory_kb */:
-        field.get(&min_anonymous_memory_kb_);
-        break;
-      case 16 /* max_heapprofd_memory_kb */:
-        field.get(&max_heapprofd_memory_kb_);
-        break;
-      case 17 /* max_heapprofd_cpu_secs */:
-        field.get(&max_heapprofd_cpu_secs_);
-        break;
-      case 7 /* skip_symbol_prefix */:
-        skip_symbol_prefix_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &skip_symbol_prefix_.back());
-        break;
-      case 6 /* continuous_dump_config */:
-        (*continuous_dump_config_).ParseFromArray(field.data(), field.size());
-        break;
-      case 8 /* shmem_size_bytes */:
-        field.get(&shmem_size_bytes_);
-        break;
-      case 9 /* block_client */:
-        field.get(&block_client_);
-        break;
-      case 14 /* block_client_timeout_us */:
-        field.get(&block_client_timeout_us_);
+      case 1 /* poll_ms */:
+        field.get(&poll_ms_);
         break;
-      case 10 /* no_startup */:
-        field.get(&no_startup_);
+      case 2 /* aggregation_threshold */:
+        field.get(&aggregation_threshold_);
         break;
-      case 11 /* no_running */:
-        field.get(&no_running_);
+      case 3 /* intern_limit */:
+        field.get(&intern_limit_);
         break;
-      case 13 /* dump_at_max */:
-        field.get(&dump_at_max_);
+      case 4 /* drop_local_port */:
+        field.get(&drop_local_port_);
         break;
-      case 18 /* disable_fork_teardown */:
-        field.get(&disable_fork_teardown_);
+      case 5 /* drop_remote_port */:
+        field.get(&drop_remote_port_);
         break;
-      case 19 /* disable_vfork_detection */:
-        field.get(&disable_vfork_detection_);
+      case 6 /* drop_tcp_flags */:
+        field.get(&drop_tcp_flags_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -25513,162 +25265,92 @@ bool HeapprofdConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string HeapprofdConfig::SerializeAsString() const {
+std::string NetworkPacketTraceConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> HeapprofdConfig::SerializeAsArray() const {
+std::vector<uint8_t> NetworkPacketTraceConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void HeapprofdConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: sampling_interval_bytes
+void NetworkPacketTraceConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: poll_ms
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, sampling_interval_bytes_, msg);
-  }
-
-  // Field 24: adaptive_sampling_shmem_threshold
-  if (_has_field_[24]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(24, adaptive_sampling_shmem_threshold_, msg);
-  }
-
-  // Field 25: adaptive_sampling_max_sampling_interval_bytes
-  if (_has_field_[25]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(25, adaptive_sampling_max_sampling_interval_bytes_, msg);
-  }
-
-  // Field 2: process_cmdline
-  for (auto& it : process_cmdline_) {
-    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
-  }
-
-  // Field 4: pid
-  for (auto& it : pid_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, it, msg);
-  }
-
-  // Field 26: target_installed_by
-  for (auto& it : target_installed_by_) {
-    ::protozero::internal::gen_helpers::SerializeString(26, it, msg);
-  }
-
-  // Field 20: heaps
-  for (auto& it : heaps_) {
-    ::protozero::internal::gen_helpers::SerializeString(20, it, msg);
-  }
-
-  // Field 27: exclude_heaps
-  for (auto& it : exclude_heaps_) {
-    ::protozero::internal::gen_helpers::SerializeString(27, it, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, poll_ms_, msg);
   }

-  // Field 23: stream_allocations
-  if (_has_field_[23]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(23, stream_allocations_, msg);
+  // Field 2: aggregation_threshold
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, aggregation_threshold_, msg);
   }

-  // Field 22: heap_sampling_intervals
-  for (auto& it : heap_sampling_intervals_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(22, it, msg);
+  // Field 3: intern_limit
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, intern_limit_, msg);
   }

-  // Field 21: all_heaps
-  if (_has_field_[21]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(21, all_heaps_, msg);
+  // Field 4: drop_local_port
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, drop_local_port_, msg);
   }

-  // Field 5: all
+  // Field 5: drop_remote_port
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, all_, msg);
-  }
-
-  // Field 15: min_anonymous_memory_kb
-  if (_has_field_[15]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(15, min_anonymous_memory_kb_, msg);
-  }
-
-  // Field 16: max_heapprofd_memory_kb
-  if (_has_field_[16]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(16, max_heapprofd_memory_kb_, msg);
-  }
-
-  // Field 17: max_heapprofd_cpu_secs
-  if (_has_field_[17]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(17, max_heapprofd_cpu_secs_, msg);
-  }
-
-  // Field 7: skip_symbol_prefix
-  for (auto& it : skip_symbol_prefix_) {
-    ::protozero::internal::gen_helpers::SerializeString(7, it, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, drop_remote_port_, msg);
   }

-  // Field 6: continuous_dump_config
+  // Field 6: drop_tcp_flags
   if (_has_field_[6]) {
-    (*continuous_dump_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
-  }
-
-  // Field 8: shmem_size_bytes
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, shmem_size_bytes_, msg);
-  }
-
-  // Field 9: block_client
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, block_client_, msg);
-  }
-
-  // Field 14: block_client_timeout_us
-  if (_has_field_[14]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(14, block_client_timeout_us_, msg);
-  }
-
-  // Field 10: no_startup
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(10, no_startup_, msg);
-  }
-
-  // Field 11: no_running
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(11, no_running_, msg);
-  }
-
-  // Field 13: dump_at_max
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(13, dump_at_max_, msg);
-  }
-
-  // Field 18: disable_fork_teardown
-  if (_has_field_[18]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(18, disable_fork_teardown_, msg);
-  }
-
-  // Field 19: disable_vfork_detection
-  if (_has_field_[19]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(19, disable_vfork_detection_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(6, drop_tcp_flags_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/packages_list_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/packages_list_config.gen.h"

-HeapprofdConfig_ContinuousDumpConfig::HeapprofdConfig_ContinuousDumpConfig() = default;
-HeapprofdConfig_ContinuousDumpConfig::~HeapprofdConfig_ContinuousDumpConfig() = default;
-HeapprofdConfig_ContinuousDumpConfig::HeapprofdConfig_ContinuousDumpConfig(const HeapprofdConfig_ContinuousDumpConfig&) = default;
-HeapprofdConfig_ContinuousDumpConfig& HeapprofdConfig_ContinuousDumpConfig::operator=(const HeapprofdConfig_ContinuousDumpConfig&) = default;
-HeapprofdConfig_ContinuousDumpConfig::HeapprofdConfig_ContinuousDumpConfig(HeapprofdConfig_ContinuousDumpConfig&&) noexcept = default;
-HeapprofdConfig_ContinuousDumpConfig& HeapprofdConfig_ContinuousDumpConfig::operator=(HeapprofdConfig_ContinuousDumpConfig&&) = default;
+namespace perfetto {
+namespace protos {
+namespace gen {

-bool HeapprofdConfig_ContinuousDumpConfig::operator==(const HeapprofdConfig_ContinuousDumpConfig& other) const {
+PackagesListConfig::PackagesListConfig() = default;
+PackagesListConfig::~PackagesListConfig() = default;
+PackagesListConfig::PackagesListConfig(const PackagesListConfig&) = default;
+PackagesListConfig& PackagesListConfig::operator=(const PackagesListConfig&) = default;
+PackagesListConfig::PackagesListConfig(PackagesListConfig&&) noexcept = default;
+PackagesListConfig& PackagesListConfig::operator=(PackagesListConfig&&) = default;
+
+bool PackagesListConfig::operator==(const PackagesListConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(dump_phase_ms_, other.dump_phase_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(dump_interval_ms_, other.dump_interval_ms_);
+   && ::protozero::internal::gen_helpers::EqualsField(package_name_filter_, other.package_name_filter_)
+   && ::protozero::internal::gen_helpers::EqualsField(package_name_regex_filter_, other.package_name_regex_filter_)
+   && ::protozero::internal::gen_helpers::EqualsField(only_write_on_cpu_use_every_ms_, other.only_write_on_cpu_use_every_ms_);
 }

-bool HeapprofdConfig_ContinuousDumpConfig::ParseFromArray(const void* raw, size_t size) {
+bool PackagesListConfig::ParseFromArray(const void* raw, size_t size) {
+  package_name_filter_.clear();
+  package_name_regex_filter_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -25678,11 +25360,16 @@ bool HeapprofdConfig_ContinuousDumpConfig::ParseFromArray(const void* raw, size_
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 5 /* dump_phase_ms */:
-        field.get(&dump_phase_ms_);
+      case 1 /* package_name_filter */:
+        package_name_filter_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &package_name_filter_.back());
         break;
-      case 6 /* dump_interval_ms */:
-        field.get(&dump_interval_ms_);
+      case 3 /* package_name_regex_filter */:
+        package_name_regex_filter_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &package_name_regex_filter_.back());
+        break;
+      case 2 /* only_write_on_cpu_use_every_ms */:
+        field.get(&only_write_on_cpu_use_every_ms_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -25692,27 +25379,32 @@ bool HeapprofdConfig_ContinuousDumpConfig::ParseFromArray(const void* raw, size_
   return !packed_error && !dec.bytes_left();
 }

-std::string HeapprofdConfig_ContinuousDumpConfig::SerializeAsString() const {
+std::string PackagesListConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> HeapprofdConfig_ContinuousDumpConfig::SerializeAsArray() const {
+std::vector<uint8_t> PackagesListConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void HeapprofdConfig_ContinuousDumpConfig::Serialize(::protozero::Message* msg) const {
-  // Field 5: dump_phase_ms
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, dump_phase_ms_, msg);
+void PackagesListConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: package_name_filter
+  for (auto& it : package_name_filter_) {
+    ::protozero::internal::gen_helpers::SerializeString(1, it, msg);
   }

-  // Field 6: dump_interval_ms
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, dump_interval_ms_, msg);
+  // Field 3: package_name_regex_filter
+  for (auto& it : package_name_regex_filter_) {
+    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
+  }
+
+  // Field 2: only_write_on_cpu_use_every_ms
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, only_write_on_cpu_use_every_ms_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -25724,7 +25416,7 @@ void HeapprofdConfig_ContinuousDumpConfig::Serialize(::protozero::Message* msg)
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/profiling/java_hprof_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/pixel_modem_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -25735,39 +25427,29 @@ void HeapprofdConfig_ContinuousDumpConfig::Serialize(::protozero::Message* msg)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/java_hprof_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/smaps_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/pixel_modem_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-JavaHprofConfig::JavaHprofConfig() = default;
-JavaHprofConfig::~JavaHprofConfig() = default;
-JavaHprofConfig::JavaHprofConfig(const JavaHprofConfig&) = default;
-JavaHprofConfig& JavaHprofConfig::operator=(const JavaHprofConfig&) = default;
-JavaHprofConfig::JavaHprofConfig(JavaHprofConfig&&) noexcept = default;
-JavaHprofConfig& JavaHprofConfig::operator=(JavaHprofConfig&&) = default;
+PixelModemConfig::PixelModemConfig() = default;
+PixelModemConfig::~PixelModemConfig() = default;
+PixelModemConfig::PixelModemConfig(const PixelModemConfig&) = default;
+PixelModemConfig& PixelModemConfig::operator=(const PixelModemConfig&) = default;
+PixelModemConfig::PixelModemConfig(PixelModemConfig&&) noexcept = default;
+PixelModemConfig& PixelModemConfig::operator=(PixelModemConfig&&) = default;

-bool JavaHprofConfig::operator==(const JavaHprofConfig& other) const {
+bool PixelModemConfig::operator==(const PixelModemConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(process_cmdline_, other.process_cmdline_)
-   && ::protozero::internal::gen_helpers::EqualsField(pid_, other.pid_)
-   && ::protozero::internal::gen_helpers::EqualsField(target_installed_by_, other.target_installed_by_)
-   && ::protozero::internal::gen_helpers::EqualsField(continuous_dump_config_, other.continuous_dump_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(min_anonymous_memory_kb_, other.min_anonymous_memory_kb_)
-   && ::protozero::internal::gen_helpers::EqualsField(min_java_heap_size_kb_, other.min_java_heap_size_kb_)
-   && ::protozero::internal::gen_helpers::EqualsField(dump_smaps_, other.dump_smaps_)
-   && ::protozero::internal::gen_helpers::EqualsField(smaps_config_, other.smaps_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(ignored_types_, other.ignored_types_)
-   && ::protozero::internal::gen_helpers::EqualsField(dump_oome_callstack_, other.dump_oome_callstack_);
+   && ::protozero::internal::gen_helpers::EqualsField(event_group_, other.event_group_)
+   && ::protozero::internal::gen_helpers::EqualsField(pigweed_hash_allow_list_, other.pigweed_hash_allow_list_)
+   && ::protozero::internal::gen_helpers::EqualsField(pigweed_hash_deny_list_, other.pigweed_hash_deny_list_);
 }

-bool JavaHprofConfig::ParseFromArray(const void* raw, size_t size) {
-  process_cmdline_.clear();
-  pid_.clear();
-  target_installed_by_.clear();
-  ignored_types_.clear();
+bool PixelModemConfig::ParseFromArray(const void* raw, size_t size) {
+  pigweed_hash_allow_list_.clear();
+  pigweed_hash_deny_list_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -25777,39 +25459,16 @@ bool JavaHprofConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* process_cmdline */:
-        process_cmdline_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &process_cmdline_.back());
-        break;
-      case 2 /* pid */:
-        pid_.emplace_back();
-        field.get(&pid_.back());
-        break;
-      case 7 /* target_installed_by */:
-        target_installed_by_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &target_installed_by_.back());
-        break;
-      case 3 /* continuous_dump_config */:
-        (*continuous_dump_config_).ParseFromArray(field.data(), field.size());
-        break;
-      case 4 /* min_anonymous_memory_kb */:
-        field.get(&min_anonymous_memory_kb_);
-        break;
-      case 9 /* min_java_heap_size_kb */:
-        field.get(&min_java_heap_size_kb_);
-        break;
-      case 5 /* dump_smaps */:
-        field.get(&dump_smaps_);
-        break;
-      case 8 /* smaps_config */:
-        (*smaps_config_).ParseFromArray(field.data(), field.size());
+      case 1 /* event_group */:
+        field.get(&event_group_);
         break;
-      case 6 /* ignored_types */:
-        ignored_types_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &ignored_types_.back());
+      case 2 /* pigweed_hash_allow_list */:
+        pigweed_hash_allow_list_.emplace_back();
+        field.get(&pigweed_hash_allow_list_.back());
         break;
-      case 10 /* dump_oome_callstack */:
-        field.get(&dump_oome_callstack_);
+      case 3 /* pigweed_hash_deny_list */:
+        pigweed_hash_deny_list_.emplace_back();
+        field.get(&pigweed_hash_deny_list_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -25819,88 +25478,152 @@ bool JavaHprofConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string JavaHprofConfig::SerializeAsString() const {
+std::string PixelModemConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> JavaHprofConfig::SerializeAsArray() const {
+std::vector<uint8_t> PixelModemConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void JavaHprofConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: process_cmdline
-  for (auto& it : process_cmdline_) {
-    ::protozero::internal::gen_helpers::SerializeString(1, it, msg);
+void PixelModemConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: event_group
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, event_group_, msg);
   }

-  // Field 2: pid
-  for (auto& it : pid_) {
+  // Field 2: pigweed_hash_allow_list
+  for (auto& it : pigweed_hash_allow_list_) {
     ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
   }

-  // Field 7: target_installed_by
-  for (auto& it : target_installed_by_) {
-    ::protozero::internal::gen_helpers::SerializeString(7, it, msg);
+  // Field 3: pigweed_hash_deny_list
+  for (auto& it : pigweed_hash_deny_list_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, it, msg);
   }

-  // Field 3: continuous_dump_config
-  if (_has_field_[3]) {
-    (*continuous_dump_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
-  }
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}

-  // Field 4: min_anonymous_memory_kb
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, min_anonymous_memory_kb_, msg);
-  }
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/protolog_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/protolog_config.gen.h"

-  // Field 9: min_java_heap_size_kb
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(9, min_java_heap_size_kb_, msg);
-  }
+namespace perfetto {
+namespace protos {
+namespace gen {

-  // Field 5: dump_smaps
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, dump_smaps_, msg);
+ProtoLogGroup::ProtoLogGroup() = default;
+ProtoLogGroup::~ProtoLogGroup() = default;
+ProtoLogGroup::ProtoLogGroup(const ProtoLogGroup&) = default;
+ProtoLogGroup& ProtoLogGroup::operator=(const ProtoLogGroup&) = default;
+ProtoLogGroup::ProtoLogGroup(ProtoLogGroup&&) noexcept = default;
+ProtoLogGroup& ProtoLogGroup::operator=(ProtoLogGroup&&) = default;
+
+bool ProtoLogGroup::operator==(const ProtoLogGroup& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(group_name_, other.group_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(log_from_, other.log_from_)
+   && ::protozero::internal::gen_helpers::EqualsField(collect_stacktrace_, other.collect_stacktrace_);
+}
+
+bool ProtoLogGroup::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* group_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &group_name_);
+        break;
+      case 2 /* log_from */:
+        field.get(&log_from_);
+        break;
+      case 3 /* collect_stacktrace */:
+        field.get(&collect_stacktrace_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 8: smaps_config
-  if (_has_field_[8]) {
-    (*smaps_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
+std::string ProtoLogGroup::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ProtoLogGroup::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ProtoLogGroup::Serialize(::protozero::Message* msg) const {
+  // Field 1: group_name
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, group_name_, msg);
   }

-  // Field 6: ignored_types
-  for (auto& it : ignored_types_) {
-    ::protozero::internal::gen_helpers::SerializeString(6, it, msg);
+  // Field 2: log_from
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, log_from_, msg);
   }

-  // Field 10: dump_oome_callstack
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(10, dump_oome_callstack_, msg);
+  // Field 3: collect_stacktrace
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, collect_stacktrace_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-JavaHprofConfig_ContinuousDumpConfig::JavaHprofConfig_ContinuousDumpConfig() = default;
-JavaHprofConfig_ContinuousDumpConfig::~JavaHprofConfig_ContinuousDumpConfig() = default;
-JavaHprofConfig_ContinuousDumpConfig::JavaHprofConfig_ContinuousDumpConfig(const JavaHprofConfig_ContinuousDumpConfig&) = default;
-JavaHprofConfig_ContinuousDumpConfig& JavaHprofConfig_ContinuousDumpConfig::operator=(const JavaHprofConfig_ContinuousDumpConfig&) = default;
-JavaHprofConfig_ContinuousDumpConfig::JavaHprofConfig_ContinuousDumpConfig(JavaHprofConfig_ContinuousDumpConfig&&) noexcept = default;
-JavaHprofConfig_ContinuousDumpConfig& JavaHprofConfig_ContinuousDumpConfig::operator=(JavaHprofConfig_ContinuousDumpConfig&&) = default;
+ProtoLogConfig::ProtoLogConfig() = default;
+ProtoLogConfig::~ProtoLogConfig() = default;
+ProtoLogConfig::ProtoLogConfig(const ProtoLogConfig&) = default;
+ProtoLogConfig& ProtoLogConfig::operator=(const ProtoLogConfig&) = default;
+ProtoLogConfig::ProtoLogConfig(ProtoLogConfig&&) noexcept = default;
+ProtoLogConfig& ProtoLogConfig::operator=(ProtoLogConfig&&) = default;

-bool JavaHprofConfig_ContinuousDumpConfig::operator==(const JavaHprofConfig_ContinuousDumpConfig& other) const {
+bool ProtoLogConfig::operator==(const ProtoLogConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(dump_phase_ms_, other.dump_phase_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(dump_interval_ms_, other.dump_interval_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(scan_pids_only_on_start_, other.scan_pids_only_on_start_);
+   && ::protozero::internal::gen_helpers::EqualsField(group_overrides_, other.group_overrides_)
+   && ::protozero::internal::gen_helpers::EqualsField(tracing_mode_, other.tracing_mode_)
+   && ::protozero::internal::gen_helpers::EqualsField(default_log_from_level_, other.default_log_from_level_);
 }

-bool JavaHprofConfig_ContinuousDumpConfig::ParseFromArray(const void* raw, size_t size) {
+int ProtoLogConfig::group_overrides_size() const { return static_cast<int>(group_overrides_.size()); }
+void ProtoLogConfig::clear_group_overrides() { group_overrides_.clear(); }
+ProtoLogGroup* ProtoLogConfig::add_group_overrides() { group_overrides_.emplace_back(); return &group_overrides_.back(); }
+bool ProtoLogConfig::ParseFromArray(const void* raw, size_t size) {
+  group_overrides_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -25910,14 +25633,15 @@ bool JavaHprofConfig_ContinuousDumpConfig::ParseFromArray(const void* raw, size_
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* dump_phase_ms */:
-        field.get(&dump_phase_ms_);
+      case 1 /* group_overrides */:
+        group_overrides_.emplace_back();
+        group_overrides_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 2 /* dump_interval_ms */:
-        field.get(&dump_interval_ms_);
+      case 2 /* tracing_mode */:
+        field.get(&tracing_mode_);
         break;
-      case 3 /* scan_pids_only_on_start */:
-        field.get(&scan_pids_only_on_start_);
+      case 3 /* default_log_from_level */:
+        field.get(&default_log_from_level_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -25927,32 +25651,32 @@ bool JavaHprofConfig_ContinuousDumpConfig::ParseFromArray(const void* raw, size_
   return !packed_error && !dec.bytes_left();
 }

-std::string JavaHprofConfig_ContinuousDumpConfig::SerializeAsString() const {
+std::string ProtoLogConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> JavaHprofConfig_ContinuousDumpConfig::SerializeAsArray() const {
+std::vector<uint8_t> ProtoLogConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void JavaHprofConfig_ContinuousDumpConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: dump_phase_ms
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, dump_phase_ms_, msg);
+void ProtoLogConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: group_overrides
+  for (auto& it : group_overrides_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 2: dump_interval_ms
+  // Field 2: tracing_mode
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, dump_interval_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, tracing_mode_, msg);
   }

-  // Field 3: scan_pids_only_on_start
+  // Field 3: default_log_from_level
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, scan_pids_only_on_start_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, default_log_from_level_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -25964,7 +25688,7 @@ void JavaHprofConfig_ContinuousDumpConfig::Serialize(::protozero::Message* msg)
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/profiling/perf_event_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/surfaceflinger_layers_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -25975,57 +25699,27 @@ void JavaHprofConfig_ContinuousDumpConfig::Serialize(::protozero::Message* msg)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/perf_event_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/perf_events.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_layers_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-PerfEventConfig::PerfEventConfig() = default;
-PerfEventConfig::~PerfEventConfig() = default;
-PerfEventConfig::PerfEventConfig(const PerfEventConfig&) = default;
-PerfEventConfig& PerfEventConfig::operator=(const PerfEventConfig&) = default;
-PerfEventConfig::PerfEventConfig(PerfEventConfig&&) noexcept = default;
-PerfEventConfig& PerfEventConfig::operator=(PerfEventConfig&&) = default;
+SurfaceFlingerLayersConfig::SurfaceFlingerLayersConfig() = default;
+SurfaceFlingerLayersConfig::~SurfaceFlingerLayersConfig() = default;
+SurfaceFlingerLayersConfig::SurfaceFlingerLayersConfig(const SurfaceFlingerLayersConfig&) = default;
+SurfaceFlingerLayersConfig& SurfaceFlingerLayersConfig::operator=(const SurfaceFlingerLayersConfig&) = default;
+SurfaceFlingerLayersConfig::SurfaceFlingerLayersConfig(SurfaceFlingerLayersConfig&&) noexcept = default;
+SurfaceFlingerLayersConfig& SurfaceFlingerLayersConfig::operator=(SurfaceFlingerLayersConfig&&) = default;

-bool PerfEventConfig::operator==(const PerfEventConfig& other) const {
+bool SurfaceFlingerLayersConfig::operator==(const SurfaceFlingerLayersConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(timebase_, other.timebase_)
-   && ::protozero::internal::gen_helpers::EqualsField(followers_, other.followers_)
-   && ::protozero::internal::gen_helpers::EqualsField(callstack_sampling_, other.callstack_sampling_)
-   && ::protozero::internal::gen_helpers::EqualsField(target_cpu_, other.target_cpu_)
-   && ::protozero::internal::gen_helpers::EqualsField(ignore_open_failure_, other.ignore_open_failure_)
-   && ::protozero::internal::gen_helpers::EqualsField(cpuid_, other.cpuid_)
-   && ::protozero::internal::gen_helpers::EqualsField(ring_buffer_read_period_ms_, other.ring_buffer_read_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(ring_buffer_pages_, other.ring_buffer_pages_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_enqueued_footprint_kb_, other.max_enqueued_footprint_kb_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_daemon_memory_kb_, other.max_daemon_memory_kb_)
-   && ::protozero::internal::gen_helpers::EqualsField(remote_descriptor_timeout_ms_, other.remote_descriptor_timeout_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(unwind_state_clear_period_ms_, other.unwind_state_clear_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(target_installed_by_, other.target_installed_by_)
-   && ::protozero::internal::gen_helpers::EqualsField(all_cpus_, other.all_cpus_)
-   && ::protozero::internal::gen_helpers::EqualsField(sampling_frequency_, other.sampling_frequency_)
-   && ::protozero::internal::gen_helpers::EqualsField(kernel_frames_, other.kernel_frames_)
-   && ::protozero::internal::gen_helpers::EqualsField(target_pid_, other.target_pid_)
-   && ::protozero::internal::gen_helpers::EqualsField(target_cmdline_, other.target_cmdline_)
-   && ::protozero::internal::gen_helpers::EqualsField(exclude_pid_, other.exclude_pid_)
-   && ::protozero::internal::gen_helpers::EqualsField(exclude_cmdline_, other.exclude_cmdline_)
-   && ::protozero::internal::gen_helpers::EqualsField(additional_cmdline_count_, other.additional_cmdline_count_);
+   && ::protozero::internal::gen_helpers::EqualsField(mode_, other.mode_)
+   && ::protozero::internal::gen_helpers::EqualsField(trace_flags_, other.trace_flags_);
 }

-int PerfEventConfig::followers_size() const { return static_cast<int>(followers_.size()); }
-void PerfEventConfig::clear_followers() { followers_.clear(); }
-FollowerEvent* PerfEventConfig::add_followers() { followers_.emplace_back(); return &followers_.back(); }
-bool PerfEventConfig::ParseFromArray(const void* raw, size_t size) {
-  followers_.clear();
-  target_cpu_.clear();
-  cpuid_.clear();
-  target_installed_by_.clear();
-  target_pid_.clear();
-  target_cmdline_.clear();
-  exclude_pid_.clear();
-  exclude_cmdline_.clear();
+bool SurfaceFlingerLayersConfig::ParseFromArray(const void* raw, size_t size) {
+  trace_flags_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -26035,76 +25729,12 @@ bool PerfEventConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 15 /* timebase */:
-        (*timebase_).ParseFromArray(field.data(), field.size());
-        break;
-      case 19 /* followers */:
-        followers_.emplace_back();
-        followers_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 16 /* callstack_sampling */:
-        (*callstack_sampling_).ParseFromArray(field.data(), field.size());
-        break;
-      case 20 /* target_cpu */:
-        target_cpu_.emplace_back();
-        field.get(&target_cpu_.back());
-        break;
-      case 21 /* ignore_open_failure */:
-        field.get(&ignore_open_failure_);
-        break;
-      case 22 /* cpuid */:
-        cpuid_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &cpuid_.back());
-        break;
-      case 8 /* ring_buffer_read_period_ms */:
-        field.get(&ring_buffer_read_period_ms_);
-        break;
-      case 3 /* ring_buffer_pages */:
-        field.get(&ring_buffer_pages_);
-        break;
-      case 17 /* max_enqueued_footprint_kb */:
-        field.get(&max_enqueued_footprint_kb_);
-        break;
-      case 13 /* max_daemon_memory_kb */:
-        field.get(&max_daemon_memory_kb_);
-        break;
-      case 9 /* remote_descriptor_timeout_ms */:
-        field.get(&remote_descriptor_timeout_ms_);
-        break;
-      case 10 /* unwind_state_clear_period_ms */:
-        field.get(&unwind_state_clear_period_ms_);
-        break;
-      case 18 /* target_installed_by */:
-        target_installed_by_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &target_installed_by_.back());
-        break;
-      case 1 /* all_cpus */:
-        field.get(&all_cpus_);
-        break;
-      case 2 /* sampling_frequency */:
-        field.get(&sampling_frequency_);
-        break;
-      case 12 /* kernel_frames */:
-        field.get(&kernel_frames_);
-        break;
-      case 4 /* target_pid */:
-        target_pid_.emplace_back();
-        field.get(&target_pid_.back());
-        break;
-      case 5 /* target_cmdline */:
-        target_cmdline_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &target_cmdline_.back());
-        break;
-      case 6 /* exclude_pid */:
-        exclude_pid_.emplace_back();
-        field.get(&exclude_pid_.back());
-        break;
-      case 7 /* exclude_cmdline */:
-        exclude_cmdline_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &exclude_cmdline_.back());
+      case 1 /* mode */:
+        field.get(&mode_);
         break;
-      case 11 /* additional_cmdline_count */:
-        field.get(&additional_cmdline_count_);
+      case 2 /* trace_flags */:
+        trace_flags_.emplace_back();
+        field.get(&trace_flags_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -26114,143 +25744,146 @@ bool PerfEventConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string PerfEventConfig::SerializeAsString() const {
+std::string SurfaceFlingerLayersConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> PerfEventConfig::SerializeAsArray() const {
+std::vector<uint8_t> SurfaceFlingerLayersConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void PerfEventConfig::Serialize(::protozero::Message* msg) const {
-  // Field 15: timebase
-  if (_has_field_[15]) {
-    (*timebase_).Serialize(msg->BeginNestedMessage<::protozero::Message>(15));
-  }
-
-  // Field 19: followers
-  for (auto& it : followers_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(19));
-  }
-
-  // Field 16: callstack_sampling
-  if (_has_field_[16]) {
-    (*callstack_sampling_).Serialize(msg->BeginNestedMessage<::protozero::Message>(16));
+void SurfaceFlingerLayersConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: mode
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, mode_, msg);
   }

-  // Field 20: target_cpu
-  for (auto& it : target_cpu_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(20, it, msg);
+  // Field 2: trace_flags
+  for (auto& it : trace_flags_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
   }

-  // Field 21: ignore_open_failure
-  if (_has_field_[21]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(21, ignore_open_failure_, msg);
-  }
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}

-  // Field 22: cpuid
-  for (auto& it : cpuid_) {
-    ::protozero::internal::gen_helpers::SerializeString(22, it, msg);
-  }
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/surfaceflinger_transactions_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_transactions_config.gen.h"

-  // Field 8: ring_buffer_read_period_ms
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, ring_buffer_read_period_ms_, msg);
-  }
+namespace perfetto {
+namespace protos {
+namespace gen {

-  // Field 3: ring_buffer_pages
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, ring_buffer_pages_, msg);
-  }
+SurfaceFlingerTransactionsConfig::SurfaceFlingerTransactionsConfig() = default;
+SurfaceFlingerTransactionsConfig::~SurfaceFlingerTransactionsConfig() = default;
+SurfaceFlingerTransactionsConfig::SurfaceFlingerTransactionsConfig(const SurfaceFlingerTransactionsConfig&) = default;
+SurfaceFlingerTransactionsConfig& SurfaceFlingerTransactionsConfig::operator=(const SurfaceFlingerTransactionsConfig&) = default;
+SurfaceFlingerTransactionsConfig::SurfaceFlingerTransactionsConfig(SurfaceFlingerTransactionsConfig&&) noexcept = default;
+SurfaceFlingerTransactionsConfig& SurfaceFlingerTransactionsConfig::operator=(SurfaceFlingerTransactionsConfig&&) = default;

-  // Field 17: max_enqueued_footprint_kb
-  if (_has_field_[17]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(17, max_enqueued_footprint_kb_, msg);
-  }
+bool SurfaceFlingerTransactionsConfig::operator==(const SurfaceFlingerTransactionsConfig& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(mode_, other.mode_);
+}

-  // Field 13: max_daemon_memory_kb
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(13, max_daemon_memory_kb_, msg);
-  }
+bool SurfaceFlingerTransactionsConfig::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;

-  // Field 9: remote_descriptor_timeout_ms
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(9, remote_descriptor_timeout_ms_, msg);
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* mode */:
+        field.get(&mode_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 10: unwind_state_clear_period_ms
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(10, unwind_state_clear_period_ms_, msg);
-  }
+std::string SurfaceFlingerTransactionsConfig::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}

-  // Field 18: target_installed_by
-  for (auto& it : target_installed_by_) {
-    ::protozero::internal::gen_helpers::SerializeString(18, it, msg);
-  }
+std::vector<uint8_t> SurfaceFlingerTransactionsConfig::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}

-  // Field 1: all_cpus
+void SurfaceFlingerTransactionsConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: mode
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, all_cpus_, msg);
-  }
-
-  // Field 2: sampling_frequency
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, sampling_frequency_, msg);
-  }
-
-  // Field 12: kernel_frames
-  if (_has_field_[12]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(12, kernel_frames_, msg);
-  }
-
-  // Field 4: target_pid
-  for (auto& it : target_pid_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, it, msg);
-  }
-
-  // Field 5: target_cmdline
-  for (auto& it : target_cmdline_) {
-    ::protozero::internal::gen_helpers::SerializeString(5, it, msg);
-  }
-
-  // Field 6: exclude_pid
-  for (auto& it : exclude_pid_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, it, msg);
-  }
-
-  // Field 7: exclude_cmdline
-  for (auto& it : exclude_cmdline_) {
-    ::protozero::internal::gen_helpers::SerializeString(7, it, msg);
-  }
-
-  // Field 11: additional_cmdline_count
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(11, additional_cmdline_count_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, mode_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/user_list_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/user_list_config.gen.h"

-PerfEventConfig_CallstackSampling::PerfEventConfig_CallstackSampling() = default;
-PerfEventConfig_CallstackSampling::~PerfEventConfig_CallstackSampling() = default;
-PerfEventConfig_CallstackSampling::PerfEventConfig_CallstackSampling(const PerfEventConfig_CallstackSampling&) = default;
-PerfEventConfig_CallstackSampling& PerfEventConfig_CallstackSampling::operator=(const PerfEventConfig_CallstackSampling&) = default;
-PerfEventConfig_CallstackSampling::PerfEventConfig_CallstackSampling(PerfEventConfig_CallstackSampling&&) noexcept = default;
-PerfEventConfig_CallstackSampling& PerfEventConfig_CallstackSampling::operator=(PerfEventConfig_CallstackSampling&&) = default;
+namespace perfetto {
+namespace protos {
+namespace gen {

-bool PerfEventConfig_CallstackSampling::operator==(const PerfEventConfig_CallstackSampling& other) const {
+AndroidUserListConfig::AndroidUserListConfig() = default;
+AndroidUserListConfig::~AndroidUserListConfig() = default;
+AndroidUserListConfig::AndroidUserListConfig(const AndroidUserListConfig&) = default;
+AndroidUserListConfig& AndroidUserListConfig::operator=(const AndroidUserListConfig&) = default;
+AndroidUserListConfig::AndroidUserListConfig(AndroidUserListConfig&&) noexcept = default;
+AndroidUserListConfig& AndroidUserListConfig::operator=(AndroidUserListConfig&&) = default;
+
+bool AndroidUserListConfig::operator==(const AndroidUserListConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(scope_, other.scope_)
-   && ::protozero::internal::gen_helpers::EqualsField(kernel_frames_, other.kernel_frames_)
-   && ::protozero::internal::gen_helpers::EqualsField(user_frames_, other.user_frames_);
+   && ::protozero::internal::gen_helpers::EqualsField(user_type_filter_, other.user_type_filter_);
 }

-bool PerfEventConfig_CallstackSampling::ParseFromArray(const void* raw, size_t size) {
+bool AndroidUserListConfig::ParseFromArray(const void* raw, size_t size) {
+  user_type_filter_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -26260,14 +25893,9 @@ bool PerfEventConfig_CallstackSampling::ParseFromArray(const void* raw, size_t s
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* scope */:
-        (*scope_).ParseFromArray(field.data(), field.size());
-        break;
-      case 2 /* kernel_frames */:
-        field.get(&kernel_frames_);
-        break;
-      case 3 /* user_frames */:
-        field.get(&user_frames_);
+      case 1 /* user_type_filter */:
+        user_type_filter_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &user_type_filter_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -26277,140 +25905,22 @@ bool PerfEventConfig_CallstackSampling::ParseFromArray(const void* raw, size_t s
   return !packed_error && !dec.bytes_left();
 }

-std::string PerfEventConfig_CallstackSampling::SerializeAsString() const {
+std::string AndroidUserListConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> PerfEventConfig_CallstackSampling::SerializeAsArray() const {
+std::vector<uint8_t> AndroidUserListConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void PerfEventConfig_CallstackSampling::Serialize(::protozero::Message* msg) const {
-  // Field 1: scope
-  if (_has_field_[1]) {
-    (*scope_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
-  }
-
-  // Field 2: kernel_frames
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, kernel_frames_, msg);
-  }
-
-  // Field 3: user_frames
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, user_frames_, msg);
-  }
-
-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
-
-
-PerfEventConfig_Scope::PerfEventConfig_Scope() = default;
-PerfEventConfig_Scope::~PerfEventConfig_Scope() = default;
-PerfEventConfig_Scope::PerfEventConfig_Scope(const PerfEventConfig_Scope&) = default;
-PerfEventConfig_Scope& PerfEventConfig_Scope::operator=(const PerfEventConfig_Scope&) = default;
-PerfEventConfig_Scope::PerfEventConfig_Scope(PerfEventConfig_Scope&&) noexcept = default;
-PerfEventConfig_Scope& PerfEventConfig_Scope::operator=(PerfEventConfig_Scope&&) = default;
-
-bool PerfEventConfig_Scope::operator==(const PerfEventConfig_Scope& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(target_pid_, other.target_pid_)
-   && ::protozero::internal::gen_helpers::EqualsField(target_cmdline_, other.target_cmdline_)
-   && ::protozero::internal::gen_helpers::EqualsField(exclude_pid_, other.exclude_pid_)
-   && ::protozero::internal::gen_helpers::EqualsField(exclude_cmdline_, other.exclude_cmdline_)
-   && ::protozero::internal::gen_helpers::EqualsField(additional_cmdline_count_, other.additional_cmdline_count_)
-   && ::protozero::internal::gen_helpers::EqualsField(process_shard_count_, other.process_shard_count_);
-}
-
-bool PerfEventConfig_Scope::ParseFromArray(const void* raw, size_t size) {
-  target_pid_.clear();
-  target_cmdline_.clear();
-  exclude_pid_.clear();
-  exclude_cmdline_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
-
-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* target_pid */:
-        target_pid_.emplace_back();
-        field.get(&target_pid_.back());
-        break;
-      case 2 /* target_cmdline */:
-        target_cmdline_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &target_cmdline_.back());
-        break;
-      case 3 /* exclude_pid */:
-        exclude_pid_.emplace_back();
-        field.get(&exclude_pid_.back());
-        break;
-      case 4 /* exclude_cmdline */:
-        exclude_cmdline_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &exclude_cmdline_.back());
-        break;
-      case 5 /* additional_cmdline_count */:
-        field.get(&additional_cmdline_count_);
-        break;
-      case 6 /* process_shard_count */:
-        field.get(&process_shard_count_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
-}
-
-std::string PerfEventConfig_Scope::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
-
-std::vector<uint8_t> PerfEventConfig_Scope::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
-
-void PerfEventConfig_Scope::Serialize(::protozero::Message* msg) const {
-  // Field 1: target_pid
-  for (auto& it : target_pid_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
-  }
-
-  // Field 2: target_cmdline
-  for (auto& it : target_cmdline_) {
-    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
-  }
-
-  // Field 3: exclude_pid
-  for (auto& it : exclude_pid_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, it, msg);
-  }
-
-  // Field 4: exclude_cmdline
-  for (auto& it : exclude_cmdline_) {
-    ::protozero::internal::gen_helpers::SerializeString(4, it, msg);
-  }
-
-  // Field 5: additional_cmdline_count
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, additional_cmdline_count_, msg);
-  }
-
-  // Field 6: process_shard_count
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, process_shard_count_, msg);
+void AndroidUserListConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: user_type_filter
+  for (auto& it : user_type_filter_) {
+    ::protozero::internal::gen_helpers::SerializeString(1, it, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -26422,7 +25932,7 @@ void PerfEventConfig_Scope::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/profiling/smaps_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/windowmanager_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -26433,30 +25943,27 @@ void PerfEventConfig_Scope::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/smaps_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/windowmanager_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-RedactionRule::RedactionRule() = default;
-RedactionRule::~RedactionRule() = default;
-RedactionRule::RedactionRule(const RedactionRule&) = default;
-RedactionRule& RedactionRule::operator=(const RedactionRule&) = default;
-RedactionRule::RedactionRule(RedactionRule&&) noexcept = default;
-RedactionRule& RedactionRule::operator=(RedactionRule&&) = default;
+WindowManagerConfig::WindowManagerConfig() = default;
+WindowManagerConfig::~WindowManagerConfig() = default;
+WindowManagerConfig::WindowManagerConfig(const WindowManagerConfig&) = default;
+WindowManagerConfig& WindowManagerConfig::operator=(const WindowManagerConfig&) = default;
+WindowManagerConfig::WindowManagerConfig(WindowManagerConfig&&) noexcept = default;
+WindowManagerConfig& WindowManagerConfig::operator=(WindowManagerConfig&&) = default;

-bool RedactionRule::operator==(const RedactionRule& other) const {
+bool WindowManagerConfig::operator==(const WindowManagerConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(pattern_, other.pattern_)
-   && ::protozero::internal::gen_helpers::EqualsField(match_mode_, other.match_mode_)
-   && ::protozero::internal::gen_helpers::EqualsField(keep_full_, other.keep_full_)
-   && ::protozero::internal::gen_helpers::EqualsField(replacement_name_, other.replacement_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(keep_file_extension_, other.keep_file_extension_)
-   && ::protozero::internal::gen_helpers::EqualsField(keep_path_elements_, other.keep_path_elements_);
+   && ::protozero::internal::gen_helpers::EqualsField(log_frequency_, other.log_frequency_)
+   && ::protozero::internal::gen_helpers::EqualsField(log_level_, other.log_level_)
+   && ::protozero::internal::gen_helpers::EqualsField(log_mode_, other.log_mode_);
 }

-bool RedactionRule::ParseFromArray(const void* raw, size_t size) {
+bool WindowManagerConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -26466,23 +25973,14 @@ bool RedactionRule::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* pattern */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &pattern_);
-        break;
-      case 2 /* match_mode */:
-        field.get(&match_mode_);
-        break;
-      case 3 /* keep_full */:
-        field.get(&keep_full_);
-        break;
-      case 4 /* replacement_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &replacement_name_);
+      case 1 /* log_frequency */:
+        field.get(&log_frequency_);
         break;
-      case 5 /* keep_file_extension */:
-        field.get(&keep_file_extension_);
+      case 2 /* log_level */:
+        field.get(&log_level_);
         break;
-      case 6 /* keep_path_elements */:
-        field.get(&keep_path_elements_);
+      case 3 /* log_mode */:
+        field.get(&log_mode_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -26492,127 +25990,32 @@ bool RedactionRule::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string RedactionRule::SerializeAsString() const {
+std::string WindowManagerConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> RedactionRule::SerializeAsArray() const {
+std::vector<uint8_t> WindowManagerConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void RedactionRule::Serialize(::protozero::Message* msg) const {
-  // Field 1: pattern
+void WindowManagerConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: log_frequency
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, pattern_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, log_frequency_, msg);
   }

-  // Field 2: match_mode
+  // Field 2: log_level
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, match_mode_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, log_level_, msg);
   }

-  // Field 3: keep_full
+  // Field 3: log_mode
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, keep_full_, msg);
-  }
-
-  // Field 4: replacement_name
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeString(4, replacement_name_, msg);
-  }
-
-  // Field 5: keep_file_extension
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, keep_file_extension_, msg);
-  }
-
-  // Field 6: keep_path_elements
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, keep_path_elements_, msg);
-  }
-
-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
-
-
-SmapsConfig::SmapsConfig() = default;
-SmapsConfig::~SmapsConfig() = default;
-SmapsConfig::SmapsConfig(const SmapsConfig&) = default;
-SmapsConfig& SmapsConfig::operator=(const SmapsConfig&) = default;
-SmapsConfig::SmapsConfig(SmapsConfig&&) noexcept = default;
-SmapsConfig& SmapsConfig::operator=(SmapsConfig&&) = default;
-
-bool SmapsConfig::operator==(const SmapsConfig& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(vma_fields_, other.vma_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(unaggregated_, other.unaggregated_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_redaction_rules_, other.name_redaction_rules_);
-}
-
-int SmapsConfig::name_redaction_rules_size() const { return static_cast<int>(name_redaction_rules_.size()); }
-void SmapsConfig::clear_name_redaction_rules() { name_redaction_rules_.clear(); }
-RedactionRule* SmapsConfig::add_name_redaction_rules() { name_redaction_rules_.emplace_back(); return &name_redaction_rules_.back(); }
-bool SmapsConfig::ParseFromArray(const void* raw, size_t size) {
-  vma_fields_.clear();
-  name_redaction_rules_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
-
-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* vma_fields */:
-        vma_fields_.emplace_back();
-        field.get(&vma_fields_.back());
-        break;
-      case 2 /* unaggregated */:
-        field.get(&unaggregated_);
-        break;
-      case 3 /* name_redaction_rules */:
-        name_redaction_rules_.emplace_back();
-        name_redaction_rules_.back().ParseFromArray(field.data(), field.size());
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
-}
-
-std::string SmapsConfig::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
-
-std::vector<uint8_t> SmapsConfig::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
-
-void SmapsConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: vma_fields
-  for (auto& it : vma_fields_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
-  }
-
-  // Field 2: unaggregated
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, unaggregated_, msg);
-  }
-
-  // Field 3: name_redaction_rules
-  for (auto& it : name_redaction_rules_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, log_mode_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -26624,7 +26027,7 @@ void SmapsConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/protovm/protovm_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/ftrace/frozen_ftrace_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -26635,25 +26038,25 @@ void SmapsConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/protovm/protovm_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/frozen_ftrace_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-ProtoVmConfig::ProtoVmConfig() = default;
-ProtoVmConfig::~ProtoVmConfig() = default;
-ProtoVmConfig::ProtoVmConfig(const ProtoVmConfig&) = default;
-ProtoVmConfig& ProtoVmConfig::operator=(const ProtoVmConfig&) = default;
-ProtoVmConfig::ProtoVmConfig(ProtoVmConfig&&) noexcept = default;
-ProtoVmConfig& ProtoVmConfig::operator=(ProtoVmConfig&&) = default;
+FrozenFtraceConfig::FrozenFtraceConfig() = default;
+FrozenFtraceConfig::~FrozenFtraceConfig() = default;
+FrozenFtraceConfig::FrozenFtraceConfig(const FrozenFtraceConfig&) = default;
+FrozenFtraceConfig& FrozenFtraceConfig::operator=(const FrozenFtraceConfig&) = default;
+FrozenFtraceConfig::FrozenFtraceConfig(FrozenFtraceConfig&&) noexcept = default;
+FrozenFtraceConfig& FrozenFtraceConfig::operator=(FrozenFtraceConfig&&) = default;

-bool ProtoVmConfig::operator==(const ProtoVmConfig& other) const {
+bool FrozenFtraceConfig::operator==(const FrozenFtraceConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(memory_limit_kb_, other.memory_limit_kb_);
+   && ::protozero::internal::gen_helpers::EqualsField(instance_name_, other.instance_name_);
 }

-bool ProtoVmConfig::ParseFromArray(const void* raw, size_t size) {
+bool FrozenFtraceConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -26663,8 +26066,8 @@ bool ProtoVmConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* memory_limit_kb */:
-        field.get(&memory_limit_kb_);
+      case 1 /* instance_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &instance_name_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -26674,22 +26077,22 @@ bool ProtoVmConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ProtoVmConfig::SerializeAsString() const {
+std::string FrozenFtraceConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ProtoVmConfig::SerializeAsArray() const {
+std::vector<uint8_t> FrozenFtraceConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ProtoVmConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: memory_limit_kb
+void FrozenFtraceConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: instance_name
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, memory_limit_kb_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, instance_name_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -26701,7 +26104,7 @@ void ProtoVmConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/qnx/qnx_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/ftrace/ftrace_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -26712,30 +26115,70 @@ void ProtoVmConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/qnx/qnx_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/ftrace_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-QnxConfig::QnxConfig() = default;
-QnxConfig::~QnxConfig() = default;
-QnxConfig::QnxConfig(const QnxConfig&) = default;
-QnxConfig& QnxConfig::operator=(const QnxConfig&) = default;
-QnxConfig::QnxConfig(QnxConfig&&) noexcept = default;
-QnxConfig& QnxConfig::operator=(QnxConfig&&) = default;
+FtraceConfig::FtraceConfig() = default;
+FtraceConfig::~FtraceConfig() = default;
+FtraceConfig::FtraceConfig(const FtraceConfig&) = default;
+FtraceConfig& FtraceConfig::operator=(const FtraceConfig&) = default;
+FtraceConfig::FtraceConfig(FtraceConfig&&) noexcept = default;
+FtraceConfig& FtraceConfig::operator=(FtraceConfig&&) = default;

-bool QnxConfig::operator==(const QnxConfig& other) const {
+bool FtraceConfig::operator==(const FtraceConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(qnx_kernel_buffers_, other.qnx_kernel_buffers_)
-   && ::protozero::internal::gen_helpers::EqualsField(qnx_kernel_kbuffers_, other.qnx_kernel_kbuffers_)
-   && ::protozero::internal::gen_helpers::EqualsField(qnx_kernel_wide_events_, other.qnx_kernel_wide_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(qnx_cache_pages_, other.qnx_cache_pages_)
-   && ::protozero::internal::gen_helpers::EqualsField(qnx_cache_max_pages_, other.qnx_cache_max_pages_)
-   && ::protozero::internal::gen_helpers::EqualsField(qnx_trace_buffer_init_bytes_, other.qnx_trace_buffer_init_bytes_);
+   && ::protozero::internal::gen_helpers::EqualsField(ftrace_events_, other.ftrace_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(atrace_categories_, other.atrace_categories_)
+   && ::protozero::internal::gen_helpers::EqualsField(atrace_apps_, other.atrace_apps_)
+   && ::protozero::internal::gen_helpers::EqualsField(atrace_categories_prefer_sdk_, other.atrace_categories_prefer_sdk_)
+   && ::protozero::internal::gen_helpers::EqualsField(atrace_userspace_only_, other.atrace_userspace_only_)
+   && ::protozero::internal::gen_helpers::EqualsField(buffer_size_kb_, other.buffer_size_kb_)
+   && ::protozero::internal::gen_helpers::EqualsField(buffer_size_lower_bound_, other.buffer_size_lower_bound_)
+   && ::protozero::internal::gen_helpers::EqualsField(drain_period_ms_, other.drain_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(drain_buffer_percent_, other.drain_buffer_percent_)
+   && ::protozero::internal::gen_helpers::EqualsField(compact_sched_, other.compact_sched_)
+   && ::protozero::internal::gen_helpers::EqualsField(print_filter_, other.print_filter_)
+   && ::protozero::internal::gen_helpers::EqualsField(symbolize_ksyms_, other.symbolize_ksyms_)
+   && ::protozero::internal::gen_helpers::EqualsField(ksyms_mem_policy_, other.ksyms_mem_policy_)
+   && ::protozero::internal::gen_helpers::EqualsField(throttle_rss_stat_, other.throttle_rss_stat_)
+   && ::protozero::internal::gen_helpers::EqualsField(denser_generic_event_encoding_, other.denser_generic_event_encoding_)
+   && ::protozero::internal::gen_helpers::EqualsField(disable_generic_events_, other.disable_generic_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(syscall_events_, other.syscall_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(enable_function_graph_, other.enable_function_graph_)
+   && ::protozero::internal::gen_helpers::EqualsField(function_filters_, other.function_filters_)
+   && ::protozero::internal::gen_helpers::EqualsField(function_graph_roots_, other.function_graph_roots_)
+   && ::protozero::internal::gen_helpers::EqualsField(function_graph_max_depth_, other.function_graph_max_depth_)
+   && ::protozero::internal::gen_helpers::EqualsField(kprobe_events_, other.kprobe_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(preserve_ftrace_buffer_, other.preserve_ftrace_buffer_)
+   && ::protozero::internal::gen_helpers::EqualsField(use_monotonic_raw_clock_, other.use_monotonic_raw_clock_)
+   && ::protozero::internal::gen_helpers::EqualsField(instance_name_, other.instance_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(debug_ftrace_abi_, other.debug_ftrace_abi_)
+   && ::protozero::internal::gen_helpers::EqualsField(tids_to_trace_, other.tids_to_trace_)
+   && ::protozero::internal::gen_helpers::EqualsField(tracefs_options_, other.tracefs_options_)
+   && ::protozero::internal::gen_helpers::EqualsField(tracing_cpumask_, other.tracing_cpumask_)
+   && ::protozero::internal::gen_helpers::EqualsField(initialize_ksyms_synchronously_for_testing_, other.initialize_ksyms_synchronously_for_testing_);
 }

-bool QnxConfig::ParseFromArray(const void* raw, size_t size) {
+int FtraceConfig::kprobe_events_size() const { return static_cast<int>(kprobe_events_.size()); }
+void FtraceConfig::clear_kprobe_events() { kprobe_events_.clear(); }
+FtraceConfig_KprobeEvent* FtraceConfig::add_kprobe_events() { kprobe_events_.emplace_back(); return &kprobe_events_.back(); }
+int FtraceConfig::tracefs_options_size() const { return static_cast<int>(tracefs_options_.size()); }
+void FtraceConfig::clear_tracefs_options() { tracefs_options_.clear(); }
+FtraceConfig_TracefsOption* FtraceConfig::add_tracefs_options() { tracefs_options_.emplace_back(); return &tracefs_options_.back(); }
+bool FtraceConfig::ParseFromArray(const void* raw, size_t size) {
+  ftrace_events_.clear();
+  atrace_categories_.clear();
+  atrace_apps_.clear();
+  atrace_categories_prefer_sdk_.clear();
+  syscall_events_.clear();
+  function_filters_.clear();
+  function_graph_roots_.clear();
+  kprobe_events_.clear();
+  tids_to_trace_.clear();
+  tracefs_options_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -26745,23 +26188,105 @@ bool QnxConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* qnx_kernel_buffers */:
-        field.get(&qnx_kernel_buffers_);
+      case 1 /* ftrace_events */:
+        ftrace_events_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &ftrace_events_.back());
         break;
-      case 2 /* qnx_kernel_kbuffers */:
-        field.get(&qnx_kernel_kbuffers_);
+      case 2 /* atrace_categories */:
+        atrace_categories_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &atrace_categories_.back());
         break;
-      case 3 /* qnx_kernel_wide_events */:
-        field.get(&qnx_kernel_wide_events_);
+      case 3 /* atrace_apps */:
+        atrace_apps_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &atrace_apps_.back());
         break;
-      case 4 /* qnx_cache_pages */:
-        field.get(&qnx_cache_pages_);
+      case 28 /* atrace_categories_prefer_sdk */:
+        atrace_categories_prefer_sdk_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &atrace_categories_prefer_sdk_.back());
         break;
-      case 5 /* qnx_cache_max_pages */:
-        field.get(&qnx_cache_max_pages_);
+      case 34 /* atrace_userspace_only */:
+        field.get(&atrace_userspace_only_);
         break;
-      case 6 /* qnx_trace_buffer_init_bytes */:
-        field.get(&qnx_trace_buffer_init_bytes_);
+      case 10 /* buffer_size_kb */:
+        field.get(&buffer_size_kb_);
+        break;
+      case 27 /* buffer_size_lower_bound */:
+        field.get(&buffer_size_lower_bound_);
+        break;
+      case 11 /* drain_period_ms */:
+        field.get(&drain_period_ms_);
+        break;
+      case 29 /* drain_buffer_percent */:
+        field.get(&drain_buffer_percent_);
+        break;
+      case 12 /* compact_sched */:
+        (*compact_sched_).ParseFromArray(field.data(), field.size());
+        break;
+      case 22 /* print_filter */:
+        (*print_filter_).ParseFromArray(field.data(), field.size());
+        break;
+      case 13 /* symbolize_ksyms */:
+        field.get(&symbolize_ksyms_);
+        break;
+      case 17 /* ksyms_mem_policy */:
+        field.get(&ksyms_mem_policy_);
+        break;
+      case 15 /* throttle_rss_stat */:
+        field.get(&throttle_rss_stat_);
+        break;
+      case 32 /* denser_generic_event_encoding */:
+        field.get(&denser_generic_event_encoding_);
+        break;
+      case 16 /* disable_generic_events */:
+        field.get(&disable_generic_events_);
+        break;
+      case 18 /* syscall_events */:
+        syscall_events_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &syscall_events_.back());
+        break;
+      case 19 /* enable_function_graph */:
+        field.get(&enable_function_graph_);
+        break;
+      case 20 /* function_filters */:
+        function_filters_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &function_filters_.back());
+        break;
+      case 21 /* function_graph_roots */:
+        function_graph_roots_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &function_graph_roots_.back());
+        break;
+      case 33 /* function_graph_max_depth */:
+        field.get(&function_graph_max_depth_);
+        break;
+      case 30 /* kprobe_events */:
+        kprobe_events_.emplace_back();
+        kprobe_events_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 23 /* preserve_ftrace_buffer */:
+        field.get(&preserve_ftrace_buffer_);
+        break;
+      case 24 /* use_monotonic_raw_clock */:
+        field.get(&use_monotonic_raw_clock_);
+        break;
+      case 25 /* instance_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &instance_name_);
+        break;
+      case 31 /* debug_ftrace_abi */:
+        field.get(&debug_ftrace_abi_);
+        break;
+      case 35 /* tids_to_trace */:
+        tids_to_trace_.emplace_back();
+        field.get(&tids_to_trace_.back());
+        break;
+      case 36 /* tracefs_options */:
+        tracefs_options_.emplace_back();
+        tracefs_options_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 37 /* tracing_cpumask */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &tracing_cpumask_);
+        break;
+      case 14 /* initialize_ksyms_synchronously_for_testing */:
+        field.get(&initialize_ksyms_synchronously_for_testing_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -26771,117 +26296,187 @@ bool QnxConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string QnxConfig::SerializeAsString() const {
+std::string FtraceConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> QnxConfig::SerializeAsArray() const {
+std::vector<uint8_t> FtraceConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void QnxConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: qnx_kernel_buffers
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, qnx_kernel_buffers_, msg);
-  }
+void FtraceConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: ftrace_events
+  for (auto& it : ftrace_events_) {
+    ::protozero::internal::gen_helpers::SerializeString(1, it, msg);
+  }

-  // Field 2: qnx_kernel_kbuffers
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, qnx_kernel_kbuffers_, msg);
+  // Field 2: atrace_categories
+  for (auto& it : atrace_categories_) {
+    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
   }

-  // Field 3: qnx_kernel_wide_events
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, qnx_kernel_wide_events_, msg);
+  // Field 3: atrace_apps
+  for (auto& it : atrace_apps_) {
+    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
   }

-  // Field 4: qnx_cache_pages
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, qnx_cache_pages_, msg);
+  // Field 28: atrace_categories_prefer_sdk
+  for (auto& it : atrace_categories_prefer_sdk_) {
+    ::protozero::internal::gen_helpers::SerializeString(28, it, msg);
   }

-  // Field 5: qnx_cache_max_pages
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, qnx_cache_max_pages_, msg);
+  // Field 34: atrace_userspace_only
+  if (_has_field_[34]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(34, atrace_userspace_only_, msg);
   }

-  // Field 6: qnx_trace_buffer_init_bytes
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, qnx_trace_buffer_init_bytes_, msg);
+  // Field 10: buffer_size_kb
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(10, buffer_size_kb_, msg);
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+  // Field 27: buffer_size_lower_bound
+  if (_has_field_[27]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(27, buffer_size_lower_bound_, msg);
+  }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/statsd/atom_ids.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/atom_ids.gen.h"
+  // Field 11: drain_period_ms
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(11, drain_period_ms_, msg);
+  }

-namespace perfetto {
-namespace protos {
-namespace gen {
-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/statsd/statsd_tracing_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/statsd_tracing_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/atom_ids.gen.h"
+  // Field 29: drain_buffer_percent
+  if (_has_field_[29]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(29, drain_buffer_percent_, msg);
+  }

-namespace perfetto {
-namespace protos {
-namespace gen {
+  // Field 12: compact_sched
+  if (_has_field_[12]) {
+    (*compact_sched_).Serialize(msg->BeginNestedMessage<::protozero::Message>(12));
+  }

-StatsdPullAtomConfig::StatsdPullAtomConfig() = default;
-StatsdPullAtomConfig::~StatsdPullAtomConfig() = default;
-StatsdPullAtomConfig::StatsdPullAtomConfig(const StatsdPullAtomConfig&) = default;
-StatsdPullAtomConfig& StatsdPullAtomConfig::operator=(const StatsdPullAtomConfig&) = default;
-StatsdPullAtomConfig::StatsdPullAtomConfig(StatsdPullAtomConfig&&) noexcept = default;
-StatsdPullAtomConfig& StatsdPullAtomConfig::operator=(StatsdPullAtomConfig&&) = default;
+  // Field 22: print_filter
+  if (_has_field_[22]) {
+    (*print_filter_).Serialize(msg->BeginNestedMessage<::protozero::Message>(22));
+  }

-bool StatsdPullAtomConfig::operator==(const StatsdPullAtomConfig& other) const {
+  // Field 13: symbolize_ksyms
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(13, symbolize_ksyms_, msg);
+  }
+
+  // Field 17: ksyms_mem_policy
+  if (_has_field_[17]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(17, ksyms_mem_policy_, msg);
+  }
+
+  // Field 15: throttle_rss_stat
+  if (_has_field_[15]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(15, throttle_rss_stat_, msg);
+  }
+
+  // Field 32: denser_generic_event_encoding
+  if (_has_field_[32]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(32, denser_generic_event_encoding_, msg);
+  }
+
+  // Field 16: disable_generic_events
+  if (_has_field_[16]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(16, disable_generic_events_, msg);
+  }
+
+  // Field 18: syscall_events
+  for (auto& it : syscall_events_) {
+    ::protozero::internal::gen_helpers::SerializeString(18, it, msg);
+  }
+
+  // Field 19: enable_function_graph
+  if (_has_field_[19]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(19, enable_function_graph_, msg);
+  }
+
+  // Field 20: function_filters
+  for (auto& it : function_filters_) {
+    ::protozero::internal::gen_helpers::SerializeString(20, it, msg);
+  }
+
+  // Field 21: function_graph_roots
+  for (auto& it : function_graph_roots_) {
+    ::protozero::internal::gen_helpers::SerializeString(21, it, msg);
+  }
+
+  // Field 33: function_graph_max_depth
+  if (_has_field_[33]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(33, function_graph_max_depth_, msg);
+  }
+
+  // Field 30: kprobe_events
+  for (auto& it : kprobe_events_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(30));
+  }
+
+  // Field 23: preserve_ftrace_buffer
+  if (_has_field_[23]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(23, preserve_ftrace_buffer_, msg);
+  }
+
+  // Field 24: use_monotonic_raw_clock
+  if (_has_field_[24]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(24, use_monotonic_raw_clock_, msg);
+  }
+
+  // Field 25: instance_name
+  if (_has_field_[25]) {
+    ::protozero::internal::gen_helpers::SerializeString(25, instance_name_, msg);
+  }
+
+  // Field 31: debug_ftrace_abi
+  if (_has_field_[31]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(31, debug_ftrace_abi_, msg);
+  }
+
+  // Field 35: tids_to_trace
+  for (auto& it : tids_to_trace_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(35, it, msg);
+  }
+
+  // Field 36: tracefs_options
+  for (auto& it : tracefs_options_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(36));
+  }
+
+  // Field 37: tracing_cpumask
+  if (_has_field_[37]) {
+    ::protozero::internal::gen_helpers::SerializeString(37, tracing_cpumask_, msg);
+  }
+
+  // Field 14: initialize_ksyms_synchronously_for_testing
+  if (_has_field_[14]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(14, initialize_ksyms_synchronously_for_testing_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+FtraceConfig_TracefsOption::FtraceConfig_TracefsOption() = default;
+FtraceConfig_TracefsOption::~FtraceConfig_TracefsOption() = default;
+FtraceConfig_TracefsOption::FtraceConfig_TracefsOption(const FtraceConfig_TracefsOption&) = default;
+FtraceConfig_TracefsOption& FtraceConfig_TracefsOption::operator=(const FtraceConfig_TracefsOption&) = default;
+FtraceConfig_TracefsOption::FtraceConfig_TracefsOption(FtraceConfig_TracefsOption&&) noexcept = default;
+FtraceConfig_TracefsOption& FtraceConfig_TracefsOption::operator=(FtraceConfig_TracefsOption&&) = default;
+
+bool FtraceConfig_TracefsOption::operator==(const FtraceConfig_TracefsOption& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(pull_atom_id_, other.pull_atom_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(raw_pull_atom_id_, other.raw_pull_atom_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(pull_frequency_ms_, other.pull_frequency_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(packages_, other.packages_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(state_, other.state_);
 }

-bool StatsdPullAtomConfig::ParseFromArray(const void* raw, size_t size) {
-  pull_atom_id_.clear();
-  raw_pull_atom_id_.clear();
-  packages_.clear();
+bool FtraceConfig_TracefsOption::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -26891,20 +26486,11 @@ bool StatsdPullAtomConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* pull_atom_id */:
-        pull_atom_id_.emplace_back();
-        field.get(&pull_atom_id_.back());
-        break;
-      case 2 /* raw_pull_atom_id */:
-        raw_pull_atom_id_.emplace_back();
-        field.get(&raw_pull_atom_id_.back());
-        break;
-      case 3 /* pull_frequency_ms */:
-        field.get(&pull_frequency_ms_);
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 4 /* packages */:
-        packages_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &packages_.back());
+      case 2 /* state */:
+        field.get(&state_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -26914,64 +26500,47 @@ bool StatsdPullAtomConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string StatsdPullAtomConfig::SerializeAsString() const {
+std::string FtraceConfig_TracefsOption::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> StatsdPullAtomConfig::SerializeAsArray() const {
+std::vector<uint8_t> FtraceConfig_TracefsOption::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void StatsdPullAtomConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: pull_atom_id
-  for (auto& it : pull_atom_id_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
-  }
-
-  // Field 2: raw_pull_atom_id
-  for (auto& it : raw_pull_atom_id_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
-  }
-
-  // Field 3: pull_frequency_ms
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, pull_frequency_ms_, msg);
+void FtraceConfig_TracefsOption::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  // Field 4: packages
-  for (auto& it : packages_) {
-    ::protozero::internal::gen_helpers::SerializeString(4, it, msg);
+  // Field 2: state
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, state_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-StatsdTracingConfig::StatsdTracingConfig() = default;
-StatsdTracingConfig::~StatsdTracingConfig() = default;
-StatsdTracingConfig::StatsdTracingConfig(const StatsdTracingConfig&) = default;
-StatsdTracingConfig& StatsdTracingConfig::operator=(const StatsdTracingConfig&) = default;
-StatsdTracingConfig::StatsdTracingConfig(StatsdTracingConfig&&) noexcept = default;
-StatsdTracingConfig& StatsdTracingConfig::operator=(StatsdTracingConfig&&) = default;
+FtraceConfig_KprobeEvent::FtraceConfig_KprobeEvent() = default;
+FtraceConfig_KprobeEvent::~FtraceConfig_KprobeEvent() = default;
+FtraceConfig_KprobeEvent::FtraceConfig_KprobeEvent(const FtraceConfig_KprobeEvent&) = default;
+FtraceConfig_KprobeEvent& FtraceConfig_KprobeEvent::operator=(const FtraceConfig_KprobeEvent&) = default;
+FtraceConfig_KprobeEvent::FtraceConfig_KprobeEvent(FtraceConfig_KprobeEvent&&) noexcept = default;
+FtraceConfig_KprobeEvent& FtraceConfig_KprobeEvent::operator=(FtraceConfig_KprobeEvent&&) = default;

-bool StatsdTracingConfig::operator==(const StatsdTracingConfig& other) const {
+bool FtraceConfig_KprobeEvent::operator==(const FtraceConfig_KprobeEvent& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(push_atom_id_, other.push_atom_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(raw_push_atom_id_, other.raw_push_atom_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(pull_config_, other.pull_config_);
+   && ::protozero::internal::gen_helpers::EqualsField(probe_, other.probe_)
+   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_);
 }

-int StatsdTracingConfig::pull_config_size() const { return static_cast<int>(pull_config_.size()); }
-void StatsdTracingConfig::clear_pull_config() { pull_config_.clear(); }
-StatsdPullAtomConfig* StatsdTracingConfig::add_pull_config() { pull_config_.emplace_back(); return &pull_config_.back(); }
-bool StatsdTracingConfig::ParseFromArray(const void* raw, size_t size) {
-  push_atom_id_.clear();
-  raw_push_atom_id_.clear();
-  pull_config_.clear();
+bool FtraceConfig_KprobeEvent::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -26981,17 +26550,11 @@ bool StatsdTracingConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* push_atom_id */:
-        push_atom_id_.emplace_back();
-        field.get(&push_atom_id_.back());
-        break;
-      case 2 /* raw_push_atom_id */:
-        raw_push_atom_id_.emplace_back();
-        field.get(&raw_push_atom_id_.back());
+      case 1 /* probe */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &probe_);
         break;
-      case 3 /* pull_config */:
-        pull_config_.emplace_back();
-        pull_config_.back().ParseFromArray(field.data(), field.size());
+      case 2 /* type */:
+        field.get(&type_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -27001,91 +26564,50 @@ bool StatsdTracingConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string StatsdTracingConfig::SerializeAsString() const {
+std::string FtraceConfig_KprobeEvent::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> StatsdTracingConfig::SerializeAsArray() const {
+std::vector<uint8_t> FtraceConfig_KprobeEvent::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void StatsdTracingConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: push_atom_id
-  for (auto& it : push_atom_id_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
-  }
-
-  // Field 2: raw_push_atom_id
-  for (auto& it : raw_push_atom_id_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
+void FtraceConfig_KprobeEvent::Serialize(::protozero::Message* msg) const {
+  // Field 1: probe
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, probe_, msg);
   }

-  // Field 3: pull_config
-  for (auto& it : pull_config_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+  // Field 2: type
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, type_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/sys_stats/sys_stats_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/sys_stats/sys_stats_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/sys_stats_counters.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-SysStatsConfig::SysStatsConfig() = default;
-SysStatsConfig::~SysStatsConfig() = default;
-SysStatsConfig::SysStatsConfig(const SysStatsConfig&) = default;
-SysStatsConfig& SysStatsConfig::operator=(const SysStatsConfig&) = default;
-SysStatsConfig::SysStatsConfig(SysStatsConfig&&) noexcept = default;
-SysStatsConfig& SysStatsConfig::operator=(SysStatsConfig&&) = default;
+FtraceConfig_PrintFilter::FtraceConfig_PrintFilter() = default;
+FtraceConfig_PrintFilter::~FtraceConfig_PrintFilter() = default;
+FtraceConfig_PrintFilter::FtraceConfig_PrintFilter(const FtraceConfig_PrintFilter&) = default;
+FtraceConfig_PrintFilter& FtraceConfig_PrintFilter::operator=(const FtraceConfig_PrintFilter&) = default;
+FtraceConfig_PrintFilter::FtraceConfig_PrintFilter(FtraceConfig_PrintFilter&&) noexcept = default;
+FtraceConfig_PrintFilter& FtraceConfig_PrintFilter::operator=(FtraceConfig_PrintFilter&&) = default;

-bool SysStatsConfig::operator==(const SysStatsConfig& other) const {
+bool FtraceConfig_PrintFilter::operator==(const FtraceConfig_PrintFilter& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(meminfo_period_ms_, other.meminfo_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(meminfo_counters_, other.meminfo_counters_)
-   && ::protozero::internal::gen_helpers::EqualsField(vmstat_period_ms_, other.vmstat_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(vmstat_counters_, other.vmstat_counters_)
-   && ::protozero::internal::gen_helpers::EqualsField(stat_period_ms_, other.stat_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(stat_counters_, other.stat_counters_)
-   && ::protozero::internal::gen_helpers::EqualsField(devfreq_period_ms_, other.devfreq_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(cpufreq_period_ms_, other.cpufreq_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(buddyinfo_period_ms_, other.buddyinfo_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(diskstat_period_ms_, other.diskstat_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(psi_period_ms_, other.psi_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(thermal_period_ms_, other.thermal_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(cpuidle_period_ms_, other.cpuidle_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(gpufreq_period_ms_, other.gpufreq_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(slab_period_ms_, other.slab_period_ms_);
+   && ::protozero::internal::gen_helpers::EqualsField(rules_, other.rules_);
 }

-bool SysStatsConfig::ParseFromArray(const void* raw, size_t size) {
-  meminfo_counters_.clear();
-  vmstat_counters_.clear();
-  stat_counters_.clear();
+int FtraceConfig_PrintFilter::rules_size() const { return static_cast<int>(rules_.size()); }
+void FtraceConfig_PrintFilter::clear_rules() { rules_.clear(); }
+FtraceConfig_PrintFilter_Rule* FtraceConfig_PrintFilter::add_rules() { rules_.emplace_back(); return &rules_.back(); }
+bool FtraceConfig_PrintFilter::ParseFromArray(const void* raw, size_t size) {
+  rules_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -27095,53 +26617,9 @@ bool SysStatsConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* meminfo_period_ms */:
-        field.get(&meminfo_period_ms_);
-        break;
-      case 2 /* meminfo_counters */:
-        meminfo_counters_.emplace_back();
-        field.get(&meminfo_counters_.back());
-        break;
-      case 3 /* vmstat_period_ms */:
-        field.get(&vmstat_period_ms_);
-        break;
-      case 4 /* vmstat_counters */:
-        vmstat_counters_.emplace_back();
-        field.get(&vmstat_counters_.back());
-        break;
-      case 5 /* stat_period_ms */:
-        field.get(&stat_period_ms_);
-        break;
-      case 6 /* stat_counters */:
-        stat_counters_.emplace_back();
-        field.get(&stat_counters_.back());
-        break;
-      case 7 /* devfreq_period_ms */:
-        field.get(&devfreq_period_ms_);
-        break;
-      case 8 /* cpufreq_period_ms */:
-        field.get(&cpufreq_period_ms_);
-        break;
-      case 9 /* buddyinfo_period_ms */:
-        field.get(&buddyinfo_period_ms_);
-        break;
-      case 10 /* diskstat_period_ms */:
-        field.get(&diskstat_period_ms_);
-        break;
-      case 11 /* psi_period_ms */:
-        field.get(&psi_period_ms_);
-        break;
-      case 12 /* thermal_period_ms */:
-        field.get(&thermal_period_ms_);
-        break;
-      case 13 /* cpuidle_period_ms */:
-        field.get(&cpuidle_period_ms_);
-        break;
-      case 14 /* gpufreq_period_ms */:
-        field.get(&gpufreq_period_ms_);
-        break;
-      case 15 /* slab_period_ms */:
-        field.get(&slab_period_ms_);
+      case 1 /* rules */:
+        rules_.emplace_back();
+        rules_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -27151,133 +26629,115 @@ bool SysStatsConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string SysStatsConfig::SerializeAsString() const {
+std::string FtraceConfig_PrintFilter::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> SysStatsConfig::SerializeAsArray() const {
+std::vector<uint8_t> FtraceConfig_PrintFilter::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void SysStatsConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: meminfo_period_ms
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, meminfo_period_ms_, msg);
-  }
-
-  // Field 2: meminfo_counters
-  for (auto& it : meminfo_counters_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
-  }
-
-  // Field 3: vmstat_period_ms
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, vmstat_period_ms_, msg);
-  }
-
-  // Field 4: vmstat_counters
-  for (auto& it : vmstat_counters_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, it, msg);
+void FtraceConfig_PrintFilter::Serialize(::protozero::Message* msg) const {
+  // Field 1: rules
+  for (auto& it : rules_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 5: stat_period_ms
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, stat_period_ms_, msg);
-  }
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}

-  // Field 6: stat_counters
-  for (auto& it : stat_counters_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, it, msg);
-  }

-  // Field 7: devfreq_period_ms
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, devfreq_period_ms_, msg);
-  }
+FtraceConfig_PrintFilter_Rule::FtraceConfig_PrintFilter_Rule() = default;
+FtraceConfig_PrintFilter_Rule::~FtraceConfig_PrintFilter_Rule() = default;
+FtraceConfig_PrintFilter_Rule::FtraceConfig_PrintFilter_Rule(const FtraceConfig_PrintFilter_Rule&) = default;
+FtraceConfig_PrintFilter_Rule& FtraceConfig_PrintFilter_Rule::operator=(const FtraceConfig_PrintFilter_Rule&) = default;
+FtraceConfig_PrintFilter_Rule::FtraceConfig_PrintFilter_Rule(FtraceConfig_PrintFilter_Rule&&) noexcept = default;
+FtraceConfig_PrintFilter_Rule& FtraceConfig_PrintFilter_Rule::operator=(FtraceConfig_PrintFilter_Rule&&) = default;

-  // Field 8: cpufreq_period_ms
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, cpufreq_period_ms_, msg);
-  }
+bool FtraceConfig_PrintFilter_Rule::operator==(const FtraceConfig_PrintFilter_Rule& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(prefix_, other.prefix_)
+   && ::protozero::internal::gen_helpers::EqualsField(atrace_msg_, other.atrace_msg_)
+   && ::protozero::internal::gen_helpers::EqualsField(allow_, other.allow_);
+}

-  // Field 9: buddyinfo_period_ms
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(9, buddyinfo_period_ms_, msg);
-  }
+bool FtraceConfig_PrintFilter_Rule::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;

-  // Field 10: diskstat_period_ms
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(10, diskstat_period_ms_, msg);
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* prefix */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &prefix_);
+        break;
+      case 3 /* atrace_msg */:
+        (*atrace_msg_).ParseFromArray(field.data(), field.size());
+        break;
+      case 2 /* allow */:
+        field.get(&allow_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 11: psi_period_ms
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(11, psi_period_ms_, msg);
-  }
+std::string FtraceConfig_PrintFilter_Rule::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}

-  // Field 12: thermal_period_ms
-  if (_has_field_[12]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(12, thermal_period_ms_, msg);
-  }
+std::vector<uint8_t> FtraceConfig_PrintFilter_Rule::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}

-  // Field 13: cpuidle_period_ms
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(13, cpuidle_period_ms_, msg);
+void FtraceConfig_PrintFilter_Rule::Serialize(::protozero::Message* msg) const {
+  // Field 1: prefix
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, prefix_, msg);
   }

-  // Field 14: gpufreq_period_ms
-  if (_has_field_[14]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(14, gpufreq_period_ms_, msg);
+  // Field 3: atrace_msg
+  if (_has_field_[3]) {
+    (*atrace_msg_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
   }

-  // Field 15: slab_period_ms
-  if (_has_field_[15]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(15, slab_period_ms_, msg);
+  // Field 2: allow
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, allow_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/system_info/system_info_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/system_info/system_info_config.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-SystemInfoConfig::SystemInfoConfig() = default;
-SystemInfoConfig::~SystemInfoConfig() = default;
-SystemInfoConfig::SystemInfoConfig(const SystemInfoConfig&) = default;
-SystemInfoConfig& SystemInfoConfig::operator=(const SystemInfoConfig&) = default;
-SystemInfoConfig::SystemInfoConfig(SystemInfoConfig&&) noexcept = default;
-SystemInfoConfig& SystemInfoConfig::operator=(SystemInfoConfig&&) = default;
+FtraceConfig_PrintFilter_Rule_AtraceMessage::FtraceConfig_PrintFilter_Rule_AtraceMessage() = default;
+FtraceConfig_PrintFilter_Rule_AtraceMessage::~FtraceConfig_PrintFilter_Rule_AtraceMessage() = default;
+FtraceConfig_PrintFilter_Rule_AtraceMessage::FtraceConfig_PrintFilter_Rule_AtraceMessage(const FtraceConfig_PrintFilter_Rule_AtraceMessage&) = default;
+FtraceConfig_PrintFilter_Rule_AtraceMessage& FtraceConfig_PrintFilter_Rule_AtraceMessage::operator=(const FtraceConfig_PrintFilter_Rule_AtraceMessage&) = default;
+FtraceConfig_PrintFilter_Rule_AtraceMessage::FtraceConfig_PrintFilter_Rule_AtraceMessage(FtraceConfig_PrintFilter_Rule_AtraceMessage&&) noexcept = default;
+FtraceConfig_PrintFilter_Rule_AtraceMessage& FtraceConfig_PrintFilter_Rule_AtraceMessage::operator=(FtraceConfig_PrintFilter_Rule_AtraceMessage&&) = default;

-bool SystemInfoConfig::operator==(const SystemInfoConfig& other) const {
+bool FtraceConfig_PrintFilter_Rule_AtraceMessage::operator==(const FtraceConfig_PrintFilter_Rule_AtraceMessage& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(irq_names_, other.irq_names_);
+   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
+   && ::protozero::internal::gen_helpers::EqualsField(prefix_, other.prefix_);
 }

-bool SystemInfoConfig::ParseFromArray(const void* raw, size_t size) {
+bool FtraceConfig_PrintFilter_Rule_AtraceMessage::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -27287,8 +26747,11 @@ bool SystemInfoConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* irq_names */:
-        field.get(&irq_names_);
+      case 1 /* type */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &type_);
+        break;
+      case 2 /* prefix */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &prefix_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -27298,76 +26761,46 @@ bool SystemInfoConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string SystemInfoConfig::SerializeAsString() const {
+std::string FtraceConfig_PrintFilter_Rule_AtraceMessage::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> SystemInfoConfig::SerializeAsArray() const {
+std::vector<uint8_t> FtraceConfig_PrintFilter_Rule_AtraceMessage::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void SystemInfoConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: irq_names
+void FtraceConfig_PrintFilter_Rule_AtraceMessage::Serialize(::protozero::Message* msg) const {
+  // Field 1: type
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, irq_names_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, type_, msg);
+  }
+
+  // Field 2: prefix
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, prefix_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/track_event/track_event_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/track_event/track_event_config.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-TrackEventConfig::TrackEventConfig() = default;
-TrackEventConfig::~TrackEventConfig() = default;
-TrackEventConfig::TrackEventConfig(const TrackEventConfig&) = default;
-TrackEventConfig& TrackEventConfig::operator=(const TrackEventConfig&) = default;
-TrackEventConfig::TrackEventConfig(TrackEventConfig&&) noexcept = default;
-TrackEventConfig& TrackEventConfig::operator=(TrackEventConfig&&) = default;
+FtraceConfig_CompactSchedConfig::FtraceConfig_CompactSchedConfig() = default;
+FtraceConfig_CompactSchedConfig::~FtraceConfig_CompactSchedConfig() = default;
+FtraceConfig_CompactSchedConfig::FtraceConfig_CompactSchedConfig(const FtraceConfig_CompactSchedConfig&) = default;
+FtraceConfig_CompactSchedConfig& FtraceConfig_CompactSchedConfig::operator=(const FtraceConfig_CompactSchedConfig&) = default;
+FtraceConfig_CompactSchedConfig::FtraceConfig_CompactSchedConfig(FtraceConfig_CompactSchedConfig&&) noexcept = default;
+FtraceConfig_CompactSchedConfig& FtraceConfig_CompactSchedConfig::operator=(FtraceConfig_CompactSchedConfig&&) = default;

-bool TrackEventConfig::operator==(const TrackEventConfig& other) const {
+bool FtraceConfig_CompactSchedConfig::operator==(const FtraceConfig_CompactSchedConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(disabled_categories_, other.disabled_categories_)
-   && ::protozero::internal::gen_helpers::EqualsField(enabled_categories_, other.enabled_categories_)
-   && ::protozero::internal::gen_helpers::EqualsField(disabled_tags_, other.disabled_tags_)
-   && ::protozero::internal::gen_helpers::EqualsField(enabled_tags_, other.enabled_tags_)
-   && ::protozero::internal::gen_helpers::EqualsField(disable_incremental_timestamps_, other.disable_incremental_timestamps_)
-   && ::protozero::internal::gen_helpers::EqualsField(timestamp_unit_multiplier_, other.timestamp_unit_multiplier_)
-   && ::protozero::internal::gen_helpers::EqualsField(filter_debug_annotations_, other.filter_debug_annotations_)
-   && ::protozero::internal::gen_helpers::EqualsField(enable_thread_time_sampling_, other.enable_thread_time_sampling_)
-   && ::protozero::internal::gen_helpers::EqualsField(thread_time_subsampling_ns_, other.thread_time_subsampling_ns_)
-   && ::protozero::internal::gen_helpers::EqualsField(filter_dynamic_event_names_, other.filter_dynamic_event_names_);
+   && ::protozero::internal::gen_helpers::EqualsField(enabled_, other.enabled_);
 }

-bool TrackEventConfig::ParseFromArray(const void* raw, size_t size) {
-  disabled_categories_.clear();
-  enabled_categories_.clear();
-  disabled_tags_.clear();
-  enabled_tags_.clear();
+bool FtraceConfig_CompactSchedConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -27377,39 +26810,8 @@ bool TrackEventConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* disabled_categories */:
-        disabled_categories_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &disabled_categories_.back());
-        break;
-      case 2 /* enabled_categories */:
-        enabled_categories_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &enabled_categories_.back());
-        break;
-      case 3 /* disabled_tags */:
-        disabled_tags_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &disabled_tags_.back());
-        break;
-      case 4 /* enabled_tags */:
-        enabled_tags_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &enabled_tags_.back());
-        break;
-      case 5 /* disable_incremental_timestamps */:
-        field.get(&disable_incremental_timestamps_);
-        break;
-      case 6 /* timestamp_unit_multiplier */:
-        field.get(&timestamp_unit_multiplier_);
-        break;
-      case 7 /* filter_debug_annotations */:
-        field.get(&filter_debug_annotations_);
-        break;
-      case 8 /* enable_thread_time_sampling */:
-        field.get(&enable_thread_time_sampling_);
-        break;
-      case 10 /* thread_time_subsampling_ns */:
-        field.get(&thread_time_subsampling_ns_);
-        break;
-      case 9 /* filter_dynamic_event_names */:
-        field.get(&filter_dynamic_event_names_);
+      case 1 /* enabled */:
+        field.get(&enabled_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -27419,67 +26821,22 @@ bool TrackEventConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TrackEventConfig::SerializeAsString() const {
+std::string FtraceConfig_CompactSchedConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TrackEventConfig::SerializeAsArray() const {
+std::vector<uint8_t> FtraceConfig_CompactSchedConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TrackEventConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: disabled_categories
-  for (auto& it : disabled_categories_) {
-    ::protozero::internal::gen_helpers::SerializeString(1, it, msg);
-  }
-
-  // Field 2: enabled_categories
-  for (auto& it : enabled_categories_) {
-    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
-  }
-
-  // Field 3: disabled_tags
-  for (auto& it : disabled_tags_) {
-    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
-  }
-
-  // Field 4: enabled_tags
-  for (auto& it : enabled_tags_) {
-    ::protozero::internal::gen_helpers::SerializeString(4, it, msg);
-  }
-
-  // Field 5: disable_incremental_timestamps
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, disable_incremental_timestamps_, msg);
-  }
-
-  // Field 6: timestamp_unit_multiplier
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, timestamp_unit_multiplier_, msg);
-  }
-
-  // Field 7: filter_debug_annotations
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, filter_debug_annotations_, msg);
-  }
-
-  // Field 8: enable_thread_time_sampling
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, enable_thread_time_sampling_, msg);
-  }
-
-  // Field 10: thread_time_subsampling_ns
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(10, thread_time_subsampling_ns_, msg);
-  }
-
-  // Field 9: filter_dynamic_event_names
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, filter_dynamic_event_names_, msg);
+void FtraceConfig_CompactSchedConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: enabled
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, enabled_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -27491,7 +26848,7 @@ void TrackEventConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/chrome/chrome_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/gpu/gpu_counter_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -27502,30 +26859,32 @@ void TrackEventConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/chrome_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_counter_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-ChromeConfig::ChromeConfig() = default;
-ChromeConfig::~ChromeConfig() = default;
-ChromeConfig::ChromeConfig(const ChromeConfig&) = default;
-ChromeConfig& ChromeConfig::operator=(const ChromeConfig&) = default;
-ChromeConfig::ChromeConfig(ChromeConfig&&) noexcept = default;
-ChromeConfig& ChromeConfig::operator=(ChromeConfig&&) = default;
+GpuCounterConfig::GpuCounterConfig() = default;
+GpuCounterConfig::~GpuCounterConfig() = default;
+GpuCounterConfig::GpuCounterConfig(const GpuCounterConfig&) = default;
+GpuCounterConfig& GpuCounterConfig::operator=(const GpuCounterConfig&) = default;
+GpuCounterConfig::GpuCounterConfig(GpuCounterConfig&&) noexcept = default;
+GpuCounterConfig& GpuCounterConfig::operator=(GpuCounterConfig&&) = default;

-bool ChromeConfig::operator==(const ChromeConfig& other) const {
+bool GpuCounterConfig::operator==(const GpuCounterConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_config_, other.trace_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(privacy_filtering_enabled_, other.privacy_filtering_enabled_)
-   && ::protozero::internal::gen_helpers::EqualsField(convert_to_legacy_json_, other.convert_to_legacy_json_)
-   && ::protozero::internal::gen_helpers::EqualsField(client_priority_, other.client_priority_)
-   && ::protozero::internal::gen_helpers::EqualsField(json_agent_label_filter_, other.json_agent_label_filter_)
-   && ::protozero::internal::gen_helpers::EqualsField(event_package_name_filter_enabled_, other.event_package_name_filter_enabled_);
+   && ::protozero::internal::gen_helpers::EqualsField(counter_period_ns_, other.counter_period_ns_)
+   && ::protozero::internal::gen_helpers::EqualsField(counter_ids_, other.counter_ids_)
+   && ::protozero::internal::gen_helpers::EqualsField(counter_names_, other.counter_names_)
+   && ::protozero::internal::gen_helpers::EqualsField(instrumented_sampling_, other.instrumented_sampling_)
+   && ::protozero::internal::gen_helpers::EqualsField(instrumented_sampling_config_, other.instrumented_sampling_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(fix_gpu_clock_, other.fix_gpu_clock_);
 }

-bool ChromeConfig::ParseFromArray(const void* raw, size_t size) {
+bool GpuCounterConfig::ParseFromArray(const void* raw, size_t size) {
+  counter_ids_.clear();
+  counter_names_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -27535,23 +26894,25 @@ bool ChromeConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* trace_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &trace_config_);
+      case 1 /* counter_period_ns */:
+        field.get(&counter_period_ns_);
         break;
-      case 2 /* privacy_filtering_enabled */:
-        field.get(&privacy_filtering_enabled_);
+      case 2 /* counter_ids */:
+        counter_ids_.emplace_back();
+        field.get(&counter_ids_.back());
         break;
-      case 3 /* convert_to_legacy_json */:
-        field.get(&convert_to_legacy_json_);
+      case 6 /* counter_names */:
+        counter_names_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &counter_names_.back());
         break;
-      case 4 /* client_priority */:
-        field.get(&client_priority_);
+      case 3 /* instrumented_sampling */:
+        field.get(&instrumented_sampling_);
         break;
-      case 5 /* json_agent_label_filter */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &json_agent_label_filter_);
+      case 5 /* instrumented_sampling_config */:
+        (*instrumented_sampling_config_).ParseFromArray(field.data(), field.size());
         break;
-      case 6 /* event_package_name_filter_enabled */:
-        field.get(&event_package_name_filter_enabled_);
+      case 4 /* fix_gpu_clock */:
+        field.get(&fix_gpu_clock_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -27561,93 +26922,79 @@ bool ChromeConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeConfig::SerializeAsString() const {
+std::string GpuCounterConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeConfig::SerializeAsArray() const {
+std::vector<uint8_t> GpuCounterConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: trace_config
+void GpuCounterConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: counter_period_ns
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, trace_config_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, counter_period_ns_, msg);
   }

-  // Field 2: privacy_filtering_enabled
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, privacy_filtering_enabled_, msg);
+  // Field 2: counter_ids
+  for (auto& it : counter_ids_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
   }

-  // Field 3: convert_to_legacy_json
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, convert_to_legacy_json_, msg);
+  // Field 6: counter_names
+  for (auto& it : counter_names_) {
+    ::protozero::internal::gen_helpers::SerializeString(6, it, msg);
   }

-  // Field 4: client_priority
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, client_priority_, msg);
+  // Field 3: instrumented_sampling
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, instrumented_sampling_, msg);
   }

-  // Field 5: json_agent_label_filter
+  // Field 5: instrumented_sampling_config
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeString(5, json_agent_label_filter_, msg);
+    (*instrumented_sampling_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
   }

-  // Field 6: event_package_name_filter_enabled
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(6, event_package_name_filter_enabled_, msg);
+  // Field 4: fix_gpu_clock
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, fix_gpu_clock_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/chrome/histogram_samples.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/histogram_samples.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-ChromiumHistogramSamplesConfig::ChromiumHistogramSamplesConfig() = default;
-ChromiumHistogramSamplesConfig::~ChromiumHistogramSamplesConfig() = default;
-ChromiumHistogramSamplesConfig::ChromiumHistogramSamplesConfig(const ChromiumHistogramSamplesConfig&) = default;
-ChromiumHistogramSamplesConfig& ChromiumHistogramSamplesConfig::operator=(const ChromiumHistogramSamplesConfig&) = default;
-ChromiumHistogramSamplesConfig::ChromiumHistogramSamplesConfig(ChromiumHistogramSamplesConfig&&) noexcept = default;
-ChromiumHistogramSamplesConfig& ChromiumHistogramSamplesConfig::operator=(ChromiumHistogramSamplesConfig&&) = default;
+GpuCounterConfig_InstrumentedSamplingConfig::GpuCounterConfig_InstrumentedSamplingConfig() = default;
+GpuCounterConfig_InstrumentedSamplingConfig::~GpuCounterConfig_InstrumentedSamplingConfig() = default;
+GpuCounterConfig_InstrumentedSamplingConfig::GpuCounterConfig_InstrumentedSamplingConfig(const GpuCounterConfig_InstrumentedSamplingConfig&) = default;
+GpuCounterConfig_InstrumentedSamplingConfig& GpuCounterConfig_InstrumentedSamplingConfig::operator=(const GpuCounterConfig_InstrumentedSamplingConfig&) = default;
+GpuCounterConfig_InstrumentedSamplingConfig::GpuCounterConfig_InstrumentedSamplingConfig(GpuCounterConfig_InstrumentedSamplingConfig&&) noexcept = default;
+GpuCounterConfig_InstrumentedSamplingConfig& GpuCounterConfig_InstrumentedSamplingConfig::operator=(GpuCounterConfig_InstrumentedSamplingConfig&&) = default;

-bool ChromiumHistogramSamplesConfig::operator==(const ChromiumHistogramSamplesConfig& other) const {
+bool GpuCounterConfig_InstrumentedSamplingConfig::operator==(const GpuCounterConfig_InstrumentedSamplingConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(histograms_, other.histograms_)
-   && ::protozero::internal::gen_helpers::EqualsField(filter_histogram_names_, other.filter_histogram_names_);
+   && ::protozero::internal::gen_helpers::EqualsField(activity_name_filters_, other.activity_name_filters_)
+   && ::protozero::internal::gen_helpers::EqualsField(activity_tx_include_globs_, other.activity_tx_include_globs_)
+   && ::protozero::internal::gen_helpers::EqualsField(activity_tx_exclude_globs_, other.activity_tx_exclude_globs_)
+   && ::protozero::internal::gen_helpers::EqualsField(activity_ranges_, other.activity_ranges_);
 }

-int ChromiumHistogramSamplesConfig::histograms_size() const { return static_cast<int>(histograms_.size()); }
-void ChromiumHistogramSamplesConfig::clear_histograms() { histograms_.clear(); }
-ChromiumHistogramSamplesConfig_HistogramSample* ChromiumHistogramSamplesConfig::add_histograms() { histograms_.emplace_back(); return &histograms_.back(); }
-bool ChromiumHistogramSamplesConfig::ParseFromArray(const void* raw, size_t size) {
-  histograms_.clear();
+int GpuCounterConfig_InstrumentedSamplingConfig::activity_name_filters_size() const { return static_cast<int>(activity_name_filters_.size()); }
+void GpuCounterConfig_InstrumentedSamplingConfig::clear_activity_name_filters() { activity_name_filters_.clear(); }
+GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter* GpuCounterConfig_InstrumentedSamplingConfig::add_activity_name_filters() { activity_name_filters_.emplace_back(); return &activity_name_filters_.back(); }
+int GpuCounterConfig_InstrumentedSamplingConfig::activity_ranges_size() const { return static_cast<int>(activity_ranges_.size()); }
+void GpuCounterConfig_InstrumentedSamplingConfig::clear_activity_ranges() { activity_ranges_.clear(); }
+GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange* GpuCounterConfig_InstrumentedSamplingConfig::add_activity_ranges() { activity_ranges_.emplace_back(); return &activity_ranges_.back(); }
+bool GpuCounterConfig_InstrumentedSamplingConfig::ParseFromArray(const void* raw, size_t size) {
+  activity_name_filters_.clear();
+  activity_tx_include_globs_.clear();
+  activity_tx_exclude_globs_.clear();
+  activity_ranges_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -27657,12 +27004,21 @@ bool ChromiumHistogramSamplesConfig::ParseFromArray(const void* raw, size_t size
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* histograms */:
-        histograms_.emplace_back();
-        histograms_.back().ParseFromArray(field.data(), field.size());
+      case 3 /* activity_name_filters */:
+        activity_name_filters_.emplace_back();
+        activity_name_filters_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 2 /* filter_histogram_names */:
-        field.get(&filter_histogram_names_);
+      case 6 /* activity_tx_include_globs */:
+        activity_tx_include_globs_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &activity_tx_include_globs_.back());
+        break;
+      case 7 /* activity_tx_exclude_globs */:
+        activity_tx_exclude_globs_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &activity_tx_exclude_globs_.back());
+        break;
+      case 5 /* activity_ranges */:
+        activity_ranges_.emplace_back();
+        activity_ranges_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -27672,48 +27028,57 @@ bool ChromiumHistogramSamplesConfig::ParseFromArray(const void* raw, size_t size
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromiumHistogramSamplesConfig::SerializeAsString() const {
+std::string GpuCounterConfig_InstrumentedSamplingConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromiumHistogramSamplesConfig::SerializeAsArray() const {
+std::vector<uint8_t> GpuCounterConfig_InstrumentedSamplingConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromiumHistogramSamplesConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: histograms
-  for (auto& it : histograms_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+void GpuCounterConfig_InstrumentedSamplingConfig::Serialize(::protozero::Message* msg) const {
+  // Field 3: activity_name_filters
+  for (auto& it : activity_name_filters_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
   }

-  // Field 2: filter_histogram_names
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, filter_histogram_names_, msg);
+  // Field 6: activity_tx_include_globs
+  for (auto& it : activity_tx_include_globs_) {
+    ::protozero::internal::gen_helpers::SerializeString(6, it, msg);
+  }
+
+  // Field 7: activity_tx_exclude_globs
+  for (auto& it : activity_tx_exclude_globs_) {
+    ::protozero::internal::gen_helpers::SerializeString(7, it, msg);
+  }
+
+  // Field 5: activity_ranges
+  for (auto& it : activity_ranges_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-ChromiumHistogramSamplesConfig_HistogramSample::ChromiumHistogramSamplesConfig_HistogramSample() = default;
-ChromiumHistogramSamplesConfig_HistogramSample::~ChromiumHistogramSamplesConfig_HistogramSample() = default;
-ChromiumHistogramSamplesConfig_HistogramSample::ChromiumHistogramSamplesConfig_HistogramSample(const ChromiumHistogramSamplesConfig_HistogramSample&) = default;
-ChromiumHistogramSamplesConfig_HistogramSample& ChromiumHistogramSamplesConfig_HistogramSample::operator=(const ChromiumHistogramSamplesConfig_HistogramSample&) = default;
-ChromiumHistogramSamplesConfig_HistogramSample::ChromiumHistogramSamplesConfig_HistogramSample(ChromiumHistogramSamplesConfig_HistogramSample&&) noexcept = default;
-ChromiumHistogramSamplesConfig_HistogramSample& ChromiumHistogramSamplesConfig_HistogramSample::operator=(ChromiumHistogramSamplesConfig_HistogramSample&&) = default;
+GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange() = default;
+GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::~GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange() = default;
+GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange(const GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange&) = default;
+GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange& GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::operator=(const GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange&) = default;
+GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange(GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange&&) noexcept = default;
+GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange& GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::operator=(GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange&&) = default;

-bool ChromiumHistogramSamplesConfig_HistogramSample::operator==(const ChromiumHistogramSamplesConfig_HistogramSample& other) const {
+bool GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::operator==(const GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(histogram_name_, other.histogram_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(min_value_, other.min_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_value_, other.max_value_);
+   && ::protozero::internal::gen_helpers::EqualsField(skip_, other.skip_)
+   && ::protozero::internal::gen_helpers::EqualsField(count_, other.count_);
 }

-bool ChromiumHistogramSamplesConfig_HistogramSample::ParseFromArray(const void* raw, size_t size) {
+bool GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -27723,14 +27088,11 @@ bool ChromiumHistogramSamplesConfig_HistogramSample::ParseFromArray(const void*
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* histogram_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &histogram_name_);
-        break;
-      case 2 /* min_value */:
-        field.get(&min_value_);
+      case 1 /* skip */:
+        field.get(&skip_);
         break;
-      case 3 /* max_value */:
-        field.get(&max_value_);
+      case 2 /* count */:
+        field.get(&count_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -27740,134 +27102,47 @@ bool ChromiumHistogramSamplesConfig_HistogramSample::ParseFromArray(const void*
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromiumHistogramSamplesConfig_HistogramSample::SerializeAsString() const {
+std::string GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromiumHistogramSamplesConfig_HistogramSample::SerializeAsArray() const {
+std::vector<uint8_t> GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromiumHistogramSamplesConfig_HistogramSample::Serialize(::protozero::Message* msg) const {
-  // Field 1: histogram_name
+void GpuCounterConfig_InstrumentedSamplingConfig_ActivityRange::Serialize(::protozero::Message* msg) const {
+  // Field 1: skip
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, histogram_name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, skip_, msg);
   }

-  // Field 2: min_value
+  // Field 2: count
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, min_value_, msg);
-  }
-
-  // Field 3: max_value
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, max_value_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, count_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/chrome/scenario_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/scenario_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/trace_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/priority_boost/priority_boost_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/data_source_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/qnx/qnx_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/linux/journald_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/histogram_samples.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/system_info/system_info_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/track_event/track_event_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/test_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/sys_stats/sys_stats_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/sys_stats_counters.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/perf_event_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/perf_events.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/java_hprof_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/smaps_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/heapprofd_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/process_stats/process_stats_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/statsd_tracing_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/atom_ids.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/protovm/protovm_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/power/android_power_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/interceptor_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/interceptors/console_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/inode_file/inode_file_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_renderstages_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/vulkan_memory_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_counter_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/frozen_ftrace_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/system_metrics.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/etw/etw_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/v8_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/chrome_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/windowmanager_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/user_list_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_transactions_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_layers_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/display_video_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/protolog_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/pixel_modem_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/packages_list_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/network_trace_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/kernel_wakelocks_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/inputmethod_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/cpu_per_uid_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/app_wakelock_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_sdk_sysprop_guard_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_system_property_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_polled_state_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_log_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/android_log_constants.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_input_event_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_game_intervention_list_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_aflags_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/trace_attributes.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/semantic_type.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/builtin_clock.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-TracingTriggerRulesConfig::TracingTriggerRulesConfig() = default;
-TracingTriggerRulesConfig::~TracingTriggerRulesConfig() = default;
-TracingTriggerRulesConfig::TracingTriggerRulesConfig(const TracingTriggerRulesConfig&) = default;
-TracingTriggerRulesConfig& TracingTriggerRulesConfig::operator=(const TracingTriggerRulesConfig&) = default;
-TracingTriggerRulesConfig::TracingTriggerRulesConfig(TracingTriggerRulesConfig&&) noexcept = default;
-TracingTriggerRulesConfig& TracingTriggerRulesConfig::operator=(TracingTriggerRulesConfig&&) = default;
+GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter() = default;
+GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::~GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter() = default;
+GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter(const GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter&) = default;
+GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter& GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::operator=(const GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter&) = default;
+GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter(GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter&&) noexcept = default;
+GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter& GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::operator=(GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter&&) = default;

-bool TracingTriggerRulesConfig::operator==(const TracingTriggerRulesConfig& other) const {
+bool GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::operator==(const GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(rules_, other.rules_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_glob_, other.name_glob_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_base_, other.name_base_);
 }

-int TracingTriggerRulesConfig::rules_size() const { return static_cast<int>(rules_.size()); }
-void TracingTriggerRulesConfig::clear_rules() { rules_.clear(); }
-TriggerRule* TracingTriggerRulesConfig::add_rules() { rules_.emplace_back(); return &rules_.back(); }
-bool TracingTriggerRulesConfig::ParseFromArray(const void* raw, size_t size) {
-  rules_.clear();
+bool GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -27877,9 +27152,11 @@ bool TracingTriggerRulesConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* rules */:
-        rules_.emplace_back();
-        rules_.back().ParseFromArray(field.data(), field.size());
+      case 1 /* name_glob */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_glob_);
+        break;
+      case 2 /* name_base */:
+        field.get(&name_base_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -27889,47 +27166,72 @@ bool TracingTriggerRulesConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TracingTriggerRulesConfig::SerializeAsString() const {
+std::string GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TracingTriggerRulesConfig::SerializeAsArray() const {
+std::vector<uint8_t> GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TracingTriggerRulesConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: rules
-  for (auto& it : rules_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+void GpuCounterConfig_InstrumentedSamplingConfig_ActivityNameFilter::Serialize(::protozero::Message* msg) const {
+  // Field 1: name_glob
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, name_glob_, msg);
+  }
+
+  // Field 2: name_base
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, name_base_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/gpu/gpu_renderstages_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_renderstages_config.gen.h"

-TriggerRule::TriggerRule() = default;
-TriggerRule::~TriggerRule() = default;
-TriggerRule::TriggerRule(const TriggerRule&) = default;
-TriggerRule& TriggerRule::operator=(const TriggerRule&) = default;
-TriggerRule::TriggerRule(TriggerRule&&) noexcept = default;
-TriggerRule& TriggerRule::operator=(TriggerRule&&) = default;
+namespace perfetto {
+namespace protos {
+namespace gen {

-bool TriggerRule::operator==(const TriggerRule& other) const {
+GpuRenderStagesConfig::GpuRenderStagesConfig() = default;
+GpuRenderStagesConfig::~GpuRenderStagesConfig() = default;
+GpuRenderStagesConfig::GpuRenderStagesConfig(const GpuRenderStagesConfig&) = default;
+GpuRenderStagesConfig& GpuRenderStagesConfig::operator=(const GpuRenderStagesConfig&) = default;
+GpuRenderStagesConfig::GpuRenderStagesConfig(GpuRenderStagesConfig&&) noexcept = default;
+GpuRenderStagesConfig& GpuRenderStagesConfig::operator=(GpuRenderStagesConfig&&) = default;
+
+bool GpuRenderStagesConfig::operator==(const GpuRenderStagesConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(trigger_chance_, other.trigger_chance_)
-   && ::protozero::internal::gen_helpers::EqualsField(delay_ms_, other.delay_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(activation_delay_ms_, other.activation_delay_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(manual_trigger_name_, other.manual_trigger_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(histogram_, other.histogram_)
-   && ::protozero::internal::gen_helpers::EqualsField(repeating_interval_, other.repeating_interval_);
+   && ::protozero::internal::gen_helpers::EqualsField(full_loadstore_, other.full_loadstore_)
+   && ::protozero::internal::gen_helpers::EqualsField(low_overhead_, other.low_overhead_)
+   && ::protozero::internal::gen_helpers::EqualsField(trace_metrics_, other.trace_metrics_)
+   && ::protozero::internal::gen_helpers::EqualsField(drawcall_stages_, other.drawcall_stages_);
 }

-bool TriggerRule::ParseFromArray(const void* raw, size_t size) {
+bool GpuRenderStagesConfig::ParseFromArray(const void* raw, size_t size) {
+  trace_metrics_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -27939,26 +27241,18 @@ bool TriggerRule::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 2 /* trigger_chance */:
-        field.get(&trigger_chance_);
-        break;
-      case 3 /* delay_ms */:
-        field.get(&delay_ms_);
-        break;
-      case 8 /* activation_delay_ms */:
-        field.get(&activation_delay_ms_);
+      case 1 /* full_loadstore */:
+        field.get(&full_loadstore_);
         break;
-      case 4 /* manual_trigger_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &manual_trigger_name_);
+      case 2 /* low_overhead */:
+        field.get(&low_overhead_);
         break;
-      case 5 /* histogram */:
-        (*histogram_).ParseFromArray(field.data(), field.size());
+      case 3 /* trace_metrics */:
+        trace_metrics_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &trace_metrics_.back());
         break;
-      case 6 /* repeating_interval */:
-        (*repeating_interval_).ParseFromArray(field.data(), field.size());
+      case 4 /* drawcall_stages */:
+        field.get(&drawcall_stages_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -27968,72 +27262,79 @@ bool TriggerRule::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TriggerRule::SerializeAsString() const {
+std::string GpuRenderStagesConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TriggerRule::SerializeAsArray() const {
+std::vector<uint8_t> GpuRenderStagesConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TriggerRule::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
+void GpuRenderStagesConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: full_loadstore
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, full_loadstore_, msg);
   }

-  // Field 2: trigger_chance
+  // Field 2: low_overhead
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(2, trigger_chance_, msg);
-  }
-
-  // Field 3: delay_ms
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, delay_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, low_overhead_, msg);
   }

-  // Field 8: activation_delay_ms
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, activation_delay_ms_, msg);
+  // Field 3: trace_metrics
+  for (auto& it : trace_metrics_) {
+    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
   }

-  // Field 4: manual_trigger_name
+  // Field 4: drawcall_stages
   if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeString(4, manual_trigger_name_, msg);
-  }
-
-  // Field 5: histogram
-  if (_has_field_[5]) {
-    (*histogram_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
-  }
-
-  // Field 6: repeating_interval
-  if (_has_field_[6]) {
-    (*repeating_interval_).Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, drawcall_stages_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/gpu/vulkan_memory_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/vulkan_memory_config.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-TriggerRule_RepeatingInterval::TriggerRule_RepeatingInterval() = default;
-TriggerRule_RepeatingInterval::~TriggerRule_RepeatingInterval() = default;
-TriggerRule_RepeatingInterval::TriggerRule_RepeatingInterval(const TriggerRule_RepeatingInterval&) = default;
-TriggerRule_RepeatingInterval& TriggerRule_RepeatingInterval::operator=(const TriggerRule_RepeatingInterval&) = default;
-TriggerRule_RepeatingInterval::TriggerRule_RepeatingInterval(TriggerRule_RepeatingInterval&&) noexcept = default;
-TriggerRule_RepeatingInterval& TriggerRule_RepeatingInterval::operator=(TriggerRule_RepeatingInterval&&) = default;
+VulkanMemoryConfig::VulkanMemoryConfig() = default;
+VulkanMemoryConfig::~VulkanMemoryConfig() = default;
+VulkanMemoryConfig::VulkanMemoryConfig(const VulkanMemoryConfig&) = default;
+VulkanMemoryConfig& VulkanMemoryConfig::operator=(const VulkanMemoryConfig&) = default;
+VulkanMemoryConfig::VulkanMemoryConfig(VulkanMemoryConfig&&) noexcept = default;
+VulkanMemoryConfig& VulkanMemoryConfig::operator=(VulkanMemoryConfig&&) = default;

-bool TriggerRule_RepeatingInterval::operator==(const TriggerRule_RepeatingInterval& other) const {
+bool VulkanMemoryConfig::operator==(const VulkanMemoryConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(period_ms_, other.period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(randomized_, other.randomized_);
+   && ::protozero::internal::gen_helpers::EqualsField(track_driver_memory_usage_, other.track_driver_memory_usage_)
+   && ::protozero::internal::gen_helpers::EqualsField(track_device_memory_usage_, other.track_device_memory_usage_);
 }

-bool TriggerRule_RepeatingInterval::ParseFromArray(const void* raw, size_t size) {
+bool VulkanMemoryConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -28043,11 +27344,11 @@ bool TriggerRule_RepeatingInterval::ParseFromArray(const void* raw, size_t size)
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* period_ms */:
-        field.get(&period_ms_);
+      case 1 /* track_driver_memory_usage */:
+        field.get(&track_driver_memory_usage_);
         break;
-      case 2 /* randomized */:
-        field.get(&randomized_);
+      case 2 /* track_device_memory_usage */:
+        field.get(&track_device_memory_usage_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -28057,48 +27358,78 @@ bool TriggerRule_RepeatingInterval::ParseFromArray(const void* raw, size_t size)
   return !packed_error && !dec.bytes_left();
 }

-std::string TriggerRule_RepeatingInterval::SerializeAsString() const {
+std::string VulkanMemoryConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TriggerRule_RepeatingInterval::SerializeAsArray() const {
+std::vector<uint8_t> VulkanMemoryConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TriggerRule_RepeatingInterval::Serialize(::protozero::Message* msg) const {
-  // Field 1: period_ms
+void VulkanMemoryConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: track_driver_memory_usage
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, period_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, track_driver_memory_usage_, msg);
   }

-  // Field 2: randomized
+  // Field 2: track_device_memory_usage
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, randomized_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, track_device_memory_usage_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/inode_file/inode_file_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/inode_file/inode_file_config.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-TriggerRule_HistogramTrigger::TriggerRule_HistogramTrigger() = default;
-TriggerRule_HistogramTrigger::~TriggerRule_HistogramTrigger() = default;
-TriggerRule_HistogramTrigger::TriggerRule_HistogramTrigger(const TriggerRule_HistogramTrigger&) = default;
-TriggerRule_HistogramTrigger& TriggerRule_HistogramTrigger::operator=(const TriggerRule_HistogramTrigger&) = default;
-TriggerRule_HistogramTrigger::TriggerRule_HistogramTrigger(TriggerRule_HistogramTrigger&&) noexcept = default;
-TriggerRule_HistogramTrigger& TriggerRule_HistogramTrigger::operator=(TriggerRule_HistogramTrigger&&) = default;
+InodeFileConfig::InodeFileConfig() = default;
+InodeFileConfig::~InodeFileConfig() = default;
+InodeFileConfig::InodeFileConfig(const InodeFileConfig&) = default;
+InodeFileConfig& InodeFileConfig::operator=(const InodeFileConfig&) = default;
+InodeFileConfig::InodeFileConfig(InodeFileConfig&&) noexcept = default;
+InodeFileConfig& InodeFileConfig::operator=(InodeFileConfig&&) = default;

-bool TriggerRule_HistogramTrigger::operator==(const TriggerRule_HistogramTrigger& other) const {
+bool InodeFileConfig::operator==(const InodeFileConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(histogram_name_, other.histogram_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(min_value_, other.min_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_value_, other.max_value_);
+   && ::protozero::internal::gen_helpers::EqualsField(scan_interval_ms_, other.scan_interval_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(scan_delay_ms_, other.scan_delay_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(scan_batch_size_, other.scan_batch_size_)
+   && ::protozero::internal::gen_helpers::EqualsField(do_not_scan_, other.do_not_scan_)
+   && ::protozero::internal::gen_helpers::EqualsField(scan_mount_points_, other.scan_mount_points_)
+   && ::protozero::internal::gen_helpers::EqualsField(mount_point_mapping_, other.mount_point_mapping_);
 }

-bool TriggerRule_HistogramTrigger::ParseFromArray(const void* raw, size_t size) {
+int InodeFileConfig::mount_point_mapping_size() const { return static_cast<int>(mount_point_mapping_.size()); }
+void InodeFileConfig::clear_mount_point_mapping() { mount_point_mapping_.clear(); }
+InodeFileConfig_MountPointMappingEntry* InodeFileConfig::add_mount_point_mapping() { mount_point_mapping_.emplace_back(); return &mount_point_mapping_.back(); }
+bool InodeFileConfig::ParseFromArray(const void* raw, size_t size) {
+  scan_mount_points_.clear();
+  mount_point_mapping_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -28108,14 +27439,25 @@ bool TriggerRule_HistogramTrigger::ParseFromArray(const void* raw, size_t size)
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* histogram_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &histogram_name_);
+      case 1 /* scan_interval_ms */:
+        field.get(&scan_interval_ms_);
         break;
-      case 2 /* min_value */:
-        field.get(&min_value_);
+      case 2 /* scan_delay_ms */:
+        field.get(&scan_delay_ms_);
         break;
-      case 3 /* max_value */:
-        field.get(&max_value_);
+      case 3 /* scan_batch_size */:
+        field.get(&scan_batch_size_);
+        break;
+      case 4 /* do_not_scan */:
+        field.get(&do_not_scan_);
+        break;
+      case 5 /* scan_mount_points */:
+        scan_mount_points_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &scan_mount_points_.back());
+        break;
+      case 6 /* mount_point_mapping */:
+        mount_point_mapping_.emplace_back();
+        mount_point_mapping_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -28125,55 +27467,68 @@ bool TriggerRule_HistogramTrigger::ParseFromArray(const void* raw, size_t size)
   return !packed_error && !dec.bytes_left();
 }

-std::string TriggerRule_HistogramTrigger::SerializeAsString() const {
+std::string InodeFileConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TriggerRule_HistogramTrigger::SerializeAsArray() const {
+std::vector<uint8_t> InodeFileConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TriggerRule_HistogramTrigger::Serialize(::protozero::Message* msg) const {
-  // Field 1: histogram_name
+void InodeFileConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: scan_interval_ms
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, histogram_name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, scan_interval_ms_, msg);
   }

-  // Field 2: min_value
+  // Field 2: scan_delay_ms
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, min_value_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, scan_delay_ms_, msg);
   }

-  // Field 3: max_value
+  // Field 3: scan_batch_size
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, max_value_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, scan_batch_size_, msg);
+  }
+
+  // Field 4: do_not_scan
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, do_not_scan_, msg);
+  }
+
+  // Field 5: scan_mount_points
+  for (auto& it : scan_mount_points_) {
+    ::protozero::internal::gen_helpers::SerializeString(5, it, msg);
+  }
+
+  // Field 6: mount_point_mapping
+  for (auto& it : mount_point_mapping_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-ChromeFieldTracingConfig::ChromeFieldTracingConfig() = default;
-ChromeFieldTracingConfig::~ChromeFieldTracingConfig() = default;
-ChromeFieldTracingConfig::ChromeFieldTracingConfig(const ChromeFieldTracingConfig&) = default;
-ChromeFieldTracingConfig& ChromeFieldTracingConfig::operator=(const ChromeFieldTracingConfig&) = default;
-ChromeFieldTracingConfig::ChromeFieldTracingConfig(ChromeFieldTracingConfig&&) noexcept = default;
-ChromeFieldTracingConfig& ChromeFieldTracingConfig::operator=(ChromeFieldTracingConfig&&) = default;
+InodeFileConfig_MountPointMappingEntry::InodeFileConfig_MountPointMappingEntry() = default;
+InodeFileConfig_MountPointMappingEntry::~InodeFileConfig_MountPointMappingEntry() = default;
+InodeFileConfig_MountPointMappingEntry::InodeFileConfig_MountPointMappingEntry(const InodeFileConfig_MountPointMappingEntry&) = default;
+InodeFileConfig_MountPointMappingEntry& InodeFileConfig_MountPointMappingEntry::operator=(const InodeFileConfig_MountPointMappingEntry&) = default;
+InodeFileConfig_MountPointMappingEntry::InodeFileConfig_MountPointMappingEntry(InodeFileConfig_MountPointMappingEntry&&) noexcept = default;
+InodeFileConfig_MountPointMappingEntry& InodeFileConfig_MountPointMappingEntry::operator=(InodeFileConfig_MountPointMappingEntry&&) = default;

-bool ChromeFieldTracingConfig::operator==(const ChromeFieldTracingConfig& other) const {
+bool InodeFileConfig_MountPointMappingEntry::operator==(const InodeFileConfig_MountPointMappingEntry& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(scenarios_, other.scenarios_);
+   && ::protozero::internal::gen_helpers::EqualsField(mountpoint_, other.mountpoint_)
+   && ::protozero::internal::gen_helpers::EqualsField(scan_roots_, other.scan_roots_);
 }

-int ChromeFieldTracingConfig::scenarios_size() const { return static_cast<int>(scenarios_.size()); }
-void ChromeFieldTracingConfig::clear_scenarios() { scenarios_.clear(); }
-ScenarioConfig* ChromeFieldTracingConfig::add_scenarios() { scenarios_.emplace_back(); return &scenarios_.back(); }
-bool ChromeFieldTracingConfig::ParseFromArray(const void* raw, size_t size) {
-  scenarios_.clear();
+bool InodeFileConfig_MountPointMappingEntry::ParseFromArray(const void* raw, size_t size) {
+  scan_roots_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -28183,9 +27538,12 @@ bool ChromeFieldTracingConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* scenarios */:
-        scenarios_.emplace_back();
-        scenarios_.back().ParseFromArray(field.data(), field.size());
+      case 1 /* mountpoint */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &mountpoint_);
+        break;
+      case 2 /* scan_roots */:
+        scan_roots_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &scan_roots_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -28195,69 +27553,69 @@ bool ChromeFieldTracingConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeFieldTracingConfig::SerializeAsString() const {
+std::string InodeFileConfig_MountPointMappingEntry::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeFieldTracingConfig::SerializeAsArray() const {
+std::vector<uint8_t> InodeFileConfig_MountPointMappingEntry::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeFieldTracingConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: scenarios
-  for (auto& it : scenarios_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+void InodeFileConfig_MountPointMappingEntry::Serialize(::protozero::Message* msg) const {
+  // Field 1: mountpoint
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, mountpoint_, msg);
+  }
+
+  // Field 2: scan_roots
+  for (auto& it : scan_roots_) {
+    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/interceptors/console_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/interceptors/console_config.gen.h"

-ScenarioConfig::ScenarioConfig() = default;
-ScenarioConfig::~ScenarioConfig() = default;
-ScenarioConfig::ScenarioConfig(const ScenarioConfig&) = default;
-ScenarioConfig& ScenarioConfig::operator=(const ScenarioConfig&) = default;
-ScenarioConfig::ScenarioConfig(ScenarioConfig&&) noexcept = default;
-ScenarioConfig& ScenarioConfig::operator=(ScenarioConfig&&) = default;
+namespace perfetto {
+namespace protos {
+namespace gen {

-bool ScenarioConfig::operator==(const ScenarioConfig& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(scenario_name_, other.scenario_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(scenario_description_, other.scenario_description_)
-   && ::protozero::internal::gen_helpers::EqualsField(start_rules_, other.start_rules_)
-   && ::protozero::internal::gen_helpers::EqualsField(stop_rules_, other.stop_rules_)
-   && ::protozero::internal::gen_helpers::EqualsField(upload_rules_, other.upload_rules_)
-   && ::protozero::internal::gen_helpers::EqualsField(setup_rules_, other.setup_rules_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_config_, other.trace_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(nested_scenarios_, other.nested_scenarios_)
-   && ::protozero::internal::gen_helpers::EqualsField(use_system_backend_, other.use_system_backend_);
-}
+ConsoleConfig::ConsoleConfig() = default;
+ConsoleConfig::~ConsoleConfig() = default;
+ConsoleConfig::ConsoleConfig(const ConsoleConfig&) = default;
+ConsoleConfig& ConsoleConfig::operator=(const ConsoleConfig&) = default;
+ConsoleConfig::ConsoleConfig(ConsoleConfig&&) noexcept = default;
+ConsoleConfig& ConsoleConfig::operator=(ConsoleConfig&&) = default;

-int ScenarioConfig::start_rules_size() const { return static_cast<int>(start_rules_.size()); }
-void ScenarioConfig::clear_start_rules() { start_rules_.clear(); }
-TriggerRule* ScenarioConfig::add_start_rules() { start_rules_.emplace_back(); return &start_rules_.back(); }
-int ScenarioConfig::stop_rules_size() const { return static_cast<int>(stop_rules_.size()); }
-void ScenarioConfig::clear_stop_rules() { stop_rules_.clear(); }
-TriggerRule* ScenarioConfig::add_stop_rules() { stop_rules_.emplace_back(); return &stop_rules_.back(); }
-int ScenarioConfig::upload_rules_size() const { return static_cast<int>(upload_rules_.size()); }
-void ScenarioConfig::clear_upload_rules() { upload_rules_.clear(); }
-TriggerRule* ScenarioConfig::add_upload_rules() { upload_rules_.emplace_back(); return &upload_rules_.back(); }
-int ScenarioConfig::setup_rules_size() const { return static_cast<int>(setup_rules_.size()); }
-void ScenarioConfig::clear_setup_rules() { setup_rules_.clear(); }
-TriggerRule* ScenarioConfig::add_setup_rules() { setup_rules_.emplace_back(); return &setup_rules_.back(); }
-int ScenarioConfig::nested_scenarios_size() const { return static_cast<int>(nested_scenarios_.size()); }
-void ScenarioConfig::clear_nested_scenarios() { nested_scenarios_.clear(); }
-NestedScenarioConfig* ScenarioConfig::add_nested_scenarios() { nested_scenarios_.emplace_back(); return &nested_scenarios_.back(); }
-bool ScenarioConfig::ParseFromArray(const void* raw, size_t size) {
-  start_rules_.clear();
-  stop_rules_.clear();
-  upload_rules_.clear();
-  setup_rules_.clear();
-  nested_scenarios_.clear();
+bool ConsoleConfig::operator==(const ConsoleConfig& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(output_, other.output_)
+   && ::protozero::internal::gen_helpers::EqualsField(enable_colors_, other.enable_colors_);
+}
+
+bool ConsoleConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -28267,37 +27625,11 @@ bool ScenarioConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* scenario_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &scenario_name_);
-        break;
-      case 9 /* scenario_description */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &scenario_description_);
-        break;
-      case 2 /* start_rules */:
-        start_rules_.emplace_back();
-        start_rules_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 3 /* stop_rules */:
-        stop_rules_.emplace_back();
-        stop_rules_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 4 /* upload_rules */:
-        upload_rules_.emplace_back();
-        upload_rules_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 5 /* setup_rules */:
-        setup_rules_.emplace_back();
-        setup_rules_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 6 /* trace_config */:
-        (*trace_config_).ParseFromArray(field.data(), field.size());
-        break;
-      case 7 /* nested_scenarios */:
-        nested_scenarios_.emplace_back();
-        nested_scenarios_.back().ParseFromArray(field.data(), field.size());
+      case 1 /* output */:
+        field.get(&output_);
         break;
-      case 8 /* use_system_backend */:
-        field.get(&use_system_backend_);
+      case 2 /* enable_colors */:
+        field.get(&enable_colors_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -28307,96 +27639,72 @@ bool ScenarioConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ScenarioConfig::SerializeAsString() const {
+std::string ConsoleConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ScenarioConfig::SerializeAsArray() const {
+std::vector<uint8_t> ConsoleConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ScenarioConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: scenario_name
+void ConsoleConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: output
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, scenario_name_, msg);
-  }
-
-  // Field 9: scenario_description
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeString(9, scenario_description_, msg);
-  }
-
-  // Field 2: start_rules
-  for (auto& it : start_rules_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
-  }
-
-  // Field 3: stop_rules
-  for (auto& it : stop_rules_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
-  }
-
-  // Field 4: upload_rules
-  for (auto& it : upload_rules_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
-  }
-
-  // Field 5: setup_rules
-  for (auto& it : setup_rules_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
-  }
-
-  // Field 6: trace_config
-  if (_has_field_[6]) {
-    (*trace_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
-  }
-
-  // Field 7: nested_scenarios
-  for (auto& it : nested_scenarios_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(7));
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, output_, msg);
   }

-  // Field 8: use_system_backend
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, use_system_backend_, msg);
+  // Field 2: enable_colors
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, enable_colors_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/linux/journald_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/linux/journald_config.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-NestedScenarioConfig::NestedScenarioConfig() = default;
-NestedScenarioConfig::~NestedScenarioConfig() = default;
-NestedScenarioConfig::NestedScenarioConfig(const NestedScenarioConfig&) = default;
-NestedScenarioConfig& NestedScenarioConfig::operator=(const NestedScenarioConfig&) = default;
-NestedScenarioConfig::NestedScenarioConfig(NestedScenarioConfig&&) noexcept = default;
-NestedScenarioConfig& NestedScenarioConfig::operator=(NestedScenarioConfig&&) = default;
+SystemdJournaldConfig::SystemdJournaldConfig() = default;
+SystemdJournaldConfig::~SystemdJournaldConfig() = default;
+SystemdJournaldConfig::SystemdJournaldConfig(const SystemdJournaldConfig&) = default;
+SystemdJournaldConfig& SystemdJournaldConfig::operator=(const SystemdJournaldConfig&) = default;
+SystemdJournaldConfig::SystemdJournaldConfig(SystemdJournaldConfig&&) noexcept = default;
+SystemdJournaldConfig& SystemdJournaldConfig::operator=(SystemdJournaldConfig&&) = default;

-bool NestedScenarioConfig::operator==(const NestedScenarioConfig& other) const {
+bool SystemdJournaldConfig::operator==(const SystemdJournaldConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(scenario_name_, other.scenario_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(start_rules_, other.start_rules_)
-   && ::protozero::internal::gen_helpers::EqualsField(stop_rules_, other.stop_rules_)
-   && ::protozero::internal::gen_helpers::EqualsField(upload_rules_, other.upload_rules_);
+   && ::protozero::internal::gen_helpers::EqualsField(min_prio_, other.min_prio_)
+   && ::protozero::internal::gen_helpers::EqualsField(filter_identifiers_, other.filter_identifiers_)
+   && ::protozero::internal::gen_helpers::EqualsField(filter_units_, other.filter_units_);
 }

-int NestedScenarioConfig::start_rules_size() const { return static_cast<int>(start_rules_.size()); }
-void NestedScenarioConfig::clear_start_rules() { start_rules_.clear(); }
-TriggerRule* NestedScenarioConfig::add_start_rules() { start_rules_.emplace_back(); return &start_rules_.back(); }
-int NestedScenarioConfig::stop_rules_size() const { return static_cast<int>(stop_rules_.size()); }
-void NestedScenarioConfig::clear_stop_rules() { stop_rules_.clear(); }
-TriggerRule* NestedScenarioConfig::add_stop_rules() { stop_rules_.emplace_back(); return &stop_rules_.back(); }
-int NestedScenarioConfig::upload_rules_size() const { return static_cast<int>(upload_rules_.size()); }
-void NestedScenarioConfig::clear_upload_rules() { upload_rules_.clear(); }
-TriggerRule* NestedScenarioConfig::add_upload_rules() { upload_rules_.emplace_back(); return &upload_rules_.back(); }
-bool NestedScenarioConfig::ParseFromArray(const void* raw, size_t size) {
-  start_rules_.clear();
-  stop_rules_.clear();
-  upload_rules_.clear();
+bool SystemdJournaldConfig::ParseFromArray(const void* raw, size_t size) {
+  filter_identifiers_.clear();
+  filter_units_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -28406,20 +27714,16 @@ bool NestedScenarioConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* scenario_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &scenario_name_);
-        break;
-      case 2 /* start_rules */:
-        start_rules_.emplace_back();
-        start_rules_.back().ParseFromArray(field.data(), field.size());
+      case 1 /* min_prio */:
+        field.get(&min_prio_);
         break;
-      case 3 /* stop_rules */:
-        stop_rules_.emplace_back();
-        stop_rules_.back().ParseFromArray(field.data(), field.size());
+      case 2 /* filter_identifiers */:
+        filter_identifiers_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &filter_identifiers_.back());
         break;
-      case 4 /* upload_rules */:
-        upload_rules_.emplace_back();
-        upload_rules_.back().ParseFromArray(field.data(), field.size());
+      case 3 /* filter_units */:
+        filter_units_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &filter_units_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -28429,37 +27733,32 @@ bool NestedScenarioConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string NestedScenarioConfig::SerializeAsString() const {
+std::string SystemdJournaldConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> NestedScenarioConfig::SerializeAsArray() const {
+std::vector<uint8_t> SystemdJournaldConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void NestedScenarioConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: scenario_name
+void SystemdJournaldConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: min_prio
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, scenario_name_, msg);
-  }
-
-  // Field 2: start_rules
-  for (auto& it : start_rules_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, min_prio_, msg);
   }

-  // Field 3: stop_rules
-  for (auto& it : stop_rules_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+  // Field 2: filter_identifiers
+  for (auto& it : filter_identifiers_) {
+    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
   }

-  // Field 4: upload_rules
-  for (auto& it : upload_rules_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
+  // Field 3: filter_units
+  for (auto& it : filter_units_) {
+    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -28471,7 +27770,7 @@ void NestedScenarioConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/chrome/system_metrics.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/power/android_power_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -28482,25 +27781,30 @@ void NestedScenarioConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/system_metrics.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/power/android_power_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-ChromiumSystemMetricsConfig::ChromiumSystemMetricsConfig() = default;
-ChromiumSystemMetricsConfig::~ChromiumSystemMetricsConfig() = default;
-ChromiumSystemMetricsConfig::ChromiumSystemMetricsConfig(const ChromiumSystemMetricsConfig&) = default;
-ChromiumSystemMetricsConfig& ChromiumSystemMetricsConfig::operator=(const ChromiumSystemMetricsConfig&) = default;
-ChromiumSystemMetricsConfig::ChromiumSystemMetricsConfig(ChromiumSystemMetricsConfig&&) noexcept = default;
-ChromiumSystemMetricsConfig& ChromiumSystemMetricsConfig::operator=(ChromiumSystemMetricsConfig&&) = default;
+AndroidPowerConfig::AndroidPowerConfig() = default;
+AndroidPowerConfig::~AndroidPowerConfig() = default;
+AndroidPowerConfig::AndroidPowerConfig(const AndroidPowerConfig&) = default;
+AndroidPowerConfig& AndroidPowerConfig::operator=(const AndroidPowerConfig&) = default;
+AndroidPowerConfig::AndroidPowerConfig(AndroidPowerConfig&&) noexcept = default;
+AndroidPowerConfig& AndroidPowerConfig::operator=(AndroidPowerConfig&&) = default;

-bool ChromiumSystemMetricsConfig::operator==(const ChromiumSystemMetricsConfig& other) const {
+bool AndroidPowerConfig::operator==(const AndroidPowerConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(sampling_interval_ms_, other.sampling_interval_ms_);
+   && ::protozero::internal::gen_helpers::EqualsField(battery_poll_ms_, other.battery_poll_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(battery_counters_, other.battery_counters_)
+   && ::protozero::internal::gen_helpers::EqualsField(collect_power_rails_, other.collect_power_rails_)
+   && ::protozero::internal::gen_helpers::EqualsField(collect_energy_estimation_breakdown_, other.collect_energy_estimation_breakdown_)
+   && ::protozero::internal::gen_helpers::EqualsField(collect_entity_state_residency_, other.collect_entity_state_residency_);
 }

-bool ChromiumSystemMetricsConfig::ParseFromArray(const void* raw, size_t size) {
+bool AndroidPowerConfig::ParseFromArray(const void* raw, size_t size) {
+  battery_counters_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -28510,8 +27814,21 @@ bool ChromiumSystemMetricsConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* sampling_interval_ms */:
-        field.get(&sampling_interval_ms_);
+      case 1 /* battery_poll_ms */:
+        field.get(&battery_poll_ms_);
+        break;
+      case 2 /* battery_counters */:
+        battery_counters_.emplace_back();
+        field.get(&battery_counters_.back());
+        break;
+      case 3 /* collect_power_rails */:
+        field.get(&collect_power_rails_);
+        break;
+      case 4 /* collect_energy_estimation_breakdown */:
+        field.get(&collect_energy_estimation_breakdown_);
+        break;
+      case 5 /* collect_entity_state_residency */:
+        field.get(&collect_entity_state_residency_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -28521,22 +27838,42 @@ bool ChromiumSystemMetricsConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromiumSystemMetricsConfig::SerializeAsString() const {
+std::string AndroidPowerConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromiumSystemMetricsConfig::SerializeAsArray() const {
+std::vector<uint8_t> AndroidPowerConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromiumSystemMetricsConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: sampling_interval_ms
+void AndroidPowerConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: battery_poll_ms
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, sampling_interval_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, battery_poll_ms_, msg);
+  }
+
+  // Field 2: battery_counters
+  for (auto& it : battery_counters_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
+  }
+
+  // Field 3: collect_power_rails
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, collect_power_rails_, msg);
+  }
+
+  // Field 4: collect_energy_estimation_breakdown
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, collect_energy_estimation_breakdown_, msg);
+  }
+
+  // Field 5: collect_entity_state_residency
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, collect_entity_state_residency_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -28548,7 +27885,7 @@ void ChromiumSystemMetricsConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/chrome/v8_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/priority_boost/priority_boost_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -28559,26 +27896,26 @@ void ChromiumSystemMetricsConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/v8_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/priority_boost/priority_boost_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-V8Config::V8Config() = default;
-V8Config::~V8Config() = default;
-V8Config::V8Config(const V8Config&) = default;
-V8Config& V8Config::operator=(const V8Config&) = default;
-V8Config::V8Config(V8Config&&) noexcept = default;
-V8Config& V8Config::operator=(V8Config&&) = default;
+PriorityBoostConfig::PriorityBoostConfig() = default;
+PriorityBoostConfig::~PriorityBoostConfig() = default;
+PriorityBoostConfig::PriorityBoostConfig(const PriorityBoostConfig&) = default;
+PriorityBoostConfig& PriorityBoostConfig::operator=(const PriorityBoostConfig&) = default;
+PriorityBoostConfig::PriorityBoostConfig(PriorityBoostConfig&&) noexcept = default;
+PriorityBoostConfig& PriorityBoostConfig::operator=(PriorityBoostConfig&&) = default;

-bool V8Config::operator==(const V8Config& other) const {
+bool PriorityBoostConfig::operator==(const PriorityBoostConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(log_script_sources_, other.log_script_sources_)
-   && ::protozero::internal::gen_helpers::EqualsField(log_instructions_, other.log_instructions_);
+   && ::protozero::internal::gen_helpers::EqualsField(policy_, other.policy_)
+   && ::protozero::internal::gen_helpers::EqualsField(priority_, other.priority_);
 }

-bool V8Config::ParseFromArray(const void* raw, size_t size) {
+bool PriorityBoostConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -28588,11 +27925,11 @@ bool V8Config::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* log_script_sources */:
-        field.get(&log_script_sources_);
+      case 1 /* policy */:
+        field.get(&policy_);
         break;
-      case 2 /* log_instructions */:
-        field.get(&log_instructions_);
+      case 2 /* priority */:
+        field.get(&priority_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -28602,27 +27939,27 @@ bool V8Config::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string V8Config::SerializeAsString() const {
+std::string PriorityBoostConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> V8Config::SerializeAsArray() const {
+std::vector<uint8_t> PriorityBoostConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void V8Config::Serialize(::protozero::Message* msg) const {
-  // Field 1: log_script_sources
+void PriorityBoostConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: policy
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, log_script_sources_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, policy_, msg);
   }

-  // Field 2: log_instructions
+  // Field 2: priority
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, log_instructions_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, priority_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -28634,7 +27971,7 @@ void V8Config::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/data_source_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/process_stats/process_stats_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -28645,88 +27982,36 @@ void V8Config::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/data_source_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/system_info/system_info_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/test_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/priority_boost/priority_boost_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/protovm/protovm_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/interceptor_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/interceptors/console_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/chrome_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/process_stats/process_stats_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-DataSourceConfig::DataSourceConfig() = default;
-DataSourceConfig::~DataSourceConfig() = default;
-DataSourceConfig::DataSourceConfig(const DataSourceConfig&) = default;
-DataSourceConfig& DataSourceConfig::operator=(const DataSourceConfig&) = default;
-DataSourceConfig::DataSourceConfig(DataSourceConfig&&) noexcept = default;
-DataSourceConfig& DataSourceConfig::operator=(DataSourceConfig&&) = default;
+ProcessStatsConfig::ProcessStatsConfig() = default;
+ProcessStatsConfig::~ProcessStatsConfig() = default;
+ProcessStatsConfig::ProcessStatsConfig(const ProcessStatsConfig&) = default;
+ProcessStatsConfig& ProcessStatsConfig::operator=(const ProcessStatsConfig&) = default;
+ProcessStatsConfig::ProcessStatsConfig(ProcessStatsConfig&&) noexcept = default;
+ProcessStatsConfig& ProcessStatsConfig::operator=(ProcessStatsConfig&&) = default;

-bool DataSourceConfig::operator==(const DataSourceConfig& other) const {
+bool ProcessStatsConfig::operator==(const ProcessStatsConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(target_buffer_, other.target_buffer_)
-   && ::protozero::internal::gen_helpers::EqualsField(target_buffer_name_, other.target_buffer_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_duration_ms_, other.trace_duration_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(prefer_suspend_clock_for_duration_, other.prefer_suspend_clock_for_duration_)
-   && ::protozero::internal::gen_helpers::EqualsField(stop_timeout_ms_, other.stop_timeout_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(enable_extra_guardrails_, other.enable_extra_guardrails_)
-   && ::protozero::internal::gen_helpers::EqualsField(session_initiator_, other.session_initiator_)
-   && ::protozero::internal::gen_helpers::EqualsField(tracing_session_id_, other.tracing_session_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(buffer_exhausted_policy_, other.buffer_exhausted_policy_)
-   && ::protozero::internal::gen_helpers::EqualsField(priority_boost_, other.priority_boost_)
-   && ::protozero::internal::gen_helpers::EqualsField(protovm_config_, other.protovm_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(ftrace_config_, other.ftrace_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(inode_file_config_, other.inode_file_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(process_stats_config_, other.process_stats_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(sys_stats_config_, other.sys_stats_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(heapprofd_config_, other.heapprofd_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(java_hprof_config_, other.java_hprof_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_power_config_, other.android_power_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_log_config_, other.android_log_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(gpu_counter_config_, other.gpu_counter_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_game_intervention_list_config_, other.android_game_intervention_list_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(packages_list_config_, other.packages_list_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(perf_event_config_, other.perf_event_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(vulkan_memory_config_, other.vulkan_memory_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(track_event_config_, other.track_event_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_polled_state_config_, other.android_polled_state_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_system_property_config_, other.android_system_property_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(statsd_tracing_config_, other.statsd_tracing_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(system_info_config_, other.system_info_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(frozen_ftrace_config_, other.frozen_ftrace_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_config_, other.chrome_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(v8_config_, other.v8_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(interceptor_config_, other.interceptor_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(network_packet_trace_config_, other.network_packet_trace_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(surfaceflinger_layers_config_, other.surfaceflinger_layers_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(surfaceflinger_transactions_config_, other.surfaceflinger_transactions_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_sdk_sysprop_guard_config_, other.android_sdk_sysprop_guard_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(etw_config_, other.etw_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(protolog_config_, other.protolog_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_input_event_config_, other.android_input_event_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(pixel_modem_config_, other.pixel_modem_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(windowmanager_config_, other.windowmanager_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(chromium_system_metrics_, other.chromium_system_metrics_)
-   && ::protozero::internal::gen_helpers::EqualsField(kernel_wakelocks_config_, other.kernel_wakelocks_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(gpu_renderstages_config_, other.gpu_renderstages_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(chromium_histogram_samples_, other.chromium_histogram_samples_)
-   && ::protozero::internal::gen_helpers::EqualsField(app_wakelocks_config_, other.app_wakelocks_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(cpu_per_uid_config_, other.cpu_per_uid_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(user_list_config_, other.user_list_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(inputmethod_config_, other.inputmethod_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_aflags_config_, other.android_aflags_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(journald_config_, other.journald_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(display_video_config_, other.display_video_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(qnx_config_, other.qnx_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(legacy_config_, other.legacy_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(for_testing_, other.for_testing_);
+   && ::protozero::internal::gen_helpers::EqualsField(quirks_, other.quirks_)
+   && ::protozero::internal::gen_helpers::EqualsField(scan_all_processes_on_start_, other.scan_all_processes_on_start_)
+   && ::protozero::internal::gen_helpers::EqualsField(record_thread_names_, other.record_thread_names_)
+   && ::protozero::internal::gen_helpers::EqualsField(proc_stats_poll_ms_, other.proc_stats_poll_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(proc_stats_cache_ttl_ms_, other.proc_stats_cache_ttl_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(scan_smaps_rollup_, other.scan_smaps_rollup_)
+   && ::protozero::internal::gen_helpers::EqualsField(record_process_age_, other.record_process_age_)
+   && ::protozero::internal::gen_helpers::EqualsField(record_process_runtime_, other.record_process_runtime_)
+   && ::protozero::internal::gen_helpers::EqualsField(record_process_dmabuf_rss_, other.record_process_dmabuf_rss_)
+   && ::protozero::internal::gen_helpers::EqualsField(resolve_process_fds_, other.resolve_process_fds_)
+   && ::protozero::internal::gen_helpers::EqualsField(skip_main_thread_message_, other.skip_main_thread_message_);
 }

-bool DataSourceConfig::ParseFromArray(const void* raw, size_t size) {
+bool ProcessStatsConfig::ParseFromArray(const void* raw, size_t size) {
+  quirks_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -28736,176 +28021,276 @@ bool DataSourceConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 2 /* target_buffer */:
-        field.get(&target_buffer_);
-        break;
-      case 11 /* target_buffer_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &target_buffer_name_);
-        break;
-      case 3 /* trace_duration_ms */:
-        field.get(&trace_duration_ms_);
-        break;
-      case 122 /* prefer_suspend_clock_for_duration */:
-        field.get(&prefer_suspend_clock_for_duration_);
-        break;
-      case 7 /* stop_timeout_ms */:
-        field.get(&stop_timeout_ms_);
-        break;
-      case 6 /* enable_extra_guardrails */:
-        field.get(&enable_extra_guardrails_);
-        break;
-      case 8 /* session_initiator */:
-        field.get(&session_initiator_);
-        break;
-      case 4 /* tracing_session_id */:
-        field.get(&tracing_session_id_);
-        break;
-      case 9 /* buffer_exhausted_policy */:
-        field.get(&buffer_exhausted_policy_);
+      case 1 /* quirks */:
+        quirks_.emplace_back();
+        field.get(&quirks_.back());
         break;
-      case 10 /* priority_boost */:
-        (*priority_boost_).ParseFromArray(field.data(), field.size());
+      case 2 /* scan_all_processes_on_start */:
+        field.get(&scan_all_processes_on_start_);
         break;
-      case 12 /* protovm_config */:
-        (*protovm_config_).ParseFromArray(field.data(), field.size());
+      case 3 /* record_thread_names */:
+        field.get(&record_thread_names_);
         break;
-      case 100 /* ftrace_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &ftrace_config_);
+      case 4 /* proc_stats_poll_ms */:
+        field.get(&proc_stats_poll_ms_);
         break;
-      case 102 /* inode_file_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &inode_file_config_);
+      case 6 /* proc_stats_cache_ttl_ms */:
+        field.get(&proc_stats_cache_ttl_ms_);
         break;
-      case 103 /* process_stats_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &process_stats_config_);
+      case 10 /* scan_smaps_rollup */:
+        field.get(&scan_smaps_rollup_);
         break;
-      case 104 /* sys_stats_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &sys_stats_config_);
+      case 11 /* record_process_age */:
+        field.get(&record_process_age_);
         break;
-      case 105 /* heapprofd_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &heapprofd_config_);
+      case 12 /* record_process_runtime */:
+        field.get(&record_process_runtime_);
         break;
-      case 110 /* java_hprof_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &java_hprof_config_);
+      case 13 /* record_process_dmabuf_rss */:
+        field.get(&record_process_dmabuf_rss_);
         break;
-      case 106 /* android_power_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_power_config_);
+      case 9 /* resolve_process_fds */:
+        field.get(&resolve_process_fds_);
         break;
-      case 107 /* android_log_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_log_config_);
+      case 14 /* skip_main_thread_message */:
+        field.get(&skip_main_thread_message_);
         break;
-      case 108 /* gpu_counter_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &gpu_counter_config_);
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
         break;
-      case 116 /* android_game_intervention_list_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_game_intervention_list_config_);
-        break;
-      case 109 /* packages_list_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &packages_list_config_);
-        break;
-      case 111 /* perf_event_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &perf_event_config_);
-        break;
-      case 112 /* vulkan_memory_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &vulkan_memory_config_);
-        break;
-      case 113 /* track_event_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &track_event_config_);
-        break;
-      case 114 /* android_polled_state_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_polled_state_config_);
-        break;
-      case 118 /* android_system_property_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_system_property_config_);
-        break;
-      case 117 /* statsd_tracing_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &statsd_tracing_config_);
-        break;
-      case 119 /* system_info_config */:
-        (*system_info_config_).ParseFromArray(field.data(), field.size());
-        break;
-      case 136 /* frozen_ftrace_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &frozen_ftrace_config_);
-        break;
-      case 101 /* chrome_config */:
-        (*chrome_config_).ParseFromArray(field.data(), field.size());
-        break;
-      case 127 /* v8_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &v8_config_);
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string ProcessStatsConfig::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ProcessStatsConfig::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ProcessStatsConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: quirks
+  for (auto& it : quirks_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
+  }
+
+  // Field 2: scan_all_processes_on_start
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, scan_all_processes_on_start_, msg);
+  }
+
+  // Field 3: record_thread_names
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, record_thread_names_, msg);
+  }
+
+  // Field 4: proc_stats_poll_ms
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, proc_stats_poll_ms_, msg);
+  }
+
+  // Field 6: proc_stats_cache_ttl_ms
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, proc_stats_cache_ttl_ms_, msg);
+  }
+
+  // Field 10: scan_smaps_rollup
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(10, scan_smaps_rollup_, msg);
+  }
+
+  // Field 11: record_process_age
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(11, record_process_age_, msg);
+  }
+
+  // Field 12: record_process_runtime
+  if (_has_field_[12]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(12, record_process_runtime_, msg);
+  }
+
+  // Field 13: record_process_dmabuf_rss
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(13, record_process_dmabuf_rss_, msg);
+  }
+
+  // Field 9: resolve_process_fds
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, resolve_process_fds_, msg);
+  }
+
+  // Field 14: skip_main_thread_message
+  if (_has_field_[14]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(14, skip_main_thread_message_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/profiling/heapprofd_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/heapprofd_config.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+HeapprofdConfig::HeapprofdConfig() = default;
+HeapprofdConfig::~HeapprofdConfig() = default;
+HeapprofdConfig::HeapprofdConfig(const HeapprofdConfig&) = default;
+HeapprofdConfig& HeapprofdConfig::operator=(const HeapprofdConfig&) = default;
+HeapprofdConfig::HeapprofdConfig(HeapprofdConfig&&) noexcept = default;
+HeapprofdConfig& HeapprofdConfig::operator=(HeapprofdConfig&&) = default;
+
+bool HeapprofdConfig::operator==(const HeapprofdConfig& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(sampling_interval_bytes_, other.sampling_interval_bytes_)
+   && ::protozero::internal::gen_helpers::EqualsField(adaptive_sampling_shmem_threshold_, other.adaptive_sampling_shmem_threshold_)
+   && ::protozero::internal::gen_helpers::EqualsField(adaptive_sampling_max_sampling_interval_bytes_, other.adaptive_sampling_max_sampling_interval_bytes_)
+   && ::protozero::internal::gen_helpers::EqualsField(process_cmdline_, other.process_cmdline_)
+   && ::protozero::internal::gen_helpers::EqualsField(pid_, other.pid_)
+   && ::protozero::internal::gen_helpers::EqualsField(target_installed_by_, other.target_installed_by_)
+   && ::protozero::internal::gen_helpers::EqualsField(heaps_, other.heaps_)
+   && ::protozero::internal::gen_helpers::EqualsField(exclude_heaps_, other.exclude_heaps_)
+   && ::protozero::internal::gen_helpers::EqualsField(stream_allocations_, other.stream_allocations_)
+   && ::protozero::internal::gen_helpers::EqualsField(heap_sampling_intervals_, other.heap_sampling_intervals_)
+   && ::protozero::internal::gen_helpers::EqualsField(all_heaps_, other.all_heaps_)
+   && ::protozero::internal::gen_helpers::EqualsField(all_, other.all_)
+   && ::protozero::internal::gen_helpers::EqualsField(min_anonymous_memory_kb_, other.min_anonymous_memory_kb_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_heapprofd_memory_kb_, other.max_heapprofd_memory_kb_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_heapprofd_cpu_secs_, other.max_heapprofd_cpu_secs_)
+   && ::protozero::internal::gen_helpers::EqualsField(skip_symbol_prefix_, other.skip_symbol_prefix_)
+   && ::protozero::internal::gen_helpers::EqualsField(continuous_dump_config_, other.continuous_dump_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(shmem_size_bytes_, other.shmem_size_bytes_)
+   && ::protozero::internal::gen_helpers::EqualsField(block_client_, other.block_client_)
+   && ::protozero::internal::gen_helpers::EqualsField(block_client_timeout_us_, other.block_client_timeout_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(no_startup_, other.no_startup_)
+   && ::protozero::internal::gen_helpers::EqualsField(no_running_, other.no_running_)
+   && ::protozero::internal::gen_helpers::EqualsField(dump_at_max_, other.dump_at_max_)
+   && ::protozero::internal::gen_helpers::EqualsField(disable_fork_teardown_, other.disable_fork_teardown_)
+   && ::protozero::internal::gen_helpers::EqualsField(disable_vfork_detection_, other.disable_vfork_detection_);
+}
+
+bool HeapprofdConfig::ParseFromArray(const void* raw, size_t size) {
+  process_cmdline_.clear();
+  pid_.clear();
+  target_installed_by_.clear();
+  heaps_.clear();
+  exclude_heaps_.clear();
+  heap_sampling_intervals_.clear();
+  skip_symbol_prefix_.clear();
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* sampling_interval_bytes */:
+        field.get(&sampling_interval_bytes_);
         break;
-      case 115 /* interceptor_config */:
-        (*interceptor_config_).ParseFromArray(field.data(), field.size());
+      case 24 /* adaptive_sampling_shmem_threshold */:
+        field.get(&adaptive_sampling_shmem_threshold_);
         break;
-      case 120 /* network_packet_trace_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &network_packet_trace_config_);
+      case 25 /* adaptive_sampling_max_sampling_interval_bytes */:
+        field.get(&adaptive_sampling_max_sampling_interval_bytes_);
         break;
-      case 121 /* surfaceflinger_layers_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &surfaceflinger_layers_config_);
+      case 2 /* process_cmdline */:
+        process_cmdline_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &process_cmdline_.back());
         break;
-      case 123 /* surfaceflinger_transactions_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &surfaceflinger_transactions_config_);
+      case 4 /* pid */:
+        pid_.emplace_back();
+        field.get(&pid_.back());
         break;
-      case 124 /* android_sdk_sysprop_guard_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_sdk_sysprop_guard_config_);
+      case 26 /* target_installed_by */:
+        target_installed_by_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &target_installed_by_.back());
         break;
-      case 125 /* etw_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &etw_config_);
+      case 20 /* heaps */:
+        heaps_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &heaps_.back());
         break;
-      case 126 /* protolog_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &protolog_config_);
+      case 27 /* exclude_heaps */:
+        exclude_heaps_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &exclude_heaps_.back());
         break;
-      case 128 /* android_input_event_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_input_event_config_);
+      case 23 /* stream_allocations */:
+        field.get(&stream_allocations_);
         break;
-      case 129 /* pixel_modem_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &pixel_modem_config_);
+      case 22 /* heap_sampling_intervals */:
+        heap_sampling_intervals_.emplace_back();
+        field.get(&heap_sampling_intervals_.back());
         break;
-      case 130 /* windowmanager_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &windowmanager_config_);
+      case 21 /* all_heaps */:
+        field.get(&all_heaps_);
         break;
-      case 131 /* chromium_system_metrics */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &chromium_system_metrics_);
+      case 5 /* all */:
+        field.get(&all_);
         break;
-      case 132 /* kernel_wakelocks_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &kernel_wakelocks_config_);
+      case 15 /* min_anonymous_memory_kb */:
+        field.get(&min_anonymous_memory_kb_);
         break;
-      case 133 /* gpu_renderstages_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &gpu_renderstages_config_);
+      case 16 /* max_heapprofd_memory_kb */:
+        field.get(&max_heapprofd_memory_kb_);
         break;
-      case 134 /* chromium_histogram_samples */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &chromium_histogram_samples_);
+      case 17 /* max_heapprofd_cpu_secs */:
+        field.get(&max_heapprofd_cpu_secs_);
         break;
-      case 135 /* app_wakelocks_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &app_wakelocks_config_);
+      case 7 /* skip_symbol_prefix */:
+        skip_symbol_prefix_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &skip_symbol_prefix_.back());
         break;
-      case 137 /* cpu_per_uid_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &cpu_per_uid_config_);
+      case 6 /* continuous_dump_config */:
+        (*continuous_dump_config_).ParseFromArray(field.data(), field.size());
         break;
-      case 138 /* user_list_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &user_list_config_);
+      case 8 /* shmem_size_bytes */:
+        field.get(&shmem_size_bytes_);
         break;
-      case 139 /* inputmethod_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &inputmethod_config_);
+      case 9 /* block_client */:
+        field.get(&block_client_);
         break;
-      case 140 /* android_aflags_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &android_aflags_config_);
+      case 14 /* block_client_timeout_us */:
+        field.get(&block_client_timeout_us_);
         break;
-      case 141 /* journald_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &journald_config_);
+      case 10 /* no_startup */:
+        field.get(&no_startup_);
         break;
-      case 142 /* display_video_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &display_video_config_);
+      case 11 /* no_running */:
+        field.get(&no_running_);
         break;
-      case 150 /* qnx_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &qnx_config_);
+      case 13 /* dump_at_max */:
+        field.get(&dump_at_max_);
         break;
-      case 1000 /* legacy_config */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &legacy_config_);
+      case 18 /* disable_fork_teardown */:
+        field.get(&disable_fork_teardown_);
         break;
-      case 1001 /* for_testing */:
-        (*for_testing_).ParseFromArray(field.data(), field.size());
+      case 19 /* disable_vfork_detection */:
+        field.get(&disable_vfork_detection_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -28915,302 +28300,206 @@ bool DataSourceConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string DataSourceConfig::SerializeAsString() const {
+std::string HeapprofdConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> DataSourceConfig::SerializeAsArray() const {
+std::vector<uint8_t> HeapprofdConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void DataSourceConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
+void HeapprofdConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: sampling_interval_bytes
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, sampling_interval_bytes_, msg);
   }

-  // Field 2: target_buffer
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, target_buffer_, msg);
+  // Field 24: adaptive_sampling_shmem_threshold
+  if (_has_field_[24]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(24, adaptive_sampling_shmem_threshold_, msg);
   }

-  // Field 11: target_buffer_name
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeString(11, target_buffer_name_, msg);
+  // Field 25: adaptive_sampling_max_sampling_interval_bytes
+  if (_has_field_[25]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(25, adaptive_sampling_max_sampling_interval_bytes_, msg);
   }

-  // Field 3: trace_duration_ms
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, trace_duration_ms_, msg);
+  // Field 2: process_cmdline
+  for (auto& it : process_cmdline_) {
+    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
   }

-  // Field 122: prefer_suspend_clock_for_duration
-  if (_has_field_[122]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(122, prefer_suspend_clock_for_duration_, msg);
+  // Field 4: pid
+  for (auto& it : pid_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, it, msg);
   }

-  // Field 7: stop_timeout_ms
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, stop_timeout_ms_, msg);
+  // Field 26: target_installed_by
+  for (auto& it : target_installed_by_) {
+    ::protozero::internal::gen_helpers::SerializeString(26, it, msg);
   }

-  // Field 6: enable_extra_guardrails
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(6, enable_extra_guardrails_, msg);
+  // Field 20: heaps
+  for (auto& it : heaps_) {
+    ::protozero::internal::gen_helpers::SerializeString(20, it, msg);
   }

-  // Field 8: session_initiator
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, session_initiator_, msg);
+  // Field 27: exclude_heaps
+  for (auto& it : exclude_heaps_) {
+    ::protozero::internal::gen_helpers::SerializeString(27, it, msg);
   }

-  // Field 4: tracing_session_id
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, tracing_session_id_, msg);
+  // Field 23: stream_allocations
+  if (_has_field_[23]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(23, stream_allocations_, msg);
   }

-  // Field 9: buffer_exhausted_policy
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(9, buffer_exhausted_policy_, msg);
+  // Field 22: heap_sampling_intervals
+  for (auto& it : heap_sampling_intervals_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(22, it, msg);
   }

-  // Field 10: priority_boost
-  if (_has_field_[10]) {
-    (*priority_boost_).Serialize(msg->BeginNestedMessage<::protozero::Message>(10));
+  // Field 21: all_heaps
+  if (_has_field_[21]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(21, all_heaps_, msg);
   }

-  // Field 12: protovm_config
-  if (_has_field_[12]) {
-    (*protovm_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(12));
+  // Field 5: all
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, all_, msg);
   }

-  // Field 100: ftrace_config
-  if (_has_field_[100]) {
-    msg->AppendString(100, ftrace_config_);
+  // Field 15: min_anonymous_memory_kb
+  if (_has_field_[15]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(15, min_anonymous_memory_kb_, msg);
   }

-  // Field 102: inode_file_config
-  if (_has_field_[102]) {
-    msg->AppendString(102, inode_file_config_);
+  // Field 16: max_heapprofd_memory_kb
+  if (_has_field_[16]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(16, max_heapprofd_memory_kb_, msg);
   }

-  // Field 103: process_stats_config
-  if (_has_field_[103]) {
-    msg->AppendString(103, process_stats_config_);
+  // Field 17: max_heapprofd_cpu_secs
+  if (_has_field_[17]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(17, max_heapprofd_cpu_secs_, msg);
   }

-  // Field 104: sys_stats_config
-  if (_has_field_[104]) {
-    msg->AppendString(104, sys_stats_config_);
+  // Field 7: skip_symbol_prefix
+  for (auto& it : skip_symbol_prefix_) {
+    ::protozero::internal::gen_helpers::SerializeString(7, it, msg);
   }

-  // Field 105: heapprofd_config
-  if (_has_field_[105]) {
-    msg->AppendString(105, heapprofd_config_);
+  // Field 6: continuous_dump_config
+  if (_has_field_[6]) {
+    (*continuous_dump_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
   }

-  // Field 110: java_hprof_config
-  if (_has_field_[110]) {
-    msg->AppendString(110, java_hprof_config_);
+  // Field 8: shmem_size_bytes
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, shmem_size_bytes_, msg);
   }

-  // Field 106: android_power_config
-  if (_has_field_[106]) {
-    msg->AppendString(106, android_power_config_);
+  // Field 9: block_client
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, block_client_, msg);
   }

-  // Field 107: android_log_config
-  if (_has_field_[107]) {
-    msg->AppendString(107, android_log_config_);
+  // Field 14: block_client_timeout_us
+  if (_has_field_[14]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(14, block_client_timeout_us_, msg);
   }

-  // Field 108: gpu_counter_config
-  if (_has_field_[108]) {
-    msg->AppendString(108, gpu_counter_config_);
+  // Field 10: no_startup
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(10, no_startup_, msg);
   }

-  // Field 116: android_game_intervention_list_config
-  if (_has_field_[116]) {
-    msg->AppendString(116, android_game_intervention_list_config_);
+  // Field 11: no_running
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(11, no_running_, msg);
   }

-  // Field 109: packages_list_config
-  if (_has_field_[109]) {
-    msg->AppendString(109, packages_list_config_);
+  // Field 13: dump_at_max
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(13, dump_at_max_, msg);
   }

-  // Field 111: perf_event_config
-  if (_has_field_[111]) {
-    msg->AppendString(111, perf_event_config_);
+  // Field 18: disable_fork_teardown
+  if (_has_field_[18]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(18, disable_fork_teardown_, msg);
   }

-  // Field 112: vulkan_memory_config
-  if (_has_field_[112]) {
-    msg->AppendString(112, vulkan_memory_config_);
+  // Field 19: disable_vfork_detection
+  if (_has_field_[19]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(19, disable_vfork_detection_, msg);
   }

-  // Field 113: track_event_config
-  if (_has_field_[113]) {
-    msg->AppendString(113, track_event_config_);
-  }
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}

-  // Field 114: android_polled_state_config
-  if (_has_field_[114]) {
-    msg->AppendString(114, android_polled_state_config_);
-  }

-  // Field 118: android_system_property_config
-  if (_has_field_[118]) {
-    msg->AppendString(118, android_system_property_config_);
-  }
+HeapprofdConfig_ContinuousDumpConfig::HeapprofdConfig_ContinuousDumpConfig() = default;
+HeapprofdConfig_ContinuousDumpConfig::~HeapprofdConfig_ContinuousDumpConfig() = default;
+HeapprofdConfig_ContinuousDumpConfig::HeapprofdConfig_ContinuousDumpConfig(const HeapprofdConfig_ContinuousDumpConfig&) = default;
+HeapprofdConfig_ContinuousDumpConfig& HeapprofdConfig_ContinuousDumpConfig::operator=(const HeapprofdConfig_ContinuousDumpConfig&) = default;
+HeapprofdConfig_ContinuousDumpConfig::HeapprofdConfig_ContinuousDumpConfig(HeapprofdConfig_ContinuousDumpConfig&&) noexcept = default;
+HeapprofdConfig_ContinuousDumpConfig& HeapprofdConfig_ContinuousDumpConfig::operator=(HeapprofdConfig_ContinuousDumpConfig&&) = default;

-  // Field 117: statsd_tracing_config
-  if (_has_field_[117]) {
-    msg->AppendString(117, statsd_tracing_config_);
-  }
+bool HeapprofdConfig_ContinuousDumpConfig::operator==(const HeapprofdConfig_ContinuousDumpConfig& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(dump_phase_ms_, other.dump_phase_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(dump_interval_ms_, other.dump_interval_ms_);
+}

-  // Field 119: system_info_config
-  if (_has_field_[119]) {
-    (*system_info_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(119));
-  }
+bool HeapprofdConfig_ContinuousDumpConfig::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;

-  // Field 136: frozen_ftrace_config
-  if (_has_field_[136]) {
-    msg->AppendString(136, frozen_ftrace_config_);
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 5 /* dump_phase_ms */:
+        field.get(&dump_phase_ms_);
+        break;
+      case 6 /* dump_interval_ms */:
+        field.get(&dump_interval_ms_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 101: chrome_config
-  if (_has_field_[101]) {
-    (*chrome_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(101));
-  }
+std::string HeapprofdConfig_ContinuousDumpConfig::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}

-  // Field 127: v8_config
-  if (_has_field_[127]) {
-    msg->AppendString(127, v8_config_);
-  }
-
-  // Field 115: interceptor_config
-  if (_has_field_[115]) {
-    (*interceptor_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(115));
-  }
-
-  // Field 120: network_packet_trace_config
-  if (_has_field_[120]) {
-    msg->AppendString(120, network_packet_trace_config_);
-  }
-
-  // Field 121: surfaceflinger_layers_config
-  if (_has_field_[121]) {
-    msg->AppendString(121, surfaceflinger_layers_config_);
-  }
-
-  // Field 123: surfaceflinger_transactions_config
-  if (_has_field_[123]) {
-    msg->AppendString(123, surfaceflinger_transactions_config_);
-  }
-
-  // Field 124: android_sdk_sysprop_guard_config
-  if (_has_field_[124]) {
-    msg->AppendString(124, android_sdk_sysprop_guard_config_);
-  }
-
-  // Field 125: etw_config
-  if (_has_field_[125]) {
-    msg->AppendString(125, etw_config_);
-  }
-
-  // Field 126: protolog_config
-  if (_has_field_[126]) {
-    msg->AppendString(126, protolog_config_);
-  }
-
-  // Field 128: android_input_event_config
-  if (_has_field_[128]) {
-    msg->AppendString(128, android_input_event_config_);
-  }
-
-  // Field 129: pixel_modem_config
-  if (_has_field_[129]) {
-    msg->AppendString(129, pixel_modem_config_);
-  }
-
-  // Field 130: windowmanager_config
-  if (_has_field_[130]) {
-    msg->AppendString(130, windowmanager_config_);
-  }
-
-  // Field 131: chromium_system_metrics
-  if (_has_field_[131]) {
-    msg->AppendString(131, chromium_system_metrics_);
-  }
-
-  // Field 132: kernel_wakelocks_config
-  if (_has_field_[132]) {
-    msg->AppendString(132, kernel_wakelocks_config_);
-  }
-
-  // Field 133: gpu_renderstages_config
-  if (_has_field_[133]) {
-    msg->AppendString(133, gpu_renderstages_config_);
-  }
-
-  // Field 134: chromium_histogram_samples
-  if (_has_field_[134]) {
-    msg->AppendString(134, chromium_histogram_samples_);
-  }
-
-  // Field 135: app_wakelocks_config
-  if (_has_field_[135]) {
-    msg->AppendString(135, app_wakelocks_config_);
-  }
-
-  // Field 137: cpu_per_uid_config
-  if (_has_field_[137]) {
-    msg->AppendString(137, cpu_per_uid_config_);
-  }
-
-  // Field 138: user_list_config
-  if (_has_field_[138]) {
-    msg->AppendString(138, user_list_config_);
-  }
-
-  // Field 139: inputmethod_config
-  if (_has_field_[139]) {
-    msg->AppendString(139, inputmethod_config_);
-  }
-
-  // Field 140: android_aflags_config
-  if (_has_field_[140]) {
-    msg->AppendString(140, android_aflags_config_);
-  }
-
-  // Field 141: journald_config
-  if (_has_field_[141]) {
-    msg->AppendString(141, journald_config_);
-  }
-
-  // Field 142: display_video_config
-  if (_has_field_[142]) {
-    msg->AppendString(142, display_video_config_);
-  }
-
-  // Field 150: qnx_config
-  if (_has_field_[150]) {
-    msg->AppendString(150, qnx_config_);
-  }
+std::vector<uint8_t> HeapprofdConfig_ContinuousDumpConfig::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}

-  // Field 1000: legacy_config
-  if (_has_field_[1000]) {
-    ::protozero::internal::gen_helpers::SerializeString(1000, legacy_config_, msg);
+void HeapprofdConfig_ContinuousDumpConfig::Serialize(::protozero::Message* msg) const {
+  // Field 5: dump_phase_ms
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, dump_phase_ms_, msg);
   }

-  // Field 1001: for_testing
-  if (_has_field_[1001]) {
-    (*for_testing_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1001));
+  // Field 6: dump_interval_ms
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, dump_interval_ms_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -29222,7 +28511,7 @@ void DataSourceConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/etw/etw_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/profiling/java_hprof_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -29233,38 +28522,39 @@ void DataSourceConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/etw/etw_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/java_hprof_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/smaps_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-EtwConfig::EtwConfig() = default;
-EtwConfig::~EtwConfig() = default;
-EtwConfig::EtwConfig(const EtwConfig&) = default;
-EtwConfig& EtwConfig::operator=(const EtwConfig&) = default;
-EtwConfig::EtwConfig(EtwConfig&&) noexcept = default;
-EtwConfig& EtwConfig::operator=(EtwConfig&&) = default;
+JavaHprofConfig::JavaHprofConfig() = default;
+JavaHprofConfig::~JavaHprofConfig() = default;
+JavaHprofConfig::JavaHprofConfig(const JavaHprofConfig&) = default;
+JavaHprofConfig& JavaHprofConfig::operator=(const JavaHprofConfig&) = default;
+JavaHprofConfig::JavaHprofConfig(JavaHprofConfig&&) noexcept = default;
+JavaHprofConfig& JavaHprofConfig::operator=(JavaHprofConfig&&) = default;

-bool EtwConfig::operator==(const EtwConfig& other) const {
+bool JavaHprofConfig::operator==(const JavaHprofConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(kernel_flags_, other.kernel_flags_)
-   && ::protozero::internal::gen_helpers::EqualsField(scheduler_provider_events_, other.scheduler_provider_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(memory_provider_events_, other.memory_provider_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(file_provider_events_, other.file_provider_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(stack_sampling_events_, other.stack_sampling_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(disk_provider_events_, other.disk_provider_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(system_io_provider_events_, other.system_io_provider_events_);
+   && ::protozero::internal::gen_helpers::EqualsField(process_cmdline_, other.process_cmdline_)
+   && ::protozero::internal::gen_helpers::EqualsField(pid_, other.pid_)
+   && ::protozero::internal::gen_helpers::EqualsField(target_installed_by_, other.target_installed_by_)
+   && ::protozero::internal::gen_helpers::EqualsField(continuous_dump_config_, other.continuous_dump_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(min_anonymous_memory_kb_, other.min_anonymous_memory_kb_)
+   && ::protozero::internal::gen_helpers::EqualsField(min_java_heap_size_kb_, other.min_java_heap_size_kb_)
+   && ::protozero::internal::gen_helpers::EqualsField(dump_smaps_, other.dump_smaps_)
+   && ::protozero::internal::gen_helpers::EqualsField(smaps_config_, other.smaps_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(ignored_types_, other.ignored_types_)
+   && ::protozero::internal::gen_helpers::EqualsField(dump_oome_callstack_, other.dump_oome_callstack_);
 }

-bool EtwConfig::ParseFromArray(const void* raw, size_t size) {
-  kernel_flags_.clear();
-  scheduler_provider_events_.clear();
-  memory_provider_events_.clear();
-  file_provider_events_.clear();
-  stack_sampling_events_.clear();
-  disk_provider_events_.clear();
-  system_io_provider_events_.clear();
+bool JavaHprofConfig::ParseFromArray(const void* raw, size_t size) {
+  process_cmdline_.clear();
+  pid_.clear();
+  target_installed_by_.clear();
+  ignored_types_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -29274,33 +28564,39 @@ bool EtwConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* kernel_flags */:
-        kernel_flags_.emplace_back();
-        field.get(&kernel_flags_.back());
+      case 1 /* process_cmdline */:
+        process_cmdline_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &process_cmdline_.back());
         break;
-      case 2 /* scheduler_provider_events */:
-        scheduler_provider_events_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &scheduler_provider_events_.back());
+      case 2 /* pid */:
+        pid_.emplace_back();
+        field.get(&pid_.back());
         break;
-      case 3 /* memory_provider_events */:
-        memory_provider_events_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &memory_provider_events_.back());
+      case 7 /* target_installed_by */:
+        target_installed_by_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &target_installed_by_.back());
         break;
-      case 4 /* file_provider_events */:
-        file_provider_events_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &file_provider_events_.back());
+      case 3 /* continuous_dump_config */:
+        (*continuous_dump_config_).ParseFromArray(field.data(), field.size());
         break;
-      case 5 /* stack_sampling_events */:
-        stack_sampling_events_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &stack_sampling_events_.back());
+      case 4 /* min_anonymous_memory_kb */:
+        field.get(&min_anonymous_memory_kb_);
         break;
-      case 6 /* disk_provider_events */:
-        disk_provider_events_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &disk_provider_events_.back());
+      case 9 /* min_java_heap_size_kb */:
+        field.get(&min_java_heap_size_kb_);
         break;
-      case 7 /* system_io_provider_events */:
-        system_io_provider_events_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &system_io_provider_events_.back());
+      case 5 /* dump_smaps */:
+        field.get(&dump_smaps_);
+        break;
+      case 8 /* smaps_config */:
+        (*smaps_config_).ParseFromArray(field.data(), field.size());
+        break;
+      case 6 /* ignored_types */:
+        ignored_types_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &ignored_types_.back());
+        break;
+      case 10 /* dump_oome_callstack */:
+        field.get(&dump_oome_callstack_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -29310,95 +28606,88 @@ bool EtwConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string EtwConfig::SerializeAsString() const {
+std::string JavaHprofConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> EtwConfig::SerializeAsArray() const {
+std::vector<uint8_t> JavaHprofConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void EtwConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: kernel_flags
-  for (auto& it : kernel_flags_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
+void JavaHprofConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: process_cmdline
+  for (auto& it : process_cmdline_) {
+    ::protozero::internal::gen_helpers::SerializeString(1, it, msg);
   }

-  // Field 2: scheduler_provider_events
-  for (auto& it : scheduler_provider_events_) {
-    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
+  // Field 2: pid
+  for (auto& it : pid_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
   }

-  // Field 3: memory_provider_events
-  for (auto& it : memory_provider_events_) {
-    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
+  // Field 7: target_installed_by
+  for (auto& it : target_installed_by_) {
+    ::protozero::internal::gen_helpers::SerializeString(7, it, msg);
   }

-  // Field 4: file_provider_events
-  for (auto& it : file_provider_events_) {
-    ::protozero::internal::gen_helpers::SerializeString(4, it, msg);
+  // Field 3: continuous_dump_config
+  if (_has_field_[3]) {
+    (*continuous_dump_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
   }

-  // Field 5: stack_sampling_events
-  for (auto& it : stack_sampling_events_) {
-    ::protozero::internal::gen_helpers::SerializeString(5, it, msg);
+  // Field 4: min_anonymous_memory_kb
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, min_anonymous_memory_kb_, msg);
   }

-  // Field 6: disk_provider_events
-  for (auto& it : disk_provider_events_) {
+  // Field 9: min_java_heap_size_kb
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(9, min_java_heap_size_kb_, msg);
+  }
+
+  // Field 5: dump_smaps
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, dump_smaps_, msg);
+  }
+
+  // Field 8: smaps_config
+  if (_has_field_[8]) {
+    (*smaps_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
+  }
+
+  // Field 6: ignored_types
+  for (auto& it : ignored_types_) {
     ::protozero::internal::gen_helpers::SerializeString(6, it, msg);
   }

-  // Field 7: system_io_provider_events
-  for (auto& it : system_io_provider_events_) {
-    ::protozero::internal::gen_helpers::SerializeString(7, it, msg);
+  // Field 10: dump_oome_callstack
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(10, dump_oome_callstack_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/interceptor_config.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/interceptor_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/interceptors/console_config.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-InterceptorConfig::InterceptorConfig() = default;
-InterceptorConfig::~InterceptorConfig() = default;
-InterceptorConfig::InterceptorConfig(const InterceptorConfig&) = default;
-InterceptorConfig& InterceptorConfig::operator=(const InterceptorConfig&) = default;
-InterceptorConfig::InterceptorConfig(InterceptorConfig&&) noexcept = default;
-InterceptorConfig& InterceptorConfig::operator=(InterceptorConfig&&) = default;
+JavaHprofConfig_ContinuousDumpConfig::JavaHprofConfig_ContinuousDumpConfig() = default;
+JavaHprofConfig_ContinuousDumpConfig::~JavaHprofConfig_ContinuousDumpConfig() = default;
+JavaHprofConfig_ContinuousDumpConfig::JavaHprofConfig_ContinuousDumpConfig(const JavaHprofConfig_ContinuousDumpConfig&) = default;
+JavaHprofConfig_ContinuousDumpConfig& JavaHprofConfig_ContinuousDumpConfig::operator=(const JavaHprofConfig_ContinuousDumpConfig&) = default;
+JavaHprofConfig_ContinuousDumpConfig::JavaHprofConfig_ContinuousDumpConfig(JavaHprofConfig_ContinuousDumpConfig&&) noexcept = default;
+JavaHprofConfig_ContinuousDumpConfig& JavaHprofConfig_ContinuousDumpConfig::operator=(JavaHprofConfig_ContinuousDumpConfig&&) = default;

-bool InterceptorConfig::operator==(const InterceptorConfig& other) const {
+bool JavaHprofConfig_ContinuousDumpConfig::operator==(const JavaHprofConfig_ContinuousDumpConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(console_config_, other.console_config_);
+   && ::protozero::internal::gen_helpers::EqualsField(dump_phase_ms_, other.dump_phase_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(dump_interval_ms_, other.dump_interval_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(scan_pids_only_on_start_, other.scan_pids_only_on_start_);
 }

-bool InterceptorConfig::ParseFromArray(const void* raw, size_t size) {
+bool JavaHprofConfig_ContinuousDumpConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -29408,11 +28697,14 @@ bool InterceptorConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+      case 1 /* dump_phase_ms */:
+        field.get(&dump_phase_ms_);
         break;
-      case 100 /* console_config */:
-        (*console_config_).ParseFromArray(field.data(), field.size());
+      case 2 /* dump_interval_ms */:
+        field.get(&dump_interval_ms_);
+        break;
+      case 3 /* scan_pids_only_on_start */:
+        field.get(&scan_pids_only_on_start_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -29422,27 +28714,32 @@ bool InterceptorConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string InterceptorConfig::SerializeAsString() const {
+std::string JavaHprofConfig_ContinuousDumpConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> InterceptorConfig::SerializeAsArray() const {
+std::vector<uint8_t> JavaHprofConfig_ContinuousDumpConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void InterceptorConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
+void JavaHprofConfig_ContinuousDumpConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: dump_phase_ms
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, dump_phase_ms_, msg);
   }

-  // Field 100: console_config
-  if (_has_field_[100]) {
-    (*console_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(100));
+  // Field 2: dump_interval_ms
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, dump_interval_ms_, msg);
+  }
+
+  // Field 3: scan_pids_only_on_start
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, scan_pids_only_on_start_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -29454,7 +28751,7 @@ void InterceptorConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/stress_test_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/profiling/perf_event_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -29465,92 +28762,57 @@ void InterceptorConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/stress_test_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/trace_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/priority_boost/priority_boost_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/data_source_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/qnx/qnx_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/linux/journald_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/histogram_samples.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/system_info/system_info_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/track_event/track_event_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/test_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/sys_stats/sys_stats_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/sys_stats_counters.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/profiling/perf_event_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/common/perf_events.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/java_hprof_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/smaps_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/heapprofd_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/process_stats/process_stats_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/statsd_tracing_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/atom_ids.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/protovm/protovm_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/power/android_power_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/interceptor_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/interceptors/console_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/inode_file/inode_file_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_renderstages_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/vulkan_memory_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_counter_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/frozen_ftrace_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/system_metrics.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/etw/etw_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/v8_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/chrome_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/windowmanager_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/user_list_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_transactions_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_layers_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/display_video_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/protolog_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/pixel_modem_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/packages_list_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/network_trace_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/kernel_wakelocks_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/inputmethod_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/cpu_per_uid_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/app_wakelock_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_sdk_sysprop_guard_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_system_property_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_polled_state_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_log_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/android_log_constants.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_input_event_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_game_intervention_list_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_aflags_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/trace_attributes.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/semantic_type.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/builtin_clock.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-StressTestConfig::StressTestConfig() = default;
-StressTestConfig::~StressTestConfig() = default;
-StressTestConfig::StressTestConfig(const StressTestConfig&) = default;
-StressTestConfig& StressTestConfig::operator=(const StressTestConfig&) = default;
-StressTestConfig::StressTestConfig(StressTestConfig&&) noexcept = default;
-StressTestConfig& StressTestConfig::operator=(StressTestConfig&&) = default;
+PerfEventConfig::PerfEventConfig() = default;
+PerfEventConfig::~PerfEventConfig() = default;
+PerfEventConfig::PerfEventConfig(const PerfEventConfig&) = default;
+PerfEventConfig& PerfEventConfig::operator=(const PerfEventConfig&) = default;
+PerfEventConfig::PerfEventConfig(PerfEventConfig&&) noexcept = default;
+PerfEventConfig& PerfEventConfig::operator=(PerfEventConfig&&) = default;

-bool StressTestConfig::operator==(const StressTestConfig& other) const {
+bool PerfEventConfig::operator==(const PerfEventConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_config_, other.trace_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(shmem_size_kb_, other.shmem_size_kb_)
-   && ::protozero::internal::gen_helpers::EqualsField(shmem_page_size_kb_, other.shmem_page_size_kb_)
-   && ::protozero::internal::gen_helpers::EqualsField(num_processes_, other.num_processes_)
-   && ::protozero::internal::gen_helpers::EqualsField(num_threads_, other.num_threads_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_events_, other.max_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(nesting_, other.nesting_)
-   && ::protozero::internal::gen_helpers::EqualsField(steady_state_timings_, other.steady_state_timings_)
-   && ::protozero::internal::gen_helpers::EqualsField(burst_period_ms_, other.burst_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(burst_duration_ms_, other.burst_duration_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(burst_timings_, other.burst_timings_);
+   && ::protozero::internal::gen_helpers::EqualsField(timebase_, other.timebase_)
+   && ::protozero::internal::gen_helpers::EqualsField(followers_, other.followers_)
+   && ::protozero::internal::gen_helpers::EqualsField(callstack_sampling_, other.callstack_sampling_)
+   && ::protozero::internal::gen_helpers::EqualsField(target_cpu_, other.target_cpu_)
+   && ::protozero::internal::gen_helpers::EqualsField(ignore_open_failure_, other.ignore_open_failure_)
+   && ::protozero::internal::gen_helpers::EqualsField(cpuid_, other.cpuid_)
+   && ::protozero::internal::gen_helpers::EqualsField(ring_buffer_read_period_ms_, other.ring_buffer_read_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(ring_buffer_pages_, other.ring_buffer_pages_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_enqueued_footprint_kb_, other.max_enqueued_footprint_kb_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_daemon_memory_kb_, other.max_daemon_memory_kb_)
+   && ::protozero::internal::gen_helpers::EqualsField(remote_descriptor_timeout_ms_, other.remote_descriptor_timeout_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(unwind_state_clear_period_ms_, other.unwind_state_clear_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(target_installed_by_, other.target_installed_by_)
+   && ::protozero::internal::gen_helpers::EqualsField(all_cpus_, other.all_cpus_)
+   && ::protozero::internal::gen_helpers::EqualsField(sampling_frequency_, other.sampling_frequency_)
+   && ::protozero::internal::gen_helpers::EqualsField(kernel_frames_, other.kernel_frames_)
+   && ::protozero::internal::gen_helpers::EqualsField(target_pid_, other.target_pid_)
+   && ::protozero::internal::gen_helpers::EqualsField(target_cmdline_, other.target_cmdline_)
+   && ::protozero::internal::gen_helpers::EqualsField(exclude_pid_, other.exclude_pid_)
+   && ::protozero::internal::gen_helpers::EqualsField(exclude_cmdline_, other.exclude_cmdline_)
+   && ::protozero::internal::gen_helpers::EqualsField(additional_cmdline_count_, other.additional_cmdline_count_);
 }

-bool StressTestConfig::ParseFromArray(const void* raw, size_t size) {
+int PerfEventConfig::followers_size() const { return static_cast<int>(followers_.size()); }
+void PerfEventConfig::clear_followers() { followers_.clear(); }
+FollowerEvent* PerfEventConfig::add_followers() { followers_.emplace_back(); return &followers_.back(); }
+bool PerfEventConfig::ParseFromArray(const void* raw, size_t size) {
+  followers_.clear();
+  target_cpu_.clear();
+  cpuid_.clear();
+  target_installed_by_.clear();
+  target_pid_.clear();
+  target_cmdline_.clear();
+  exclude_pid_.clear();
+  exclude_cmdline_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -29560,38 +28822,76 @@ bool StressTestConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* trace_config */:
-        (*trace_config_).ParseFromArray(field.data(), field.size());
+      case 15 /* timebase */:
+        (*timebase_).ParseFromArray(field.data(), field.size());
         break;
-      case 2 /* shmem_size_kb */:
-        field.get(&shmem_size_kb_);
+      case 19 /* followers */:
+        followers_.emplace_back();
+        followers_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 3 /* shmem_page_size_kb */:
-        field.get(&shmem_page_size_kb_);
+      case 16 /* callstack_sampling */:
+        (*callstack_sampling_).ParseFromArray(field.data(), field.size());
         break;
-      case 4 /* num_processes */:
-        field.get(&num_processes_);
+      case 20 /* target_cpu */:
+        target_cpu_.emplace_back();
+        field.get(&target_cpu_.back());
         break;
-      case 5 /* num_threads */:
-        field.get(&num_threads_);
+      case 21 /* ignore_open_failure */:
+        field.get(&ignore_open_failure_);
         break;
-      case 6 /* max_events */:
-        field.get(&max_events_);
+      case 22 /* cpuid */:
+        cpuid_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &cpuid_.back());
         break;
-      case 7 /* nesting */:
-        field.get(&nesting_);
+      case 8 /* ring_buffer_read_period_ms */:
+        field.get(&ring_buffer_read_period_ms_);
         break;
-      case 8 /* steady_state_timings */:
-        (*steady_state_timings_).ParseFromArray(field.data(), field.size());
+      case 3 /* ring_buffer_pages */:
+        field.get(&ring_buffer_pages_);
         break;
-      case 9 /* burst_period_ms */:
-        field.get(&burst_period_ms_);
+      case 17 /* max_enqueued_footprint_kb */:
+        field.get(&max_enqueued_footprint_kb_);
         break;
-      case 10 /* burst_duration_ms */:
-        field.get(&burst_duration_ms_);
+      case 13 /* max_daemon_memory_kb */:
+        field.get(&max_daemon_memory_kb_);
         break;
-      case 11 /* burst_timings */:
-        (*burst_timings_).ParseFromArray(field.data(), field.size());
+      case 9 /* remote_descriptor_timeout_ms */:
+        field.get(&remote_descriptor_timeout_ms_);
+        break;
+      case 10 /* unwind_state_clear_period_ms */:
+        field.get(&unwind_state_clear_period_ms_);
+        break;
+      case 18 /* target_installed_by */:
+        target_installed_by_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &target_installed_by_.back());
+        break;
+      case 1 /* all_cpus */:
+        field.get(&all_cpus_);
+        break;
+      case 2 /* sampling_frequency */:
+        field.get(&sampling_frequency_);
+        break;
+      case 12 /* kernel_frames */:
+        field.get(&kernel_frames_);
+        break;
+      case 4 /* target_pid */:
+        target_pid_.emplace_back();
+        field.get(&target_pid_.back());
+        break;
+      case 5 /* target_cmdline */:
+        target_cmdline_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &target_cmdline_.back());
+        break;
+      case 6 /* exclude_pid */:
+        exclude_pid_.emplace_back();
+        field.get(&exclude_pid_.back());
+        break;
+      case 7 /* exclude_cmdline */:
+        exclude_cmdline_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &exclude_cmdline_.back());
+        break;
+      case 11 /* additional_cmdline_count */:
+        field.get(&additional_cmdline_count_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -29601,95 +28901,143 @@ bool StressTestConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string StressTestConfig::SerializeAsString() const {
+std::string PerfEventConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> StressTestConfig::SerializeAsArray() const {
+std::vector<uint8_t> PerfEventConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void StressTestConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: trace_config
-  if (_has_field_[1]) {
-    (*trace_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+void PerfEventConfig::Serialize(::protozero::Message* msg) const {
+  // Field 15: timebase
+  if (_has_field_[15]) {
+    (*timebase_).Serialize(msg->BeginNestedMessage<::protozero::Message>(15));
   }

-  // Field 2: shmem_size_kb
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, shmem_size_kb_, msg);
+  // Field 19: followers
+  for (auto& it : followers_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(19));
   }

-  // Field 3: shmem_page_size_kb
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, shmem_page_size_kb_, msg);
+  // Field 16: callstack_sampling
+  if (_has_field_[16]) {
+    (*callstack_sampling_).Serialize(msg->BeginNestedMessage<::protozero::Message>(16));
   }

-  // Field 4: num_processes
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, num_processes_, msg);
+  // Field 20: target_cpu
+  for (auto& it : target_cpu_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(20, it, msg);
   }

-  // Field 5: num_threads
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, num_threads_, msg);
+  // Field 21: ignore_open_failure
+  if (_has_field_[21]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(21, ignore_open_failure_, msg);
   }

-  // Field 6: max_events
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, max_events_, msg);
+  // Field 22: cpuid
+  for (auto& it : cpuid_) {
+    ::protozero::internal::gen_helpers::SerializeString(22, it, msg);
   }

-  // Field 7: nesting
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, nesting_, msg);
+  // Field 8: ring_buffer_read_period_ms
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, ring_buffer_read_period_ms_, msg);
   }

-  // Field 8: steady_state_timings
-  if (_has_field_[8]) {
-    (*steady_state_timings_).Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
+  // Field 3: ring_buffer_pages
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, ring_buffer_pages_, msg);
   }

-  // Field 9: burst_period_ms
+  // Field 17: max_enqueued_footprint_kb
+  if (_has_field_[17]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(17, max_enqueued_footprint_kb_, msg);
+  }
+
+  // Field 13: max_daemon_memory_kb
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(13, max_daemon_memory_kb_, msg);
+  }
+
+  // Field 9: remote_descriptor_timeout_ms
   if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(9, burst_period_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(9, remote_descriptor_timeout_ms_, msg);
   }

-  // Field 10: burst_duration_ms
+  // Field 10: unwind_state_clear_period_ms
   if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(10, burst_duration_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(10, unwind_state_clear_period_ms_, msg);
   }

-  // Field 11: burst_timings
+  // Field 18: target_installed_by
+  for (auto& it : target_installed_by_) {
+    ::protozero::internal::gen_helpers::SerializeString(18, it, msg);
+  }
+
+  // Field 1: all_cpus
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, all_cpus_, msg);
+  }
+
+  // Field 2: sampling_frequency
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, sampling_frequency_, msg);
+  }
+
+  // Field 12: kernel_frames
+  if (_has_field_[12]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(12, kernel_frames_, msg);
+  }
+
+  // Field 4: target_pid
+  for (auto& it : target_pid_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, it, msg);
+  }
+
+  // Field 5: target_cmdline
+  for (auto& it : target_cmdline_) {
+    ::protozero::internal::gen_helpers::SerializeString(5, it, msg);
+  }
+
+  // Field 6: exclude_pid
+  for (auto& it : exclude_pid_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, it, msg);
+  }
+
+  // Field 7: exclude_cmdline
+  for (auto& it : exclude_cmdline_) {
+    ::protozero::internal::gen_helpers::SerializeString(7, it, msg);
+  }
+
+  // Field 11: additional_cmdline_count
   if (_has_field_[11]) {
-    (*burst_timings_).Serialize(msg->BeginNestedMessage<::protozero::Message>(11));
+    ::protozero::internal::gen_helpers::SerializeVarInt(11, additional_cmdline_count_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-StressTestConfig_WriterTiming::StressTestConfig_WriterTiming() = default;
-StressTestConfig_WriterTiming::~StressTestConfig_WriterTiming() = default;
-StressTestConfig_WriterTiming::StressTestConfig_WriterTiming(const StressTestConfig_WriterTiming&) = default;
-StressTestConfig_WriterTiming& StressTestConfig_WriterTiming::operator=(const StressTestConfig_WriterTiming&) = default;
-StressTestConfig_WriterTiming::StressTestConfig_WriterTiming(StressTestConfig_WriterTiming&&) noexcept = default;
-StressTestConfig_WriterTiming& StressTestConfig_WriterTiming::operator=(StressTestConfig_WriterTiming&&) = default;
+PerfEventConfig_CallstackSampling::PerfEventConfig_CallstackSampling() = default;
+PerfEventConfig_CallstackSampling::~PerfEventConfig_CallstackSampling() = default;
+PerfEventConfig_CallstackSampling::PerfEventConfig_CallstackSampling(const PerfEventConfig_CallstackSampling&) = default;
+PerfEventConfig_CallstackSampling& PerfEventConfig_CallstackSampling::operator=(const PerfEventConfig_CallstackSampling&) = default;
+PerfEventConfig_CallstackSampling::PerfEventConfig_CallstackSampling(PerfEventConfig_CallstackSampling&&) noexcept = default;
+PerfEventConfig_CallstackSampling& PerfEventConfig_CallstackSampling::operator=(PerfEventConfig_CallstackSampling&&) = default;

-bool StressTestConfig_WriterTiming::operator==(const StressTestConfig_WriterTiming& other) const {
+bool PerfEventConfig_CallstackSampling::operator==(const PerfEventConfig_CallstackSampling& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(payload_mean_, other.payload_mean_)
-   && ::protozero::internal::gen_helpers::EqualsField(payload_stddev_, other.payload_stddev_)
-   && ::protozero::internal::gen_helpers::EqualsField(rate_mean_, other.rate_mean_)
-   && ::protozero::internal::gen_helpers::EqualsField(rate_stddev_, other.rate_stddev_)
-   && ::protozero::internal::gen_helpers::EqualsField(payload_write_time_ms_, other.payload_write_time_ms_);
+   && ::protozero::internal::gen_helpers::EqualsField(scope_, other.scope_)
+   && ::protozero::internal::gen_helpers::EqualsField(kernel_frames_, other.kernel_frames_)
+   && ::protozero::internal::gen_helpers::EqualsField(user_frames_, other.user_frames_);
 }

-bool StressTestConfig_WriterTiming::ParseFromArray(const void* raw, size_t size) {
+bool PerfEventConfig_CallstackSampling::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -29699,20 +29047,14 @@ bool StressTestConfig_WriterTiming::ParseFromArray(const void* raw, size_t size)
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* payload_mean */:
-        field.get(&payload_mean_);
-        break;
-      case 2 /* payload_stddev */:
-        field.get(&payload_stddev_);
-        break;
-      case 3 /* rate_mean */:
-        field.get(&rate_mean_);
+      case 1 /* scope */:
+        (*scope_).ParseFromArray(field.data(), field.size());
         break;
-      case 4 /* rate_stddev */:
-        field.get(&rate_stddev_);
+      case 2 /* kernel_frames */:
+        field.get(&kernel_frames_);
         break;
-      case 5 /* payload_write_time_ms */:
-        field.get(&payload_write_time_ms_);
+      case 3 /* user_frames */:
+        field.get(&user_frames_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -29722,42 +29064,140 @@ bool StressTestConfig_WriterTiming::ParseFromArray(const void* raw, size_t size)
   return !packed_error && !dec.bytes_left();
 }

-std::string StressTestConfig_WriterTiming::SerializeAsString() const {
+std::string PerfEventConfig_CallstackSampling::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> StressTestConfig_WriterTiming::SerializeAsArray() const {
+std::vector<uint8_t> PerfEventConfig_CallstackSampling::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void StressTestConfig_WriterTiming::Serialize(::protozero::Message* msg) const {
-  // Field 1: payload_mean
+void PerfEventConfig_CallstackSampling::Serialize(::protozero::Message* msg) const {
+  // Field 1: scope
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(1, payload_mean_, msg);
+    (*scope_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 2: payload_stddev
+  // Field 2: kernel_frames
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(2, payload_stddev_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, kernel_frames_, msg);
   }

-  // Field 3: rate_mean
+  // Field 3: user_frames
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(3, rate_mean_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, user_frames_, msg);
   }

-  // Field 4: rate_stddev
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(4, rate_stddev_, msg);
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+PerfEventConfig_Scope::PerfEventConfig_Scope() = default;
+PerfEventConfig_Scope::~PerfEventConfig_Scope() = default;
+PerfEventConfig_Scope::PerfEventConfig_Scope(const PerfEventConfig_Scope&) = default;
+PerfEventConfig_Scope& PerfEventConfig_Scope::operator=(const PerfEventConfig_Scope&) = default;
+PerfEventConfig_Scope::PerfEventConfig_Scope(PerfEventConfig_Scope&&) noexcept = default;
+PerfEventConfig_Scope& PerfEventConfig_Scope::operator=(PerfEventConfig_Scope&&) = default;
+
+bool PerfEventConfig_Scope::operator==(const PerfEventConfig_Scope& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(target_pid_, other.target_pid_)
+   && ::protozero::internal::gen_helpers::EqualsField(target_cmdline_, other.target_cmdline_)
+   && ::protozero::internal::gen_helpers::EqualsField(exclude_pid_, other.exclude_pid_)
+   && ::protozero::internal::gen_helpers::EqualsField(exclude_cmdline_, other.exclude_cmdline_)
+   && ::protozero::internal::gen_helpers::EqualsField(additional_cmdline_count_, other.additional_cmdline_count_)
+   && ::protozero::internal::gen_helpers::EqualsField(process_shard_count_, other.process_shard_count_);
+}
+
+bool PerfEventConfig_Scope::ParseFromArray(const void* raw, size_t size) {
+  target_pid_.clear();
+  target_cmdline_.clear();
+  exclude_pid_.clear();
+  exclude_cmdline_.clear();
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* target_pid */:
+        target_pid_.emplace_back();
+        field.get(&target_pid_.back());
+        break;
+      case 2 /* target_cmdline */:
+        target_cmdline_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &target_cmdline_.back());
+        break;
+      case 3 /* exclude_pid */:
+        exclude_pid_.emplace_back();
+        field.get(&exclude_pid_.back());
+        break;
+      case 4 /* exclude_cmdline */:
+        exclude_cmdline_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &exclude_cmdline_.back());
+        break;
+      case 5 /* additional_cmdline_count */:
+        field.get(&additional_cmdline_count_);
+        break;
+      case 6 /* process_shard_count */:
+        field.get(&process_shard_count_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 5: payload_write_time_ms
+std::string PerfEventConfig_Scope::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> PerfEventConfig_Scope::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void PerfEventConfig_Scope::Serialize(::protozero::Message* msg) const {
+  // Field 1: target_pid
+  for (auto& it : target_pid_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
+  }
+
+  // Field 2: target_cmdline
+  for (auto& it : target_cmdline_) {
+    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
+  }
+
+  // Field 3: exclude_pid
+  for (auto& it : exclude_pid_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, it, msg);
+  }
+
+  // Field 4: exclude_cmdline
+  for (auto& it : exclude_cmdline_) {
+    ::protozero::internal::gen_helpers::SerializeString(4, it, msg);
+  }
+
+  // Field 5: additional_cmdline_count
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, payload_write_time_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, additional_cmdline_count_, msg);
+  }
+
+  // Field 6: process_shard_count
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, process_shard_count_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -29769,7 +29209,7 @@ void StressTestConfig_WriterTiming::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/test_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/profiling/smaps_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -29780,30 +29220,30 @@ void StressTestConfig_WriterTiming::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/test_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/smaps_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-TestConfig::TestConfig() = default;
-TestConfig::~TestConfig() = default;
-TestConfig::TestConfig(const TestConfig&) = default;
-TestConfig& TestConfig::operator=(const TestConfig&) = default;
-TestConfig::TestConfig(TestConfig&&) noexcept = default;
-TestConfig& TestConfig::operator=(TestConfig&&) = default;
+RedactionRule::RedactionRule() = default;
+RedactionRule::~RedactionRule() = default;
+RedactionRule::RedactionRule(const RedactionRule&) = default;
+RedactionRule& RedactionRule::operator=(const RedactionRule&) = default;
+RedactionRule::RedactionRule(RedactionRule&&) noexcept = default;
+RedactionRule& RedactionRule::operator=(RedactionRule&&) = default;

-bool TestConfig::operator==(const TestConfig& other) const {
+bool RedactionRule::operator==(const RedactionRule& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(message_count_, other.message_count_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_messages_per_second_, other.max_messages_per_second_)
-   && ::protozero::internal::gen_helpers::EqualsField(seed_, other.seed_)
-   && ::protozero::internal::gen_helpers::EqualsField(message_size_, other.message_size_)
-   && ::protozero::internal::gen_helpers::EqualsField(send_batch_on_register_, other.send_batch_on_register_)
-   && ::protozero::internal::gen_helpers::EqualsField(dummy_fields_, other.dummy_fields_);
+   && ::protozero::internal::gen_helpers::EqualsField(pattern_, other.pattern_)
+   && ::protozero::internal::gen_helpers::EqualsField(match_mode_, other.match_mode_)
+   && ::protozero::internal::gen_helpers::EqualsField(keep_full_, other.keep_full_)
+   && ::protozero::internal::gen_helpers::EqualsField(replacement_name_, other.replacement_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(keep_file_extension_, other.keep_file_extension_)
+   && ::protozero::internal::gen_helpers::EqualsField(keep_path_elements_, other.keep_path_elements_);
 }

-bool TestConfig::ParseFromArray(const void* raw, size_t size) {
+bool RedactionRule::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -29813,23 +29253,23 @@ bool TestConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* message_count */:
-        field.get(&message_count_);
+      case 1 /* pattern */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &pattern_);
         break;
-      case 2 /* max_messages_per_second */:
-        field.get(&max_messages_per_second_);
+      case 2 /* match_mode */:
+        field.get(&match_mode_);
         break;
-      case 3 /* seed */:
-        field.get(&seed_);
+      case 3 /* keep_full */:
+        field.get(&keep_full_);
         break;
-      case 4 /* message_size */:
-        field.get(&message_size_);
+      case 4 /* replacement_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &replacement_name_);
         break;
-      case 5 /* send_batch_on_register */:
-        field.get(&send_batch_on_register_);
+      case 5 /* keep_file_extension */:
+        field.get(&keep_file_extension_);
         break;
-      case 6 /* dummy_fields */:
-        (*dummy_fields_).ParseFromArray(field.data(), field.size());
+      case 6 /* keep_path_elements */:
+        field.get(&keep_path_elements_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -29839,79 +29279,73 @@ bool TestConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TestConfig::SerializeAsString() const {
+std::string RedactionRule::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TestConfig::SerializeAsArray() const {
+std::vector<uint8_t> RedactionRule::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TestConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: message_count
+void RedactionRule::Serialize(::protozero::Message* msg) const {
+  // Field 1: pattern
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, message_count_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, pattern_, msg);
   }

-  // Field 2: max_messages_per_second
+  // Field 2: match_mode
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, max_messages_per_second_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, match_mode_, msg);
   }

-  // Field 3: seed
+  // Field 3: keep_full
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, seed_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, keep_full_, msg);
   }

-  // Field 4: message_size
+  // Field 4: replacement_name
   if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, message_size_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(4, replacement_name_, msg);
   }

-  // Field 5: send_batch_on_register
+  // Field 5: keep_file_extension
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, send_batch_on_register_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, keep_file_extension_, msg);
   }

-  // Field 6: dummy_fields
+  // Field 6: keep_path_elements
   if (_has_field_[6]) {
-    (*dummy_fields_).Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, keep_path_elements_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TestConfig_DummyFields::TestConfig_DummyFields() = default;
-TestConfig_DummyFields::~TestConfig_DummyFields() = default;
-TestConfig_DummyFields::TestConfig_DummyFields(const TestConfig_DummyFields&) = default;
-TestConfig_DummyFields& TestConfig_DummyFields::operator=(const TestConfig_DummyFields&) = default;
-TestConfig_DummyFields::TestConfig_DummyFields(TestConfig_DummyFields&&) noexcept = default;
-TestConfig_DummyFields& TestConfig_DummyFields::operator=(TestConfig_DummyFields&&) = default;
+SmapsConfig::SmapsConfig() = default;
+SmapsConfig::~SmapsConfig() = default;
+SmapsConfig::SmapsConfig(const SmapsConfig&) = default;
+SmapsConfig& SmapsConfig::operator=(const SmapsConfig&) = default;
+SmapsConfig::SmapsConfig(SmapsConfig&&) noexcept = default;
+SmapsConfig& SmapsConfig::operator=(SmapsConfig&&) = default;

-bool TestConfig_DummyFields::operator==(const TestConfig_DummyFields& other) const {
+bool SmapsConfig::operator==(const SmapsConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_uint32_, other.field_uint32_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_int32_, other.field_int32_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_uint64_, other.field_uint64_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_int64_, other.field_int64_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_fixed64_, other.field_fixed64_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_sfixed64_, other.field_sfixed64_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_fixed32_, other.field_fixed32_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_sfixed32_, other.field_sfixed32_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_double_, other.field_double_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_float_, other.field_float_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_sint64_, other.field_sint64_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_sint32_, other.field_sint32_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_string_, other.field_string_)
-   && ::protozero::internal::gen_helpers::EqualsField(field_bytes_, other.field_bytes_);
+   && ::protozero::internal::gen_helpers::EqualsField(vma_fields_, other.vma_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(unaggregated_, other.unaggregated_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_redaction_rules_, other.name_redaction_rules_);
 }

-bool TestConfig_DummyFields::ParseFromArray(const void* raw, size_t size) {
+int SmapsConfig::name_redaction_rules_size() const { return static_cast<int>(name_redaction_rules_.size()); }
+void SmapsConfig::clear_name_redaction_rules() { name_redaction_rules_.clear(); }
+RedactionRule* SmapsConfig::add_name_redaction_rules() { name_redaction_rules_.emplace_back(); return &name_redaction_rules_.back(); }
+bool SmapsConfig::ParseFromArray(const void* raw, size_t size) {
+  vma_fields_.clear();
+  name_redaction_rules_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -29921,47 +29355,16 @@ bool TestConfig_DummyFields::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* field_uint32 */:
-        field.get(&field_uint32_);
-        break;
-      case 2 /* field_int32 */:
-        field.get(&field_int32_);
-        break;
-      case 3 /* field_uint64 */:
-        field.get(&field_uint64_);
-        break;
-      case 4 /* field_int64 */:
-        field.get(&field_int64_);
-        break;
-      case 5 /* field_fixed64 */:
-        field.get(&field_fixed64_);
-        break;
-      case 6 /* field_sfixed64 */:
-        field.get(&field_sfixed64_);
-        break;
-      case 7 /* field_fixed32 */:
-        field.get(&field_fixed32_);
-        break;
-      case 8 /* field_sfixed32 */:
-        field.get(&field_sfixed32_);
-        break;
-      case 9 /* field_double */:
-        field.get(&field_double_);
-        break;
-      case 10 /* field_float */:
-        field.get(&field_float_);
-        break;
-      case 11 /* field_sint64 */:
-        field.get_signed(&field_sint64_);
-        break;
-      case 12 /* field_sint32 */:
-        field.get_signed(&field_sint32_);
+      case 1 /* vma_fields */:
+        vma_fields_.emplace_back();
+        field.get(&vma_fields_.back());
         break;
-      case 13 /* field_string */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &field_string_);
+      case 2 /* unaggregated */:
+        field.get(&unaggregated_);
         break;
-      case 14 /* field_bytes */:
-        field.get(&field_bytes_);
+      case 3 /* name_redaction_rules */:
+        name_redaction_rules_.emplace_back();
+        name_redaction_rules_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -29971,87 +29374,258 @@ bool TestConfig_DummyFields::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TestConfig_DummyFields::SerializeAsString() const {
+std::string SmapsConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TestConfig_DummyFields::SerializeAsArray() const {
+std::vector<uint8_t> SmapsConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TestConfig_DummyFields::Serialize(::protozero::Message* msg) const {
-  // Field 1: field_uint32
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, field_uint32_, msg);
+void SmapsConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: vma_fields
+  for (auto& it : vma_fields_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
   }

-  // Field 2: field_int32
+  // Field 2: unaggregated
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, field_int32_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, unaggregated_, msg);
   }

-  // Field 3: field_uint64
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, field_uint64_, msg);
+  // Field 3: name_redaction_rules
+  for (auto& it : name_redaction_rules_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
   }

-  // Field 4: field_int64
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, field_int64_, msg);
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+ProcessSmapsConfig::ProcessSmapsConfig() = default;
+ProcessSmapsConfig::~ProcessSmapsConfig() = default;
+ProcessSmapsConfig::ProcessSmapsConfig(const ProcessSmapsConfig&) = default;
+ProcessSmapsConfig& ProcessSmapsConfig::operator=(const ProcessSmapsConfig&) = default;
+ProcessSmapsConfig::ProcessSmapsConfig(ProcessSmapsConfig&&) noexcept = default;
+ProcessSmapsConfig& ProcessSmapsConfig::operator=(ProcessSmapsConfig&&) = default;
+
+bool ProcessSmapsConfig::operator==(const ProcessSmapsConfig& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(smaps_config_, other.smaps_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(scope_, other.scope_)
+   && ::protozero::internal::gen_helpers::EqualsField(read_period_ms_, other.read_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_processes_per_period_, other.max_processes_per_period_);
+}
+
+bool ProcessSmapsConfig::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* smaps_config */:
+        (*smaps_config_).ParseFromArray(field.data(), field.size());
+        break;
+      case 2 /* scope */:
+        (*scope_).ParseFromArray(field.data(), field.size());
+        break;
+      case 3 /* read_period_ms */:
+        field.get(&read_period_ms_);
+        break;
+      case 4 /* max_processes_per_period */:
+        field.get(&max_processes_per_period_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 5: field_fixed64
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(5, field_fixed64_, msg);
+std::string ProcessSmapsConfig::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ProcessSmapsConfig::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ProcessSmapsConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: smaps_config
+  if (_has_field_[1]) {
+    (*smaps_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 6: field_sfixed64
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(6, field_sfixed64_, msg);
+  // Field 2: scope
+  if (_has_field_[2]) {
+    (*scope_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

-  // Field 7: field_fixed32
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(7, field_fixed32_, msg);
+  // Field 3: read_period_ms
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, read_period_ms_, msg);
   }

-  // Field 8: field_sfixed32
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(8, field_sfixed32_, msg);
+  // Field 4: max_processes_per_period
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, max_processes_per_period_, msg);
   }

-  // Field 9: field_double
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(9, field_double_, msg);
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+ProcessSmapsConfig_Scope::ProcessSmapsConfig_Scope() = default;
+ProcessSmapsConfig_Scope::~ProcessSmapsConfig_Scope() = default;
+ProcessSmapsConfig_Scope::ProcessSmapsConfig_Scope(const ProcessSmapsConfig_Scope&) = default;
+ProcessSmapsConfig_Scope& ProcessSmapsConfig_Scope::operator=(const ProcessSmapsConfig_Scope&) = default;
+ProcessSmapsConfig_Scope::ProcessSmapsConfig_Scope(ProcessSmapsConfig_Scope&&) noexcept = default;
+ProcessSmapsConfig_Scope& ProcessSmapsConfig_Scope::operator=(ProcessSmapsConfig_Scope&&) = default;
+
+bool ProcessSmapsConfig_Scope::operator==(const ProcessSmapsConfig_Scope& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(target_cmdline_, other.target_cmdline_)
+   && ::protozero::internal::gen_helpers::EqualsField(target_uid_, other.target_uid_);
+}
+
+int ProcessSmapsConfig_Scope::target_uid_size() const { return static_cast<int>(target_uid_.size()); }
+void ProcessSmapsConfig_Scope::clear_target_uid() { target_uid_.clear(); }
+ProcessSmapsConfig_Scope_UidRange* ProcessSmapsConfig_Scope::add_target_uid() { target_uid_.emplace_back(); return &target_uid_.back(); }
+bool ProcessSmapsConfig_Scope::ParseFromArray(const void* raw, size_t size) {
+  target_cmdline_.clear();
+  target_uid_.clear();
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* target_cmdline */:
+        target_cmdline_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &target_cmdline_.back());
+        break;
+      case 2 /* target_uid */:
+        target_uid_.emplace_back();
+        target_uid_.back().ParseFromArray(field.data(), field.size());
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 10: field_float
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(10, field_float_, msg);
+std::string ProcessSmapsConfig_Scope::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ProcessSmapsConfig_Scope::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ProcessSmapsConfig_Scope::Serialize(::protozero::Message* msg) const {
+  // Field 1: target_cmdline
+  for (auto& it : target_cmdline_) {
+    ::protozero::internal::gen_helpers::SerializeString(1, it, msg);
   }

-  // Field 11: field_sint64
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeSignedVarInt(11, field_sint64_, msg);
+  // Field 2: target_uid
+  for (auto& it : target_uid_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

-  // Field 12: field_sint32
-  if (_has_field_[12]) {
-    ::protozero::internal::gen_helpers::SerializeSignedVarInt(12, field_sint32_, msg);
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+ProcessSmapsConfig_Scope_UidRange::ProcessSmapsConfig_Scope_UidRange() = default;
+ProcessSmapsConfig_Scope_UidRange::~ProcessSmapsConfig_Scope_UidRange() = default;
+ProcessSmapsConfig_Scope_UidRange::ProcessSmapsConfig_Scope_UidRange(const ProcessSmapsConfig_Scope_UidRange&) = default;
+ProcessSmapsConfig_Scope_UidRange& ProcessSmapsConfig_Scope_UidRange::operator=(const ProcessSmapsConfig_Scope_UidRange&) = default;
+ProcessSmapsConfig_Scope_UidRange::ProcessSmapsConfig_Scope_UidRange(ProcessSmapsConfig_Scope_UidRange&&) noexcept = default;
+ProcessSmapsConfig_Scope_UidRange& ProcessSmapsConfig_Scope_UidRange::operator=(ProcessSmapsConfig_Scope_UidRange&&) = default;
+
+bool ProcessSmapsConfig_Scope_UidRange::operator==(const ProcessSmapsConfig_Scope_UidRange& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(min_uid_, other.min_uid_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_uid_, other.max_uid_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_match_across_profiles_, other.android_match_across_profiles_);
+}
+
+bool ProcessSmapsConfig_Scope_UidRange::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* min_uid */:
+        field.get(&min_uid_);
+        break;
+      case 2 /* max_uid */:
+        field.get(&max_uid_);
+        break;
+      case 3 /* android_match_across_profiles */:
+        field.get(&android_match_across_profiles_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 13: field_string
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeString(13, field_string_, msg);
+std::string ProcessSmapsConfig_Scope_UidRange::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ProcessSmapsConfig_Scope_UidRange::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ProcessSmapsConfig_Scope_UidRange::Serialize(::protozero::Message* msg) const {
+  // Field 1: min_uid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, min_uid_, msg);
   }

-  // Field 14: field_bytes
-  if (_has_field_[14]) {
-    ::protozero::internal::gen_helpers::SerializeString(14, field_bytes_, msg);
+  // Field 2: max_uid
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, max_uid_, msg);
+  }
+
+  // Field 3: android_match_across_profiles
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, android_match_across_profiles_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -30063,7 +29637,7 @@ void TestConfig_DummyFields::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/config/trace_config.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/protovm/protovm_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -30074,139 +29648,25 @@ void TestConfig_DummyFields::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/config/trace_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/priority_boost/priority_boost_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/data_source_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/qnx/qnx_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/linux/journald_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/histogram_samples.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/system_info/system_info_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/track_event/track_event_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/test_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/sys_stats/sys_stats_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/sys_stats_counters.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/perf_event_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/perf_events.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/java_hprof_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/smaps_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/heapprofd_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/process_stats/process_stats_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/statsd_tracing_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/atom_ids.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/protovm/protovm_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/power/android_power_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/interceptor_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/interceptors/console_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/inode_file/inode_file_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_renderstages_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/vulkan_memory_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_counter_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/frozen_ftrace_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/system_metrics.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/etw/etw_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/v8_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/chrome_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/windowmanager_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/user_list_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_transactions_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_layers_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/display_video_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/protolog_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/pixel_modem_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/packages_list_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/network_trace_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/kernel_wakelocks_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/inputmethod_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/cpu_per_uid_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/app_wakelock_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_sdk_sysprop_guard_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_system_property_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_polled_state_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_log_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/android_log_constants.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_input_event_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_game_intervention_list_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_aflags_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/trace_attributes.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/semantic_type.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/builtin_clock.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-TraceConfig::TraceConfig() = default;
-TraceConfig::~TraceConfig() = default;
-TraceConfig::TraceConfig(const TraceConfig&) = default;
-TraceConfig& TraceConfig::operator=(const TraceConfig&) = default;
-TraceConfig::TraceConfig(TraceConfig&&) noexcept = default;
-TraceConfig& TraceConfig::operator=(TraceConfig&&) = default;
+ProtoVmConfig::ProtoVmConfig() = default;
+ProtoVmConfig::~ProtoVmConfig() = default;
+ProtoVmConfig::ProtoVmConfig(const ProtoVmConfig&) = default;
+ProtoVmConfig& ProtoVmConfig::operator=(const ProtoVmConfig&) = default;
+ProtoVmConfig::ProtoVmConfig(ProtoVmConfig&&) noexcept = default;
+ProtoVmConfig& ProtoVmConfig::operator=(ProtoVmConfig&&) = default;

-bool TraceConfig::operator==(const TraceConfig& other) const {
+bool ProtoVmConfig::operator==(const ProtoVmConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(buffers_, other.buffers_)
-   && ::protozero::internal::gen_helpers::EqualsField(data_sources_, other.data_sources_)
-   && ::protozero::internal::gen_helpers::EqualsField(builtin_data_sources_, other.builtin_data_sources_)
-   && ::protozero::internal::gen_helpers::EqualsField(duration_ms_, other.duration_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(prefer_suspend_clock_for_duration_, other.prefer_suspend_clock_for_duration_)
-   && ::protozero::internal::gen_helpers::EqualsField(enable_extra_guardrails_, other.enable_extra_guardrails_)
-   && ::protozero::internal::gen_helpers::EqualsField(lockdown_mode_, other.lockdown_mode_)
-   && ::protozero::internal::gen_helpers::EqualsField(producers_, other.producers_)
-   && ::protozero::internal::gen_helpers::EqualsField(statsd_metadata_, other.statsd_metadata_)
-   && ::protozero::internal::gen_helpers::EqualsField(write_into_file_, other.write_into_file_)
-   && ::protozero::internal::gen_helpers::EqualsField(output_path_, other.output_path_)
-   && ::protozero::internal::gen_helpers::EqualsField(file_write_period_ms_, other.file_write_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_file_size_bytes_, other.max_file_size_bytes_)
-   && ::protozero::internal::gen_helpers::EqualsField(guardrail_overrides_, other.guardrail_overrides_)
-   && ::protozero::internal::gen_helpers::EqualsField(deferred_start_, other.deferred_start_)
-   && ::protozero::internal::gen_helpers::EqualsField(flush_period_ms_, other.flush_period_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(flush_timeout_ms_, other.flush_timeout_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(data_source_stop_timeout_ms_, other.data_source_stop_timeout_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(notify_traceur_, other.notify_traceur_)
-   && ::protozero::internal::gen_helpers::EqualsField(bugreport_score_, other.bugreport_score_)
-   && ::protozero::internal::gen_helpers::EqualsField(bugreport_filename_, other.bugreport_filename_)
-   && ::protozero::internal::gen_helpers::EqualsField(trigger_config_, other.trigger_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(activate_triggers_, other.activate_triggers_)
-   && ::protozero::internal::gen_helpers::EqualsField(incremental_state_config_, other.incremental_state_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(allow_user_build_tracing_, other.allow_user_build_tracing_)
-   && ::protozero::internal::gen_helpers::EqualsField(unique_session_name_, other.unique_session_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(compression_type_, other.compression_type_)
-   && ::protozero::internal::gen_helpers::EqualsField(compression_, other.compression_)
-   && ::protozero::internal::gen_helpers::EqualsField(incident_report_config_, other.incident_report_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(statsd_logging_, other.statsd_logging_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_uuid_msb_, other.trace_uuid_msb_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_uuid_lsb_, other.trace_uuid_lsb_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_filter_, other.trace_filter_)
-   && ::protozero::internal::gen_helpers::EqualsField(android_report_config_, other.android_report_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(cmd_trace_start_delay_, other.cmd_trace_start_delay_)
-   && ::protozero::internal::gen_helpers::EqualsField(session_semaphores_, other.session_semaphores_)
-   && ::protozero::internal::gen_helpers::EqualsField(priority_boost_, other.priority_boost_)
-   && ::protozero::internal::gen_helpers::EqualsField(exclusive_prio_, other.exclusive_prio_)
-   && ::protozero::internal::gen_helpers::EqualsField(write_flush_mode_, other.write_flush_mode_)
-   && ::protozero::internal::gen_helpers::EqualsField(fflush_post_write_, other.fflush_post_write_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_all_machines_, other.trace_all_machines_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_attributes_, other.trace_attributes_);
+   && ::protozero::internal::gen_helpers::EqualsField(memory_limit_kb_, other.memory_limit_kb_);
 }

-int TraceConfig::buffers_size() const { return static_cast<int>(buffers_.size()); }
-void TraceConfig::clear_buffers() { buffers_.clear(); }
-TraceConfig_BufferConfig* TraceConfig::add_buffers() { buffers_.emplace_back(); return &buffers_.back(); }
-int TraceConfig::data_sources_size() const { return static_cast<int>(data_sources_.size()); }
-void TraceConfig::clear_data_sources() { data_sources_.clear(); }
-TraceConfig_DataSource* TraceConfig::add_data_sources() { data_sources_.emplace_back(); return &data_sources_.back(); }
-int TraceConfig::producers_size() const { return static_cast<int>(producers_.size()); }
-void TraceConfig::clear_producers() { producers_.clear(); }
-TraceConfig_ProducerConfig* TraceConfig::add_producers() { producers_.emplace_back(); return &producers_.back(); }
-int TraceConfig::session_semaphores_size() const { return static_cast<int>(session_semaphores_.size()); }
-void TraceConfig::clear_session_semaphores() { session_semaphores_.clear(); }
-TraceConfig_SessionSemaphore* TraceConfig::add_session_semaphores() { session_semaphores_.emplace_back(); return &session_semaphores_.back(); }
-bool TraceConfig::ParseFromArray(const void* raw, size_t size) {
-  buffers_.clear();
-  data_sources_.clear();
-  producers_.clear();
-  activate_triggers_.clear();
-  session_semaphores_.clear();
+bool ProtoVmConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -30216,136 +29676,8 @@ bool TraceConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* buffers */:
-        buffers_.emplace_back();
-        buffers_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 2 /* data_sources */:
-        data_sources_.emplace_back();
-        data_sources_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 20 /* builtin_data_sources */:
-        (*builtin_data_sources_).ParseFromArray(field.data(), field.size());
-        break;
-      case 3 /* duration_ms */:
-        field.get(&duration_ms_);
-        break;
-      case 36 /* prefer_suspend_clock_for_duration */:
-        field.get(&prefer_suspend_clock_for_duration_);
-        break;
-      case 4 /* enable_extra_guardrails */:
-        field.get(&enable_extra_guardrails_);
-        break;
-      case 5 /* lockdown_mode */:
-        field.get(&lockdown_mode_);
-        break;
-      case 6 /* producers */:
-        producers_.emplace_back();
-        producers_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 7 /* statsd_metadata */:
-        (*statsd_metadata_).ParseFromArray(field.data(), field.size());
-        break;
-      case 8 /* write_into_file */:
-        field.get(&write_into_file_);
-        break;
-      case 29 /* output_path */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &output_path_);
-        break;
-      case 9 /* file_write_period_ms */:
-        field.get(&file_write_period_ms_);
-        break;
-      case 10 /* max_file_size_bytes */:
-        field.get(&max_file_size_bytes_);
-        break;
-      case 11 /* guardrail_overrides */:
-        (*guardrail_overrides_).ParseFromArray(field.data(), field.size());
-        break;
-      case 12 /* deferred_start */:
-        field.get(&deferred_start_);
-        break;
-      case 13 /* flush_period_ms */:
-        field.get(&flush_period_ms_);
-        break;
-      case 14 /* flush_timeout_ms */:
-        field.get(&flush_timeout_ms_);
-        break;
-      case 23 /* data_source_stop_timeout_ms */:
-        field.get(&data_source_stop_timeout_ms_);
-        break;
-      case 16 /* notify_traceur */:
-        field.get(&notify_traceur_);
-        break;
-      case 30 /* bugreport_score */:
-        field.get(&bugreport_score_);
-        break;
-      case 38 /* bugreport_filename */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &bugreport_filename_);
-        break;
-      case 17 /* trigger_config */:
-        (*trigger_config_).ParseFromArray(field.data(), field.size());
-        break;
-      case 18 /* activate_triggers */:
-        activate_triggers_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &activate_triggers_.back());
-        break;
-      case 21 /* incremental_state_config */:
-        (*incremental_state_config_).ParseFromArray(field.data(), field.size());
-        break;
-      case 19 /* allow_user_build_tracing */:
-        field.get(&allow_user_build_tracing_);
-        break;
-      case 22 /* unique_session_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &unique_session_name_);
-        break;
-      case 24 /* compression_type */:
-        field.get(&compression_type_);
-        break;
-      case 47 /* compression */:
-        (*compression_).ParseFromArray(field.data(), field.size());
-        break;
-      case 25 /* incident_report_config */:
-        (*incident_report_config_).ParseFromArray(field.data(), field.size());
-        break;
-      case 31 /* statsd_logging */:
-        field.get(&statsd_logging_);
-        break;
-      case 27 /* trace_uuid_msb */:
-        field.get(&trace_uuid_msb_);
-        break;
-      case 28 /* trace_uuid_lsb */:
-        field.get(&trace_uuid_lsb_);
-        break;
-      case 33 /* trace_filter */:
-        (*trace_filter_).ParseFromArray(field.data(), field.size());
-        break;
-      case 34 /* android_report_config */:
-        (*android_report_config_).ParseFromArray(field.data(), field.size());
-        break;
-      case 35 /* cmd_trace_start_delay */:
-        (*cmd_trace_start_delay_).ParseFromArray(field.data(), field.size());
-        break;
-      case 39 /* session_semaphores */:
-        session_semaphores_.emplace_back();
-        session_semaphores_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 40 /* priority_boost */:
-        (*priority_boost_).ParseFromArray(field.data(), field.size());
-        break;
-      case 41 /* exclusive_prio */:
-        field.get(&exclusive_prio_);
-        break;
-      case 44 /* write_flush_mode */:
-        field.get(&write_flush_mode_);
-        break;
-      case 45 /* fflush_post_write */:
-        field.get(&fflush_post_write_);
-        break;
-      case 43 /* trace_all_machines */:
-        field.get(&trace_all_machines_);
-        break;
-      case 48 /* trace_attributes */:
-        (*trace_attributes_).ParseFromArray(field.data(), field.size());
+      case 1 /* memory_limit_kb */:
+        field.get(&memory_limit_kb_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -30355,247 +29687,68 @@ bool TraceConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig::SerializeAsString() const {
+std::string ProtoVmConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig::SerializeAsArray() const {
+std::vector<uint8_t> ProtoVmConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: buffers
-  for (auto& it : buffers_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+void ProtoVmConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: memory_limit_kb
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, memory_limit_kb_, msg);
   }

-  // Field 2: data_sources
-  for (auto& it : data_sources_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
-  }
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}

-  // Field 20: builtin_data_sources
-  if (_has_field_[20]) {
-    (*builtin_data_sources_).Serialize(msg->BeginNestedMessage<::protozero::Message>(20));
-  }
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/qnx/qnx_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/qnx/qnx_config.gen.h"

-  // Field 3: duration_ms
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, duration_ms_, msg);
-  }
+namespace perfetto {
+namespace protos {
+namespace gen {

-  // Field 36: prefer_suspend_clock_for_duration
-  if (_has_field_[36]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(36, prefer_suspend_clock_for_duration_, msg);
-  }
+QnxConfig::QnxConfig() = default;
+QnxConfig::~QnxConfig() = default;
+QnxConfig::QnxConfig(const QnxConfig&) = default;
+QnxConfig& QnxConfig::operator=(const QnxConfig&) = default;
+QnxConfig::QnxConfig(QnxConfig&&) noexcept = default;
+QnxConfig& QnxConfig::operator=(QnxConfig&&) = default;

-  // Field 4: enable_extra_guardrails
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, enable_extra_guardrails_, msg);
-  }
+bool QnxConfig::operator==(const QnxConfig& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(qnx_kernel_buffers_, other.qnx_kernel_buffers_)
+   && ::protozero::internal::gen_helpers::EqualsField(qnx_kernel_kbuffers_, other.qnx_kernel_kbuffers_)
+   && ::protozero::internal::gen_helpers::EqualsField(qnx_kernel_wide_events_, other.qnx_kernel_wide_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(qnx_cache_pages_, other.qnx_cache_pages_)
+   && ::protozero::internal::gen_helpers::EqualsField(qnx_cache_max_pages_, other.qnx_cache_max_pages_)
+   && ::protozero::internal::gen_helpers::EqualsField(qnx_trace_buffer_init_bytes_, other.qnx_trace_buffer_init_bytes_);
+}

-  // Field 5: lockdown_mode
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, lockdown_mode_, msg);
-  }
-
-  // Field 6: producers
-  for (auto& it : producers_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
-  }
-
-  // Field 7: statsd_metadata
-  if (_has_field_[7]) {
-    (*statsd_metadata_).Serialize(msg->BeginNestedMessage<::protozero::Message>(7));
-  }
-
-  // Field 8: write_into_file
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, write_into_file_, msg);
-  }
-
-  // Field 29: output_path
-  if (_has_field_[29]) {
-    ::protozero::internal::gen_helpers::SerializeString(29, output_path_, msg);
-  }
-
-  // Field 9: file_write_period_ms
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(9, file_write_period_ms_, msg);
-  }
-
-  // Field 10: max_file_size_bytes
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(10, max_file_size_bytes_, msg);
-  }
-
-  // Field 11: guardrail_overrides
-  if (_has_field_[11]) {
-    (*guardrail_overrides_).Serialize(msg->BeginNestedMessage<::protozero::Message>(11));
-  }
-
-  // Field 12: deferred_start
-  if (_has_field_[12]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(12, deferred_start_, msg);
-  }
-
-  // Field 13: flush_period_ms
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(13, flush_period_ms_, msg);
-  }
-
-  // Field 14: flush_timeout_ms
-  if (_has_field_[14]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(14, flush_timeout_ms_, msg);
-  }
-
-  // Field 23: data_source_stop_timeout_ms
-  if (_has_field_[23]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(23, data_source_stop_timeout_ms_, msg);
-  }
-
-  // Field 16: notify_traceur
-  if (_has_field_[16]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(16, notify_traceur_, msg);
-  }
-
-  // Field 30: bugreport_score
-  if (_has_field_[30]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(30, bugreport_score_, msg);
-  }
-
-  // Field 38: bugreport_filename
-  if (_has_field_[38]) {
-    ::protozero::internal::gen_helpers::SerializeString(38, bugreport_filename_, msg);
-  }
-
-  // Field 17: trigger_config
-  if (_has_field_[17]) {
-    (*trigger_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(17));
-  }
-
-  // Field 18: activate_triggers
-  for (auto& it : activate_triggers_) {
-    ::protozero::internal::gen_helpers::SerializeString(18, it, msg);
-  }
-
-  // Field 21: incremental_state_config
-  if (_has_field_[21]) {
-    (*incremental_state_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(21));
-  }
-
-  // Field 19: allow_user_build_tracing
-  if (_has_field_[19]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(19, allow_user_build_tracing_, msg);
-  }
-
-  // Field 22: unique_session_name
-  if (_has_field_[22]) {
-    ::protozero::internal::gen_helpers::SerializeString(22, unique_session_name_, msg);
-  }
-
-  // Field 24: compression_type
-  if (_has_field_[24]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(24, compression_type_, msg);
-  }
-
-  // Field 47: compression
-  if (_has_field_[47]) {
-    (*compression_).Serialize(msg->BeginNestedMessage<::protozero::Message>(47));
-  }
-
-  // Field 25: incident_report_config
-  if (_has_field_[25]) {
-    (*incident_report_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(25));
-  }
-
-  // Field 31: statsd_logging
-  if (_has_field_[31]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(31, statsd_logging_, msg);
-  }
-
-  // Field 27: trace_uuid_msb
-  if (_has_field_[27]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(27, trace_uuid_msb_, msg);
-  }
-
-  // Field 28: trace_uuid_lsb
-  if (_has_field_[28]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(28, trace_uuid_lsb_, msg);
-  }
-
-  // Field 33: trace_filter
-  if (_has_field_[33]) {
-    (*trace_filter_).Serialize(msg->BeginNestedMessage<::protozero::Message>(33));
-  }
-
-  // Field 34: android_report_config
-  if (_has_field_[34]) {
-    (*android_report_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(34));
-  }
-
-  // Field 35: cmd_trace_start_delay
-  if (_has_field_[35]) {
-    (*cmd_trace_start_delay_).Serialize(msg->BeginNestedMessage<::protozero::Message>(35));
-  }
-
-  // Field 39: session_semaphores
-  for (auto& it : session_semaphores_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(39));
-  }
-
-  // Field 40: priority_boost
-  if (_has_field_[40]) {
-    (*priority_boost_).Serialize(msg->BeginNestedMessage<::protozero::Message>(40));
-  }
-
-  // Field 41: exclusive_prio
-  if (_has_field_[41]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(41, exclusive_prio_, msg);
-  }
-
-  // Field 44: write_flush_mode
-  if (_has_field_[44]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(44, write_flush_mode_, msg);
-  }
-
-  // Field 45: fflush_post_write
-  if (_has_field_[45]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(45, fflush_post_write_, msg);
-  }
-
-  // Field 43: trace_all_machines
-  if (_has_field_[43]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(43, trace_all_machines_, msg);
-  }
-
-  // Field 48: trace_attributes
-  if (_has_field_[48]) {
-    (*trace_attributes_).Serialize(msg->BeginNestedMessage<::protozero::Message>(48));
-  }
-
-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
-
-
-TraceConfig_SessionSemaphore::TraceConfig_SessionSemaphore() = default;
-TraceConfig_SessionSemaphore::~TraceConfig_SessionSemaphore() = default;
-TraceConfig_SessionSemaphore::TraceConfig_SessionSemaphore(const TraceConfig_SessionSemaphore&) = default;
-TraceConfig_SessionSemaphore& TraceConfig_SessionSemaphore::operator=(const TraceConfig_SessionSemaphore&) = default;
-TraceConfig_SessionSemaphore::TraceConfig_SessionSemaphore(TraceConfig_SessionSemaphore&&) noexcept = default;
-TraceConfig_SessionSemaphore& TraceConfig_SessionSemaphore::operator=(TraceConfig_SessionSemaphore&&) = default;
-
-bool TraceConfig_SessionSemaphore::operator==(const TraceConfig_SessionSemaphore& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_other_session_count_, other.max_other_session_count_);
-}
-
-bool TraceConfig_SessionSemaphore::ParseFromArray(const void* raw, size_t size) {
+bool QnxConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -30605,11 +29758,23 @@ bool TraceConfig_SessionSemaphore::ParseFromArray(const void* raw, size_t size)
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+      case 1 /* qnx_kernel_buffers */:
+        field.get(&qnx_kernel_buffers_);
         break;
-      case 2 /* max_other_session_count */:
-        field.get(&max_other_session_count_);
+      case 2 /* qnx_kernel_kbuffers */:
+        field.get(&qnx_kernel_kbuffers_);
+        break;
+      case 3 /* qnx_kernel_wide_events */:
+        field.get(&qnx_kernel_wide_events_);
+        break;
+      case 4 /* qnx_cache_pages */:
+        field.get(&qnx_cache_pages_);
+        break;
+      case 5 /* qnx_cache_max_pages */:
+        field.get(&qnx_cache_max_pages_);
+        break;
+      case 6 /* qnx_trace_buffer_init_bytes */:
+        field.get(&qnx_trace_buffer_init_bytes_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -30619,47 +29784,117 @@ bool TraceConfig_SessionSemaphore::ParseFromArray(const void* raw, size_t size)
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_SessionSemaphore::SerializeAsString() const {
+std::string QnxConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_SessionSemaphore::SerializeAsArray() const {
+std::vector<uint8_t> QnxConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_SessionSemaphore::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
+void QnxConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: qnx_kernel_buffers
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, qnx_kernel_buffers_, msg);
   }

-  // Field 2: max_other_session_count
+  // Field 2: qnx_kernel_kbuffers
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, max_other_session_count_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, qnx_kernel_kbuffers_, msg);
+  }
+
+  // Field 3: qnx_kernel_wide_events
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, qnx_kernel_wide_events_, msg);
+  }
+
+  // Field 4: qnx_cache_pages
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, qnx_cache_pages_, msg);
+  }
+
+  // Field 5: qnx_cache_max_pages
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, qnx_cache_max_pages_, msg);
+  }
+
+  // Field 6: qnx_trace_buffer_init_bytes
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, qnx_trace_buffer_init_bytes_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/statsd/atom_ids.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/atom_ids.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/statsd/statsd_tracing_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/statsd_tracing_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/atom_ids.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-TraceConfig_CmdTraceStartDelay::TraceConfig_CmdTraceStartDelay() = default;
-TraceConfig_CmdTraceStartDelay::~TraceConfig_CmdTraceStartDelay() = default;
-TraceConfig_CmdTraceStartDelay::TraceConfig_CmdTraceStartDelay(const TraceConfig_CmdTraceStartDelay&) = default;
-TraceConfig_CmdTraceStartDelay& TraceConfig_CmdTraceStartDelay::operator=(const TraceConfig_CmdTraceStartDelay&) = default;
-TraceConfig_CmdTraceStartDelay::TraceConfig_CmdTraceStartDelay(TraceConfig_CmdTraceStartDelay&&) noexcept = default;
-TraceConfig_CmdTraceStartDelay& TraceConfig_CmdTraceStartDelay::operator=(TraceConfig_CmdTraceStartDelay&&) = default;
+StatsdPullAtomConfig::StatsdPullAtomConfig() = default;
+StatsdPullAtomConfig::~StatsdPullAtomConfig() = default;
+StatsdPullAtomConfig::StatsdPullAtomConfig(const StatsdPullAtomConfig&) = default;
+StatsdPullAtomConfig& StatsdPullAtomConfig::operator=(const StatsdPullAtomConfig&) = default;
+StatsdPullAtomConfig::StatsdPullAtomConfig(StatsdPullAtomConfig&&) noexcept = default;
+StatsdPullAtomConfig& StatsdPullAtomConfig::operator=(StatsdPullAtomConfig&&) = default;

-bool TraceConfig_CmdTraceStartDelay::operator==(const TraceConfig_CmdTraceStartDelay& other) const {
+bool StatsdPullAtomConfig::operator==(const StatsdPullAtomConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(min_delay_ms_, other.min_delay_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_delay_ms_, other.max_delay_ms_);
+   && ::protozero::internal::gen_helpers::EqualsField(pull_atom_id_, other.pull_atom_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(raw_pull_atom_id_, other.raw_pull_atom_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(pull_frequency_ms_, other.pull_frequency_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(packages_, other.packages_);
 }

-bool TraceConfig_CmdTraceStartDelay::ParseFromArray(const void* raw, size_t size) {
+bool StatsdPullAtomConfig::ParseFromArray(const void* raw, size_t size) {
+  pull_atom_id_.clear();
+  raw_pull_atom_id_.clear();
+  packages_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -30669,11 +29904,20 @@ bool TraceConfig_CmdTraceStartDelay::ParseFromArray(const void* raw, size_t size
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* min_delay_ms */:
-        field.get(&min_delay_ms_);
+      case 1 /* pull_atom_id */:
+        pull_atom_id_.emplace_back();
+        field.get(&pull_atom_id_.back());
         break;
-      case 2 /* max_delay_ms */:
-        field.get(&max_delay_ms_);
+      case 2 /* raw_pull_atom_id */:
+        raw_pull_atom_id_.emplace_back();
+        field.get(&raw_pull_atom_id_.back());
+        break;
+      case 3 /* pull_frequency_ms */:
+        field.get(&pull_frequency_ms_);
+        break;
+      case 4 /* packages */:
+        packages_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &packages_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -30683,49 +29927,64 @@ bool TraceConfig_CmdTraceStartDelay::ParseFromArray(const void* raw, size_t size
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_CmdTraceStartDelay::SerializeAsString() const {
+std::string StatsdPullAtomConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_CmdTraceStartDelay::SerializeAsArray() const {
+std::vector<uint8_t> StatsdPullAtomConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_CmdTraceStartDelay::Serialize(::protozero::Message* msg) const {
-  // Field 1: min_delay_ms
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, min_delay_ms_, msg);
+void StatsdPullAtomConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: pull_atom_id
+  for (auto& it : pull_atom_id_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
   }

-  // Field 2: max_delay_ms
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, max_delay_ms_, msg);
+  // Field 2: raw_pull_atom_id
+  for (auto& it : raw_pull_atom_id_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
+  }
+
+  // Field 3: pull_frequency_ms
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, pull_frequency_ms_, msg);
+  }
+
+  // Field 4: packages
+  for (auto& it : packages_) {
+    ::protozero::internal::gen_helpers::SerializeString(4, it, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TraceConfig_AndroidReportConfig::TraceConfig_AndroidReportConfig() = default;
-TraceConfig_AndroidReportConfig::~TraceConfig_AndroidReportConfig() = default;
-TraceConfig_AndroidReportConfig::TraceConfig_AndroidReportConfig(const TraceConfig_AndroidReportConfig&) = default;
-TraceConfig_AndroidReportConfig& TraceConfig_AndroidReportConfig::operator=(const TraceConfig_AndroidReportConfig&) = default;
-TraceConfig_AndroidReportConfig::TraceConfig_AndroidReportConfig(TraceConfig_AndroidReportConfig&&) noexcept = default;
-TraceConfig_AndroidReportConfig& TraceConfig_AndroidReportConfig::operator=(TraceConfig_AndroidReportConfig&&) = default;
+StatsdTracingConfig::StatsdTracingConfig() = default;
+StatsdTracingConfig::~StatsdTracingConfig() = default;
+StatsdTracingConfig::StatsdTracingConfig(const StatsdTracingConfig&) = default;
+StatsdTracingConfig& StatsdTracingConfig::operator=(const StatsdTracingConfig&) = default;
+StatsdTracingConfig::StatsdTracingConfig(StatsdTracingConfig&&) noexcept = default;
+StatsdTracingConfig& StatsdTracingConfig::operator=(StatsdTracingConfig&&) = default;

-bool TraceConfig_AndroidReportConfig::operator==(const TraceConfig_AndroidReportConfig& other) const {
+bool StatsdTracingConfig::operator==(const StatsdTracingConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(reporter_service_package_, other.reporter_service_package_)
-   && ::protozero::internal::gen_helpers::EqualsField(reporter_service_class_, other.reporter_service_class_)
-   && ::protozero::internal::gen_helpers::EqualsField(skip_report_, other.skip_report_)
-   && ::protozero::internal::gen_helpers::EqualsField(use_pipe_in_framework_for_testing_, other.use_pipe_in_framework_for_testing_);
+   && ::protozero::internal::gen_helpers::EqualsField(push_atom_id_, other.push_atom_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(raw_push_atom_id_, other.raw_push_atom_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(pull_config_, other.pull_config_);
 }

-bool TraceConfig_AndroidReportConfig::ParseFromArray(const void* raw, size_t size) {
+int StatsdTracingConfig::pull_config_size() const { return static_cast<int>(pull_config_.size()); }
+void StatsdTracingConfig::clear_pull_config() { pull_config_.clear(); }
+StatsdPullAtomConfig* StatsdTracingConfig::add_pull_config() { pull_config_.emplace_back(); return &pull_config_.back(); }
+bool StatsdTracingConfig::ParseFromArray(const void* raw, size_t size) {
+  push_atom_id_.clear();
+  raw_push_atom_id_.clear();
+  pull_config_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -30735,17 +29994,17 @@ bool TraceConfig_AndroidReportConfig::ParseFromArray(const void* raw, size_t siz
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* reporter_service_package */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &reporter_service_package_);
-        break;
-      case 2 /* reporter_service_class */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &reporter_service_class_);
+      case 1 /* push_atom_id */:
+        push_atom_id_.emplace_back();
+        field.get(&push_atom_id_.back());
         break;
-      case 3 /* skip_report */:
-        field.get(&skip_report_);
+      case 2 /* raw_push_atom_id */:
+        raw_push_atom_id_.emplace_back();
+        field.get(&raw_push_atom_id_.back());
         break;
-      case 4 /* use_pipe_in_framework_for_testing */:
-        field.get(&use_pipe_in_framework_for_testing_);
+      case 3 /* pull_config */:
+        pull_config_.emplace_back();
+        pull_config_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -30755,60 +30014,91 @@ bool TraceConfig_AndroidReportConfig::ParseFromArray(const void* raw, size_t siz
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_AndroidReportConfig::SerializeAsString() const {
+std::string StatsdTracingConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_AndroidReportConfig::SerializeAsArray() const {
+std::vector<uint8_t> StatsdTracingConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_AndroidReportConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: reporter_service_package
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, reporter_service_package_, msg);
-  }
-
-  // Field 2: reporter_service_class
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, reporter_service_class_, msg);
+void StatsdTracingConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: push_atom_id
+  for (auto& it : push_atom_id_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
   }

-  // Field 3: skip_report
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, skip_report_, msg);
+  // Field 2: raw_push_atom_id
+  for (auto& it : raw_push_atom_id_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
   }

-  // Field 4: use_pipe_in_framework_for_testing
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, use_pipe_in_framework_for_testing_, msg);
+  // Field 3: pull_config
+  for (auto& it : pull_config_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/sys_stats/sys_stats_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/sys_stats/sys_stats_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/sys_stats_counters.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-TraceConfig_TraceFilter::TraceConfig_TraceFilter() = default;
-TraceConfig_TraceFilter::~TraceConfig_TraceFilter() = default;
-TraceConfig_TraceFilter::TraceConfig_TraceFilter(const TraceConfig_TraceFilter&) = default;
-TraceConfig_TraceFilter& TraceConfig_TraceFilter::operator=(const TraceConfig_TraceFilter&) = default;
-TraceConfig_TraceFilter::TraceConfig_TraceFilter(TraceConfig_TraceFilter&&) noexcept = default;
-TraceConfig_TraceFilter& TraceConfig_TraceFilter::operator=(TraceConfig_TraceFilter&&) = default;
+SysStatsConfig::SysStatsConfig() = default;
+SysStatsConfig::~SysStatsConfig() = default;
+SysStatsConfig::SysStatsConfig(const SysStatsConfig&) = default;
+SysStatsConfig& SysStatsConfig::operator=(const SysStatsConfig&) = default;
+SysStatsConfig::SysStatsConfig(SysStatsConfig&&) noexcept = default;
+SysStatsConfig& SysStatsConfig::operator=(SysStatsConfig&&) = default;

-bool TraceConfig_TraceFilter::operator==(const TraceConfig_TraceFilter& other) const {
+bool SysStatsConfig::operator==(const SysStatsConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(bytecode_, other.bytecode_)
-   && ::protozero::internal::gen_helpers::EqualsField(bytecode_v2_, other.bytecode_v2_)
-   && ::protozero::internal::gen_helpers::EqualsField(string_filter_chain_, other.string_filter_chain_)
-   && ::protozero::internal::gen_helpers::EqualsField(bytecode_overlay_v54_, other.bytecode_overlay_v54_)
-   && ::protozero::internal::gen_helpers::EqualsField(string_filter_chain_v54_, other.string_filter_chain_v54_);
+   && ::protozero::internal::gen_helpers::EqualsField(meminfo_period_ms_, other.meminfo_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(meminfo_counters_, other.meminfo_counters_)
+   && ::protozero::internal::gen_helpers::EqualsField(vmstat_period_ms_, other.vmstat_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(vmstat_counters_, other.vmstat_counters_)
+   && ::protozero::internal::gen_helpers::EqualsField(stat_period_ms_, other.stat_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(stat_counters_, other.stat_counters_)
+   && ::protozero::internal::gen_helpers::EqualsField(devfreq_period_ms_, other.devfreq_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(cpufreq_period_ms_, other.cpufreq_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(buddyinfo_period_ms_, other.buddyinfo_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(diskstat_period_ms_, other.diskstat_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(psi_period_ms_, other.psi_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(thermal_period_ms_, other.thermal_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(cpuidle_period_ms_, other.cpuidle_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(gpufreq_period_ms_, other.gpufreq_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(slab_period_ms_, other.slab_period_ms_);
 }

-bool TraceConfig_TraceFilter::ParseFromArray(const void* raw, size_t size) {
+bool SysStatsConfig::ParseFromArray(const void* raw, size_t size) {
+  meminfo_counters_.clear();
+  vmstat_counters_.clear();
+  stat_counters_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -30818,20 +30108,53 @@ bool TraceConfig_TraceFilter::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* bytecode */:
-        field.get(&bytecode_);
+      case 1 /* meminfo_period_ms */:
+        field.get(&meminfo_period_ms_);
         break;
-      case 2 /* bytecode_v2 */:
-        field.get(&bytecode_v2_);
+      case 2 /* meminfo_counters */:
+        meminfo_counters_.emplace_back();
+        field.get(&meminfo_counters_.back());
         break;
-      case 3 /* string_filter_chain */:
-        (*string_filter_chain_).ParseFromArray(field.data(), field.size());
+      case 3 /* vmstat_period_ms */:
+        field.get(&vmstat_period_ms_);
         break;
-      case 4 /* bytecode_overlay_v54 */:
-        field.get(&bytecode_overlay_v54_);
+      case 4 /* vmstat_counters */:
+        vmstat_counters_.emplace_back();
+        field.get(&vmstat_counters_.back());
         break;
-      case 5 /* string_filter_chain_v54 */:
-        (*string_filter_chain_v54_).ParseFromArray(field.data(), field.size());
+      case 5 /* stat_period_ms */:
+        field.get(&stat_period_ms_);
+        break;
+      case 6 /* stat_counters */:
+        stat_counters_.emplace_back();
+        field.get(&stat_counters_.back());
+        break;
+      case 7 /* devfreq_period_ms */:
+        field.get(&devfreq_period_ms_);
+        break;
+      case 8 /* cpufreq_period_ms */:
+        field.get(&cpufreq_period_ms_);
+        break;
+      case 9 /* buddyinfo_period_ms */:
+        field.get(&buddyinfo_period_ms_);
+        break;
+      case 10 /* diskstat_period_ms */:
+        field.get(&diskstat_period_ms_);
+        break;
+      case 11 /* psi_period_ms */:
+        field.get(&psi_period_ms_);
+        break;
+      case 12 /* thermal_period_ms */:
+        field.get(&thermal_period_ms_);
+        break;
+      case 13 /* cpuidle_period_ms */:
+        field.get(&cpuidle_period_ms_);
+        break;
+      case 14 /* gpufreq_period_ms */:
+        field.get(&gpufreq_period_ms_);
+        break;
+      case 15 /* slab_period_ms */:
+        field.get(&slab_period_ms_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -30841,65 +30164,133 @@ bool TraceConfig_TraceFilter::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_TraceFilter::SerializeAsString() const {
+std::string SysStatsConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_TraceFilter::SerializeAsArray() const {
+std::vector<uint8_t> SysStatsConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_TraceFilter::Serialize(::protozero::Message* msg) const {
-  // Field 1: bytecode
+void SysStatsConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: meminfo_period_ms
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, bytecode_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, meminfo_period_ms_, msg);
   }

-  // Field 2: bytecode_v2
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, bytecode_v2_, msg);
+  // Field 2: meminfo_counters
+  for (auto& it : meminfo_counters_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, it, msg);
   }

-  // Field 3: string_filter_chain
+  // Field 3: vmstat_period_ms
   if (_has_field_[3]) {
-    (*string_filter_chain_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, vmstat_period_ms_, msg);
   }

-  // Field 4: bytecode_overlay_v54
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeString(4, bytecode_overlay_v54_, msg);
+  // Field 4: vmstat_counters
+  for (auto& it : vmstat_counters_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, it, msg);
   }

-  // Field 5: string_filter_chain_v54
+  // Field 5: stat_period_ms
   if (_has_field_[5]) {
-    (*string_filter_chain_v54_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, stat_period_ms_, msg);
+  }
+
+  // Field 6: stat_counters
+  for (auto& it : stat_counters_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, it, msg);
+  }
+
+  // Field 7: devfreq_period_ms
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, devfreq_period_ms_, msg);
+  }
+
+  // Field 8: cpufreq_period_ms
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, cpufreq_period_ms_, msg);
+  }
+
+  // Field 9: buddyinfo_period_ms
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(9, buddyinfo_period_ms_, msg);
+  }
+
+  // Field 10: diskstat_period_ms
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(10, diskstat_period_ms_, msg);
+  }
+
+  // Field 11: psi_period_ms
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(11, psi_period_ms_, msg);
+  }
+
+  // Field 12: thermal_period_ms
+  if (_has_field_[12]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(12, thermal_period_ms_, msg);
+  }
+
+  // Field 13: cpuidle_period_ms
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(13, cpuidle_period_ms_, msg);
+  }
+
+  // Field 14: gpufreq_period_ms
+  if (_has_field_[14]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(14, gpufreq_period_ms_, msg);
+  }
+
+  // Field 15: slab_period_ms
+  if (_has_field_[15]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(15, slab_period_ms_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/system_info/system_info_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/system_info/system_info_config.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-TraceConfig_TraceFilter_StringFilterChain::TraceConfig_TraceFilter_StringFilterChain() = default;
-TraceConfig_TraceFilter_StringFilterChain::~TraceConfig_TraceFilter_StringFilterChain() = default;
-TraceConfig_TraceFilter_StringFilterChain::TraceConfig_TraceFilter_StringFilterChain(const TraceConfig_TraceFilter_StringFilterChain&) = default;
-TraceConfig_TraceFilter_StringFilterChain& TraceConfig_TraceFilter_StringFilterChain::operator=(const TraceConfig_TraceFilter_StringFilterChain&) = default;
-TraceConfig_TraceFilter_StringFilterChain::TraceConfig_TraceFilter_StringFilterChain(TraceConfig_TraceFilter_StringFilterChain&&) noexcept = default;
-TraceConfig_TraceFilter_StringFilterChain& TraceConfig_TraceFilter_StringFilterChain::operator=(TraceConfig_TraceFilter_StringFilterChain&&) = default;
+SystemInfoConfig::SystemInfoConfig() = default;
+SystemInfoConfig::~SystemInfoConfig() = default;
+SystemInfoConfig::SystemInfoConfig(const SystemInfoConfig&) = default;
+SystemInfoConfig& SystemInfoConfig::operator=(const SystemInfoConfig&) = default;
+SystemInfoConfig::SystemInfoConfig(SystemInfoConfig&&) noexcept = default;
+SystemInfoConfig& SystemInfoConfig::operator=(SystemInfoConfig&&) = default;

-bool TraceConfig_TraceFilter_StringFilterChain::operator==(const TraceConfig_TraceFilter_StringFilterChain& other) const {
+bool SystemInfoConfig::operator==(const SystemInfoConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(rules_, other.rules_);
+   && ::protozero::internal::gen_helpers::EqualsField(irq_names_, other.irq_names_);
 }

-int TraceConfig_TraceFilter_StringFilterChain::rules_size() const { return static_cast<int>(rules_.size()); }
-void TraceConfig_TraceFilter_StringFilterChain::clear_rules() { rules_.clear(); }
-TraceConfig_TraceFilter_StringFilterRule* TraceConfig_TraceFilter_StringFilterChain::add_rules() { rules_.emplace_back(); return &rules_.back(); }
-bool TraceConfig_TraceFilter_StringFilterChain::ParseFromArray(const void* raw, size_t size) {
-  rules_.clear();
+bool SystemInfoConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -30909,9 +30300,8 @@ bool TraceConfig_TraceFilter_StringFilterChain::ParseFromArray(const void* raw,
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* rules */:
-        rules_.emplace_back();
-        rules_.back().ParseFromArray(field.data(), field.size());
+      case 1 /* irq_names */:
+        field.get(&irq_names_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -30921,46 +30311,76 @@ bool TraceConfig_TraceFilter_StringFilterChain::ParseFromArray(const void* raw,
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_TraceFilter_StringFilterChain::SerializeAsString() const {
+std::string SystemInfoConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_TraceFilter_StringFilterChain::SerializeAsArray() const {
+std::vector<uint8_t> SystemInfoConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_TraceFilter_StringFilterChain::Serialize(::protozero::Message* msg) const {
-  // Field 1: rules
-  for (auto& it : rules_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+void SystemInfoConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: irq_names
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, irq_names_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/track_event/track_event_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/track_event/track_event_config.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-TraceConfig_TraceFilter_StringFilterRule::TraceConfig_TraceFilter_StringFilterRule() = default;
-TraceConfig_TraceFilter_StringFilterRule::~TraceConfig_TraceFilter_StringFilterRule() = default;
-TraceConfig_TraceFilter_StringFilterRule::TraceConfig_TraceFilter_StringFilterRule(const TraceConfig_TraceFilter_StringFilterRule&) = default;
-TraceConfig_TraceFilter_StringFilterRule& TraceConfig_TraceFilter_StringFilterRule::operator=(const TraceConfig_TraceFilter_StringFilterRule&) = default;
-TraceConfig_TraceFilter_StringFilterRule::TraceConfig_TraceFilter_StringFilterRule(TraceConfig_TraceFilter_StringFilterRule&&) noexcept = default;
-TraceConfig_TraceFilter_StringFilterRule& TraceConfig_TraceFilter_StringFilterRule::operator=(TraceConfig_TraceFilter_StringFilterRule&&) = default;
+TrackEventConfig::TrackEventConfig() = default;
+TrackEventConfig::~TrackEventConfig() = default;
+TrackEventConfig::TrackEventConfig(const TrackEventConfig&) = default;
+TrackEventConfig& TrackEventConfig::operator=(const TrackEventConfig&) = default;
+TrackEventConfig::TrackEventConfig(TrackEventConfig&&) noexcept = default;
+TrackEventConfig& TrackEventConfig::operator=(TrackEventConfig&&) = default;

-bool TraceConfig_TraceFilter_StringFilterRule::operator==(const TraceConfig_TraceFilter_StringFilterRule& other) const {
+bool TrackEventConfig::operator==(const TrackEventConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(policy_, other.policy_)
-   && ::protozero::internal::gen_helpers::EqualsField(regex_pattern_, other.regex_pattern_)
-   && ::protozero::internal::gen_helpers::EqualsField(atrace_payload_starts_with_, other.atrace_payload_starts_with_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(semantic_type_, other.semantic_type_);
+   && ::protozero::internal::gen_helpers::EqualsField(disabled_categories_, other.disabled_categories_)
+   && ::protozero::internal::gen_helpers::EqualsField(enabled_categories_, other.enabled_categories_)
+   && ::protozero::internal::gen_helpers::EqualsField(disabled_tags_, other.disabled_tags_)
+   && ::protozero::internal::gen_helpers::EqualsField(enabled_tags_, other.enabled_tags_)
+   && ::protozero::internal::gen_helpers::EqualsField(disable_incremental_timestamps_, other.disable_incremental_timestamps_)
+   && ::protozero::internal::gen_helpers::EqualsField(timestamp_unit_multiplier_, other.timestamp_unit_multiplier_)
+   && ::protozero::internal::gen_helpers::EqualsField(filter_debug_annotations_, other.filter_debug_annotations_)
+   && ::protozero::internal::gen_helpers::EqualsField(enable_thread_time_sampling_, other.enable_thread_time_sampling_)
+   && ::protozero::internal::gen_helpers::EqualsField(thread_time_subsampling_ns_, other.thread_time_subsampling_ns_)
+   && ::protozero::internal::gen_helpers::EqualsField(filter_dynamic_event_names_, other.filter_dynamic_event_names_);
 }

-bool TraceConfig_TraceFilter_StringFilterRule::ParseFromArray(const void* raw, size_t size) {
-  semantic_type_.clear();
+bool TrackEventConfig::ParseFromArray(const void* raw, size_t size) {
+  disabled_categories_.clear();
+  enabled_categories_.clear();
+  disabled_tags_.clear();
+  enabled_tags_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -30970,21 +30390,39 @@ bool TraceConfig_TraceFilter_StringFilterRule::ParseFromArray(const void* raw, s
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* policy */:
-        field.get(&policy_);
+      case 1 /* disabled_categories */:
+        disabled_categories_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &disabled_categories_.back());
         break;
-      case 2 /* regex_pattern */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &regex_pattern_);
+      case 2 /* enabled_categories */:
+        enabled_categories_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &enabled_categories_.back());
         break;
-      case 3 /* atrace_payload_starts_with */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &atrace_payload_starts_with_);
+      case 3 /* disabled_tags */:
+        disabled_tags_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &disabled_tags_.back());
         break;
-      case 4 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+      case 4 /* enabled_tags */:
+        enabled_tags_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &enabled_tags_.back());
         break;
-      case 5 /* semantic_type */:
-        semantic_type_.emplace_back();
-        field.get(&semantic_type_.back());
+      case 5 /* disable_incremental_timestamps */:
+        field.get(&disable_incremental_timestamps_);
+        break;
+      case 6 /* timestamp_unit_multiplier */:
+        field.get(&timestamp_unit_multiplier_);
+        break;
+      case 7 /* filter_debug_annotations */:
+        field.get(&filter_debug_annotations_);
+        break;
+      case 8 /* enable_thread_time_sampling */:
+        field.get(&enable_thread_time_sampling_);
+        break;
+      case 10 /* thread_time_subsampling_ns */:
+        field.get(&thread_time_subsampling_ns_);
+        break;
+      case 9 /* filter_dynamic_event_names */:
+        field.get(&filter_dynamic_event_names_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -30994,65 +30432,113 @@ bool TraceConfig_TraceFilter_StringFilterRule::ParseFromArray(const void* raw, s
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_TraceFilter_StringFilterRule::SerializeAsString() const {
+std::string TrackEventConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_TraceFilter_StringFilterRule::SerializeAsArray() const {
+std::vector<uint8_t> TrackEventConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_TraceFilter_StringFilterRule::Serialize(::protozero::Message* msg) const {
-  // Field 1: policy
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, policy_, msg);
+void TrackEventConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: disabled_categories
+  for (auto& it : disabled_categories_) {
+    ::protozero::internal::gen_helpers::SerializeString(1, it, msg);
   }

-  // Field 2: regex_pattern
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, regex_pattern_, msg);
+  // Field 2: enabled_categories
+  for (auto& it : enabled_categories_) {
+    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
   }

-  // Field 3: atrace_payload_starts_with
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeString(3, atrace_payload_starts_with_, msg);
+  // Field 3: disabled_tags
+  for (auto& it : disabled_tags_) {
+    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
   }

-  // Field 4: name
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeString(4, name_, msg);
+  // Field 4: enabled_tags
+  for (auto& it : enabled_tags_) {
+    ::protozero::internal::gen_helpers::SerializeString(4, it, msg);
   }

-  // Field 5: semantic_type
-  for (auto& it : semantic_type_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, it, msg);
+  // Field 5: disable_incremental_timestamps
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, disable_incremental_timestamps_, msg);
+  }
+
+  // Field 6: timestamp_unit_multiplier
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, timestamp_unit_multiplier_, msg);
+  }
+
+  // Field 7: filter_debug_annotations
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, filter_debug_annotations_, msg);
+  }
+
+  // Field 8: enable_thread_time_sampling
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, enable_thread_time_sampling_, msg);
+  }
+
+  // Field 10: thread_time_subsampling_ns
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(10, thread_time_subsampling_ns_, msg);
+  }
+
+  // Field 9: filter_dynamic_event_names
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, filter_dynamic_event_names_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/chrome/chrome_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/chrome_config.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-TraceConfig_IncidentReportConfig::TraceConfig_IncidentReportConfig() = default;
-TraceConfig_IncidentReportConfig::~TraceConfig_IncidentReportConfig() = default;
-TraceConfig_IncidentReportConfig::TraceConfig_IncidentReportConfig(const TraceConfig_IncidentReportConfig&) = default;
-TraceConfig_IncidentReportConfig& TraceConfig_IncidentReportConfig::operator=(const TraceConfig_IncidentReportConfig&) = default;
-TraceConfig_IncidentReportConfig::TraceConfig_IncidentReportConfig(TraceConfig_IncidentReportConfig&&) noexcept = default;
-TraceConfig_IncidentReportConfig& TraceConfig_IncidentReportConfig::operator=(TraceConfig_IncidentReportConfig&&) = default;
+ChromeConfig::ChromeConfig() = default;
+ChromeConfig::~ChromeConfig() = default;
+ChromeConfig::ChromeConfig(const ChromeConfig&) = default;
+ChromeConfig& ChromeConfig::operator=(const ChromeConfig&) = default;
+ChromeConfig::ChromeConfig(ChromeConfig&&) noexcept = default;
+ChromeConfig& ChromeConfig::operator=(ChromeConfig&&) = default;

-bool TraceConfig_IncidentReportConfig::operator==(const TraceConfig_IncidentReportConfig& other) const {
+bool ChromeConfig::operator==(const ChromeConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(destination_package_, other.destination_package_)
-   && ::protozero::internal::gen_helpers::EqualsField(destination_class_, other.destination_class_)
-   && ::protozero::internal::gen_helpers::EqualsField(privacy_level_, other.privacy_level_)
-   && ::protozero::internal::gen_helpers::EqualsField(skip_incidentd_, other.skip_incidentd_)
-   && ::protozero::internal::gen_helpers::EqualsField(skip_dropbox_, other.skip_dropbox_);
+   && ::protozero::internal::gen_helpers::EqualsField(trace_config_, other.trace_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(privacy_filtering_enabled_, other.privacy_filtering_enabled_)
+   && ::protozero::internal::gen_helpers::EqualsField(convert_to_legacy_json_, other.convert_to_legacy_json_)
+   && ::protozero::internal::gen_helpers::EqualsField(client_priority_, other.client_priority_)
+   && ::protozero::internal::gen_helpers::EqualsField(json_agent_label_filter_, other.json_agent_label_filter_)
+   && ::protozero::internal::gen_helpers::EqualsField(event_package_name_filter_enabled_, other.event_package_name_filter_enabled_);
 }

-bool TraceConfig_IncidentReportConfig::ParseFromArray(const void* raw, size_t size) {
+bool ChromeConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -31062,20 +30548,23 @@ bool TraceConfig_IncidentReportConfig::ParseFromArray(const void* raw, size_t si
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* destination_package */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &destination_package_);
+      case 1 /* trace_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &trace_config_);
         break;
-      case 2 /* destination_class */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &destination_class_);
+      case 2 /* privacy_filtering_enabled */:
+        field.get(&privacy_filtering_enabled_);
         break;
-      case 3 /* privacy_level */:
-        field.get(&privacy_level_);
+      case 3 /* convert_to_legacy_json */:
+        field.get(&convert_to_legacy_json_);
         break;
-      case 5 /* skip_incidentd */:
-        field.get(&skip_incidentd_);
+      case 4 /* client_priority */:
+        field.get(&client_priority_);
         break;
-      case 4 /* skip_dropbox */:
-        field.get(&skip_dropbox_);
+      case 5 /* json_agent_label_filter */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &json_agent_label_filter_);
+        break;
+      case 6 /* event_package_name_filter_enabled */:
+        field.get(&event_package_name_filter_enabled_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -31085,62 +30574,93 @@ bool TraceConfig_IncidentReportConfig::ParseFromArray(const void* raw, size_t si
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_IncidentReportConfig::SerializeAsString() const {
+std::string ChromeConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_IncidentReportConfig::SerializeAsArray() const {
+std::vector<uint8_t> ChromeConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_IncidentReportConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: destination_package
+void ChromeConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: trace_config
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, destination_package_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, trace_config_, msg);
   }

-  // Field 2: destination_class
+  // Field 2: privacy_filtering_enabled
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, destination_class_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, privacy_filtering_enabled_, msg);
   }

-  // Field 3: privacy_level
+  // Field 3: convert_to_legacy_json
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, privacy_level_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, convert_to_legacy_json_, msg);
   }

-  // Field 5: skip_incidentd
+  // Field 4: client_priority
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, client_priority_, msg);
+  }
+
+  // Field 5: json_agent_label_filter
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, skip_incidentd_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(5, json_agent_label_filter_, msg);
   }

-  // Field 4: skip_dropbox
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, skip_dropbox_, msg);
+  // Field 6: event_package_name_filter_enabled
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(6, event_package_name_filter_enabled_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/chrome/histogram_samples.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/histogram_samples.gen.h"

-TraceConfig_CompressionConfig::TraceConfig_CompressionConfig() = default;
-TraceConfig_CompressionConfig::~TraceConfig_CompressionConfig() = default;
-TraceConfig_CompressionConfig::TraceConfig_CompressionConfig(const TraceConfig_CompressionConfig&) = default;
-TraceConfig_CompressionConfig& TraceConfig_CompressionConfig::operator=(const TraceConfig_CompressionConfig&) = default;
-TraceConfig_CompressionConfig::TraceConfig_CompressionConfig(TraceConfig_CompressionConfig&&) noexcept = default;
-TraceConfig_CompressionConfig& TraceConfig_CompressionConfig::operator=(TraceConfig_CompressionConfig&&) = default;
+namespace perfetto {
+namespace protos {
+namespace gen {

-bool TraceConfig_CompressionConfig::operator==(const TraceConfig_CompressionConfig& other) const {
+ChromiumHistogramSamplesConfig::ChromiumHistogramSamplesConfig() = default;
+ChromiumHistogramSamplesConfig::~ChromiumHistogramSamplesConfig() = default;
+ChromiumHistogramSamplesConfig::ChromiumHistogramSamplesConfig(const ChromiumHistogramSamplesConfig&) = default;
+ChromiumHistogramSamplesConfig& ChromiumHistogramSamplesConfig::operator=(const ChromiumHistogramSamplesConfig&) = default;
+ChromiumHistogramSamplesConfig::ChromiumHistogramSamplesConfig(ChromiumHistogramSamplesConfig&&) noexcept = default;
+ChromiumHistogramSamplesConfig& ChromiumHistogramSamplesConfig::operator=(ChromiumHistogramSamplesConfig&&) = default;
+
+bool ChromiumHistogramSamplesConfig::operator==(const ChromiumHistogramSamplesConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(deflate_, other.deflate_)
-   && ::protozero::internal::gen_helpers::EqualsField(zstd_, other.zstd_);
+   && ::protozero::internal::gen_helpers::EqualsField(histograms_, other.histograms_)
+   && ::protozero::internal::gen_helpers::EqualsField(filter_histogram_names_, other.filter_histogram_names_);
 }

-bool TraceConfig_CompressionConfig::ParseFromArray(const void* raw, size_t size) {
+int ChromiumHistogramSamplesConfig::histograms_size() const { return static_cast<int>(histograms_.size()); }
+void ChromiumHistogramSamplesConfig::clear_histograms() { histograms_.clear(); }
+ChromiumHistogramSamplesConfig_HistogramSample* ChromiumHistogramSamplesConfig::add_histograms() { histograms_.emplace_back(); return &histograms_.back(); }
+bool ChromiumHistogramSamplesConfig::ParseFromArray(const void* raw, size_t size) {
+  histograms_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -31150,11 +30670,12 @@ bool TraceConfig_CompressionConfig::ParseFromArray(const void* raw, size_t size)
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* deflate */:
-        (*deflate_).ParseFromArray(field.data(), field.size());
+      case 1 /* histograms */:
+        histograms_.emplace_back();
+        histograms_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 2 /* zstd */:
-        (*zstd_).ParseFromArray(field.data(), field.size());
+      case 2 /* filter_histogram_names */:
+        field.get(&filter_histogram_names_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -31164,46 +30685,48 @@ bool TraceConfig_CompressionConfig::ParseFromArray(const void* raw, size_t size)
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_CompressionConfig::SerializeAsString() const {
+std::string ChromiumHistogramSamplesConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_CompressionConfig::SerializeAsArray() const {
+std::vector<uint8_t> ChromiumHistogramSamplesConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_CompressionConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: deflate
-  if (_has_field_[1]) {
-    (*deflate_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+void ChromiumHistogramSamplesConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: histograms
+  for (auto& it : histograms_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 2: zstd
+  // Field 2: filter_histogram_names
   if (_has_field_[2]) {
-    (*zstd_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, filter_histogram_names_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TraceConfig_CompressionConfig_Zstd::TraceConfig_CompressionConfig_Zstd() = default;
-TraceConfig_CompressionConfig_Zstd::~TraceConfig_CompressionConfig_Zstd() = default;
-TraceConfig_CompressionConfig_Zstd::TraceConfig_CompressionConfig_Zstd(const TraceConfig_CompressionConfig_Zstd&) = default;
-TraceConfig_CompressionConfig_Zstd& TraceConfig_CompressionConfig_Zstd::operator=(const TraceConfig_CompressionConfig_Zstd&) = default;
-TraceConfig_CompressionConfig_Zstd::TraceConfig_CompressionConfig_Zstd(TraceConfig_CompressionConfig_Zstd&&) noexcept = default;
-TraceConfig_CompressionConfig_Zstd& TraceConfig_CompressionConfig_Zstd::operator=(TraceConfig_CompressionConfig_Zstd&&) = default;
+ChromiumHistogramSamplesConfig_HistogramSample::ChromiumHistogramSamplesConfig_HistogramSample() = default;
+ChromiumHistogramSamplesConfig_HistogramSample::~ChromiumHistogramSamplesConfig_HistogramSample() = default;
+ChromiumHistogramSamplesConfig_HistogramSample::ChromiumHistogramSamplesConfig_HistogramSample(const ChromiumHistogramSamplesConfig_HistogramSample&) = default;
+ChromiumHistogramSamplesConfig_HistogramSample& ChromiumHistogramSamplesConfig_HistogramSample::operator=(const ChromiumHistogramSamplesConfig_HistogramSample&) = default;
+ChromiumHistogramSamplesConfig_HistogramSample::ChromiumHistogramSamplesConfig_HistogramSample(ChromiumHistogramSamplesConfig_HistogramSample&&) noexcept = default;
+ChromiumHistogramSamplesConfig_HistogramSample& ChromiumHistogramSamplesConfig_HistogramSample::operator=(ChromiumHistogramSamplesConfig_HistogramSample&&) = default;

-bool TraceConfig_CompressionConfig_Zstd::operator==(const TraceConfig_CompressionConfig_Zstd& other) const {
+bool ChromiumHistogramSamplesConfig_HistogramSample::operator==(const ChromiumHistogramSamplesConfig_HistogramSample& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(level_, other.level_);
+   && ::protozero::internal::gen_helpers::EqualsField(histogram_name_, other.histogram_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(min_value_, other.min_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_value_, other.max_value_);
 }

-bool TraceConfig_CompressionConfig_Zstd::ParseFromArray(const void* raw, size_t size) {
+bool ChromiumHistogramSamplesConfig_HistogramSample::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -31213,8 +30736,14 @@ bool TraceConfig_CompressionConfig_Zstd::ParseFromArray(const void* raw, size_t
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* level */:
-        field.get(&level_);
+      case 1 /* histogram_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &histogram_name_);
+        break;
+      case 2 /* min_value */:
+        field.get(&min_value_);
+        break;
+      case 3 /* max_value */:
+        field.get(&max_value_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -31224,87 +30753,74 @@ bool TraceConfig_CompressionConfig_Zstd::ParseFromArray(const void* raw, size_t
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_CompressionConfig_Zstd::SerializeAsString() const {
+std::string ChromiumHistogramSamplesConfig_HistogramSample::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_CompressionConfig_Zstd::SerializeAsArray() const {
+std::vector<uint8_t> ChromiumHistogramSamplesConfig_HistogramSample::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_CompressionConfig_Zstd::Serialize(::protozero::Message* msg) const {
-  // Field 1: level
+void ChromiumHistogramSamplesConfig_HistogramSample::Serialize(::protozero::Message* msg) const {
+  // Field 1: histogram_name
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, level_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, histogram_name_, msg);
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
-
-
-TraceConfig_CompressionConfig_Deflate::TraceConfig_CompressionConfig_Deflate() = default;
-TraceConfig_CompressionConfig_Deflate::~TraceConfig_CompressionConfig_Deflate() = default;
-TraceConfig_CompressionConfig_Deflate::TraceConfig_CompressionConfig_Deflate(const TraceConfig_CompressionConfig_Deflate&) = default;
-TraceConfig_CompressionConfig_Deflate& TraceConfig_CompressionConfig_Deflate::operator=(const TraceConfig_CompressionConfig_Deflate&) = default;
-TraceConfig_CompressionConfig_Deflate::TraceConfig_CompressionConfig_Deflate(TraceConfig_CompressionConfig_Deflate&&) noexcept = default;
-TraceConfig_CompressionConfig_Deflate& TraceConfig_CompressionConfig_Deflate::operator=(TraceConfig_CompressionConfig_Deflate&&) = default;
-
-bool TraceConfig_CompressionConfig_Deflate::operator==(const TraceConfig_CompressionConfig_Deflate& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
-}
-
-bool TraceConfig_CompressionConfig_Deflate::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
-
-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
+  // Field 2: min_value
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, min_value_, msg);
   }
-  return !packed_error && !dec.bytes_left();
-}
-
-std::string TraceConfig_CompressionConfig_Deflate::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}

-std::vector<uint8_t> TraceConfig_CompressionConfig_Deflate::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  // Field 3: max_value
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, max_value_, msg);
+  }

-void TraceConfig_CompressionConfig_Deflate::Serialize(::protozero::Message* msg) const {
   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/chrome/sampling_heap_profiler.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/sampling_heap_profiler.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-TraceConfig_IncrementalStateConfig::TraceConfig_IncrementalStateConfig() = default;
-TraceConfig_IncrementalStateConfig::~TraceConfig_IncrementalStateConfig() = default;
-TraceConfig_IncrementalStateConfig::TraceConfig_IncrementalStateConfig(const TraceConfig_IncrementalStateConfig&) = default;
-TraceConfig_IncrementalStateConfig& TraceConfig_IncrementalStateConfig::operator=(const TraceConfig_IncrementalStateConfig&) = default;
-TraceConfig_IncrementalStateConfig::TraceConfig_IncrementalStateConfig(TraceConfig_IncrementalStateConfig&&) noexcept = default;
-TraceConfig_IncrementalStateConfig& TraceConfig_IncrementalStateConfig::operator=(TraceConfig_IncrementalStateConfig&&) = default;
+ChromiumSamplingHeapProfilerConfig::ChromiumSamplingHeapProfilerConfig() = default;
+ChromiumSamplingHeapProfilerConfig::~ChromiumSamplingHeapProfilerConfig() = default;
+ChromiumSamplingHeapProfilerConfig::ChromiumSamplingHeapProfilerConfig(const ChromiumSamplingHeapProfilerConfig&) = default;
+ChromiumSamplingHeapProfilerConfig& ChromiumSamplingHeapProfilerConfig::operator=(const ChromiumSamplingHeapProfilerConfig&) = default;
+ChromiumSamplingHeapProfilerConfig::ChromiumSamplingHeapProfilerConfig(ChromiumSamplingHeapProfilerConfig&&) noexcept = default;
+ChromiumSamplingHeapProfilerConfig& ChromiumSamplingHeapProfilerConfig::operator=(ChromiumSamplingHeapProfilerConfig&&) = default;

-bool TraceConfig_IncrementalStateConfig::operator==(const TraceConfig_IncrementalStateConfig& other) const {
+bool ChromiumSamplingHeapProfilerConfig::operator==(const ChromiumSamplingHeapProfilerConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(clear_period_ms_, other.clear_period_ms_);
+   && ::protozero::internal::gen_helpers::EqualsField(sampling_interval_bytes_, other.sampling_interval_bytes_)
+   && ::protozero::internal::gen_helpers::EqualsField(sampling_interval_ms_, other.sampling_interval_ms_);
 }

-bool TraceConfig_IncrementalStateConfig::ParseFromArray(const void* raw, size_t size) {
+bool ChromiumSamplingHeapProfilerConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -31314,8 +30830,11 @@ bool TraceConfig_IncrementalStateConfig::ParseFromArray(const void* raw, size_t
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* clear_period_ms */:
-        field.get(&clear_period_ms_);
+      case 1 /* sampling_interval_bytes */:
+        field.get(&sampling_interval_bytes_);
+        break;
+      case 2 /* sampling_interval_ms */:
+        field.get(&sampling_interval_ms_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -31325,48 +30844,132 @@ bool TraceConfig_IncrementalStateConfig::ParseFromArray(const void* raw, size_t
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_IncrementalStateConfig::SerializeAsString() const {
+std::string ChromiumSamplingHeapProfilerConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_IncrementalStateConfig::SerializeAsArray() const {
+std::vector<uint8_t> ChromiumSamplingHeapProfilerConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_IncrementalStateConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: clear_period_ms
+void ChromiumSamplingHeapProfilerConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: sampling_interval_bytes
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, clear_period_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, sampling_interval_bytes_, msg);
+  }
+
+  // Field 2: sampling_interval_ms
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, sampling_interval_ms_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/chrome/scenario_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/scenario_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/trace_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/priority_boost/priority_boost_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/data_source_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/qnx/qnx_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/linux/journald_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/stack_sampling_profiler.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/sampling_heap_profiler.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/histogram_samples.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/system_info/system_info_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/track_event/track_event_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/test_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/sys_stats/sys_stats_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/sys_stats_counters.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/smaps_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/perf_event_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/perf_events.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/java_hprof_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/heapprofd_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/process_stats/process_stats_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/statsd_tracing_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/atom_ids.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/protovm/protovm_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/power/android_power_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/interceptor_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/interceptors/console_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/inode_file/inode_file_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_renderstages_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/vulkan_memory_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_counter_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/frozen_ftrace_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/system_metrics.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/etw/etw_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/v8_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/chrome_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/windowmanager_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/user_list_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_transactions_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_layers_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/display_video_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/protolog_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/pixel_modem_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/packages_list_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/network_trace_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/kernel_wakelocks_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/inputmethod_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/cpu_per_uid_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/app_wakelock_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_sdk_sysprop_guard_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_system_property_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_process_state_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_polled_state_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_log_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/android_log_constants.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_input_event_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_game_intervention_list_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_aflags_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/trace_attributes.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/semantic_type.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/builtin_clock.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-TraceConfig_TriggerConfig::TraceConfig_TriggerConfig() = default;
-TraceConfig_TriggerConfig::~TraceConfig_TriggerConfig() = default;
-TraceConfig_TriggerConfig::TraceConfig_TriggerConfig(const TraceConfig_TriggerConfig&) = default;
-TraceConfig_TriggerConfig& TraceConfig_TriggerConfig::operator=(const TraceConfig_TriggerConfig&) = default;
-TraceConfig_TriggerConfig::TraceConfig_TriggerConfig(TraceConfig_TriggerConfig&&) noexcept = default;
-TraceConfig_TriggerConfig& TraceConfig_TriggerConfig::operator=(TraceConfig_TriggerConfig&&) = default;
+TracingTriggerRulesConfig::TracingTriggerRulesConfig() = default;
+TracingTriggerRulesConfig::~TracingTriggerRulesConfig() = default;
+TracingTriggerRulesConfig::TracingTriggerRulesConfig(const TracingTriggerRulesConfig&) = default;
+TracingTriggerRulesConfig& TracingTriggerRulesConfig::operator=(const TracingTriggerRulesConfig&) = default;
+TracingTriggerRulesConfig::TracingTriggerRulesConfig(TracingTriggerRulesConfig&&) noexcept = default;
+TracingTriggerRulesConfig& TracingTriggerRulesConfig::operator=(TracingTriggerRulesConfig&&) = default;

-bool TraceConfig_TriggerConfig::operator==(const TraceConfig_TriggerConfig& other) const {
+bool TracingTriggerRulesConfig::operator==(const TracingTriggerRulesConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(trigger_mode_, other.trigger_mode_)
-   && ::protozero::internal::gen_helpers::EqualsField(use_clone_snapshot_if_available_, other.use_clone_snapshot_if_available_)
-   && ::protozero::internal::gen_helpers::EqualsField(triggers_, other.triggers_)
-   && ::protozero::internal::gen_helpers::EqualsField(trigger_timeout_ms_, other.trigger_timeout_ms_);
+   && ::protozero::internal::gen_helpers::EqualsField(rules_, other.rules_);
 }

-int TraceConfig_TriggerConfig::triggers_size() const { return static_cast<int>(triggers_.size()); }
-void TraceConfig_TriggerConfig::clear_triggers() { triggers_.clear(); }
-TraceConfig_TriggerConfig_Trigger* TraceConfig_TriggerConfig::add_triggers() { triggers_.emplace_back(); return &triggers_.back(); }
-bool TraceConfig_TriggerConfig::ParseFromArray(const void* raw, size_t size) {
-  triggers_.clear();
+int TracingTriggerRulesConfig::rules_size() const { return static_cast<int>(rules_.size()); }
+void TracingTriggerRulesConfig::clear_rules() { rules_.clear(); }
+TriggerRule* TracingTriggerRulesConfig::add_rules() { rules_.emplace_back(); return &rules_.back(); }
+bool TracingTriggerRulesConfig::ParseFromArray(const void* raw, size_t size) {
+  rules_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -31376,18 +30979,9 @@ bool TraceConfig_TriggerConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* trigger_mode */:
-        field.get(&trigger_mode_);
-        break;
-      case 5 /* use_clone_snapshot_if_available */:
-        field.get(&use_clone_snapshot_if_available_);
-        break;
-      case 2 /* triggers */:
-        triggers_.emplace_back();
-        triggers_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 3 /* trigger_timeout_ms */:
-        field.get(&trigger_timeout_ms_);
+      case 1 /* rules */:
+        rules_.emplace_back();
+        rules_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -31397,60 +30991,47 @@ bool TraceConfig_TriggerConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_TriggerConfig::SerializeAsString() const {
+std::string TracingTriggerRulesConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_TriggerConfig::SerializeAsArray() const {
+std::vector<uint8_t> TracingTriggerRulesConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_TriggerConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: trigger_mode
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, trigger_mode_, msg);
-  }
-
-  // Field 5: use_clone_snapshot_if_available
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, use_clone_snapshot_if_available_, msg);
-  }
-
-  // Field 2: triggers
-  for (auto& it : triggers_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
-  }
-
-  // Field 3: trigger_timeout_ms
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, trigger_timeout_ms_, msg);
+void TracingTriggerRulesConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: rules
+  for (auto& it : rules_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TraceConfig_TriggerConfig_Trigger::TraceConfig_TriggerConfig_Trigger() = default;
-TraceConfig_TriggerConfig_Trigger::~TraceConfig_TriggerConfig_Trigger() = default;
-TraceConfig_TriggerConfig_Trigger::TraceConfig_TriggerConfig_Trigger(const TraceConfig_TriggerConfig_Trigger&) = default;
-TraceConfig_TriggerConfig_Trigger& TraceConfig_TriggerConfig_Trigger::operator=(const TraceConfig_TriggerConfig_Trigger&) = default;
-TraceConfig_TriggerConfig_Trigger::TraceConfig_TriggerConfig_Trigger(TraceConfig_TriggerConfig_Trigger&&) noexcept = default;
-TraceConfig_TriggerConfig_Trigger& TraceConfig_TriggerConfig_Trigger::operator=(TraceConfig_TriggerConfig_Trigger&&) = default;
+TriggerRule::TriggerRule() = default;
+TriggerRule::~TriggerRule() = default;
+TriggerRule::TriggerRule(const TriggerRule&) = default;
+TriggerRule& TriggerRule::operator=(const TriggerRule&) = default;
+TriggerRule::TriggerRule(TriggerRule&&) noexcept = default;
+TriggerRule& TriggerRule::operator=(TriggerRule&&) = default;

-bool TraceConfig_TriggerConfig_Trigger::operator==(const TraceConfig_TriggerConfig_Trigger& other) const {
+bool TriggerRule::operator==(const TriggerRule& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
    && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(producer_name_regex_, other.producer_name_regex_)
-   && ::protozero::internal::gen_helpers::EqualsField(stop_delay_ms_, other.stop_delay_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_per_24_h_, other.max_per_24_h_)
-   && ::protozero::internal::gen_helpers::EqualsField(skip_probability_, other.skip_probability_);
+   && ::protozero::internal::gen_helpers::EqualsField(trigger_chance_, other.trigger_chance_)
+   && ::protozero::internal::gen_helpers::EqualsField(delay_ms_, other.delay_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(activation_delay_ms_, other.activation_delay_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(manual_trigger_name_, other.manual_trigger_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(histogram_, other.histogram_)
+   && ::protozero::internal::gen_helpers::EqualsField(repeating_interval_, other.repeating_interval_);
 }

-bool TraceConfig_TriggerConfig_Trigger::ParseFromArray(const void* raw, size_t size) {
+bool TriggerRule::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -31463,17 +31044,23 @@ bool TraceConfig_TriggerConfig_Trigger::ParseFromArray(const void* raw, size_t s
       case 1 /* name */:
         ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 2 /* producer_name_regex */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &producer_name_regex_);
+      case 2 /* trigger_chance */:
+        field.get(&trigger_chance_);
         break;
-      case 3 /* stop_delay_ms */:
-        field.get(&stop_delay_ms_);
+      case 3 /* delay_ms */:
+        field.get(&delay_ms_);
         break;
-      case 4 /* max_per_24_h */:
-        field.get(&max_per_24_h_);
+      case 8 /* activation_delay_ms */:
+        field.get(&activation_delay_ms_);
         break;
-      case 5 /* skip_probability */:
-        field.get(&skip_probability_);
+      case 4 /* manual_trigger_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &manual_trigger_name_);
+        break;
+      case 5 /* histogram */:
+        (*histogram_).ParseFromArray(field.data(), field.size());
+        break;
+      case 6 /* repeating_interval */:
+        (*repeating_interval_).ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -31483,62 +31070,72 @@ bool TraceConfig_TriggerConfig_Trigger::ParseFromArray(const void* raw, size_t s
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_TriggerConfig_Trigger::SerializeAsString() const {
+std::string TriggerRule::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_TriggerConfig_Trigger::SerializeAsArray() const {
+std::vector<uint8_t> TriggerRule::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_TriggerConfig_Trigger::Serialize(::protozero::Message* msg) const {
+void TriggerRule::Serialize(::protozero::Message* msg) const {
   // Field 1: name
   if (_has_field_[1]) {
     ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  // Field 2: producer_name_regex
+  // Field 2: trigger_chance
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, producer_name_regex_, msg);
+    ::protozero::internal::gen_helpers::SerializeFixed(2, trigger_chance_, msg);
   }

-  // Field 3: stop_delay_ms
+  // Field 3: delay_ms
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, stop_delay_ms_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, delay_ms_, msg);
   }

-  // Field 4: max_per_24_h
+  // Field 8: activation_delay_ms
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, activation_delay_ms_, msg);
+  }
+
+  // Field 4: manual_trigger_name
   if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, max_per_24_h_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(4, manual_trigger_name_, msg);
   }

-  // Field 5: skip_probability
+  // Field 5: histogram
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(5, skip_probability_, msg);
+    (*histogram_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
+  }
+
+  // Field 6: repeating_interval
+  if (_has_field_[6]) {
+    (*repeating_interval_).Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TraceConfig_GuardrailOverrides::TraceConfig_GuardrailOverrides() = default;
-TraceConfig_GuardrailOverrides::~TraceConfig_GuardrailOverrides() = default;
-TraceConfig_GuardrailOverrides::TraceConfig_GuardrailOverrides(const TraceConfig_GuardrailOverrides&) = default;
-TraceConfig_GuardrailOverrides& TraceConfig_GuardrailOverrides::operator=(const TraceConfig_GuardrailOverrides&) = default;
-TraceConfig_GuardrailOverrides::TraceConfig_GuardrailOverrides(TraceConfig_GuardrailOverrides&&) noexcept = default;
-TraceConfig_GuardrailOverrides& TraceConfig_GuardrailOverrides::operator=(TraceConfig_GuardrailOverrides&&) = default;
+TriggerRule_RepeatingInterval::TriggerRule_RepeatingInterval() = default;
+TriggerRule_RepeatingInterval::~TriggerRule_RepeatingInterval() = default;
+TriggerRule_RepeatingInterval::TriggerRule_RepeatingInterval(const TriggerRule_RepeatingInterval&) = default;
+TriggerRule_RepeatingInterval& TriggerRule_RepeatingInterval::operator=(const TriggerRule_RepeatingInterval&) = default;
+TriggerRule_RepeatingInterval::TriggerRule_RepeatingInterval(TriggerRule_RepeatingInterval&&) noexcept = default;
+TriggerRule_RepeatingInterval& TriggerRule_RepeatingInterval::operator=(TriggerRule_RepeatingInterval&&) = default;

-bool TraceConfig_GuardrailOverrides::operator==(const TraceConfig_GuardrailOverrides& other) const {
+bool TriggerRule_RepeatingInterval::operator==(const TriggerRule_RepeatingInterval& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_upload_per_day_bytes_, other.max_upload_per_day_bytes_)
-   && ::protozero::internal::gen_helpers::EqualsField(max_tracing_buffer_size_kb_, other.max_tracing_buffer_size_kb_);
+   && ::protozero::internal::gen_helpers::EqualsField(period_ms_, other.period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(randomized_, other.randomized_);
 }

-bool TraceConfig_GuardrailOverrides::ParseFromArray(const void* raw, size_t size) {
+bool TriggerRule_RepeatingInterval::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -31548,11 +31145,11 @@ bool TraceConfig_GuardrailOverrides::ParseFromArray(const void* raw, size_t size
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* max_upload_per_day_bytes */:
-        field.get(&max_upload_per_day_bytes_);
+      case 1 /* period_ms */:
+        field.get(&period_ms_);
         break;
-      case 2 /* max_tracing_buffer_size_kb */:
-        field.get(&max_tracing_buffer_size_kb_);
+      case 2 /* randomized */:
+        field.get(&randomized_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -31562,49 +31159,48 @@ bool TraceConfig_GuardrailOverrides::ParseFromArray(const void* raw, size_t size
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_GuardrailOverrides::SerializeAsString() const {
+std::string TriggerRule_RepeatingInterval::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_GuardrailOverrides::SerializeAsArray() const {
+std::vector<uint8_t> TriggerRule_RepeatingInterval::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_GuardrailOverrides::Serialize(::protozero::Message* msg) const {
-  // Field 1: max_upload_per_day_bytes
+void TriggerRule_RepeatingInterval::Serialize(::protozero::Message* msg) const {
+  // Field 1: period_ms
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, max_upload_per_day_bytes_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, period_ms_, msg);
   }

-  // Field 2: max_tracing_buffer_size_kb
+  // Field 2: randomized
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, max_tracing_buffer_size_kb_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, randomized_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TraceConfig_StatsdMetadata::TraceConfig_StatsdMetadata() = default;
-TraceConfig_StatsdMetadata::~TraceConfig_StatsdMetadata() = default;
-TraceConfig_StatsdMetadata::TraceConfig_StatsdMetadata(const TraceConfig_StatsdMetadata&) = default;
-TraceConfig_StatsdMetadata& TraceConfig_StatsdMetadata::operator=(const TraceConfig_StatsdMetadata&) = default;
-TraceConfig_StatsdMetadata::TraceConfig_StatsdMetadata(TraceConfig_StatsdMetadata&&) noexcept = default;
-TraceConfig_StatsdMetadata& TraceConfig_StatsdMetadata::operator=(TraceConfig_StatsdMetadata&&) = default;
+TriggerRule_HistogramTrigger::TriggerRule_HistogramTrigger() = default;
+TriggerRule_HistogramTrigger::~TriggerRule_HistogramTrigger() = default;
+TriggerRule_HistogramTrigger::TriggerRule_HistogramTrigger(const TriggerRule_HistogramTrigger&) = default;
+TriggerRule_HistogramTrigger& TriggerRule_HistogramTrigger::operator=(const TriggerRule_HistogramTrigger&) = default;
+TriggerRule_HistogramTrigger::TriggerRule_HistogramTrigger(TriggerRule_HistogramTrigger&&) noexcept = default;
+TriggerRule_HistogramTrigger& TriggerRule_HistogramTrigger::operator=(TriggerRule_HistogramTrigger&&) = default;

-bool TraceConfig_StatsdMetadata::operator==(const TraceConfig_StatsdMetadata& other) const {
+bool TriggerRule_HistogramTrigger::operator==(const TriggerRule_HistogramTrigger& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(triggering_alert_id_, other.triggering_alert_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(triggering_config_uid_, other.triggering_config_uid_)
-   && ::protozero::internal::gen_helpers::EqualsField(triggering_config_id_, other.triggering_config_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(triggering_subscription_id_, other.triggering_subscription_id_);
+   && ::protozero::internal::gen_helpers::EqualsField(histogram_name_, other.histogram_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(min_value_, other.min_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_value_, other.max_value_);
 }

-bool TraceConfig_StatsdMetadata::ParseFromArray(const void* raw, size_t size) {
+bool TriggerRule_HistogramTrigger::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -31614,17 +31210,14 @@ bool TraceConfig_StatsdMetadata::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* triggering_alert_id */:
-        field.get(&triggering_alert_id_);
-        break;
-      case 2 /* triggering_config_uid */:
-        field.get(&triggering_config_uid_);
+      case 1 /* histogram_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &histogram_name_);
         break;
-      case 3 /* triggering_config_id */:
-        field.get(&triggering_config_id_);
+      case 2 /* min_value */:
+        field.get(&min_value_);
         break;
-      case 4 /* triggering_subscription_id */:
-        field.get(&triggering_subscription_id_);
+      case 3 /* max_value */:
+        field.get(&max_value_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -31634,58 +31227,55 @@ bool TraceConfig_StatsdMetadata::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_StatsdMetadata::SerializeAsString() const {
+std::string TriggerRule_HistogramTrigger::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_StatsdMetadata::SerializeAsArray() const {
+std::vector<uint8_t> TriggerRule_HistogramTrigger::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_StatsdMetadata::Serialize(::protozero::Message* msg) const {
-  // Field 1: triggering_alert_id
+void TriggerRule_HistogramTrigger::Serialize(::protozero::Message* msg) const {
+  // Field 1: histogram_name
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, triggering_alert_id_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, histogram_name_, msg);
   }

-  // Field 2: triggering_config_uid
+  // Field 2: min_value
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, triggering_config_uid_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, min_value_, msg);
   }

-  // Field 3: triggering_config_id
+  // Field 3: max_value
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, triggering_config_id_, msg);
-  }
-
-  // Field 4: triggering_subscription_id
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, triggering_subscription_id_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, max_value_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TraceConfig_ProducerConfig::TraceConfig_ProducerConfig() = default;
-TraceConfig_ProducerConfig::~TraceConfig_ProducerConfig() = default;
-TraceConfig_ProducerConfig::TraceConfig_ProducerConfig(const TraceConfig_ProducerConfig&) = default;
-TraceConfig_ProducerConfig& TraceConfig_ProducerConfig::operator=(const TraceConfig_ProducerConfig&) = default;
-TraceConfig_ProducerConfig::TraceConfig_ProducerConfig(TraceConfig_ProducerConfig&&) noexcept = default;
-TraceConfig_ProducerConfig& TraceConfig_ProducerConfig::operator=(TraceConfig_ProducerConfig&&) = default;
+ChromeFieldTracingConfig::ChromeFieldTracingConfig() = default;
+ChromeFieldTracingConfig::~ChromeFieldTracingConfig() = default;
+ChromeFieldTracingConfig::ChromeFieldTracingConfig(const ChromeFieldTracingConfig&) = default;
+ChromeFieldTracingConfig& ChromeFieldTracingConfig::operator=(const ChromeFieldTracingConfig&) = default;
+ChromeFieldTracingConfig::ChromeFieldTracingConfig(ChromeFieldTracingConfig&&) noexcept = default;
+ChromeFieldTracingConfig& ChromeFieldTracingConfig::operator=(ChromeFieldTracingConfig&&) = default;

-bool TraceConfig_ProducerConfig::operator==(const TraceConfig_ProducerConfig& other) const {
+bool ChromeFieldTracingConfig::operator==(const ChromeFieldTracingConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(producer_name_, other.producer_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(shm_size_kb_, other.shm_size_kb_)
-   && ::protozero::internal::gen_helpers::EqualsField(page_size_kb_, other.page_size_kb_);
+   && ::protozero::internal::gen_helpers::EqualsField(scenarios_, other.scenarios_);
 }

-bool TraceConfig_ProducerConfig::ParseFromArray(const void* raw, size_t size) {
+int ChromeFieldTracingConfig::scenarios_size() const { return static_cast<int>(scenarios_.size()); }
+void ChromeFieldTracingConfig::clear_scenarios() { scenarios_.clear(); }
+ScenarioConfig* ChromeFieldTracingConfig::add_scenarios() { scenarios_.emplace_back(); return &scenarios_.back(); }
+bool ChromeFieldTracingConfig::ParseFromArray(const void* raw, size_t size) {
+  scenarios_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -31695,14 +31285,9 @@ bool TraceConfig_ProducerConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* producer_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &producer_name_);
-        break;
-      case 2 /* shm_size_kb */:
-        field.get(&shm_size_kb_);
-        break;
-      case 3 /* page_size_kb */:
-        field.get(&page_size_kb_);
+      case 1 /* scenarios */:
+        scenarios_.emplace_back();
+        scenarios_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -31712,60 +31297,69 @@ bool TraceConfig_ProducerConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_ProducerConfig::SerializeAsString() const {
+std::string ChromeFieldTracingConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_ProducerConfig::SerializeAsArray() const {
+std::vector<uint8_t> ChromeFieldTracingConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_ProducerConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: producer_name
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, producer_name_, msg);
-  }
-
-  // Field 2: shm_size_kb
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, shm_size_kb_, msg);
-  }
-
-  // Field 3: page_size_kb
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, page_size_kb_, msg);
+void ChromeFieldTracingConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: scenarios
+  for (auto& it : scenarios_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TraceConfig_BuiltinDataSource::TraceConfig_BuiltinDataSource() = default;
-TraceConfig_BuiltinDataSource::~TraceConfig_BuiltinDataSource() = default;
-TraceConfig_BuiltinDataSource::TraceConfig_BuiltinDataSource(const TraceConfig_BuiltinDataSource&) = default;
-TraceConfig_BuiltinDataSource& TraceConfig_BuiltinDataSource::operator=(const TraceConfig_BuiltinDataSource&) = default;
-TraceConfig_BuiltinDataSource::TraceConfig_BuiltinDataSource(TraceConfig_BuiltinDataSource&&) noexcept = default;
-TraceConfig_BuiltinDataSource& TraceConfig_BuiltinDataSource::operator=(TraceConfig_BuiltinDataSource&&) = default;
+ScenarioConfig::ScenarioConfig() = default;
+ScenarioConfig::~ScenarioConfig() = default;
+ScenarioConfig::ScenarioConfig(const ScenarioConfig&) = default;
+ScenarioConfig& ScenarioConfig::operator=(const ScenarioConfig&) = default;
+ScenarioConfig::ScenarioConfig(ScenarioConfig&&) noexcept = default;
+ScenarioConfig& ScenarioConfig::operator=(ScenarioConfig&&) = default;

-bool TraceConfig_BuiltinDataSource::operator==(const TraceConfig_BuiltinDataSource& other) const {
+bool ScenarioConfig::operator==(const ScenarioConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(disable_clock_snapshotting_, other.disable_clock_snapshotting_)
-   && ::protozero::internal::gen_helpers::EqualsField(disable_trace_config_, other.disable_trace_config_)
-   && ::protozero::internal::gen_helpers::EqualsField(disable_system_info_, other.disable_system_info_)
-   && ::protozero::internal::gen_helpers::EqualsField(disable_service_events_, other.disable_service_events_)
-   && ::protozero::internal::gen_helpers::EqualsField(primary_trace_clock_, other.primary_trace_clock_)
-   && ::protozero::internal::gen_helpers::EqualsField(snapshot_interval_ms_, other.snapshot_interval_ms_)
-   && ::protozero::internal::gen_helpers::EqualsField(prefer_suspend_clock_for_snapshot_, other.prefer_suspend_clock_for_snapshot_)
-   && ::protozero::internal::gen_helpers::EqualsField(disable_chunk_usage_histograms_, other.disable_chunk_usage_histograms_)
-   && ::protozero::internal::gen_helpers::EqualsField(disable_extension_descriptors_, other.disable_extension_descriptors_)
-   && ::protozero::internal::gen_helpers::EqualsField(enable_concurrent_session_events_, other.enable_concurrent_session_events_);
+   && ::protozero::internal::gen_helpers::EqualsField(scenario_name_, other.scenario_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(scenario_description_, other.scenario_description_)
+   && ::protozero::internal::gen_helpers::EqualsField(start_rules_, other.start_rules_)
+   && ::protozero::internal::gen_helpers::EqualsField(stop_rules_, other.stop_rules_)
+   && ::protozero::internal::gen_helpers::EqualsField(upload_rules_, other.upload_rules_)
+   && ::protozero::internal::gen_helpers::EqualsField(setup_rules_, other.setup_rules_)
+   && ::protozero::internal::gen_helpers::EqualsField(trace_config_, other.trace_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(nested_scenarios_, other.nested_scenarios_)
+   && ::protozero::internal::gen_helpers::EqualsField(use_system_backend_, other.use_system_backend_);
 }

-bool TraceConfig_BuiltinDataSource::ParseFromArray(const void* raw, size_t size) {
+int ScenarioConfig::start_rules_size() const { return static_cast<int>(start_rules_.size()); }
+void ScenarioConfig::clear_start_rules() { start_rules_.clear(); }
+TriggerRule* ScenarioConfig::add_start_rules() { start_rules_.emplace_back(); return &start_rules_.back(); }
+int ScenarioConfig::stop_rules_size() const { return static_cast<int>(stop_rules_.size()); }
+void ScenarioConfig::clear_stop_rules() { stop_rules_.clear(); }
+TriggerRule* ScenarioConfig::add_stop_rules() { stop_rules_.emplace_back(); return &stop_rules_.back(); }
+int ScenarioConfig::upload_rules_size() const { return static_cast<int>(upload_rules_.size()); }
+void ScenarioConfig::clear_upload_rules() { upload_rules_.clear(); }
+TriggerRule* ScenarioConfig::add_upload_rules() { upload_rules_.emplace_back(); return &upload_rules_.back(); }
+int ScenarioConfig::setup_rules_size() const { return static_cast<int>(setup_rules_.size()); }
+void ScenarioConfig::clear_setup_rules() { setup_rules_.clear(); }
+TriggerRule* ScenarioConfig::add_setup_rules() { setup_rules_.emplace_back(); return &setup_rules_.back(); }
+int ScenarioConfig::nested_scenarios_size() const { return static_cast<int>(nested_scenarios_.size()); }
+void ScenarioConfig::clear_nested_scenarios() { nested_scenarios_.clear(); }
+NestedScenarioConfig* ScenarioConfig::add_nested_scenarios() { nested_scenarios_.emplace_back(); return &nested_scenarios_.back(); }
+bool ScenarioConfig::ParseFromArray(const void* raw, size_t size) {
+  start_rules_.clear();
+  stop_rules_.clear();
+  upload_rules_.clear();
+  setup_rules_.clear();
+  nested_scenarios_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -31775,35 +31369,37 @@ bool TraceConfig_BuiltinDataSource::ParseFromArray(const void* raw, size_t size)
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* disable_clock_snapshotting */:
-        field.get(&disable_clock_snapshotting_);
-        break;
-      case 2 /* disable_trace_config */:
-        field.get(&disable_trace_config_);
+      case 1 /* scenario_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &scenario_name_);
         break;
-      case 3 /* disable_system_info */:
-        field.get(&disable_system_info_);
+      case 9 /* scenario_description */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &scenario_description_);
         break;
-      case 4 /* disable_service_events */:
-        field.get(&disable_service_events_);
+      case 2 /* start_rules */:
+        start_rules_.emplace_back();
+        start_rules_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 5 /* primary_trace_clock */:
-        field.get(&primary_trace_clock_);
+      case 3 /* stop_rules */:
+        stop_rules_.emplace_back();
+        stop_rules_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 6 /* snapshot_interval_ms */:
-        field.get(&snapshot_interval_ms_);
+      case 4 /* upload_rules */:
+        upload_rules_.emplace_back();
+        upload_rules_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 7 /* prefer_suspend_clock_for_snapshot */:
-        field.get(&prefer_suspend_clock_for_snapshot_);
+      case 5 /* setup_rules */:
+        setup_rules_.emplace_back();
+        setup_rules_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 8 /* disable_chunk_usage_histograms */:
-        field.get(&disable_chunk_usage_histograms_);
+      case 6 /* trace_config */:
+        (*trace_config_).ParseFromArray(field.data(), field.size());
         break;
-      case 9 /* disable_extension_descriptors */:
-        field.get(&disable_extension_descriptors_);
+      case 7 /* nested_scenarios */:
+        nested_scenarios_.emplace_back();
+        nested_scenarios_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 10 /* enable_concurrent_session_events */:
-        field.get(&enable_concurrent_session_events_);
+      case 8 /* use_system_backend */:
+        field.get(&use_system_backend_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -31813,92 +31409,96 @@ bool TraceConfig_BuiltinDataSource::ParseFromArray(const void* raw, size_t size)
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_BuiltinDataSource::SerializeAsString() const {
+std::string ScenarioConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_BuiltinDataSource::SerializeAsArray() const {
+std::vector<uint8_t> ScenarioConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_BuiltinDataSource::Serialize(::protozero::Message* msg) const {
-  // Field 1: disable_clock_snapshotting
+void ScenarioConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: scenario_name
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, disable_clock_snapshotting_, msg);
-  }
-
-  // Field 2: disable_trace_config
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, disable_trace_config_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, scenario_name_, msg);
   }

-  // Field 3: disable_system_info
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, disable_system_info_, msg);
+  // Field 9: scenario_description
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeString(9, scenario_description_, msg);
   }

-  // Field 4: disable_service_events
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, disable_service_events_, msg);
+  // Field 2: start_rules
+  for (auto& it : start_rules_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

-  // Field 5: primary_trace_clock
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, primary_trace_clock_, msg);
+  // Field 3: stop_rules
+  for (auto& it : stop_rules_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
   }

-  // Field 6: snapshot_interval_ms
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, snapshot_interval_ms_, msg);
+  // Field 4: upload_rules
+  for (auto& it : upload_rules_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
   }

-  // Field 7: prefer_suspend_clock_for_snapshot
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, prefer_suspend_clock_for_snapshot_, msg);
+  // Field 5: setup_rules
+  for (auto& it : setup_rules_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
   }

-  // Field 8: disable_chunk_usage_histograms
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, disable_chunk_usage_histograms_, msg);
+  // Field 6: trace_config
+  if (_has_field_[6]) {
+    (*trace_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
   }

-  // Field 9: disable_extension_descriptors
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, disable_extension_descriptors_, msg);
+  // Field 7: nested_scenarios
+  for (auto& it : nested_scenarios_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(7));
   }

-  // Field 10: enable_concurrent_session_events
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(10, enable_concurrent_session_events_, msg);
+  // Field 8: use_system_backend
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, use_system_backend_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TraceConfig_DataSource::TraceConfig_DataSource() = default;
-TraceConfig_DataSource::~TraceConfig_DataSource() = default;
-TraceConfig_DataSource::TraceConfig_DataSource(const TraceConfig_DataSource&) = default;
-TraceConfig_DataSource& TraceConfig_DataSource::operator=(const TraceConfig_DataSource&) = default;
-TraceConfig_DataSource::TraceConfig_DataSource(TraceConfig_DataSource&&) noexcept = default;
-TraceConfig_DataSource& TraceConfig_DataSource::operator=(TraceConfig_DataSource&&) = default;
+NestedScenarioConfig::NestedScenarioConfig() = default;
+NestedScenarioConfig::~NestedScenarioConfig() = default;
+NestedScenarioConfig::NestedScenarioConfig(const NestedScenarioConfig&) = default;
+NestedScenarioConfig& NestedScenarioConfig::operator=(const NestedScenarioConfig&) = default;
+NestedScenarioConfig::NestedScenarioConfig(NestedScenarioConfig&&) noexcept = default;
+NestedScenarioConfig& NestedScenarioConfig::operator=(NestedScenarioConfig&&) = default;

-bool TraceConfig_DataSource::operator==(const TraceConfig_DataSource& other) const {
+bool NestedScenarioConfig::operator==(const NestedScenarioConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(config_, other.config_)
-   && ::protozero::internal::gen_helpers::EqualsField(producer_name_filter_, other.producer_name_filter_)
-   && ::protozero::internal::gen_helpers::EqualsField(producer_name_regex_filter_, other.producer_name_regex_filter_)
-   && ::protozero::internal::gen_helpers::EqualsField(machine_name_filter_, other.machine_name_filter_);
+   && ::protozero::internal::gen_helpers::EqualsField(scenario_name_, other.scenario_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(start_rules_, other.start_rules_)
+   && ::protozero::internal::gen_helpers::EqualsField(stop_rules_, other.stop_rules_)
+   && ::protozero::internal::gen_helpers::EqualsField(upload_rules_, other.upload_rules_);
 }

-bool TraceConfig_DataSource::ParseFromArray(const void* raw, size_t size) {
-  producer_name_filter_.clear();
-  producer_name_regex_filter_.clear();
-  machine_name_filter_.clear();
+int NestedScenarioConfig::start_rules_size() const { return static_cast<int>(start_rules_.size()); }
+void NestedScenarioConfig::clear_start_rules() { start_rules_.clear(); }
+TriggerRule* NestedScenarioConfig::add_start_rules() { start_rules_.emplace_back(); return &start_rules_.back(); }
+int NestedScenarioConfig::stop_rules_size() const { return static_cast<int>(stop_rules_.size()); }
+void NestedScenarioConfig::clear_stop_rules() { stop_rules_.clear(); }
+TriggerRule* NestedScenarioConfig::add_stop_rules() { stop_rules_.emplace_back(); return &stop_rules_.back(); }
+int NestedScenarioConfig::upload_rules_size() const { return static_cast<int>(upload_rules_.size()); }
+void NestedScenarioConfig::clear_upload_rules() { upload_rules_.clear(); }
+TriggerRule* NestedScenarioConfig::add_upload_rules() { upload_rules_.emplace_back(); return &upload_rules_.back(); }
+bool NestedScenarioConfig::ParseFromArray(const void* raw, size_t size) {
+  start_rules_.clear();
+  stop_rules_.clear();
+  upload_rules_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -31908,20 +31508,20 @@ bool TraceConfig_DataSource::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* config */:
-        (*config_).ParseFromArray(field.data(), field.size());
+      case 1 /* scenario_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &scenario_name_);
         break;
-      case 2 /* producer_name_filter */:
-        producer_name_filter_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &producer_name_filter_.back());
+      case 2 /* start_rules */:
+        start_rules_.emplace_back();
+        start_rules_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 3 /* producer_name_regex_filter */:
-        producer_name_regex_filter_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &producer_name_regex_filter_.back());
+      case 3 /* stop_rules */:
+        stop_rules_.emplace_back();
+        stop_rules_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 4 /* machine_name_filter */:
-        machine_name_filter_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &machine_name_filter_.back());
+      case 4 /* upload_rules */:
+        upload_rules_.emplace_back();
+        upload_rules_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -31931,61 +31531,78 @@ bool TraceConfig_DataSource::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_DataSource::SerializeAsString() const {
+std::string NestedScenarioConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_DataSource::SerializeAsArray() const {
+std::vector<uint8_t> NestedScenarioConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_DataSource::Serialize(::protozero::Message* msg) const {
-  // Field 1: config
+void NestedScenarioConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: scenario_name
   if (_has_field_[1]) {
-    (*config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+    ::protozero::internal::gen_helpers::SerializeString(1, scenario_name_, msg);
   }

-  // Field 2: producer_name_filter
-  for (auto& it : producer_name_filter_) {
-    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
+  // Field 2: start_rules
+  for (auto& it : start_rules_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

-  // Field 3: producer_name_regex_filter
-  for (auto& it : producer_name_regex_filter_) {
-    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
+  // Field 3: stop_rules
+  for (auto& it : stop_rules_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
   }

-  // Field 4: machine_name_filter
-  for (auto& it : machine_name_filter_) {
-    ::protozero::internal::gen_helpers::SerializeString(4, it, msg);
+  // Field 4: upload_rules
+  for (auto& it : upload_rules_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/chrome/stack_sampling_profiler.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/stack_sampling_profiler.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-TraceConfig_BufferConfig::TraceConfig_BufferConfig() = default;
-TraceConfig_BufferConfig::~TraceConfig_BufferConfig() = default;
-TraceConfig_BufferConfig::TraceConfig_BufferConfig(const TraceConfig_BufferConfig&) = default;
-TraceConfig_BufferConfig& TraceConfig_BufferConfig::operator=(const TraceConfig_BufferConfig&) = default;
-TraceConfig_BufferConfig::TraceConfig_BufferConfig(TraceConfig_BufferConfig&&) noexcept = default;
-TraceConfig_BufferConfig& TraceConfig_BufferConfig::operator=(TraceConfig_BufferConfig&&) = default;
+ChromiumStackSamplingProfilerConfig::ChromiumStackSamplingProfilerConfig() = default;
+ChromiumStackSamplingProfilerConfig::~ChromiumStackSamplingProfilerConfig() = default;
+ChromiumStackSamplingProfilerConfig::ChromiumStackSamplingProfilerConfig(const ChromiumStackSamplingProfilerConfig&) = default;
+ChromiumStackSamplingProfilerConfig& ChromiumStackSamplingProfilerConfig::operator=(const ChromiumStackSamplingProfilerConfig&) = default;
+ChromiumStackSamplingProfilerConfig::ChromiumStackSamplingProfilerConfig(ChromiumStackSamplingProfilerConfig&&) noexcept = default;
+ChromiumStackSamplingProfilerConfig& ChromiumStackSamplingProfilerConfig::operator=(ChromiumStackSamplingProfilerConfig&&) = default;

-bool TraceConfig_BufferConfig::operator==(const TraceConfig_BufferConfig& other) const {
+bool ChromiumStackSamplingProfilerConfig::operator==(const ChromiumStackSamplingProfilerConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(size_kb_, other.size_kb_)
-   && ::protozero::internal::gen_helpers::EqualsField(fill_policy_, other.fill_policy_)
-   && ::protozero::internal::gen_helpers::EqualsField(transfer_on_clone_, other.transfer_on_clone_)
-   && ::protozero::internal::gen_helpers::EqualsField(clear_before_clone_, other.clear_before_clone_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(experimental_mode_, other.experimental_mode_);
+   && ::protozero::internal::gen_helpers::EqualsField(sampling_interval_ms_, other.sampling_interval_ms_);
 }

-bool TraceConfig_BufferConfig::ParseFromArray(const void* raw, size_t size) {
+bool ChromiumStackSamplingProfilerConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -31995,23 +31612,8 @@ bool TraceConfig_BufferConfig::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* size_kb */:
-        field.get(&size_kb_);
-        break;
-      case 4 /* fill_policy */:
-        field.get(&fill_policy_);
-        break;
-      case 5 /* transfer_on_clone */:
-        field.get(&transfer_on_clone_);
-        break;
-      case 6 /* clear_before_clone */:
-        field.get(&clear_before_clone_);
-        break;
-      case 7 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 8 /* experimental_mode */:
-        field.get(&experimental_mode_);
+      case 1 /* sampling_interval_ms */:
+        field.get(&sampling_interval_ms_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -32021,47 +31623,185 @@ bool TraceConfig_BufferConfig::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TraceConfig_BufferConfig::SerializeAsString() const {
+std::string ChromiumStackSamplingProfilerConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TraceConfig_BufferConfig::SerializeAsArray() const {
+std::vector<uint8_t> ChromiumStackSamplingProfilerConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TraceConfig_BufferConfig::Serialize(::protozero::Message* msg) const {
-  // Field 1: size_kb
+void ChromiumStackSamplingProfilerConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: sampling_interval_ms
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, size_kb_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, sampling_interval_ms_, msg);
   }

-  // Field 4: fill_policy
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, fill_policy_, msg);
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/chrome/system_metrics.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/system_metrics.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+ChromiumSystemMetricsConfig::ChromiumSystemMetricsConfig() = default;
+ChromiumSystemMetricsConfig::~ChromiumSystemMetricsConfig() = default;
+ChromiumSystemMetricsConfig::ChromiumSystemMetricsConfig(const ChromiumSystemMetricsConfig&) = default;
+ChromiumSystemMetricsConfig& ChromiumSystemMetricsConfig::operator=(const ChromiumSystemMetricsConfig&) = default;
+ChromiumSystemMetricsConfig::ChromiumSystemMetricsConfig(ChromiumSystemMetricsConfig&&) noexcept = default;
+ChromiumSystemMetricsConfig& ChromiumSystemMetricsConfig::operator=(ChromiumSystemMetricsConfig&&) = default;
+
+bool ChromiumSystemMetricsConfig::operator==(const ChromiumSystemMetricsConfig& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(sampling_interval_ms_, other.sampling_interval_ms_);
+}
+
+bool ChromiumSystemMetricsConfig::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* sampling_interval_ms */:
+        field.get(&sampling_interval_ms_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 5: transfer_on_clone
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, transfer_on_clone_, msg);
+std::string ChromiumSystemMetricsConfig::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ChromiumSystemMetricsConfig::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ChromiumSystemMetricsConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: sampling_interval_ms
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, sampling_interval_ms_, msg);
   }

-  // Field 6: clear_before_clone
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(6, clear_before_clone_, msg);
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/chrome/v8_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/v8_config.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+V8Config::V8Config() = default;
+V8Config::~V8Config() = default;
+V8Config::V8Config(const V8Config&) = default;
+V8Config& V8Config::operator=(const V8Config&) = default;
+V8Config::V8Config(V8Config&&) noexcept = default;
+V8Config& V8Config::operator=(V8Config&&) = default;
+
+bool V8Config::operator==(const V8Config& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(log_script_sources_, other.log_script_sources_)
+   && ::protozero::internal::gen_helpers::EqualsField(log_instructions_, other.log_instructions_);
+}
+
+bool V8Config::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* log_script_sources */:
+        field.get(&log_script_sources_);
+        break;
+      case 2 /* log_instructions */:
+        field.get(&log_instructions_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 7: name
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeString(7, name_, msg);
+std::string V8Config::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> V8Config::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void V8Config::Serialize(::protozero::Message* msg) const {
+  // Field 1: log_script_sources
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, log_script_sources_, msg);
   }

-  // Field 8: experimental_mode
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, experimental_mode_, msg);
+  // Field 2: log_instructions
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, log_instructions_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -32073,161 +31813,7 @@ void TraceConfig_BufferConfig::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/protovm/vm_program.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/android_energy_consumer_descriptor.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/android_log_constants.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/builtin_clock.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/commit_data_request.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/data_source_descriptor.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/descriptor.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/ftrace_descriptor.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/gpu_counter_descriptor.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/interceptor_descriptor.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/observable_events.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/perf_events.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/sys_stats_counters.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/system_info.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/trace_attributes.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/trace_stats.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/tracing_service_capabilities.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/tracing_service_state.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/common/track_event_descriptor.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/android/android_aflags.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/android/android_game_intervention_list.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/android/android_log.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/android/android_system_property.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/android/cpu_per_uid_data.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/android/graphics_frame_event.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/android/initial_display_state.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/android/kernel_wakelock_data.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/android/packages_list.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/android/pixel_modem_events.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/android/user_list.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/chrome/chrome_benchmark_metadata.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/chrome/chrome_metadata.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/chrome/chrome_trigger.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/chrome/v8.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/gpu/gpu_counter_event.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/gpu/gpu_log.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/gpu/gpu_mem_event.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/gpu/gpu_render_stage_event.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/gpu/vulkan_api_event.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/gpu/vulkan_memory_event.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/profiling/inline_callstack.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/profiling/art_process_metadata.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/profiling/deobfuscation.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/profiling/profile_common.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/profiling/profile_packet.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/profiling/smaps.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/profiling/stack_sample.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_active_processes.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_application_state_info.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_content_settings_event_info.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_frame_reporter.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_histogram_sample.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_keyed_service.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_latency_info.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_legacy_ipc.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_message_pump.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_mojo_event_info.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_thread_descriptor.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_user_event.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_window_handle_event_info.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/counter_descriptor.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/debug_annotation.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/log_message.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/process_descriptor.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/range_of_interest.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/screenshot.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/source_location.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/state_descriptor.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/task_execution.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/thread_descriptor.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/track_descriptor.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/track_event.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/interned_data/interned_data.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_active_processes.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/config/data_source_config.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -32238,26 +31824,94 @@ void TraceConfig_BufferConfig::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_active_processes.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/data_source_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/system_info/system_info_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/test_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/priority_boost/priority_boost_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/protovm/protovm_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/interceptor_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/interceptors/console_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/chrome_config.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-ChromeActiveProcesses::ChromeActiveProcesses() = default;
-ChromeActiveProcesses::~ChromeActiveProcesses() = default;
-ChromeActiveProcesses::ChromeActiveProcesses(const ChromeActiveProcesses&) = default;
-ChromeActiveProcesses& ChromeActiveProcesses::operator=(const ChromeActiveProcesses&) = default;
-ChromeActiveProcesses::ChromeActiveProcesses(ChromeActiveProcesses&&) noexcept = default;
-ChromeActiveProcesses& ChromeActiveProcesses::operator=(ChromeActiveProcesses&&) = default;
+DataSourceConfig::DataSourceConfig() = default;
+DataSourceConfig::~DataSourceConfig() = default;
+DataSourceConfig::DataSourceConfig(const DataSourceConfig&) = default;
+DataSourceConfig& DataSourceConfig::operator=(const DataSourceConfig&) = default;
+DataSourceConfig::DataSourceConfig(DataSourceConfig&&) noexcept = default;
+DataSourceConfig& DataSourceConfig::operator=(DataSourceConfig&&) = default;

-bool ChromeActiveProcesses::operator==(const ChromeActiveProcesses& other) const {
+bool DataSourceConfig::operator==(const DataSourceConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(pid_, other.pid_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(target_buffer_, other.target_buffer_)
+   && ::protozero::internal::gen_helpers::EqualsField(target_buffer_name_, other.target_buffer_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(trace_duration_ms_, other.trace_duration_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(prefer_suspend_clock_for_duration_, other.prefer_suspend_clock_for_duration_)
+   && ::protozero::internal::gen_helpers::EqualsField(stop_timeout_ms_, other.stop_timeout_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(enable_extra_guardrails_, other.enable_extra_guardrails_)
+   && ::protozero::internal::gen_helpers::EqualsField(session_initiator_, other.session_initiator_)
+   && ::protozero::internal::gen_helpers::EqualsField(tracing_session_id_, other.tracing_session_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(buffer_exhausted_policy_, other.buffer_exhausted_policy_)
+   && ::protozero::internal::gen_helpers::EqualsField(priority_boost_, other.priority_boost_)
+   && ::protozero::internal::gen_helpers::EqualsField(protovm_config_, other.protovm_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(supports_tracing_v2_, other.supports_tracing_v2_)
+   && ::protozero::internal::gen_helpers::EqualsField(experimental_tracing_v2_, other.experimental_tracing_v2_)
+   && ::protozero::internal::gen_helpers::EqualsField(ftrace_config_, other.ftrace_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(inode_file_config_, other.inode_file_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(process_stats_config_, other.process_stats_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(sys_stats_config_, other.sys_stats_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(heapprofd_config_, other.heapprofd_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(java_hprof_config_, other.java_hprof_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_power_config_, other.android_power_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_log_config_, other.android_log_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(gpu_counter_config_, other.gpu_counter_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_game_intervention_list_config_, other.android_game_intervention_list_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(packages_list_config_, other.packages_list_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(perf_event_config_, other.perf_event_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(vulkan_memory_config_, other.vulkan_memory_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(track_event_config_, other.track_event_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_polled_state_config_, other.android_polled_state_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_system_property_config_, other.android_system_property_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(statsd_tracing_config_, other.statsd_tracing_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(system_info_config_, other.system_info_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(frozen_ftrace_config_, other.frozen_ftrace_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_config_, other.chrome_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(v8_config_, other.v8_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(interceptor_config_, other.interceptor_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(network_packet_trace_config_, other.network_packet_trace_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(surfaceflinger_layers_config_, other.surfaceflinger_layers_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(surfaceflinger_transactions_config_, other.surfaceflinger_transactions_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_sdk_sysprop_guard_config_, other.android_sdk_sysprop_guard_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(etw_config_, other.etw_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(protolog_config_, other.protolog_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_input_event_config_, other.android_input_event_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(pixel_modem_config_, other.pixel_modem_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(windowmanager_config_, other.windowmanager_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(chromium_system_metrics_, other.chromium_system_metrics_)
+   && ::protozero::internal::gen_helpers::EqualsField(kernel_wakelocks_config_, other.kernel_wakelocks_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(gpu_renderstages_config_, other.gpu_renderstages_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(chromium_histogram_samples_, other.chromium_histogram_samples_)
+   && ::protozero::internal::gen_helpers::EqualsField(app_wakelocks_config_, other.app_wakelocks_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(cpu_per_uid_config_, other.cpu_per_uid_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(user_list_config_, other.user_list_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(inputmethod_config_, other.inputmethod_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_aflags_config_, other.android_aflags_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(journald_config_, other.journald_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(display_video_config_, other.display_video_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(chromium_stack_sampling_profiler_, other.chromium_stack_sampling_profiler_)
+   && ::protozero::internal::gen_helpers::EqualsField(chromium_sampling_heap_profiler_, other.chromium_sampling_heap_profiler_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_process_state_config_, other.android_process_state_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(process_smaps_config_, other.process_smaps_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(qnx_config_, other.qnx_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(legacy_config_, other.legacy_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(for_testing_, other.for_testing_);
 }

-bool ChromeActiveProcesses::ParseFromArray(const void* raw, size_t size) {
-  pid_.clear();
+bool DataSourceConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -32267,9 +31921,194 @@ bool ChromeActiveProcesses::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* pid */:
-        pid_.emplace_back();
-        field.get(&pid_.back());
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      case 2 /* target_buffer */:
+        field.get(&target_buffer_);
+        break;
+      case 11 /* target_buffer_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &target_buffer_name_);
+        break;
+      case 3 /* trace_duration_ms */:
+        field.get(&trace_duration_ms_);
+        break;
+      case 122 /* prefer_suspend_clock_for_duration */:
+        field.get(&prefer_suspend_clock_for_duration_);
+        break;
+      case 7 /* stop_timeout_ms */:
+        field.get(&stop_timeout_ms_);
+        break;
+      case 6 /* enable_extra_guardrails */:
+        field.get(&enable_extra_guardrails_);
+        break;
+      case 8 /* session_initiator */:
+        field.get(&session_initiator_);
+        break;
+      case 4 /* tracing_session_id */:
+        field.get(&tracing_session_id_);
+        break;
+      case 9 /* buffer_exhausted_policy */:
+        field.get(&buffer_exhausted_policy_);
+        break;
+      case 10 /* priority_boost */:
+        (*priority_boost_).ParseFromArray(field.data(), field.size());
+        break;
+      case 12 /* protovm_config */:
+        (*protovm_config_).ParseFromArray(field.data(), field.size());
+        break;
+      case 147 /* supports_tracing_v2 */:
+        field.get(&supports_tracing_v2_);
+        break;
+      case 148 /* experimental_tracing_v2 */:
+        (*experimental_tracing_v2_).ParseFromArray(field.data(), field.size());
+        break;
+      case 100 /* ftrace_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &ftrace_config_);
+        break;
+      case 102 /* inode_file_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &inode_file_config_);
+        break;
+      case 103 /* process_stats_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &process_stats_config_);
+        break;
+      case 104 /* sys_stats_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &sys_stats_config_);
+        break;
+      case 105 /* heapprofd_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &heapprofd_config_);
+        break;
+      case 110 /* java_hprof_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &java_hprof_config_);
+        break;
+      case 106 /* android_power_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_power_config_);
+        break;
+      case 107 /* android_log_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_log_config_);
+        break;
+      case 108 /* gpu_counter_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &gpu_counter_config_);
+        break;
+      case 116 /* android_game_intervention_list_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_game_intervention_list_config_);
+        break;
+      case 109 /* packages_list_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &packages_list_config_);
+        break;
+      case 111 /* perf_event_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &perf_event_config_);
+        break;
+      case 112 /* vulkan_memory_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &vulkan_memory_config_);
+        break;
+      case 113 /* track_event_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &track_event_config_);
+        break;
+      case 114 /* android_polled_state_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_polled_state_config_);
+        break;
+      case 118 /* android_system_property_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_system_property_config_);
+        break;
+      case 117 /* statsd_tracing_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &statsd_tracing_config_);
+        break;
+      case 119 /* system_info_config */:
+        (*system_info_config_).ParseFromArray(field.data(), field.size());
+        break;
+      case 136 /* frozen_ftrace_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &frozen_ftrace_config_);
+        break;
+      case 101 /* chrome_config */:
+        (*chrome_config_).ParseFromArray(field.data(), field.size());
+        break;
+      case 127 /* v8_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &v8_config_);
+        break;
+      case 115 /* interceptor_config */:
+        (*interceptor_config_).ParseFromArray(field.data(), field.size());
+        break;
+      case 120 /* network_packet_trace_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &network_packet_trace_config_);
+        break;
+      case 121 /* surfaceflinger_layers_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &surfaceflinger_layers_config_);
+        break;
+      case 123 /* surfaceflinger_transactions_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &surfaceflinger_transactions_config_);
+        break;
+      case 124 /* android_sdk_sysprop_guard_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_sdk_sysprop_guard_config_);
+        break;
+      case 125 /* etw_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &etw_config_);
+        break;
+      case 126 /* protolog_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &protolog_config_);
+        break;
+      case 128 /* android_input_event_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_input_event_config_);
+        break;
+      case 129 /* pixel_modem_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &pixel_modem_config_);
+        break;
+      case 130 /* windowmanager_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &windowmanager_config_);
+        break;
+      case 131 /* chromium_system_metrics */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &chromium_system_metrics_);
+        break;
+      case 132 /* kernel_wakelocks_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &kernel_wakelocks_config_);
+        break;
+      case 133 /* gpu_renderstages_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &gpu_renderstages_config_);
+        break;
+      case 134 /* chromium_histogram_samples */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &chromium_histogram_samples_);
+        break;
+      case 135 /* app_wakelocks_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &app_wakelocks_config_);
+        break;
+      case 137 /* cpu_per_uid_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &cpu_per_uid_config_);
+        break;
+      case 138 /* user_list_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &user_list_config_);
+        break;
+      case 139 /* inputmethod_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &inputmethod_config_);
+        break;
+      case 140 /* android_aflags_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_aflags_config_);
+        break;
+      case 141 /* journald_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &journald_config_);
+        break;
+      case 142 /* display_video_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &display_video_config_);
+        break;
+      case 143 /* chromium_stack_sampling_profiler */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &chromium_stack_sampling_profiler_);
+        break;
+      case 144 /* chromium_sampling_heap_profiler */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &chromium_sampling_heap_profiler_);
+        break;
+      case 145 /* android_process_state_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &android_process_state_config_);
+        break;
+      case 146 /* process_smaps_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &process_smaps_config_);
+        break;
+      case 150 /* qnx_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &qnx_config_);
+        break;
+      case 1000 /* legacy_config */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &legacy_config_);
+        break;
+      case 1001 /* for_testing */:
+        (*for_testing_).ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -32279,147 +32118,357 @@ bool ChromeActiveProcesses::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeActiveProcesses::SerializeAsString() const {
+std::string DataSourceConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeActiveProcesses::SerializeAsArray() const {
+std::vector<uint8_t> DataSourceConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeActiveProcesses::Serialize(::protozero::Message* msg) const {
-  // Field 1: pid
-  for (auto& it : pid_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
+void DataSourceConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+  // Field 2: target_buffer
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, target_buffer_, msg);
+  }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_application_state_info.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_application_state_info.gen.h"
+  // Field 11: target_buffer_name
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeString(11, target_buffer_name_, msg);
+  }

-namespace perfetto {
-namespace protos {
-namespace gen {
+  // Field 3: trace_duration_ms
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, trace_duration_ms_, msg);
+  }

-ChromeApplicationStateInfo::ChromeApplicationStateInfo() = default;
-ChromeApplicationStateInfo::~ChromeApplicationStateInfo() = default;
-ChromeApplicationStateInfo::ChromeApplicationStateInfo(const ChromeApplicationStateInfo&) = default;
-ChromeApplicationStateInfo& ChromeApplicationStateInfo::operator=(const ChromeApplicationStateInfo&) = default;
-ChromeApplicationStateInfo::ChromeApplicationStateInfo(ChromeApplicationStateInfo&&) noexcept = default;
-ChromeApplicationStateInfo& ChromeApplicationStateInfo::operator=(ChromeApplicationStateInfo&&) = default;
+  // Field 122: prefer_suspend_clock_for_duration
+  if (_has_field_[122]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(122, prefer_suspend_clock_for_duration_, msg);
+  }

-bool ChromeApplicationStateInfo::operator==(const ChromeApplicationStateInfo& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(application_state_, other.application_state_);
-}
+  // Field 7: stop_timeout_ms
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, stop_timeout_ms_, msg);
+  }

-bool ChromeApplicationStateInfo::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
+  // Field 6: enable_extra_guardrails
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(6, enable_extra_guardrails_, msg);
+  }

-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* application_state */:
-        field.get(&application_state_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
+  // Field 8: session_initiator
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, session_initiator_, msg);
   }
-  return !packed_error && !dec.bytes_left();
-}

-std::string ChromeApplicationStateInfo::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
+  // Field 4: tracing_session_id
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, tracing_session_id_, msg);
+  }

-std::vector<uint8_t> ChromeApplicationStateInfo::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  // Field 9: buffer_exhausted_policy
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(9, buffer_exhausted_policy_, msg);
+  }

-void ChromeApplicationStateInfo::Serialize(::protozero::Message* msg) const {
-  // Field 1: application_state
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, application_state_, msg);
+  // Field 10: priority_boost
+  if (_has_field_[10]) {
+    (*priority_boost_).Serialize(msg->BeginNestedMessage<::protozero::Message>(10));
+  }
+
+  // Field 12: protovm_config
+  if (_has_field_[12]) {
+    (*protovm_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(12));
+  }
+
+  // Field 147: supports_tracing_v2
+  if (_has_field_[147]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(147, supports_tracing_v2_, msg);
+  }
+
+  // Field 148: experimental_tracing_v2
+  if (_has_field_[148]) {
+    (*experimental_tracing_v2_).Serialize(msg->BeginNestedMessage<::protozero::Message>(148));
+  }
+
+  // Field 100: ftrace_config
+  if (_has_field_[100]) {
+    msg->AppendString(100, ftrace_config_);
+  }
+
+  // Field 102: inode_file_config
+  if (_has_field_[102]) {
+    msg->AppendString(102, inode_file_config_);
+  }
+
+  // Field 103: process_stats_config
+  if (_has_field_[103]) {
+    msg->AppendString(103, process_stats_config_);
+  }
+
+  // Field 104: sys_stats_config
+  if (_has_field_[104]) {
+    msg->AppendString(104, sys_stats_config_);
+  }
+
+  // Field 105: heapprofd_config
+  if (_has_field_[105]) {
+    msg->AppendString(105, heapprofd_config_);
+  }
+
+  // Field 110: java_hprof_config
+  if (_has_field_[110]) {
+    msg->AppendString(110, java_hprof_config_);
+  }
+
+  // Field 106: android_power_config
+  if (_has_field_[106]) {
+    msg->AppendString(106, android_power_config_);
+  }
+
+  // Field 107: android_log_config
+  if (_has_field_[107]) {
+    msg->AppendString(107, android_log_config_);
+  }
+
+  // Field 108: gpu_counter_config
+  if (_has_field_[108]) {
+    msg->AppendString(108, gpu_counter_config_);
+  }
+
+  // Field 116: android_game_intervention_list_config
+  if (_has_field_[116]) {
+    msg->AppendString(116, android_game_intervention_list_config_);
+  }
+
+  // Field 109: packages_list_config
+  if (_has_field_[109]) {
+    msg->AppendString(109, packages_list_config_);
+  }
+
+  // Field 111: perf_event_config
+  if (_has_field_[111]) {
+    msg->AppendString(111, perf_event_config_);
+  }
+
+  // Field 112: vulkan_memory_config
+  if (_has_field_[112]) {
+    msg->AppendString(112, vulkan_memory_config_);
+  }
+
+  // Field 113: track_event_config
+  if (_has_field_[113]) {
+    msg->AppendString(113, track_event_config_);
+  }
+
+  // Field 114: android_polled_state_config
+  if (_has_field_[114]) {
+    msg->AppendString(114, android_polled_state_config_);
+  }
+
+  // Field 118: android_system_property_config
+  if (_has_field_[118]) {
+    msg->AppendString(118, android_system_property_config_);
+  }
+
+  // Field 117: statsd_tracing_config
+  if (_has_field_[117]) {
+    msg->AppendString(117, statsd_tracing_config_);
+  }
+
+  // Field 119: system_info_config
+  if (_has_field_[119]) {
+    (*system_info_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(119));
+  }
+
+  // Field 136: frozen_ftrace_config
+  if (_has_field_[136]) {
+    msg->AppendString(136, frozen_ftrace_config_);
+  }
+
+  // Field 101: chrome_config
+  if (_has_field_[101]) {
+    (*chrome_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(101));
+  }
+
+  // Field 127: v8_config
+  if (_has_field_[127]) {
+    msg->AppendString(127, v8_config_);
+  }
+
+  // Field 115: interceptor_config
+  if (_has_field_[115]) {
+    (*interceptor_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(115));
+  }
+
+  // Field 120: network_packet_trace_config
+  if (_has_field_[120]) {
+    msg->AppendString(120, network_packet_trace_config_);
+  }
+
+  // Field 121: surfaceflinger_layers_config
+  if (_has_field_[121]) {
+    msg->AppendString(121, surfaceflinger_layers_config_);
+  }
+
+  // Field 123: surfaceflinger_transactions_config
+  if (_has_field_[123]) {
+    msg->AppendString(123, surfaceflinger_transactions_config_);
+  }
+
+  // Field 124: android_sdk_sysprop_guard_config
+  if (_has_field_[124]) {
+    msg->AppendString(124, android_sdk_sysprop_guard_config_);
+  }
+
+  // Field 125: etw_config
+  if (_has_field_[125]) {
+    msg->AppendString(125, etw_config_);
+  }
+
+  // Field 126: protolog_config
+  if (_has_field_[126]) {
+    msg->AppendString(126, protolog_config_);
+  }
+
+  // Field 128: android_input_event_config
+  if (_has_field_[128]) {
+    msg->AppendString(128, android_input_event_config_);
+  }
+
+  // Field 129: pixel_modem_config
+  if (_has_field_[129]) {
+    msg->AppendString(129, pixel_modem_config_);
+  }
+
+  // Field 130: windowmanager_config
+  if (_has_field_[130]) {
+    msg->AppendString(130, windowmanager_config_);
+  }
+
+  // Field 131: chromium_system_metrics
+  if (_has_field_[131]) {
+    msg->AppendString(131, chromium_system_metrics_);
+  }
+
+  // Field 132: kernel_wakelocks_config
+  if (_has_field_[132]) {
+    msg->AppendString(132, kernel_wakelocks_config_);
+  }
+
+  // Field 133: gpu_renderstages_config
+  if (_has_field_[133]) {
+    msg->AppendString(133, gpu_renderstages_config_);
+  }
+
+  // Field 134: chromium_histogram_samples
+  if (_has_field_[134]) {
+    msg->AppendString(134, chromium_histogram_samples_);
+  }
+
+  // Field 135: app_wakelocks_config
+  if (_has_field_[135]) {
+    msg->AppendString(135, app_wakelocks_config_);
+  }
+
+  // Field 137: cpu_per_uid_config
+  if (_has_field_[137]) {
+    msg->AppendString(137, cpu_per_uid_config_);
+  }
+
+  // Field 138: user_list_config
+  if (_has_field_[138]) {
+    msg->AppendString(138, user_list_config_);
+  }
+
+  // Field 139: inputmethod_config
+  if (_has_field_[139]) {
+    msg->AppendString(139, inputmethod_config_);
+  }
+
+  // Field 140: android_aflags_config
+  if (_has_field_[140]) {
+    msg->AppendString(140, android_aflags_config_);
+  }
+
+  // Field 141: journald_config
+  if (_has_field_[141]) {
+    msg->AppendString(141, journald_config_);
+  }
+
+  // Field 142: display_video_config
+  if (_has_field_[142]) {
+    msg->AppendString(142, display_video_config_);
+  }
+
+  // Field 143: chromium_stack_sampling_profiler
+  if (_has_field_[143]) {
+    msg->AppendString(143, chromium_stack_sampling_profiler_);
+  }
+
+  // Field 144: chromium_sampling_heap_profiler
+  if (_has_field_[144]) {
+    msg->AppendString(144, chromium_sampling_heap_profiler_);
+  }
+
+  // Field 145: android_process_state_config
+  if (_has_field_[145]) {
+    msg->AppendString(145, android_process_state_config_);
+  }
+
+  // Field 146: process_smaps_config
+  if (_has_field_[146]) {
+    msg->AppendString(146, process_smaps_config_);
+  }
+
+  // Field 150: qnx_config
+  if (_has_field_[150]) {
+    msg->AppendString(150, qnx_config_);
+  }
+
+  // Field 1000: legacy_config
+  if (_has_field_[1000]) {
+    ::protozero::internal::gen_helpers::SerializeString(1000, legacy_config_, msg);
+  }
+
+  // Field 1001: for_testing
+  if (_has_field_[1001]) {
+    (*for_testing_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1001));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/source_location.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-CompositorTimingHistory::CompositorTimingHistory() = default;
-CompositorTimingHistory::~CompositorTimingHistory() = default;
-CompositorTimingHistory::CompositorTimingHistory(const CompositorTimingHistory&) = default;
-CompositorTimingHistory& CompositorTimingHistory::operator=(const CompositorTimingHistory&) = default;
-CompositorTimingHistory::CompositorTimingHistory(CompositorTimingHistory&&) noexcept = default;
-CompositorTimingHistory& CompositorTimingHistory::operator=(CompositorTimingHistory&&) = default;
+DataSourceConfig_ExperimentalTracingV2Config::DataSourceConfig_ExperimentalTracingV2Config() = default;
+DataSourceConfig_ExperimentalTracingV2Config::~DataSourceConfig_ExperimentalTracingV2Config() = default;
+DataSourceConfig_ExperimentalTracingV2Config::DataSourceConfig_ExperimentalTracingV2Config(const DataSourceConfig_ExperimentalTracingV2Config&) = default;
+DataSourceConfig_ExperimentalTracingV2Config& DataSourceConfig_ExperimentalTracingV2Config::operator=(const DataSourceConfig_ExperimentalTracingV2Config&) = default;
+DataSourceConfig_ExperimentalTracingV2Config::DataSourceConfig_ExperimentalTracingV2Config(DataSourceConfig_ExperimentalTracingV2Config&&) noexcept = default;
+DataSourceConfig_ExperimentalTracingV2Config& DataSourceConfig_ExperimentalTracingV2Config::operator=(DataSourceConfig_ExperimentalTracingV2Config&&) = default;

-bool CompositorTimingHistory::operator==(const CompositorTimingHistory& other) const {
+bool DataSourceConfig_ExperimentalTracingV2Config::operator==(const DataSourceConfig_ExperimentalTracingV2Config& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(begin_main_frame_queue_critical_estimate_delta_us_, other.begin_main_frame_queue_critical_estimate_delta_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(begin_main_frame_queue_not_critical_estimate_delta_us_, other.begin_main_frame_queue_not_critical_estimate_delta_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(begin_main_frame_start_to_ready_to_commit_estimate_delta_us_, other.begin_main_frame_start_to_ready_to_commit_estimate_delta_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(commit_to_ready_to_activate_estimate_delta_us_, other.commit_to_ready_to_activate_estimate_delta_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(prepare_tiles_estimate_delta_us_, other.prepare_tiles_estimate_delta_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(activate_estimate_delta_us_, other.activate_estimate_delta_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(draw_estimate_delta_us_, other.draw_estimate_delta_us_);
+   && ::protozero::internal::gen_helpers::EqualsField(use_v2_probability_percent_, other.use_v2_probability_percent_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunk_size_options_, other.chunk_size_options_)
+   && ::protozero::internal::gen_helpers::EqualsField(drain_occupancy_percent_, other.drain_occupancy_percent_);
 }

-bool CompositorTimingHistory::ParseFromArray(const void* raw, size_t size) {
+int DataSourceConfig_ExperimentalTracingV2Config::chunk_size_options_size() const { return static_cast<int>(chunk_size_options_.size()); }
+void DataSourceConfig_ExperimentalTracingV2Config::clear_chunk_size_options() { chunk_size_options_.clear(); }
+DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption* DataSourceConfig_ExperimentalTracingV2Config::add_chunk_size_options() { chunk_size_options_.emplace_back(); return &chunk_size_options_.back(); }
+bool DataSourceConfig_ExperimentalTracingV2Config::ParseFromArray(const void* raw, size_t size) {
+  chunk_size_options_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -32429,26 +32478,15 @@ bool CompositorTimingHistory::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* begin_main_frame_queue_critical_estimate_delta_us */:
-        field.get(&begin_main_frame_queue_critical_estimate_delta_us_);
-        break;
-      case 2 /* begin_main_frame_queue_not_critical_estimate_delta_us */:
-        field.get(&begin_main_frame_queue_not_critical_estimate_delta_us_);
-        break;
-      case 3 /* begin_main_frame_start_to_ready_to_commit_estimate_delta_us */:
-        field.get(&begin_main_frame_start_to_ready_to_commit_estimate_delta_us_);
+      case 1 /* use_v2_probability_percent */:
+        field.get(&use_v2_probability_percent_);
         break;
-      case 4 /* commit_to_ready_to_activate_estimate_delta_us */:
-        field.get(&commit_to_ready_to_activate_estimate_delta_us_);
-        break;
-      case 5 /* prepare_tiles_estimate_delta_us */:
-        field.get(&prepare_tiles_estimate_delta_us_);
-        break;
-      case 6 /* activate_estimate_delta_us */:
-        field.get(&activate_estimate_delta_us_);
+      case 2 /* chunk_size_options */:
+        chunk_size_options_.emplace_back();
+        chunk_size_options_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 7 /* draw_estimate_delta_us */:
-        field.get(&draw_estimate_delta_us_);
+      case 3 /* drain_occupancy_percent */:
+        field.get(&drain_occupancy_percent_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -32458,74 +32496,52 @@ bool CompositorTimingHistory::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string CompositorTimingHistory::SerializeAsString() const {
+std::string DataSourceConfig_ExperimentalTracingV2Config::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> CompositorTimingHistory::SerializeAsArray() const {
+std::vector<uint8_t> DataSourceConfig_ExperimentalTracingV2Config::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void CompositorTimingHistory::Serialize(::protozero::Message* msg) const {
-  // Field 1: begin_main_frame_queue_critical_estimate_delta_us
+void DataSourceConfig_ExperimentalTracingV2Config::Serialize(::protozero::Message* msg) const {
+  // Field 1: use_v2_probability_percent
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, begin_main_frame_queue_critical_estimate_delta_us_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, use_v2_probability_percent_, msg);
   }

-  // Field 2: begin_main_frame_queue_not_critical_estimate_delta_us
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, begin_main_frame_queue_not_critical_estimate_delta_us_, msg);
+  // Field 2: chunk_size_options
+  for (auto& it : chunk_size_options_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

-  // Field 3: begin_main_frame_start_to_ready_to_commit_estimate_delta_us
+  // Field 3: drain_occupancy_percent
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, begin_main_frame_start_to_ready_to_commit_estimate_delta_us_, msg);
-  }
-
-  // Field 4: commit_to_ready_to_activate_estimate_delta_us
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, commit_to_ready_to_activate_estimate_delta_us_, msg);
-  }
-
-  // Field 5: prepare_tiles_estimate_delta_us
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, prepare_tiles_estimate_delta_us_, msg);
-  }
-
-  // Field 6: activate_estimate_delta_us
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, activate_estimate_delta_us_, msg);
-  }
-
-  // Field 7: draw_estimate_delta_us
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, draw_estimate_delta_us_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, drain_occupancy_percent_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-BeginFrameSourceState::BeginFrameSourceState() = default;
-BeginFrameSourceState::~BeginFrameSourceState() = default;
-BeginFrameSourceState::BeginFrameSourceState(const BeginFrameSourceState&) = default;
-BeginFrameSourceState& BeginFrameSourceState::operator=(const BeginFrameSourceState&) = default;
-BeginFrameSourceState::BeginFrameSourceState(BeginFrameSourceState&&) noexcept = default;
-BeginFrameSourceState& BeginFrameSourceState::operator=(BeginFrameSourceState&&) = default;
+DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption::DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption() = default;
+DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption::~DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption() = default;
+DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption::DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption(const DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption&) = default;
+DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption& DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption::operator=(const DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption&) = default;
+DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption::DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption(DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption&&) noexcept = default;
+DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption& DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption::operator=(DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption&&) = default;

-bool BeginFrameSourceState::operator==(const BeginFrameSourceState& other) const {
+bool DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption::operator==(const DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(source_id_, other.source_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(paused_, other.paused_)
-   && ::protozero::internal::gen_helpers::EqualsField(num_observers_, other.num_observers_)
-   && ::protozero::internal::gen_helpers::EqualsField(last_begin_frame_args_, other.last_begin_frame_args_);
+   && ::protozero::internal::gen_helpers::EqualsField(size_bytes_, other.size_bytes_)
+   && ::protozero::internal::gen_helpers::EqualsField(weight_, other.weight_);
 }

-bool BeginFrameSourceState::ParseFromArray(const void* raw, size_t size) {
+bool DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -32535,17 +32551,11 @@ bool BeginFrameSourceState::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* source_id */:
-        field.get(&source_id_);
-        break;
-      case 2 /* paused */:
-        field.get(&paused_);
-        break;
-      case 3 /* num_observers */:
-        field.get(&num_observers_);
+      case 1 /* size_bytes */:
+        field.get(&size_bytes_);
         break;
-      case 4 /* last_begin_frame_args */:
-        (*last_begin_frame_args_).ParseFromArray(field.data(), field.size());
+      case 2 /* weight */:
+        field.get(&weight_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -32555,66 +32565,86 @@ bool BeginFrameSourceState::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string BeginFrameSourceState::SerializeAsString() const {
+std::string DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> BeginFrameSourceState::SerializeAsArray() const {
+std::vector<uint8_t> DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void BeginFrameSourceState::Serialize(::protozero::Message* msg) const {
-  // Field 1: source_id
+void DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption::Serialize(::protozero::Message* msg) const {
+  // Field 1: size_bytes
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, source_id_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, size_bytes_, msg);
   }

-  // Field 2: paused
+  // Field 2: weight
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, paused_, msg);
-  }
-
-  // Field 3: num_observers
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, num_observers_, msg);
-  }
-
-  // Field 4: last_begin_frame_args
-  if (_has_field_[4]) {
-    (*last_begin_frame_args_).Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, weight_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/etw/etw_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/etw/etw_config.gen.h"

-BeginFrameArgs::BeginFrameArgs() = default;
-BeginFrameArgs::~BeginFrameArgs() = default;
-BeginFrameArgs::BeginFrameArgs(const BeginFrameArgs&) = default;
-BeginFrameArgs& BeginFrameArgs::operator=(const BeginFrameArgs&) = default;
-BeginFrameArgs::BeginFrameArgs(BeginFrameArgs&&) noexcept = default;
-BeginFrameArgs& BeginFrameArgs::operator=(BeginFrameArgs&&) = default;
+namespace perfetto {
+namespace protos {
+namespace gen {

-bool BeginFrameArgs::operator==(const BeginFrameArgs& other) const {
+EtwConfig::EtwConfig() = default;
+EtwConfig::~EtwConfig() = default;
+EtwConfig::EtwConfig(const EtwConfig&) = default;
+EtwConfig& EtwConfig::operator=(const EtwConfig&) = default;
+EtwConfig::EtwConfig(EtwConfig&&) noexcept = default;
+EtwConfig& EtwConfig::operator=(EtwConfig&&) = default;
+
+bool EtwConfig::operator==(const EtwConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
-   && ::protozero::internal::gen_helpers::EqualsField(source_id_, other.source_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(sequence_number_, other.sequence_number_)
-   && ::protozero::internal::gen_helpers::EqualsField(frame_time_us_, other.frame_time_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(deadline_us_, other.deadline_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(interval_delta_us_, other.interval_delta_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(on_critical_path_, other.on_critical_path_)
-   && ::protozero::internal::gen_helpers::EqualsField(animate_only_, other.animate_only_)
-   && ::protozero::internal::gen_helpers::EqualsField(source_location_iid_, other.source_location_iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(source_location_, other.source_location_)
-   && ::protozero::internal::gen_helpers::EqualsField(frames_throttled_since_last_, other.frames_throttled_since_last_);
+   && ::protozero::internal::gen_helpers::EqualsField(kernel_flags_, other.kernel_flags_)
+   && ::protozero::internal::gen_helpers::EqualsField(scheduler_provider_events_, other.scheduler_provider_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(memory_provider_events_, other.memory_provider_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(file_provider_events_, other.file_provider_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(stack_sampling_events_, other.stack_sampling_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(disk_provider_events_, other.disk_provider_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(system_io_provider_events_, other.system_io_provider_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(stack_sampling_debug_ids_, other.stack_sampling_debug_ids_);
 }

-bool BeginFrameArgs::ParseFromArray(const void* raw, size_t size) {
+int EtwConfig::stack_sampling_debug_ids_size() const { return static_cast<int>(stack_sampling_debug_ids_.size()); }
+void EtwConfig::clear_stack_sampling_debug_ids() { stack_sampling_debug_ids_.clear(); }
+EtwConfig_DebugId* EtwConfig::add_stack_sampling_debug_ids() { stack_sampling_debug_ids_.emplace_back(); return &stack_sampling_debug_ids_.back(); }
+bool EtwConfig::ParseFromArray(const void* raw, size_t size) {
+  kernel_flags_.clear();
+  scheduler_provider_events_.clear();
+  memory_provider_events_.clear();
+  file_provider_events_.clear();
+  stack_sampling_events_.clear();
+  disk_provider_events_.clear();
+  system_io_provider_events_.clear();
+  stack_sampling_debug_ids_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -32624,38 +32654,37 @@ bool BeginFrameArgs::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* type */:
-        field.get(&type_);
-        break;
-      case 2 /* source_id */:
-        field.get(&source_id_);
-        break;
-      case 3 /* sequence_number */:
-        field.get(&sequence_number_);
-        break;
-      case 4 /* frame_time_us */:
-        field.get(&frame_time_us_);
+      case 1 /* kernel_flags */:
+        kernel_flags_.emplace_back();
+        field.get(&kernel_flags_.back());
         break;
-      case 5 /* deadline_us */:
-        field.get(&deadline_us_);
+      case 2 /* scheduler_provider_events */:
+        scheduler_provider_events_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &scheduler_provider_events_.back());
         break;
-      case 6 /* interval_delta_us */:
-        field.get(&interval_delta_us_);
+      case 3 /* memory_provider_events */:
+        memory_provider_events_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &memory_provider_events_.back());
         break;
-      case 7 /* on_critical_path */:
-        field.get(&on_critical_path_);
+      case 4 /* file_provider_events */:
+        file_provider_events_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &file_provider_events_.back());
         break;
-      case 8 /* animate_only */:
-        field.get(&animate_only_);
+      case 5 /* stack_sampling_events */:
+        stack_sampling_events_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &stack_sampling_events_.back());
         break;
-      case 9 /* source_location_iid */:
-        field.get(&source_location_iid_);
+      case 6 /* disk_provider_events */:
+        disk_provider_events_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &disk_provider_events_.back());
         break;
-      case 10 /* source_location */:
-        (*source_location_).ParseFromArray(field.data(), field.size());
+      case 7 /* system_io_provider_events */:
+        system_io_provider_events_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &system_io_provider_events_.back());
         break;
-      case 12 /* frames_throttled_since_last */:
-        field.get(&frames_throttled_since_last_);
+      case 8 /* stack_sampling_debug_ids */:
+        stack_sampling_debug_ids_.emplace_back();
+        stack_sampling_debug_ids_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -32665,92 +32694,164 @@ bool BeginFrameArgs::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string BeginFrameArgs::SerializeAsString() const {
+std::string EtwConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> BeginFrameArgs::SerializeAsArray() const {
+std::vector<uint8_t> EtwConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void BeginFrameArgs::Serialize(::protozero::Message* msg) const {
-  // Field 1: type
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, type_, msg);
+void EtwConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: kernel_flags
+  for (auto& it : kernel_flags_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
   }

-  // Field 2: source_id
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, source_id_, msg);
+  // Field 2: scheduler_provider_events
+  for (auto& it : scheduler_provider_events_) {
+    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
   }

-  // Field 3: sequence_number
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, sequence_number_, msg);
+  // Field 3: memory_provider_events
+  for (auto& it : memory_provider_events_) {
+    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
   }

-  // Field 4: frame_time_us
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, frame_time_us_, msg);
+  // Field 4: file_provider_events
+  for (auto& it : file_provider_events_) {
+    ::protozero::internal::gen_helpers::SerializeString(4, it, msg);
   }

-  // Field 5: deadline_us
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, deadline_us_, msg);
+  // Field 5: stack_sampling_events
+  for (auto& it : stack_sampling_events_) {
+    ::protozero::internal::gen_helpers::SerializeString(5, it, msg);
   }

-  // Field 6: interval_delta_us
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, interval_delta_us_, msg);
+  // Field 6: disk_provider_events
+  for (auto& it : disk_provider_events_) {
+    ::protozero::internal::gen_helpers::SerializeString(6, it, msg);
   }

-  // Field 7: on_critical_path
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, on_critical_path_, msg);
+  // Field 7: system_io_provider_events
+  for (auto& it : system_io_provider_events_) {
+    ::protozero::internal::gen_helpers::SerializeString(7, it, msg);
   }

-  // Field 8: animate_only
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, animate_only_, msg);
+  // Field 8: stack_sampling_debug_ids
+  for (auto& it : stack_sampling_debug_ids_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
   }

-  // Field 9: source_location_iid
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(9, source_location_iid_, msg);
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+EtwConfig_DebugId::EtwConfig_DebugId() = default;
+EtwConfig_DebugId::~EtwConfig_DebugId() = default;
+EtwConfig_DebugId::EtwConfig_DebugId(const EtwConfig_DebugId&) = default;
+EtwConfig_DebugId& EtwConfig_DebugId::operator=(const EtwConfig_DebugId&) = default;
+EtwConfig_DebugId::EtwConfig_DebugId(EtwConfig_DebugId&&) noexcept = default;
+EtwConfig_DebugId& EtwConfig_DebugId::operator=(EtwConfig_DebugId&&) = default;
+
+bool EtwConfig_DebugId::operator==(const EtwConfig_DebugId& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(path_, other.path_)
+   && ::protozero::internal::gen_helpers::EqualsField(debug_id_, other.debug_id_);
+}
+
+bool EtwConfig_DebugId::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* path */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &path_);
+        break;
+      case 2 /* debug_id */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &debug_id_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 10: source_location
-  if (_has_field_[10]) {
-    (*source_location_).Serialize(msg->BeginNestedMessage<::protozero::Message>(10));
+std::string EtwConfig_DebugId::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> EtwConfig_DebugId::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void EtwConfig_DebugId::Serialize(::protozero::Message* msg) const {
+  // Field 1: path
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, path_, msg);
   }

-  // Field 12: frames_throttled_since_last
-  if (_has_field_[12]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(12, frames_throttled_since_last_, msg);
+  // Field 2: debug_id
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, debug_id_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/interceptor_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/interceptor_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/interceptors/console_config.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-BeginFrameObserverState::BeginFrameObserverState() = default;
-BeginFrameObserverState::~BeginFrameObserverState() = default;
-BeginFrameObserverState::BeginFrameObserverState(const BeginFrameObserverState&) = default;
-BeginFrameObserverState& BeginFrameObserverState::operator=(const BeginFrameObserverState&) = default;
-BeginFrameObserverState::BeginFrameObserverState(BeginFrameObserverState&&) noexcept = default;
-BeginFrameObserverState& BeginFrameObserverState::operator=(BeginFrameObserverState&&) = default;
+InterceptorConfig::InterceptorConfig() = default;
+InterceptorConfig::~InterceptorConfig() = default;
+InterceptorConfig::InterceptorConfig(const InterceptorConfig&) = default;
+InterceptorConfig& InterceptorConfig::operator=(const InterceptorConfig&) = default;
+InterceptorConfig::InterceptorConfig(InterceptorConfig&&) noexcept = default;
+InterceptorConfig& InterceptorConfig::operator=(InterceptorConfig&&) = default;

-bool BeginFrameObserverState::operator==(const BeginFrameObserverState& other) const {
+bool InterceptorConfig::operator==(const InterceptorConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(dropped_begin_frame_args_, other.dropped_begin_frame_args_)
-   && ::protozero::internal::gen_helpers::EqualsField(last_begin_frame_args_, other.last_begin_frame_args_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(console_config_, other.console_config_);
 }

-bool BeginFrameObserverState::ParseFromArray(const void* raw, size_t size) {
+bool InterceptorConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -32760,11 +32861,11 @@ bool BeginFrameObserverState::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* dropped_begin_frame_args */:
-        field.get(&dropped_begin_frame_args_);
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 2 /* last_begin_frame_args */:
-        (*last_begin_frame_args_).ParseFromArray(field.data(), field.size());
+      case 100 /* console_config */:
+        (*console_config_).ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -32774,51 +32875,138 @@ bool BeginFrameObserverState::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string BeginFrameObserverState::SerializeAsString() const {
+std::string InterceptorConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> BeginFrameObserverState::SerializeAsArray() const {
+std::vector<uint8_t> InterceptorConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void BeginFrameObserverState::Serialize(::protozero::Message* msg) const {
-  // Field 1: dropped_begin_frame_args
+void InterceptorConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, dropped_begin_frame_args_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  // Field 2: last_begin_frame_args
-  if (_has_field_[2]) {
-    (*last_begin_frame_args_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+  // Field 100: console_config
+  if (_has_field_[100]) {
+    (*console_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(100));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/stress_test_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/stress_test_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/trace_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/priority_boost/priority_boost_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/data_source_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/qnx/qnx_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/linux/journald_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/stack_sampling_profiler.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/sampling_heap_profiler.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/histogram_samples.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/system_info/system_info_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/track_event/track_event_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/test_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/sys_stats/sys_stats_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/sys_stats_counters.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/smaps_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/perf_event_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/perf_events.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/java_hprof_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/heapprofd_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/process_stats/process_stats_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/statsd_tracing_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/atom_ids.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/protovm/protovm_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/power/android_power_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/interceptor_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/interceptors/console_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/inode_file/inode_file_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_renderstages_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/vulkan_memory_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_counter_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/frozen_ftrace_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/system_metrics.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/etw/etw_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/v8_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/chrome_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/windowmanager_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/user_list_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_transactions_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_layers_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/display_video_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/protolog_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/pixel_modem_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/packages_list_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/network_trace_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/kernel_wakelocks_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/inputmethod_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/cpu_per_uid_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/app_wakelock_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_sdk_sysprop_guard_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_system_property_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_process_state_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_polled_state_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_log_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/android_log_constants.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_input_event_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_game_intervention_list_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_aflags_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/trace_attributes.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/semantic_type.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/builtin_clock.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-BeginImplFrameArgs::BeginImplFrameArgs() = default;
-BeginImplFrameArgs::~BeginImplFrameArgs() = default;
-BeginImplFrameArgs::BeginImplFrameArgs(const BeginImplFrameArgs&) = default;
-BeginImplFrameArgs& BeginImplFrameArgs::operator=(const BeginImplFrameArgs&) = default;
-BeginImplFrameArgs::BeginImplFrameArgs(BeginImplFrameArgs&&) noexcept = default;
-BeginImplFrameArgs& BeginImplFrameArgs::operator=(BeginImplFrameArgs&&) = default;
+StressTestConfig::StressTestConfig() = default;
+StressTestConfig::~StressTestConfig() = default;
+StressTestConfig::StressTestConfig(const StressTestConfig&) = default;
+StressTestConfig& StressTestConfig::operator=(const StressTestConfig&) = default;
+StressTestConfig::StressTestConfig(StressTestConfig&&) noexcept = default;
+StressTestConfig& StressTestConfig::operator=(StressTestConfig&&) = default;

-bool BeginImplFrameArgs::operator==(const BeginImplFrameArgs& other) const {
+bool StressTestConfig::operator==(const StressTestConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(updated_at_us_, other.updated_at_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(finished_at_us_, other.finished_at_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(state_, other.state_)
-   && ::protozero::internal::gen_helpers::EqualsField(current_args_, other.current_args_)
-   && ::protozero::internal::gen_helpers::EqualsField(last_args_, other.last_args_)
-   && ::protozero::internal::gen_helpers::EqualsField(timestamps_in_us_, other.timestamps_in_us_);
+   && ::protozero::internal::gen_helpers::EqualsField(trace_config_, other.trace_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(shmem_size_kb_, other.shmem_size_kb_)
+   && ::protozero::internal::gen_helpers::EqualsField(shmem_page_size_kb_, other.shmem_page_size_kb_)
+   && ::protozero::internal::gen_helpers::EqualsField(num_processes_, other.num_processes_)
+   && ::protozero::internal::gen_helpers::EqualsField(num_threads_, other.num_threads_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_events_, other.max_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(nesting_, other.nesting_)
+   && ::protozero::internal::gen_helpers::EqualsField(steady_state_timings_, other.steady_state_timings_)
+   && ::protozero::internal::gen_helpers::EqualsField(burst_period_ms_, other.burst_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(burst_duration_ms_, other.burst_duration_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(burst_timings_, other.burst_timings_);
 }

-bool BeginImplFrameArgs::ParseFromArray(const void* raw, size_t size) {
+bool StressTestConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -32828,23 +33016,38 @@ bool BeginImplFrameArgs::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* updated_at_us */:
-        field.get(&updated_at_us_);
+      case 1 /* trace_config */:
+        (*trace_config_).ParseFromArray(field.data(), field.size());
         break;
-      case 2 /* finished_at_us */:
-        field.get(&finished_at_us_);
+      case 2 /* shmem_size_kb */:
+        field.get(&shmem_size_kb_);
         break;
-      case 3 /* state */:
-        field.get(&state_);
+      case 3 /* shmem_page_size_kb */:
+        field.get(&shmem_page_size_kb_);
         break;
-      case 4 /* current_args */:
-        (*current_args_).ParseFromArray(field.data(), field.size());
+      case 4 /* num_processes */:
+        field.get(&num_processes_);
         break;
-      case 5 /* last_args */:
-        (*last_args_).ParseFromArray(field.data(), field.size());
+      case 5 /* num_threads */:
+        field.get(&num_threads_);
         break;
-      case 6 /* timestamps_in_us */:
-        (*timestamps_in_us_).ParseFromArray(field.data(), field.size());
+      case 6 /* max_events */:
+        field.get(&max_events_);
+        break;
+      case 7 /* nesting */:
+        field.get(&nesting_);
+        break;
+      case 8 /* steady_state_timings */:
+        (*steady_state_timings_).ParseFromArray(field.data(), field.size());
+        break;
+      case 9 /* burst_period_ms */:
+        field.get(&burst_period_ms_);
+        break;
+      case 10 /* burst_duration_ms */:
+        field.get(&burst_duration_ms_);
+        break;
+      case 11 /* burst_timings */:
+        (*burst_timings_).ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -32854,72 +33057,95 @@ bool BeginImplFrameArgs::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string BeginImplFrameArgs::SerializeAsString() const {
+std::string StressTestConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> BeginImplFrameArgs::SerializeAsArray() const {
+std::vector<uint8_t> StressTestConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void BeginImplFrameArgs::Serialize(::protozero::Message* msg) const {
-  // Field 1: updated_at_us
+void StressTestConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: trace_config
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, updated_at_us_, msg);
+    (*trace_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 2: finished_at_us
+  // Field 2: shmem_size_kb
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, finished_at_us_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, shmem_size_kb_, msg);
   }

-  // Field 3: state
+  // Field 3: shmem_page_size_kb
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, state_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, shmem_page_size_kb_, msg);
   }

-  // Field 4: current_args
+  // Field 4: num_processes
   if (_has_field_[4]) {
-    (*current_args_).Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, num_processes_, msg);
   }

-  // Field 5: last_args
+  // Field 5: num_threads
   if (_has_field_[5]) {
-    (*last_args_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, num_threads_, msg);
   }

-  // Field 6: timestamps_in_us
+  // Field 6: max_events
   if (_has_field_[6]) {
-    (*timestamps_in_us_).Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, max_events_, msg);
+  }
+
+  // Field 7: nesting
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, nesting_, msg);
+  }
+
+  // Field 8: steady_state_timings
+  if (_has_field_[8]) {
+    (*steady_state_timings_).Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
+  }
+
+  // Field 9: burst_period_ms
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(9, burst_period_ms_, msg);
+  }
+
+  // Field 10: burst_duration_ms
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(10, burst_duration_ms_, msg);
+  }
+
+  // Field 11: burst_timings
+  if (_has_field_[11]) {
+    (*burst_timings_).Serialize(msg->BeginNestedMessage<::protozero::Message>(11));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-BeginImplFrameArgs_TimestampsInUs::BeginImplFrameArgs_TimestampsInUs() = default;
-BeginImplFrameArgs_TimestampsInUs::~BeginImplFrameArgs_TimestampsInUs() = default;
-BeginImplFrameArgs_TimestampsInUs::BeginImplFrameArgs_TimestampsInUs(const BeginImplFrameArgs_TimestampsInUs&) = default;
-BeginImplFrameArgs_TimestampsInUs& BeginImplFrameArgs_TimestampsInUs::operator=(const BeginImplFrameArgs_TimestampsInUs&) = default;
-BeginImplFrameArgs_TimestampsInUs::BeginImplFrameArgs_TimestampsInUs(BeginImplFrameArgs_TimestampsInUs&&) noexcept = default;
-BeginImplFrameArgs_TimestampsInUs& BeginImplFrameArgs_TimestampsInUs::operator=(BeginImplFrameArgs_TimestampsInUs&&) = default;
+StressTestConfig_WriterTiming::StressTestConfig_WriterTiming() = default;
+StressTestConfig_WriterTiming::~StressTestConfig_WriterTiming() = default;
+StressTestConfig_WriterTiming::StressTestConfig_WriterTiming(const StressTestConfig_WriterTiming&) = default;
+StressTestConfig_WriterTiming& StressTestConfig_WriterTiming::operator=(const StressTestConfig_WriterTiming&) = default;
+StressTestConfig_WriterTiming::StressTestConfig_WriterTiming(StressTestConfig_WriterTiming&&) noexcept = default;
+StressTestConfig_WriterTiming& StressTestConfig_WriterTiming::operator=(StressTestConfig_WriterTiming&&) = default;

-bool BeginImplFrameArgs_TimestampsInUs::operator==(const BeginImplFrameArgs_TimestampsInUs& other) const {
+bool StressTestConfig_WriterTiming::operator==(const StressTestConfig_WriterTiming& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(interval_delta_, other.interval_delta_)
-   && ::protozero::internal::gen_helpers::EqualsField(now_to_deadline_delta_, other.now_to_deadline_delta_)
-   && ::protozero::internal::gen_helpers::EqualsField(frame_time_to_now_delta_, other.frame_time_to_now_delta_)
-   && ::protozero::internal::gen_helpers::EqualsField(frame_time_to_deadline_delta_, other.frame_time_to_deadline_delta_)
-   && ::protozero::internal::gen_helpers::EqualsField(now_, other.now_)
-   && ::protozero::internal::gen_helpers::EqualsField(frame_time_, other.frame_time_)
-   && ::protozero::internal::gen_helpers::EqualsField(deadline_, other.deadline_);
+   && ::protozero::internal::gen_helpers::EqualsField(payload_mean_, other.payload_mean_)
+   && ::protozero::internal::gen_helpers::EqualsField(payload_stddev_, other.payload_stddev_)
+   && ::protozero::internal::gen_helpers::EqualsField(rate_mean_, other.rate_mean_)
+   && ::protozero::internal::gen_helpers::EqualsField(rate_stddev_, other.rate_stddev_)
+   && ::protozero::internal::gen_helpers::EqualsField(payload_write_time_ms_, other.payload_write_time_ms_);
 }

-bool BeginImplFrameArgs_TimestampsInUs::ParseFromArray(const void* raw, size_t size) {
+bool StressTestConfig_WriterTiming::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -32929,26 +33155,20 @@ bool BeginImplFrameArgs_TimestampsInUs::ParseFromArray(const void* raw, size_t s
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* interval_delta */:
-        field.get(&interval_delta_);
-        break;
-      case 2 /* now_to_deadline_delta */:
-        field.get(&now_to_deadline_delta_);
-        break;
-      case 3 /* frame_time_to_now_delta */:
-        field.get(&frame_time_to_now_delta_);
+      case 1 /* payload_mean */:
+        field.get(&payload_mean_);
         break;
-      case 4 /* frame_time_to_deadline_delta */:
-        field.get(&frame_time_to_deadline_delta_);
+      case 2 /* payload_stddev */:
+        field.get(&payload_stddev_);
         break;
-      case 5 /* now */:
-        field.get(&now_);
+      case 3 /* rate_mean */:
+        field.get(&rate_mean_);
         break;
-      case 6 /* frame_time */:
-        field.get(&frame_time_);
+      case 4 /* rate_stddev */:
+        field.get(&rate_stddev_);
         break;
-      case 7 /* deadline */:
-        field.get(&deadline_);
+      case 5 /* payload_write_time_ms */:
+        field.get(&payload_write_time_ms_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -32958,72 +33178,88 @@ bool BeginImplFrameArgs_TimestampsInUs::ParseFromArray(const void* raw, size_t s
   return !packed_error && !dec.bytes_left();
 }

-std::string BeginImplFrameArgs_TimestampsInUs::SerializeAsString() const {
+std::string StressTestConfig_WriterTiming::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> BeginImplFrameArgs_TimestampsInUs::SerializeAsArray() const {
+std::vector<uint8_t> StressTestConfig_WriterTiming::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void BeginImplFrameArgs_TimestampsInUs::Serialize(::protozero::Message* msg) const {
-  // Field 1: interval_delta
+void StressTestConfig_WriterTiming::Serialize(::protozero::Message* msg) const {
+  // Field 1: payload_mean
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, interval_delta_, msg);
+    ::protozero::internal::gen_helpers::SerializeFixed(1, payload_mean_, msg);
   }

-  // Field 2: now_to_deadline_delta
+  // Field 2: payload_stddev
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, now_to_deadline_delta_, msg);
+    ::protozero::internal::gen_helpers::SerializeFixed(2, payload_stddev_, msg);
   }

-  // Field 3: frame_time_to_now_delta
+  // Field 3: rate_mean
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, frame_time_to_now_delta_, msg);
+    ::protozero::internal::gen_helpers::SerializeFixed(3, rate_mean_, msg);
   }

-  // Field 4: frame_time_to_deadline_delta
+  // Field 4: rate_stddev
   if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, frame_time_to_deadline_delta_, msg);
+    ::protozero::internal::gen_helpers::SerializeFixed(4, rate_stddev_, msg);
   }

-  // Field 5: now
+  // Field 5: payload_write_time_ms
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, now_, msg);
-  }
-
-  // Field 6: frame_time
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, frame_time_, msg);
-  }
-
-  // Field 7: deadline
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, deadline_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, payload_write_time_ms_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/test_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/test_config.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-ChromeCompositorStateMachine::ChromeCompositorStateMachine() = default;
-ChromeCompositorStateMachine::~ChromeCompositorStateMachine() = default;
-ChromeCompositorStateMachine::ChromeCompositorStateMachine(const ChromeCompositorStateMachine&) = default;
-ChromeCompositorStateMachine& ChromeCompositorStateMachine::operator=(const ChromeCompositorStateMachine&) = default;
-ChromeCompositorStateMachine::ChromeCompositorStateMachine(ChromeCompositorStateMachine&&) noexcept = default;
-ChromeCompositorStateMachine& ChromeCompositorStateMachine::operator=(ChromeCompositorStateMachine&&) = default;
+TestConfig::TestConfig() = default;
+TestConfig::~TestConfig() = default;
+TestConfig::TestConfig(const TestConfig&) = default;
+TestConfig& TestConfig::operator=(const TestConfig&) = default;
+TestConfig::TestConfig(TestConfig&&) noexcept = default;
+TestConfig& TestConfig::operator=(TestConfig&&) = default;

-bool ChromeCompositorStateMachine::operator==(const ChromeCompositorStateMachine& other) const {
+bool TestConfig::operator==(const TestConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(major_state_, other.major_state_)
-   && ::protozero::internal::gen_helpers::EqualsField(minor_state_, other.minor_state_);
+   && ::protozero::internal::gen_helpers::EqualsField(message_count_, other.message_count_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_messages_per_second_, other.max_messages_per_second_)
+   && ::protozero::internal::gen_helpers::EqualsField(seed_, other.seed_)
+   && ::protozero::internal::gen_helpers::EqualsField(message_size_, other.message_size_)
+   && ::protozero::internal::gen_helpers::EqualsField(send_batch_on_register_, other.send_batch_on_register_)
+   && ::protozero::internal::gen_helpers::EqualsField(dummy_fields_, other.dummy_fields_);
 }

-bool ChromeCompositorStateMachine::ParseFromArray(const void* raw, size_t size) {
+bool TestConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -33033,11 +33269,23 @@ bool ChromeCompositorStateMachine::ParseFromArray(const void* raw, size_t size)
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* major_state */:
-        (*major_state_).ParseFromArray(field.data(), field.size());
+      case 1 /* message_count */:
+        field.get(&message_count_);
         break;
-      case 2 /* minor_state */:
-        (*minor_state_).ParseFromArray(field.data(), field.size());
+      case 2 /* max_messages_per_second */:
+        field.get(&max_messages_per_second_);
+        break;
+      case 3 /* seed */:
+        field.get(&seed_);
+        break;
+      case 4 /* message_size */:
+        field.get(&message_size_);
+        break;
+      case 5 /* send_batch_on_register */:
+        field.get(&send_batch_on_register_);
+        break;
+      case 6 /* dummy_fields */:
+        (*dummy_fields_).ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -33047,90 +33295,79 @@ bool ChromeCompositorStateMachine::ParseFromArray(const void* raw, size_t size)
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeCompositorStateMachine::SerializeAsString() const {
+std::string TestConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeCompositorStateMachine::SerializeAsArray() const {
+std::vector<uint8_t> TestConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeCompositorStateMachine::Serialize(::protozero::Message* msg) const {
-  // Field 1: major_state
+void TestConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: message_count
   if (_has_field_[1]) {
-    (*major_state_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, message_count_, msg);
   }

-  // Field 2: minor_state
+  // Field 2: max_messages_per_second
   if (_has_field_[2]) {
-    (*minor_state_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, max_messages_per_second_, msg);
+  }
+
+  // Field 3: seed
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, seed_, msg);
+  }
+
+  // Field 4: message_size
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, message_size_, msg);
+  }
+
+  // Field 5: send_batch_on_register
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, send_batch_on_register_, msg);
+  }
+
+  // Field 6: dummy_fields
+  if (_has_field_[6]) {
+    (*dummy_fields_).Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-ChromeCompositorStateMachine_MinorState::ChromeCompositorStateMachine_MinorState() = default;
-ChromeCompositorStateMachine_MinorState::~ChromeCompositorStateMachine_MinorState() = default;
-ChromeCompositorStateMachine_MinorState::ChromeCompositorStateMachine_MinorState(const ChromeCompositorStateMachine_MinorState&) = default;
-ChromeCompositorStateMachine_MinorState& ChromeCompositorStateMachine_MinorState::operator=(const ChromeCompositorStateMachine_MinorState&) = default;
-ChromeCompositorStateMachine_MinorState::ChromeCompositorStateMachine_MinorState(ChromeCompositorStateMachine_MinorState&&) noexcept = default;
-ChromeCompositorStateMachine_MinorState& ChromeCompositorStateMachine_MinorState::operator=(ChromeCompositorStateMachine_MinorState&&) = default;
+TestConfig_DummyFields::TestConfig_DummyFields() = default;
+TestConfig_DummyFields::~TestConfig_DummyFields() = default;
+TestConfig_DummyFields::TestConfig_DummyFields(const TestConfig_DummyFields&) = default;
+TestConfig_DummyFields& TestConfig_DummyFields::operator=(const TestConfig_DummyFields&) = default;
+TestConfig_DummyFields::TestConfig_DummyFields(TestConfig_DummyFields&&) noexcept = default;
+TestConfig_DummyFields& TestConfig_DummyFields::operator=(TestConfig_DummyFields&&) = default;

-bool ChromeCompositorStateMachine_MinorState::operator==(const ChromeCompositorStateMachine_MinorState& other) const {
+bool TestConfig_DummyFields::operator==(const TestConfig_DummyFields& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(commit_count_, other.commit_count_)
-   && ::protozero::internal::gen_helpers::EqualsField(current_frame_number_, other.current_frame_number_)
-   && ::protozero::internal::gen_helpers::EqualsField(last_frame_number_submit_performed_, other.last_frame_number_submit_performed_)
-   && ::protozero::internal::gen_helpers::EqualsField(last_frame_number_draw_performed_, other.last_frame_number_draw_performed_)
-   && ::protozero::internal::gen_helpers::EqualsField(last_frame_number_begin_main_frame_sent_, other.last_frame_number_begin_main_frame_sent_)
-   && ::protozero::internal::gen_helpers::EqualsField(did_draw_, other.did_draw_)
-   && ::protozero::internal::gen_helpers::EqualsField(did_send_begin_main_frame_for_current_frame_, other.did_send_begin_main_frame_for_current_frame_)
-   && ::protozero::internal::gen_helpers::EqualsField(did_notify_begin_main_frame_not_expected_until_, other.did_notify_begin_main_frame_not_expected_until_)
-   && ::protozero::internal::gen_helpers::EqualsField(did_notify_begin_main_frame_not_expected_soon_, other.did_notify_begin_main_frame_not_expected_soon_)
-   && ::protozero::internal::gen_helpers::EqualsField(wants_begin_main_frame_not_expected_, other.wants_begin_main_frame_not_expected_)
-   && ::protozero::internal::gen_helpers::EqualsField(did_commit_during_frame_, other.did_commit_during_frame_)
-   && ::protozero::internal::gen_helpers::EqualsField(did_invalidate_layer_tree_frame_sink_, other.did_invalidate_layer_tree_frame_sink_)
-   && ::protozero::internal::gen_helpers::EqualsField(did_perform_impl_side_invalidaion_, other.did_perform_impl_side_invalidaion_)
-   && ::protozero::internal::gen_helpers::EqualsField(did_prepare_tiles_, other.did_prepare_tiles_)
-   && ::protozero::internal::gen_helpers::EqualsField(consecutive_checkerboard_animations_, other.consecutive_checkerboard_animations_)
-   && ::protozero::internal::gen_helpers::EqualsField(pending_submit_frames_, other.pending_submit_frames_)
-   && ::protozero::internal::gen_helpers::EqualsField(submit_frames_with_current_layer_tree_frame_sink_, other.submit_frames_with_current_layer_tree_frame_sink_)
-   && ::protozero::internal::gen_helpers::EqualsField(needs_redraw_, other.needs_redraw_)
-   && ::protozero::internal::gen_helpers::EqualsField(needs_prepare_tiles_, other.needs_prepare_tiles_)
-   && ::protozero::internal::gen_helpers::EqualsField(needs_begin_main_frame_, other.needs_begin_main_frame_)
-   && ::protozero::internal::gen_helpers::EqualsField(needs_one_begin_impl_frame_, other.needs_one_begin_impl_frame_)
-   && ::protozero::internal::gen_helpers::EqualsField(visible_, other.visible_)
-   && ::protozero::internal::gen_helpers::EqualsField(begin_frame_source_paused_, other.begin_frame_source_paused_)
-   && ::protozero::internal::gen_helpers::EqualsField(can_draw_, other.can_draw_)
-   && ::protozero::internal::gen_helpers::EqualsField(resourceless_draw_, other.resourceless_draw_)
-   && ::protozero::internal::gen_helpers::EqualsField(has_pending_tree_, other.has_pending_tree_)
-   && ::protozero::internal::gen_helpers::EqualsField(pending_tree_is_ready_for_activation_, other.pending_tree_is_ready_for_activation_)
-   && ::protozero::internal::gen_helpers::EqualsField(active_tree_needs_first_draw_, other.active_tree_needs_first_draw_)
-   && ::protozero::internal::gen_helpers::EqualsField(active_tree_is_ready_to_draw_, other.active_tree_is_ready_to_draw_)
-   && ::protozero::internal::gen_helpers::EqualsField(did_create_and_initialize_first_layer_tree_frame_sink_, other.did_create_and_initialize_first_layer_tree_frame_sink_)
-   && ::protozero::internal::gen_helpers::EqualsField(tree_priority_, other.tree_priority_)
-   && ::protozero::internal::gen_helpers::EqualsField(scroll_handler_state_, other.scroll_handler_state_)
-   && ::protozero::internal::gen_helpers::EqualsField(critical_begin_main_frame_to_activate_is_fast_, other.critical_begin_main_frame_to_activate_is_fast_)
-   && ::protozero::internal::gen_helpers::EqualsField(main_thread_missed_last_deadline_, other.main_thread_missed_last_deadline_)
-   && ::protozero::internal::gen_helpers::EqualsField(video_needs_begin_frames_, other.video_needs_begin_frames_)
-   && ::protozero::internal::gen_helpers::EqualsField(defer_begin_main_frame_, other.defer_begin_main_frame_)
-   && ::protozero::internal::gen_helpers::EqualsField(last_commit_had_no_updates_, other.last_commit_had_no_updates_)
-   && ::protozero::internal::gen_helpers::EqualsField(did_draw_in_last_frame_, other.did_draw_in_last_frame_)
-   && ::protozero::internal::gen_helpers::EqualsField(did_submit_in_last_frame_, other.did_submit_in_last_frame_)
-   && ::protozero::internal::gen_helpers::EqualsField(needs_impl_side_invalidation_, other.needs_impl_side_invalidation_)
-   && ::protozero::internal::gen_helpers::EqualsField(current_pending_tree_is_impl_side_, other.current_pending_tree_is_impl_side_)
-   && ::protozero::internal::gen_helpers::EqualsField(previous_pending_tree_was_impl_side_, other.previous_pending_tree_was_impl_side_)
-   && ::protozero::internal::gen_helpers::EqualsField(processing_animation_worklets_for_active_tree_, other.processing_animation_worklets_for_active_tree_)
-   && ::protozero::internal::gen_helpers::EqualsField(processing_animation_worklets_for_pending_tree_, other.processing_animation_worklets_for_pending_tree_)
-   && ::protozero::internal::gen_helpers::EqualsField(processing_paint_worklets_for_pending_tree_, other.processing_paint_worklets_for_pending_tree_);
+   && ::protozero::internal::gen_helpers::EqualsField(field_uint32_, other.field_uint32_)
+   && ::protozero::internal::gen_helpers::EqualsField(field_int32_, other.field_int32_)
+   && ::protozero::internal::gen_helpers::EqualsField(field_uint64_, other.field_uint64_)
+   && ::protozero::internal::gen_helpers::EqualsField(field_int64_, other.field_int64_)
+   && ::protozero::internal::gen_helpers::EqualsField(field_fixed64_, other.field_fixed64_)
+   && ::protozero::internal::gen_helpers::EqualsField(field_sfixed64_, other.field_sfixed64_)
+   && ::protozero::internal::gen_helpers::EqualsField(field_fixed32_, other.field_fixed32_)
+   && ::protozero::internal::gen_helpers::EqualsField(field_sfixed32_, other.field_sfixed32_)
+   && ::protozero::internal::gen_helpers::EqualsField(field_double_, other.field_double_)
+   && ::protozero::internal::gen_helpers::EqualsField(field_float_, other.field_float_)
+   && ::protozero::internal::gen_helpers::EqualsField(field_sint64_, other.field_sint64_)
+   && ::protozero::internal::gen_helpers::EqualsField(field_sint32_, other.field_sint32_)
+   && ::protozero::internal::gen_helpers::EqualsField(field_string_, other.field_string_)
+   && ::protozero::internal::gen_helpers::EqualsField(field_bytes_, other.field_bytes_);
 }

-bool ChromeCompositorStateMachine_MinorState::ParseFromArray(const void* raw, size_t size) {
+bool TestConfig_DummyFields::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -33140,140 +33377,47 @@ bool ChromeCompositorStateMachine_MinorState::ParseFromArray(const void* raw, si
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* commit_count */:
-        field.get(&commit_count_);
-        break;
-      case 2 /* current_frame_number */:
-        field.get(&current_frame_number_);
-        break;
-      case 3 /* last_frame_number_submit_performed */:
-        field.get(&last_frame_number_submit_performed_);
-        break;
-      case 4 /* last_frame_number_draw_performed */:
-        field.get(&last_frame_number_draw_performed_);
-        break;
-      case 5 /* last_frame_number_begin_main_frame_sent */:
-        field.get(&last_frame_number_begin_main_frame_sent_);
-        break;
-      case 6 /* did_draw */:
-        field.get(&did_draw_);
-        break;
-      case 7 /* did_send_begin_main_frame_for_current_frame */:
-        field.get(&did_send_begin_main_frame_for_current_frame_);
-        break;
-      case 8 /* did_notify_begin_main_frame_not_expected_until */:
-        field.get(&did_notify_begin_main_frame_not_expected_until_);
-        break;
-      case 9 /* did_notify_begin_main_frame_not_expected_soon */:
-        field.get(&did_notify_begin_main_frame_not_expected_soon_);
-        break;
-      case 10 /* wants_begin_main_frame_not_expected */:
-        field.get(&wants_begin_main_frame_not_expected_);
-        break;
-      case 11 /* did_commit_during_frame */:
-        field.get(&did_commit_during_frame_);
-        break;
-      case 12 /* did_invalidate_layer_tree_frame_sink */:
-        field.get(&did_invalidate_layer_tree_frame_sink_);
-        break;
-      case 13 /* did_perform_impl_side_invalidaion */:
-        field.get(&did_perform_impl_side_invalidaion_);
-        break;
-      case 14 /* did_prepare_tiles */:
-        field.get(&did_prepare_tiles_);
-        break;
-      case 15 /* consecutive_checkerboard_animations */:
-        field.get(&consecutive_checkerboard_animations_);
-        break;
-      case 16 /* pending_submit_frames */:
-        field.get(&pending_submit_frames_);
-        break;
-      case 17 /* submit_frames_with_current_layer_tree_frame_sink */:
-        field.get(&submit_frames_with_current_layer_tree_frame_sink_);
-        break;
-      case 18 /* needs_redraw */:
-        field.get(&needs_redraw_);
-        break;
-      case 19 /* needs_prepare_tiles */:
-        field.get(&needs_prepare_tiles_);
-        break;
-      case 20 /* needs_begin_main_frame */:
-        field.get(&needs_begin_main_frame_);
-        break;
-      case 21 /* needs_one_begin_impl_frame */:
-        field.get(&needs_one_begin_impl_frame_);
-        break;
-      case 22 /* visible */:
-        field.get(&visible_);
-        break;
-      case 23 /* begin_frame_source_paused */:
-        field.get(&begin_frame_source_paused_);
-        break;
-      case 24 /* can_draw */:
-        field.get(&can_draw_);
-        break;
-      case 25 /* resourceless_draw */:
-        field.get(&resourceless_draw_);
-        break;
-      case 26 /* has_pending_tree */:
-        field.get(&has_pending_tree_);
-        break;
-      case 27 /* pending_tree_is_ready_for_activation */:
-        field.get(&pending_tree_is_ready_for_activation_);
-        break;
-      case 28 /* active_tree_needs_first_draw */:
-        field.get(&active_tree_needs_first_draw_);
-        break;
-      case 29 /* active_tree_is_ready_to_draw */:
-        field.get(&active_tree_is_ready_to_draw_);
-        break;
-      case 30 /* did_create_and_initialize_first_layer_tree_frame_sink */:
-        field.get(&did_create_and_initialize_first_layer_tree_frame_sink_);
-        break;
-      case 31 /* tree_priority */:
-        field.get(&tree_priority_);
-        break;
-      case 32 /* scroll_handler_state */:
-        field.get(&scroll_handler_state_);
+      case 1 /* field_uint32 */:
+        field.get(&field_uint32_);
         break;
-      case 33 /* critical_begin_main_frame_to_activate_is_fast */:
-        field.get(&critical_begin_main_frame_to_activate_is_fast_);
+      case 2 /* field_int32 */:
+        field.get(&field_int32_);
         break;
-      case 34 /* main_thread_missed_last_deadline */:
-        field.get(&main_thread_missed_last_deadline_);
+      case 3 /* field_uint64 */:
+        field.get(&field_uint64_);
         break;
-      case 36 /* video_needs_begin_frames */:
-        field.get(&video_needs_begin_frames_);
+      case 4 /* field_int64 */:
+        field.get(&field_int64_);
         break;
-      case 37 /* defer_begin_main_frame */:
-        field.get(&defer_begin_main_frame_);
+      case 5 /* field_fixed64 */:
+        field.get(&field_fixed64_);
         break;
-      case 38 /* last_commit_had_no_updates */:
-        field.get(&last_commit_had_no_updates_);
+      case 6 /* field_sfixed64 */:
+        field.get(&field_sfixed64_);
         break;
-      case 39 /* did_draw_in_last_frame */:
-        field.get(&did_draw_in_last_frame_);
+      case 7 /* field_fixed32 */:
+        field.get(&field_fixed32_);
         break;
-      case 40 /* did_submit_in_last_frame */:
-        field.get(&did_submit_in_last_frame_);
+      case 8 /* field_sfixed32 */:
+        field.get(&field_sfixed32_);
         break;
-      case 41 /* needs_impl_side_invalidation */:
-        field.get(&needs_impl_side_invalidation_);
+      case 9 /* field_double */:
+        field.get(&field_double_);
         break;
-      case 42 /* current_pending_tree_is_impl_side */:
-        field.get(&current_pending_tree_is_impl_side_);
+      case 10 /* field_float */:
+        field.get(&field_float_);
         break;
-      case 43 /* previous_pending_tree_was_impl_side */:
-        field.get(&previous_pending_tree_was_impl_side_);
+      case 11 /* field_sint64 */:
+        field.get_signed(&field_sint64_);
         break;
-      case 44 /* processing_animation_worklets_for_active_tree */:
-        field.get(&processing_animation_worklets_for_active_tree_);
+      case 12 /* field_sint32 */:
+        field.get_signed(&field_sint32_);
         break;
-      case 45 /* processing_animation_worklets_for_pending_tree */:
-        field.get(&processing_animation_worklets_for_pending_tree_);
+      case 13 /* field_string */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &field_string_);
         break;
-      case 46 /* processing_paint_worklets_for_pending_tree */:
-        field.get(&processing_paint_worklets_for_pending_tree_);
+      case 14 /* field_bytes */:
+        field.get(&field_bytes_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -33283,265 +33427,652 @@ bool ChromeCompositorStateMachine_MinorState::ParseFromArray(const void* raw, si
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeCompositorStateMachine_MinorState::SerializeAsString() const {
+std::string TestConfig_DummyFields::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeCompositorStateMachine_MinorState::SerializeAsArray() const {
+std::vector<uint8_t> TestConfig_DummyFields::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeCompositorStateMachine_MinorState::Serialize(::protozero::Message* msg) const {
-  // Field 1: commit_count
+void TestConfig_DummyFields::Serialize(::protozero::Message* msg) const {
+  // Field 1: field_uint32
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, commit_count_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, field_uint32_, msg);
   }

-  // Field 2: current_frame_number
+  // Field 2: field_int32
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, current_frame_number_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, field_int32_, msg);
   }

-  // Field 3: last_frame_number_submit_performed
+  // Field 3: field_uint64
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, last_frame_number_submit_performed_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, field_uint64_, msg);
   }

-  // Field 4: last_frame_number_draw_performed
+  // Field 4: field_int64
   if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, last_frame_number_draw_performed_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, field_int64_, msg);
   }

-  // Field 5: last_frame_number_begin_main_frame_sent
+  // Field 5: field_fixed64
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, last_frame_number_begin_main_frame_sent_, msg);
+    ::protozero::internal::gen_helpers::SerializeFixed(5, field_fixed64_, msg);
   }

-  // Field 6: did_draw
+  // Field 6: field_sfixed64
   if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(6, did_draw_, msg);
+    ::protozero::internal::gen_helpers::SerializeFixed(6, field_sfixed64_, msg);
   }

-  // Field 7: did_send_begin_main_frame_for_current_frame
+  // Field 7: field_fixed32
   if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, did_send_begin_main_frame_for_current_frame_, msg);
+    ::protozero::internal::gen_helpers::SerializeFixed(7, field_fixed32_, msg);
   }

-  // Field 8: did_notify_begin_main_frame_not_expected_until
+  // Field 8: field_sfixed32
   if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, did_notify_begin_main_frame_not_expected_until_, msg);
+    ::protozero::internal::gen_helpers::SerializeFixed(8, field_sfixed32_, msg);
   }

-  // Field 9: did_notify_begin_main_frame_not_expected_soon
+  // Field 9: field_double
   if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, did_notify_begin_main_frame_not_expected_soon_, msg);
+    ::protozero::internal::gen_helpers::SerializeFixed(9, field_double_, msg);
   }

-  // Field 10: wants_begin_main_frame_not_expected
+  // Field 10: field_float
   if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(10, wants_begin_main_frame_not_expected_, msg);
+    ::protozero::internal::gen_helpers::SerializeFixed(10, field_float_, msg);
   }

-  // Field 11: did_commit_during_frame
+  // Field 11: field_sint64
   if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(11, did_commit_during_frame_, msg);
+    ::protozero::internal::gen_helpers::SerializeSignedVarInt(11, field_sint64_, msg);
   }

-  // Field 12: did_invalidate_layer_tree_frame_sink
+  // Field 12: field_sint32
   if (_has_field_[12]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(12, did_invalidate_layer_tree_frame_sink_, msg);
+    ::protozero::internal::gen_helpers::SerializeSignedVarInt(12, field_sint32_, msg);
   }

-  // Field 13: did_perform_impl_side_invalidaion
+  // Field 13: field_string
   if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(13, did_perform_impl_side_invalidaion_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(13, field_string_, msg);
   }

-  // Field 14: did_prepare_tiles
+  // Field 14: field_bytes
   if (_has_field_[14]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(14, did_prepare_tiles_, msg);
-  }
-
-  // Field 15: consecutive_checkerboard_animations
-  if (_has_field_[15]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(15, consecutive_checkerboard_animations_, msg);
-  }
-
-  // Field 16: pending_submit_frames
-  if (_has_field_[16]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(16, pending_submit_frames_, msg);
-  }
-
-  // Field 17: submit_frames_with_current_layer_tree_frame_sink
-  if (_has_field_[17]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(17, submit_frames_with_current_layer_tree_frame_sink_, msg);
-  }
-
-  // Field 18: needs_redraw
-  if (_has_field_[18]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(18, needs_redraw_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(14, field_bytes_, msg);
   }

-  // Field 19: needs_prepare_tiles
-  if (_has_field_[19]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(19, needs_prepare_tiles_, msg);
-  }
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}

-  // Field 20: needs_begin_main_frame
-  if (_has_field_[20]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(20, needs_begin_main_frame_, msg);
-  }
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/config/trace_config.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/config/trace_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/priority_boost/priority_boost_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/data_source_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/qnx/qnx_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/linux/journald_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/stack_sampling_profiler.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/sampling_heap_profiler.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/histogram_samples.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/system_info/system_info_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/track_event/track_event_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/test_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/sys_stats/sys_stats_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/sys_stats_counters.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/smaps_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/perf_event_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/perf_events.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/java_hprof_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/heapprofd_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/process_stats/process_stats_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/statsd_tracing_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/statsd/atom_ids.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/protovm/protovm_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/power/android_power_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/interceptor_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/interceptors/console_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/inode_file/inode_file_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_renderstages_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/vulkan_memory_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/gpu/gpu_counter_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/frozen_ftrace_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/system_metrics.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/etw/etw_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/v8_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/chrome_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/windowmanager_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/user_list_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_transactions_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/surfaceflinger_layers_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/display_video_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/protolog_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/pixel_modem_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/packages_list_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/network_trace_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/kernel_wakelocks_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/inputmethod_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/cpu_per_uid_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/app_wakelock_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_sdk_sysprop_guard_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_system_property_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_process_state_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_polled_state_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_log_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/android_log_constants.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_input_event_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_game_intervention_list_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_aflags_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/trace_attributes.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/semantic_type.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/builtin_clock.gen.h"

-  // Field 21: needs_one_begin_impl_frame
-  if (_has_field_[21]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(21, needs_one_begin_impl_frame_, msg);
-  }
+namespace perfetto {
+namespace protos {
+namespace gen {

-  // Field 22: visible
-  if (_has_field_[22]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(22, visible_, msg);
-  }
+TraceConfig::TraceConfig() = default;
+TraceConfig::~TraceConfig() = default;
+TraceConfig::TraceConfig(const TraceConfig&) = default;
+TraceConfig& TraceConfig::operator=(const TraceConfig&) = default;
+TraceConfig::TraceConfig(TraceConfig&&) noexcept = default;
+TraceConfig& TraceConfig::operator=(TraceConfig&&) = default;

-  // Field 23: begin_frame_source_paused
-  if (_has_field_[23]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(23, begin_frame_source_paused_, msg);
-  }
+bool TraceConfig::operator==(const TraceConfig& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(buffers_, other.buffers_)
+   && ::protozero::internal::gen_helpers::EqualsField(experimental_trace_buffer_v2_compaction_, other.experimental_trace_buffer_v2_compaction_)
+   && ::protozero::internal::gen_helpers::EqualsField(data_sources_, other.data_sources_)
+   && ::protozero::internal::gen_helpers::EqualsField(builtin_data_sources_, other.builtin_data_sources_)
+   && ::protozero::internal::gen_helpers::EqualsField(duration_ms_, other.duration_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(prefer_suspend_clock_for_duration_, other.prefer_suspend_clock_for_duration_)
+   && ::protozero::internal::gen_helpers::EqualsField(enable_extra_guardrails_, other.enable_extra_guardrails_)
+   && ::protozero::internal::gen_helpers::EqualsField(lockdown_mode_, other.lockdown_mode_)
+   && ::protozero::internal::gen_helpers::EqualsField(producers_, other.producers_)
+   && ::protozero::internal::gen_helpers::EqualsField(statsd_metadata_, other.statsd_metadata_)
+   && ::protozero::internal::gen_helpers::EqualsField(write_into_file_, other.write_into_file_)
+   && ::protozero::internal::gen_helpers::EqualsField(output_path_, other.output_path_)
+   && ::protozero::internal::gen_helpers::EqualsField(file_write_period_ms_, other.file_write_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_file_size_bytes_, other.max_file_size_bytes_)
+   && ::protozero::internal::gen_helpers::EqualsField(guardrail_overrides_, other.guardrail_overrides_)
+   && ::protozero::internal::gen_helpers::EqualsField(deferred_start_, other.deferred_start_)
+   && ::protozero::internal::gen_helpers::EqualsField(flush_period_ms_, other.flush_period_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(flush_timeout_ms_, other.flush_timeout_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(data_source_stop_timeout_ms_, other.data_source_stop_timeout_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(notify_traceur_, other.notify_traceur_)
+   && ::protozero::internal::gen_helpers::EqualsField(bugreport_score_, other.bugreport_score_)
+   && ::protozero::internal::gen_helpers::EqualsField(bugreport_filename_, other.bugreport_filename_)
+   && ::protozero::internal::gen_helpers::EqualsField(trigger_config_, other.trigger_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(activate_triggers_, other.activate_triggers_)
+   && ::protozero::internal::gen_helpers::EqualsField(incremental_state_config_, other.incremental_state_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(allow_user_build_tracing_, other.allow_user_build_tracing_)
+   && ::protozero::internal::gen_helpers::EqualsField(unique_session_name_, other.unique_session_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(compression_type_, other.compression_type_)
+   && ::protozero::internal::gen_helpers::EqualsField(compression_, other.compression_)
+   && ::protozero::internal::gen_helpers::EqualsField(incident_report_config_, other.incident_report_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(statsd_logging_, other.statsd_logging_)
+   && ::protozero::internal::gen_helpers::EqualsField(trace_uuid_msb_, other.trace_uuid_msb_)
+   && ::protozero::internal::gen_helpers::EqualsField(trace_uuid_lsb_, other.trace_uuid_lsb_)
+   && ::protozero::internal::gen_helpers::EqualsField(trace_filter_, other.trace_filter_)
+   && ::protozero::internal::gen_helpers::EqualsField(android_report_config_, other.android_report_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(cmd_trace_start_delay_, other.cmd_trace_start_delay_)
+   && ::protozero::internal::gen_helpers::EqualsField(session_semaphores_, other.session_semaphores_)
+   && ::protozero::internal::gen_helpers::EqualsField(priority_boost_, other.priority_boost_)
+   && ::protozero::internal::gen_helpers::EqualsField(exclusive_prio_, other.exclusive_prio_)
+   && ::protozero::internal::gen_helpers::EqualsField(write_flush_mode_, other.write_flush_mode_)
+   && ::protozero::internal::gen_helpers::EqualsField(fflush_post_write_, other.fflush_post_write_)
+   && ::protozero::internal::gen_helpers::EqualsField(trace_all_machines_, other.trace_all_machines_)
+   && ::protozero::internal::gen_helpers::EqualsField(trace_attributes_, other.trace_attributes_)
+   && ::protozero::internal::gen_helpers::EqualsField(persist_trace_across_reboots_, other.persist_trace_across_reboots_);
+}

-  // Field 24: can_draw
-  if (_has_field_[24]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(24, can_draw_, msg);
-  }
+int TraceConfig::buffers_size() const { return static_cast<int>(buffers_.size()); }
+void TraceConfig::clear_buffers() { buffers_.clear(); }
+TraceConfig_BufferConfig* TraceConfig::add_buffers() { buffers_.emplace_back(); return &buffers_.back(); }
+int TraceConfig::data_sources_size() const { return static_cast<int>(data_sources_.size()); }
+void TraceConfig::clear_data_sources() { data_sources_.clear(); }
+TraceConfig_DataSource* TraceConfig::add_data_sources() { data_sources_.emplace_back(); return &data_sources_.back(); }
+int TraceConfig::producers_size() const { return static_cast<int>(producers_.size()); }
+void TraceConfig::clear_producers() { producers_.clear(); }
+TraceConfig_ProducerConfig* TraceConfig::add_producers() { producers_.emplace_back(); return &producers_.back(); }
+int TraceConfig::session_semaphores_size() const { return static_cast<int>(session_semaphores_.size()); }
+void TraceConfig::clear_session_semaphores() { session_semaphores_.clear(); }
+TraceConfig_SessionSemaphore* TraceConfig::add_session_semaphores() { session_semaphores_.emplace_back(); return &session_semaphores_.back(); }
+bool TraceConfig::ParseFromArray(const void* raw, size_t size) {
+  buffers_.clear();
+  data_sources_.clear();
+  producers_.clear();
+  activate_triggers_.clear();
+  session_semaphores_.clear();
+  unknown_fields_.clear();
+  bool packed_error = false;

-  // Field 25: resourceless_draw
-  if (_has_field_[25]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(25, resourceless_draw_, msg);
-  }
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* buffers */:
+        buffers_.emplace_back();
+        buffers_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 50 /* experimental_trace_buffer_v2_compaction */:
+        field.get(&experimental_trace_buffer_v2_compaction_);
+        break;
+      case 2 /* data_sources */:
+        data_sources_.emplace_back();
+        data_sources_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 20 /* builtin_data_sources */:
+        (*builtin_data_sources_).ParseFromArray(field.data(), field.size());
+        break;
+      case 3 /* duration_ms */:
+        field.get(&duration_ms_);
+        break;
+      case 36 /* prefer_suspend_clock_for_duration */:
+        field.get(&prefer_suspend_clock_for_duration_);
+        break;
+      case 4 /* enable_extra_guardrails */:
+        field.get(&enable_extra_guardrails_);
+        break;
+      case 5 /* lockdown_mode */:
+        field.get(&lockdown_mode_);
+        break;
+      case 6 /* producers */:
+        producers_.emplace_back();
+        producers_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 7 /* statsd_metadata */:
+        (*statsd_metadata_).ParseFromArray(field.data(), field.size());
+        break;
+      case 8 /* write_into_file */:
+        field.get(&write_into_file_);
+        break;
+      case 29 /* output_path */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &output_path_);
+        break;
+      case 9 /* file_write_period_ms */:
+        field.get(&file_write_period_ms_);
+        break;
+      case 10 /* max_file_size_bytes */:
+        field.get(&max_file_size_bytes_);
+        break;
+      case 11 /* guardrail_overrides */:
+        (*guardrail_overrides_).ParseFromArray(field.data(), field.size());
+        break;
+      case 12 /* deferred_start */:
+        field.get(&deferred_start_);
+        break;
+      case 13 /* flush_period_ms */:
+        field.get(&flush_period_ms_);
+        break;
+      case 14 /* flush_timeout_ms */:
+        field.get(&flush_timeout_ms_);
+        break;
+      case 23 /* data_source_stop_timeout_ms */:
+        field.get(&data_source_stop_timeout_ms_);
+        break;
+      case 16 /* notify_traceur */:
+        field.get(&notify_traceur_);
+        break;
+      case 30 /* bugreport_score */:
+        field.get(&bugreport_score_);
+        break;
+      case 38 /* bugreport_filename */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &bugreport_filename_);
+        break;
+      case 17 /* trigger_config */:
+        (*trigger_config_).ParseFromArray(field.data(), field.size());
+        break;
+      case 18 /* activate_triggers */:
+        activate_triggers_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &activate_triggers_.back());
+        break;
+      case 21 /* incremental_state_config */:
+        (*incremental_state_config_).ParseFromArray(field.data(), field.size());
+        break;
+      case 19 /* allow_user_build_tracing */:
+        field.get(&allow_user_build_tracing_);
+        break;
+      case 22 /* unique_session_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &unique_session_name_);
+        break;
+      case 24 /* compression_type */:
+        field.get(&compression_type_);
+        break;
+      case 47 /* compression */:
+        (*compression_).ParseFromArray(field.data(), field.size());
+        break;
+      case 25 /* incident_report_config */:
+        (*incident_report_config_).ParseFromArray(field.data(), field.size());
+        break;
+      case 31 /* statsd_logging */:
+        field.get(&statsd_logging_);
+        break;
+      case 27 /* trace_uuid_msb */:
+        field.get(&trace_uuid_msb_);
+        break;
+      case 28 /* trace_uuid_lsb */:
+        field.get(&trace_uuid_lsb_);
+        break;
+      case 33 /* trace_filter */:
+        (*trace_filter_).ParseFromArray(field.data(), field.size());
+        break;
+      case 34 /* android_report_config */:
+        (*android_report_config_).ParseFromArray(field.data(), field.size());
+        break;
+      case 35 /* cmd_trace_start_delay */:
+        (*cmd_trace_start_delay_).ParseFromArray(field.data(), field.size());
+        break;
+      case 39 /* session_semaphores */:
+        session_semaphores_.emplace_back();
+        session_semaphores_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 40 /* priority_boost */:
+        (*priority_boost_).ParseFromArray(field.data(), field.size());
+        break;
+      case 41 /* exclusive_prio */:
+        field.get(&exclusive_prio_);
+        break;
+      case 44 /* write_flush_mode */:
+        field.get(&write_flush_mode_);
+        break;
+      case 45 /* fflush_post_write */:
+        field.get(&fflush_post_write_);
+        break;
+      case 43 /* trace_all_machines */:
+        field.get(&trace_all_machines_);
+        break;
+      case 48 /* trace_attributes */:
+        (*trace_attributes_).ParseFromArray(field.data(), field.size());
+        break;
+      case 49 /* persist_trace_across_reboots */:
+        field.get(&persist_trace_across_reboots_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 26: has_pending_tree
-  if (_has_field_[26]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(26, has_pending_tree_, msg);
+std::string TraceConfig::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> TraceConfig::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void TraceConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: buffers
+  for (auto& it : buffers_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 27: pending_tree_is_ready_for_activation
-  if (_has_field_[27]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(27, pending_tree_is_ready_for_activation_, msg);
+  // Field 50: experimental_trace_buffer_v2_compaction
+  if (_has_field_[50]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(50, experimental_trace_buffer_v2_compaction_, msg);
   }

-  // Field 28: active_tree_needs_first_draw
-  if (_has_field_[28]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(28, active_tree_needs_first_draw_, msg);
+  // Field 2: data_sources
+  for (auto& it : data_sources_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

-  // Field 29: active_tree_is_ready_to_draw
+  // Field 20: builtin_data_sources
+  if (_has_field_[20]) {
+    (*builtin_data_sources_).Serialize(msg->BeginNestedMessage<::protozero::Message>(20));
+  }
+
+  // Field 3: duration_ms
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, duration_ms_, msg);
+  }
+
+  // Field 36: prefer_suspend_clock_for_duration
+  if (_has_field_[36]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(36, prefer_suspend_clock_for_duration_, msg);
+  }
+
+  // Field 4: enable_extra_guardrails
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, enable_extra_guardrails_, msg);
+  }
+
+  // Field 5: lockdown_mode
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, lockdown_mode_, msg);
+  }
+
+  // Field 6: producers
+  for (auto& it : producers_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
+  }
+
+  // Field 7: statsd_metadata
+  if (_has_field_[7]) {
+    (*statsd_metadata_).Serialize(msg->BeginNestedMessage<::protozero::Message>(7));
+  }
+
+  // Field 8: write_into_file
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, write_into_file_, msg);
+  }
+
+  // Field 29: output_path
   if (_has_field_[29]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(29, active_tree_is_ready_to_draw_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(29, output_path_, msg);
   }

-  // Field 30: did_create_and_initialize_first_layer_tree_frame_sink
-  if (_has_field_[30]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(30, did_create_and_initialize_first_layer_tree_frame_sink_, msg);
+  // Field 9: file_write_period_ms
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(9, file_write_period_ms_, msg);
   }

-  // Field 31: tree_priority
-  if (_has_field_[31]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(31, tree_priority_, msg);
+  // Field 10: max_file_size_bytes
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(10, max_file_size_bytes_, msg);
   }

-  // Field 32: scroll_handler_state
-  if (_has_field_[32]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(32, scroll_handler_state_, msg);
+  // Field 11: guardrail_overrides
+  if (_has_field_[11]) {
+    (*guardrail_overrides_).Serialize(msg->BeginNestedMessage<::protozero::Message>(11));
   }

-  // Field 33: critical_begin_main_frame_to_activate_is_fast
-  if (_has_field_[33]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(33, critical_begin_main_frame_to_activate_is_fast_, msg);
+  // Field 12: deferred_start
+  if (_has_field_[12]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(12, deferred_start_, msg);
   }

-  // Field 34: main_thread_missed_last_deadline
-  if (_has_field_[34]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(34, main_thread_missed_last_deadline_, msg);
+  // Field 13: flush_period_ms
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(13, flush_period_ms_, msg);
   }

-  // Field 36: video_needs_begin_frames
-  if (_has_field_[36]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(36, video_needs_begin_frames_, msg);
+  // Field 14: flush_timeout_ms
+  if (_has_field_[14]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(14, flush_timeout_ms_, msg);
   }

-  // Field 37: defer_begin_main_frame
-  if (_has_field_[37]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(37, defer_begin_main_frame_, msg);
+  // Field 23: data_source_stop_timeout_ms
+  if (_has_field_[23]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(23, data_source_stop_timeout_ms_, msg);
   }

-  // Field 38: last_commit_had_no_updates
+  // Field 16: notify_traceur
+  if (_has_field_[16]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(16, notify_traceur_, msg);
+  }
+
+  // Field 30: bugreport_score
+  if (_has_field_[30]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(30, bugreport_score_, msg);
+  }
+
+  // Field 38: bugreport_filename
   if (_has_field_[38]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(38, last_commit_had_no_updates_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(38, bugreport_filename_, msg);
   }

-  // Field 39: did_draw_in_last_frame
-  if (_has_field_[39]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(39, did_draw_in_last_frame_, msg);
+  // Field 17: trigger_config
+  if (_has_field_[17]) {
+    (*trigger_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(17));
   }

-  // Field 40: did_submit_in_last_frame
-  if (_has_field_[40]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(40, did_submit_in_last_frame_, msg);
+  // Field 18: activate_triggers
+  for (auto& it : activate_triggers_) {
+    ::protozero::internal::gen_helpers::SerializeString(18, it, msg);
   }

-  // Field 41: needs_impl_side_invalidation
-  if (_has_field_[41]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(41, needs_impl_side_invalidation_, msg);
+  // Field 21: incremental_state_config
+  if (_has_field_[21]) {
+    (*incremental_state_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(21));
   }

-  // Field 42: current_pending_tree_is_impl_side
-  if (_has_field_[42]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(42, current_pending_tree_is_impl_side_, msg);
+  // Field 19: allow_user_build_tracing
+  if (_has_field_[19]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(19, allow_user_build_tracing_, msg);
   }

-  // Field 43: previous_pending_tree_was_impl_side
-  if (_has_field_[43]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(43, previous_pending_tree_was_impl_side_, msg);
+  // Field 22: unique_session_name
+  if (_has_field_[22]) {
+    ::protozero::internal::gen_helpers::SerializeString(22, unique_session_name_, msg);
   }

-  // Field 44: processing_animation_worklets_for_active_tree
+  // Field 24: compression_type
+  if (_has_field_[24]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(24, compression_type_, msg);
+  }
+
+  // Field 47: compression
+  if (_has_field_[47]) {
+    (*compression_).Serialize(msg->BeginNestedMessage<::protozero::Message>(47));
+  }
+
+  // Field 25: incident_report_config
+  if (_has_field_[25]) {
+    (*incident_report_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(25));
+  }
+
+  // Field 31: statsd_logging
+  if (_has_field_[31]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(31, statsd_logging_, msg);
+  }
+
+  // Field 27: trace_uuid_msb
+  if (_has_field_[27]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(27, trace_uuid_msb_, msg);
+  }
+
+  // Field 28: trace_uuid_lsb
+  if (_has_field_[28]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(28, trace_uuid_lsb_, msg);
+  }
+
+  // Field 33: trace_filter
+  if (_has_field_[33]) {
+    (*trace_filter_).Serialize(msg->BeginNestedMessage<::protozero::Message>(33));
+  }
+
+  // Field 34: android_report_config
+  if (_has_field_[34]) {
+    (*android_report_config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(34));
+  }
+
+  // Field 35: cmd_trace_start_delay
+  if (_has_field_[35]) {
+    (*cmd_trace_start_delay_).Serialize(msg->BeginNestedMessage<::protozero::Message>(35));
+  }
+
+  // Field 39: session_semaphores
+  for (auto& it : session_semaphores_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(39));
+  }
+
+  // Field 40: priority_boost
+  if (_has_field_[40]) {
+    (*priority_boost_).Serialize(msg->BeginNestedMessage<::protozero::Message>(40));
+  }
+
+  // Field 41: exclusive_prio
+  if (_has_field_[41]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(41, exclusive_prio_, msg);
+  }
+
+  // Field 44: write_flush_mode
   if (_has_field_[44]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(44, processing_animation_worklets_for_active_tree_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(44, write_flush_mode_, msg);
   }

-  // Field 45: processing_animation_worklets_for_pending_tree
+  // Field 45: fflush_post_write
   if (_has_field_[45]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(45, processing_animation_worklets_for_pending_tree_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(45, fflush_post_write_, msg);
   }

-  // Field 46: processing_paint_worklets_for_pending_tree
-  if (_has_field_[46]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(46, processing_paint_worklets_for_pending_tree_, msg);
+  // Field 43: trace_all_machines
+  if (_has_field_[43]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(43, trace_all_machines_, msg);
+  }
+
+  // Field 48: trace_attributes
+  if (_has_field_[48]) {
+    (*trace_attributes_).Serialize(msg->BeginNestedMessage<::protozero::Message>(48));
+  }
+
+  // Field 49: persist_trace_across_reboots
+  if (_has_field_[49]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(49, persist_trace_across_reboots_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-ChromeCompositorStateMachine_MajorState::ChromeCompositorStateMachine_MajorState() = default;
-ChromeCompositorStateMachine_MajorState::~ChromeCompositorStateMachine_MajorState() = default;
-ChromeCompositorStateMachine_MajorState::ChromeCompositorStateMachine_MajorState(const ChromeCompositorStateMachine_MajorState&) = default;
-ChromeCompositorStateMachine_MajorState& ChromeCompositorStateMachine_MajorState::operator=(const ChromeCompositorStateMachine_MajorState&) = default;
-ChromeCompositorStateMachine_MajorState::ChromeCompositorStateMachine_MajorState(ChromeCompositorStateMachine_MajorState&&) noexcept = default;
-ChromeCompositorStateMachine_MajorState& ChromeCompositorStateMachine_MajorState::operator=(ChromeCompositorStateMachine_MajorState&&) = default;
+TraceConfig_SessionSemaphore::TraceConfig_SessionSemaphore() = default;
+TraceConfig_SessionSemaphore::~TraceConfig_SessionSemaphore() = default;
+TraceConfig_SessionSemaphore::TraceConfig_SessionSemaphore(const TraceConfig_SessionSemaphore&) = default;
+TraceConfig_SessionSemaphore& TraceConfig_SessionSemaphore::operator=(const TraceConfig_SessionSemaphore&) = default;
+TraceConfig_SessionSemaphore::TraceConfig_SessionSemaphore(TraceConfig_SessionSemaphore&&) noexcept = default;
+TraceConfig_SessionSemaphore& TraceConfig_SessionSemaphore::operator=(TraceConfig_SessionSemaphore&&) = default;

-bool ChromeCompositorStateMachine_MajorState::operator==(const ChromeCompositorStateMachine_MajorState& other) const {
+bool TraceConfig_SessionSemaphore::operator==(const TraceConfig_SessionSemaphore& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(next_action_, other.next_action_)
-   && ::protozero::internal::gen_helpers::EqualsField(begin_impl_frame_state_, other.begin_impl_frame_state_)
-   && ::protozero::internal::gen_helpers::EqualsField(begin_main_frame_state_, other.begin_main_frame_state_)
-   && ::protozero::internal::gen_helpers::EqualsField(layer_tree_frame_sink_state_, other.layer_tree_frame_sink_state_)
-   && ::protozero::internal::gen_helpers::EqualsField(forced_redraw_state_, other.forced_redraw_state_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_other_session_count_, other.max_other_session_count_);
 }

-bool ChromeCompositorStateMachine_MajorState::ParseFromArray(const void* raw, size_t size) {
+bool TraceConfig_SessionSemaphore::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -33551,20 +34082,11 @@ bool ChromeCompositorStateMachine_MajorState::ParseFromArray(const void* raw, si
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* next_action */:
-        field.get(&next_action_);
-        break;
-      case 2 /* begin_impl_frame_state */:
-        field.get(&begin_impl_frame_state_);
-        break;
-      case 3 /* begin_main_frame_state */:
-        field.get(&begin_main_frame_state_);
-        break;
-      case 4 /* layer_tree_frame_sink_state */:
-        field.get(&layer_tree_frame_sink_state_);
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 5 /* forced_redraw_state */:
-        field.get(&forced_redraw_state_);
+      case 2 /* max_other_session_count */:
+        field.get(&max_other_session_count_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -33574,76 +34096,47 @@ bool ChromeCompositorStateMachine_MajorState::ParseFromArray(const void* raw, si
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeCompositorStateMachine_MajorState::SerializeAsString() const {
+std::string TraceConfig_SessionSemaphore::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeCompositorStateMachine_MajorState::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_SessionSemaphore::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeCompositorStateMachine_MajorState::Serialize(::protozero::Message* msg) const {
-  // Field 1: next_action
+void TraceConfig_SessionSemaphore::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, next_action_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  // Field 2: begin_impl_frame_state
+  // Field 2: max_other_session_count
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, begin_impl_frame_state_, msg);
-  }
-
-  // Field 3: begin_main_frame_state
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, begin_main_frame_state_, msg);
-  }
-
-  // Field 4: layer_tree_frame_sink_state
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, layer_tree_frame_sink_state_, msg);
-  }
-
-  // Field 5: forced_redraw_state
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, forced_redraw_state_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, max_other_session_count_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-ChromeCompositorSchedulerState::ChromeCompositorSchedulerState() = default;
-ChromeCompositorSchedulerState::~ChromeCompositorSchedulerState() = default;
-ChromeCompositorSchedulerState::ChromeCompositorSchedulerState(const ChromeCompositorSchedulerState&) = default;
-ChromeCompositorSchedulerState& ChromeCompositorSchedulerState::operator=(const ChromeCompositorSchedulerState&) = default;
-ChromeCompositorSchedulerState::ChromeCompositorSchedulerState(ChromeCompositorSchedulerState&&) noexcept = default;
-ChromeCompositorSchedulerState& ChromeCompositorSchedulerState::operator=(ChromeCompositorSchedulerState&&) = default;
+TraceConfig_CmdTraceStartDelay::TraceConfig_CmdTraceStartDelay() = default;
+TraceConfig_CmdTraceStartDelay::~TraceConfig_CmdTraceStartDelay() = default;
+TraceConfig_CmdTraceStartDelay::TraceConfig_CmdTraceStartDelay(const TraceConfig_CmdTraceStartDelay&) = default;
+TraceConfig_CmdTraceStartDelay& TraceConfig_CmdTraceStartDelay::operator=(const TraceConfig_CmdTraceStartDelay&) = default;
+TraceConfig_CmdTraceStartDelay::TraceConfig_CmdTraceStartDelay(TraceConfig_CmdTraceStartDelay&&) noexcept = default;
+TraceConfig_CmdTraceStartDelay& TraceConfig_CmdTraceStartDelay::operator=(TraceConfig_CmdTraceStartDelay&&) = default;

-bool ChromeCompositorSchedulerState::operator==(const ChromeCompositorSchedulerState& other) const {
+bool TraceConfig_CmdTraceStartDelay::operator==(const TraceConfig_CmdTraceStartDelay& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(state_machine_, other.state_machine_)
-   && ::protozero::internal::gen_helpers::EqualsField(observing_begin_frame_source_, other.observing_begin_frame_source_)
-   && ::protozero::internal::gen_helpers::EqualsField(begin_impl_frame_deadline_task_, other.begin_impl_frame_deadline_task_)
-   && ::protozero::internal::gen_helpers::EqualsField(pending_begin_frame_task_, other.pending_begin_frame_task_)
-   && ::protozero::internal::gen_helpers::EqualsField(skipped_last_frame_missed_exceeded_deadline_, other.skipped_last_frame_missed_exceeded_deadline_)
-   && ::protozero::internal::gen_helpers::EqualsField(inside_action_, other.inside_action_)
-   && ::protozero::internal::gen_helpers::EqualsField(deadline_mode_, other.deadline_mode_)
-   && ::protozero::internal::gen_helpers::EqualsField(deadline_us_, other.deadline_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(deadline_scheduled_at_us_, other.deadline_scheduled_at_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(now_us_, other.now_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(now_to_deadline_delta_us_, other.now_to_deadline_delta_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(now_to_deadline_scheduled_at_delta_us_, other.now_to_deadline_scheduled_at_delta_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(begin_impl_frame_args_, other.begin_impl_frame_args_)
-   && ::protozero::internal::gen_helpers::EqualsField(begin_frame_observer_state_, other.begin_frame_observer_state_)
-   && ::protozero::internal::gen_helpers::EqualsField(begin_frame_source_state_, other.begin_frame_source_state_)
-   && ::protozero::internal::gen_helpers::EqualsField(compositor_timing_history_, other.compositor_timing_history_);
+   && ::protozero::internal::gen_helpers::EqualsField(min_delay_ms_, other.min_delay_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_delay_ms_, other.max_delay_ms_);
 }

-bool ChromeCompositorSchedulerState::ParseFromArray(const void* raw, size_t size) {
+bool TraceConfig_CmdTraceStartDelay::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -33653,194 +34146,63 @@ bool ChromeCompositorSchedulerState::ParseFromArray(const void* raw, size_t size
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* state_machine */:
-        (*state_machine_).ParseFromArray(field.data(), field.size());
-        break;
-      case 2 /* observing_begin_frame_source */:
-        field.get(&observing_begin_frame_source_);
-        break;
-      case 3 /* begin_impl_frame_deadline_task */:
-        field.get(&begin_impl_frame_deadline_task_);
-        break;
-      case 4 /* pending_begin_frame_task */:
-        field.get(&pending_begin_frame_task_);
+      case 1 /* min_delay_ms */:
+        field.get(&min_delay_ms_);
         break;
-      case 5 /* skipped_last_frame_missed_exceeded_deadline */:
-        field.get(&skipped_last_frame_missed_exceeded_deadline_);
+      case 2 /* max_delay_ms */:
+        field.get(&max_delay_ms_);
         break;
-      case 7 /* inside_action */:
-        field.get(&inside_action_);
-        break;
-      case 8 /* deadline_mode */:
-        field.get(&deadline_mode_);
-        break;
-      case 9 /* deadline_us */:
-        field.get(&deadline_us_);
-        break;
-      case 10 /* deadline_scheduled_at_us */:
-        field.get(&deadline_scheduled_at_us_);
-        break;
-      case 11 /* now_us */:
-        field.get(&now_us_);
-        break;
-      case 12 /* now_to_deadline_delta_us */:
-        field.get(&now_to_deadline_delta_us_);
-        break;
-      case 13 /* now_to_deadline_scheduled_at_delta_us */:
-        field.get(&now_to_deadline_scheduled_at_delta_us_);
-        break;
-      case 14 /* begin_impl_frame_args */:
-        (*begin_impl_frame_args_).ParseFromArray(field.data(), field.size());
-        break;
-      case 15 /* begin_frame_observer_state */:
-        (*begin_frame_observer_state_).ParseFromArray(field.data(), field.size());
-        break;
-      case 16 /* begin_frame_source_state */:
-        (*begin_frame_source_state_).ParseFromArray(field.data(), field.size());
-        break;
-      case 17 /* compositor_timing_history */:
-        (*compositor_timing_history_).ParseFromArray(field.data(), field.size());
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
         break;
     }
   }
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeCompositorSchedulerState::SerializeAsString() const {
+std::string TraceConfig_CmdTraceStartDelay::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeCompositorSchedulerState::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_CmdTraceStartDelay::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeCompositorSchedulerState::Serialize(::protozero::Message* msg) const {
-  // Field 1: state_machine
+void TraceConfig_CmdTraceStartDelay::Serialize(::protozero::Message* msg) const {
+  // Field 1: min_delay_ms
   if (_has_field_[1]) {
-    (*state_machine_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, min_delay_ms_, msg);
   }

-  // Field 2: observing_begin_frame_source
+  // Field 2: max_delay_ms
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, observing_begin_frame_source_, msg);
-  }
-
-  // Field 3: begin_impl_frame_deadline_task
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, begin_impl_frame_deadline_task_, msg);
-  }
-
-  // Field 4: pending_begin_frame_task
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, pending_begin_frame_task_, msg);
-  }
-
-  // Field 5: skipped_last_frame_missed_exceeded_deadline
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, skipped_last_frame_missed_exceeded_deadline_, msg);
-  }
-
-  // Field 7: inside_action
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, inside_action_, msg);
-  }
-
-  // Field 8: deadline_mode
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, deadline_mode_, msg);
-  }
-
-  // Field 9: deadline_us
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(9, deadline_us_, msg);
-  }
-
-  // Field 10: deadline_scheduled_at_us
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(10, deadline_scheduled_at_us_, msg);
-  }
-
-  // Field 11: now_us
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(11, now_us_, msg);
-  }
-
-  // Field 12: now_to_deadline_delta_us
-  if (_has_field_[12]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(12, now_to_deadline_delta_us_, msg);
-  }
-
-  // Field 13: now_to_deadline_scheduled_at_delta_us
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(13, now_to_deadline_scheduled_at_delta_us_, msg);
-  }
-
-  // Field 14: begin_impl_frame_args
-  if (_has_field_[14]) {
-    (*begin_impl_frame_args_).Serialize(msg->BeginNestedMessage<::protozero::Message>(14));
-  }
-
-  // Field 15: begin_frame_observer_state
-  if (_has_field_[15]) {
-    (*begin_frame_observer_state_).Serialize(msg->BeginNestedMessage<::protozero::Message>(15));
-  }
-
-  // Field 16: begin_frame_source_state
-  if (_has_field_[16]) {
-    (*begin_frame_source_state_).Serialize(msg->BeginNestedMessage<::protozero::Message>(16));
-  }
-
-  // Field 17: compositor_timing_history
-  if (_has_field_[17]) {
-    (*compositor_timing_history_).Serialize(msg->BeginNestedMessage<::protozero::Message>(17));
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, max_delay_ms_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_content_settings_event_info.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_content_settings_event_info.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-ChromeContentSettingsEventInfo::ChromeContentSettingsEventInfo() = default;
-ChromeContentSettingsEventInfo::~ChromeContentSettingsEventInfo() = default;
-ChromeContentSettingsEventInfo::ChromeContentSettingsEventInfo(const ChromeContentSettingsEventInfo&) = default;
-ChromeContentSettingsEventInfo& ChromeContentSettingsEventInfo::operator=(const ChromeContentSettingsEventInfo&) = default;
-ChromeContentSettingsEventInfo::ChromeContentSettingsEventInfo(ChromeContentSettingsEventInfo&&) noexcept = default;
-ChromeContentSettingsEventInfo& ChromeContentSettingsEventInfo::operator=(ChromeContentSettingsEventInfo&&) = default;
+TraceConfig_AndroidReportConfig::TraceConfig_AndroidReportConfig() = default;
+TraceConfig_AndroidReportConfig::~TraceConfig_AndroidReportConfig() = default;
+TraceConfig_AndroidReportConfig::TraceConfig_AndroidReportConfig(const TraceConfig_AndroidReportConfig&) = default;
+TraceConfig_AndroidReportConfig& TraceConfig_AndroidReportConfig::operator=(const TraceConfig_AndroidReportConfig&) = default;
+TraceConfig_AndroidReportConfig::TraceConfig_AndroidReportConfig(TraceConfig_AndroidReportConfig&&) noexcept = default;
+TraceConfig_AndroidReportConfig& TraceConfig_AndroidReportConfig::operator=(TraceConfig_AndroidReportConfig&&) = default;

-bool ChromeContentSettingsEventInfo::operator==(const ChromeContentSettingsEventInfo& other) const {
+bool TraceConfig_AndroidReportConfig::operator==(const TraceConfig_AndroidReportConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(number_of_exceptions_, other.number_of_exceptions_);
+   && ::protozero::internal::gen_helpers::EqualsField(reporter_service_package_, other.reporter_service_package_)
+   && ::protozero::internal::gen_helpers::EqualsField(reporter_service_class_, other.reporter_service_class_)
+   && ::protozero::internal::gen_helpers::EqualsField(skip_report_, other.skip_report_)
+   && ::protozero::internal::gen_helpers::EqualsField(use_pipe_in_framework_for_testing_, other.use_pipe_in_framework_for_testing_);
 }

-bool ChromeContentSettingsEventInfo::ParseFromArray(const void* raw, size_t size) {
+bool TraceConfig_AndroidReportConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -33850,8 +34212,17 @@ bool ChromeContentSettingsEventInfo::ParseFromArray(const void* raw, size_t size
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* number_of_exceptions */:
-        field.get(&number_of_exceptions_);
+      case 1 /* reporter_service_package */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &reporter_service_package_);
+        break;
+      case 2 /* reporter_service_class */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &reporter_service_class_);
+        break;
+      case 3 /* skip_report */:
+        field.get(&skip_report_);
+        break;
+      case 4 /* use_pipe_in_framework_for_testing */:
+        field.get(&use_pipe_in_framework_for_testing_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -33861,81 +34232,60 @@ bool ChromeContentSettingsEventInfo::ParseFromArray(const void* raw, size_t size
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeContentSettingsEventInfo::SerializeAsString() const {
+std::string TraceConfig_AndroidReportConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeContentSettingsEventInfo::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_AndroidReportConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeContentSettingsEventInfo::Serialize(::protozero::Message* msg) const {
-  // Field 1: number_of_exceptions
+void TraceConfig_AndroidReportConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: reporter_service_package
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, number_of_exceptions_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, reporter_service_package_, msg);
+  }
+
+  // Field 2: reporter_service_class
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, reporter_service_class_, msg);
+  }
+
+  // Field 3: skip_report
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, skip_report_, msg);
+  }
+
+  // Field 4: use_pipe_in_framework_for_testing
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, use_pipe_in_framework_for_testing_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_frame_reporter.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_frame_reporter.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-ChromeFrameReporter::ChromeFrameReporter() = default;
-ChromeFrameReporter::~ChromeFrameReporter() = default;
-ChromeFrameReporter::ChromeFrameReporter(const ChromeFrameReporter&) = default;
-ChromeFrameReporter& ChromeFrameReporter::operator=(const ChromeFrameReporter&) = default;
-ChromeFrameReporter::ChromeFrameReporter(ChromeFrameReporter&&) noexcept = default;
-ChromeFrameReporter& ChromeFrameReporter::operator=(ChromeFrameReporter&&) = default;
+TraceConfig_TraceFilter::TraceConfig_TraceFilter() = default;
+TraceConfig_TraceFilter::~TraceConfig_TraceFilter() = default;
+TraceConfig_TraceFilter::TraceConfig_TraceFilter(const TraceConfig_TraceFilter&) = default;
+TraceConfig_TraceFilter& TraceConfig_TraceFilter::operator=(const TraceConfig_TraceFilter&) = default;
+TraceConfig_TraceFilter::TraceConfig_TraceFilter(TraceConfig_TraceFilter&&) noexcept = default;
+TraceConfig_TraceFilter& TraceConfig_TraceFilter::operator=(TraceConfig_TraceFilter&&) = default;

-bool ChromeFrameReporter::operator==(const ChromeFrameReporter& other) const {
+bool TraceConfig_TraceFilter::operator==(const TraceConfig_TraceFilter& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(state_, other.state_)
-   && ::protozero::internal::gen_helpers::EqualsField(reason_, other.reason_)
-   && ::protozero::internal::gen_helpers::EqualsField(frame_source_, other.frame_source_)
-   && ::protozero::internal::gen_helpers::EqualsField(frame_sequence_, other.frame_sequence_)
-   && ::protozero::internal::gen_helpers::EqualsField(affects_smoothness_, other.affects_smoothness_)
-   && ::protozero::internal::gen_helpers::EqualsField(scroll_state_, other.scroll_state_)
-   && ::protozero::internal::gen_helpers::EqualsField(has_main_animation_, other.has_main_animation_)
-   && ::protozero::internal::gen_helpers::EqualsField(has_compositor_animation_, other.has_compositor_animation_)
-   && ::protozero::internal::gen_helpers::EqualsField(has_smooth_input_main_, other.has_smooth_input_main_)
-   && ::protozero::internal::gen_helpers::EqualsField(has_missing_content_, other.has_missing_content_)
-   && ::protozero::internal::gen_helpers::EqualsField(layer_tree_host_id_, other.layer_tree_host_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(has_high_latency_, other.has_high_latency_)
-   && ::protozero::internal::gen_helpers::EqualsField(frame_type_, other.frame_type_)
-   && ::protozero::internal::gen_helpers::EqualsField(high_latency_contribution_stage_, other.high_latency_contribution_stage_)
-   && ::protozero::internal::gen_helpers::EqualsField(checkerboarded_needs_raster_, other.checkerboarded_needs_raster_)
-   && ::protozero::internal::gen_helpers::EqualsField(checkerboarded_needs_record_, other.checkerboarded_needs_record_)
-   && ::protozero::internal::gen_helpers::EqualsField(surface_frame_trace_id_, other.surface_frame_trace_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(display_trace_id_, other.display_trace_id_);
+   && ::protozero::internal::gen_helpers::EqualsField(bytecode_, other.bytecode_)
+   && ::protozero::internal::gen_helpers::EqualsField(bytecode_v2_, other.bytecode_v2_)
+   && ::protozero::internal::gen_helpers::EqualsField(string_filter_chain_, other.string_filter_chain_)
+   && ::protozero::internal::gen_helpers::EqualsField(bytecode_overlay_v54_, other.bytecode_overlay_v54_)
+   && ::protozero::internal::gen_helpers::EqualsField(string_filter_chain_v54_, other.string_filter_chain_v54_);
 }

-bool ChromeFrameReporter::ParseFromArray(const void* raw, size_t size) {
-  high_latency_contribution_stage_.clear();
+bool TraceConfig_TraceFilter::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -33945,60 +34295,20 @@ bool ChromeFrameReporter::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* state */:
-        field.get(&state_);
-        break;
-      case 2 /* reason */:
-        field.get(&reason_);
-        break;
-      case 3 /* frame_source */:
-        field.get(&frame_source_);
-        break;
-      case 4 /* frame_sequence */:
-        field.get(&frame_sequence_);
-        break;
-      case 5 /* affects_smoothness */:
-        field.get(&affects_smoothness_);
-        break;
-      case 6 /* scroll_state */:
-        field.get(&scroll_state_);
-        break;
-      case 7 /* has_main_animation */:
-        field.get(&has_main_animation_);
-        break;
-      case 8 /* has_compositor_animation */:
-        field.get(&has_compositor_animation_);
-        break;
-      case 9 /* has_smooth_input_main */:
-        field.get(&has_smooth_input_main_);
-        break;
-      case 10 /* has_missing_content */:
-        field.get(&has_missing_content_);
-        break;
-      case 11 /* layer_tree_host_id */:
-        field.get(&layer_tree_host_id_);
-        break;
-      case 12 /* has_high_latency */:
-        field.get(&has_high_latency_);
-        break;
-      case 13 /* frame_type */:
-        field.get(&frame_type_);
-        break;
-      case 14 /* high_latency_contribution_stage */:
-        high_latency_contribution_stage_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &high_latency_contribution_stage_.back());
+      case 1 /* bytecode */:
+        field.get(&bytecode_);
         break;
-      case 15 /* checkerboarded_needs_raster */:
-        field.get(&checkerboarded_needs_raster_);
+      case 2 /* bytecode_v2 */:
+        field.get(&bytecode_v2_);
         break;
-      case 16 /* checkerboarded_needs_record */:
-        field.get(&checkerboarded_needs_record_);
+      case 3 /* string_filter_chain */:
+        (*string_filter_chain_).ParseFromArray(field.data(), field.size());
         break;
-      case 17 /* surface_frame_trace_id */:
-        field.get(&surface_frame_trace_id_);
+      case 4 /* bytecode_overlay_v54 */:
+        field.get(&bytecode_overlay_v54_);
         break;
-      case 18 /* display_trace_id */:
-        field.get(&display_trace_id_);
+      case 5 /* string_filter_chain_v54 */:
+        (*string_filter_chain_v54_).ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -34008,151 +34318,65 @@ bool ChromeFrameReporter::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeFrameReporter::SerializeAsString() const {
+std::string TraceConfig_TraceFilter::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeFrameReporter::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_TraceFilter::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeFrameReporter::Serialize(::protozero::Message* msg) const {
-  // Field 1: state
+void TraceConfig_TraceFilter::Serialize(::protozero::Message* msg) const {
+  // Field 1: bytecode
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, state_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, bytecode_, msg);
   }

-  // Field 2: reason
+  // Field 2: bytecode_v2
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, reason_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(2, bytecode_v2_, msg);
   }

-  // Field 3: frame_source
+  // Field 3: string_filter_chain
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, frame_source_, msg);
+    (*string_filter_chain_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
   }

-  // Field 4: frame_sequence
+  // Field 4: bytecode_overlay_v54
   if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, frame_sequence_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(4, bytecode_overlay_v54_, msg);
   }

-  // Field 5: affects_smoothness
+  // Field 5: string_filter_chain_v54
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, affects_smoothness_, msg);
-  }
-
-  // Field 6: scroll_state
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, scroll_state_, msg);
-  }
-
-  // Field 7: has_main_animation
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, has_main_animation_, msg);
-  }
-
-  // Field 8: has_compositor_animation
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, has_compositor_animation_, msg);
-  }
-
-  // Field 9: has_smooth_input_main
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, has_smooth_input_main_, msg);
-  }
-
-  // Field 10: has_missing_content
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(10, has_missing_content_, msg);
-  }
-
-  // Field 11: layer_tree_host_id
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(11, layer_tree_host_id_, msg);
-  }
-
-  // Field 12: has_high_latency
-  if (_has_field_[12]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(12, has_high_latency_, msg);
-  }
-
-  // Field 13: frame_type
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(13, frame_type_, msg);
-  }
-
-  // Field 14: high_latency_contribution_stage
-  for (auto& it : high_latency_contribution_stage_) {
-    ::protozero::internal::gen_helpers::SerializeString(14, it, msg);
-  }
-
-  // Field 15: checkerboarded_needs_raster
-  if (_has_field_[15]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(15, checkerboarded_needs_raster_, msg);
-  }
-
-  // Field 16: checkerboarded_needs_record
-  if (_has_field_[16]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(16, checkerboarded_needs_record_, msg);
-  }
-
-  // Field 17: surface_frame_trace_id
-  if (_has_field_[17]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(17, surface_frame_trace_id_, msg);
-  }
-
-  // Field 18: display_trace_id
-  if (_has_field_[18]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(18, display_trace_id_, msg);
+    (*string_filter_chain_v54_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_histogram_sample.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_histogram_sample.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-ChromeHistogramSample::ChromeHistogramSample() = default;
-ChromeHistogramSample::~ChromeHistogramSample() = default;
-ChromeHistogramSample::ChromeHistogramSample(const ChromeHistogramSample&) = default;
-ChromeHistogramSample& ChromeHistogramSample::operator=(const ChromeHistogramSample&) = default;
-ChromeHistogramSample::ChromeHistogramSample(ChromeHistogramSample&&) noexcept = default;
-ChromeHistogramSample& ChromeHistogramSample::operator=(ChromeHistogramSample&&) = default;
+TraceConfig_TraceFilter_StringFilterChain::TraceConfig_TraceFilter_StringFilterChain() = default;
+TraceConfig_TraceFilter_StringFilterChain::~TraceConfig_TraceFilter_StringFilterChain() = default;
+TraceConfig_TraceFilter_StringFilterChain::TraceConfig_TraceFilter_StringFilterChain(const TraceConfig_TraceFilter_StringFilterChain&) = default;
+TraceConfig_TraceFilter_StringFilterChain& TraceConfig_TraceFilter_StringFilterChain::operator=(const TraceConfig_TraceFilter_StringFilterChain&) = default;
+TraceConfig_TraceFilter_StringFilterChain::TraceConfig_TraceFilter_StringFilterChain(TraceConfig_TraceFilter_StringFilterChain&&) noexcept = default;
+TraceConfig_TraceFilter_StringFilterChain& TraceConfig_TraceFilter_StringFilterChain::operator=(TraceConfig_TraceFilter_StringFilterChain&&) = default;

-bool ChromeHistogramSample::operator==(const ChromeHistogramSample& other) const {
+bool TraceConfig_TraceFilter_StringFilterChain::operator==(const TraceConfig_TraceFilter_StringFilterChain& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_hash_, other.name_hash_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(sample_, other.sample_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_iid_, other.name_iid_);
+   && ::protozero::internal::gen_helpers::EqualsField(rules_, other.rules_);
 }

-bool ChromeHistogramSample::ParseFromArray(const void* raw, size_t size) {
+int TraceConfig_TraceFilter_StringFilterChain::rules_size() const { return static_cast<int>(rules_.size()); }
+void TraceConfig_TraceFilter_StringFilterChain::clear_rules() { rules_.clear(); }
+TraceConfig_TraceFilter_StringFilterRule* TraceConfig_TraceFilter_StringFilterChain::add_rules() { rules_.emplace_back(); return &rules_.back(); }
+bool TraceConfig_TraceFilter_StringFilterChain::ParseFromArray(const void* raw, size_t size) {
+  rules_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -34162,17 +34386,9 @@ bool ChromeHistogramSample::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* name_hash */:
-        field.get(&name_hash_);
-        break;
-      case 2 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 3 /* sample */:
-        field.get(&sample_);
-        break;
-      case 4 /* name_iid */:
-        field.get(&name_iid_);
+      case 1 /* rules */:
+        rules_.emplace_back();
+        rules_.back().ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -34182,57 +34398,46 @@ bool ChromeHistogramSample::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeHistogramSample::SerializeAsString() const {
+std::string TraceConfig_TraceFilter_StringFilterChain::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeHistogramSample::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_TraceFilter_StringFilterChain::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeHistogramSample::Serialize(::protozero::Message* msg) const {
-  // Field 1: name_hash
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, name_hash_, msg);
-  }
-
-  // Field 2: name
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
-  }
-
-  // Field 3: sample
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, sample_, msg);
-  }
-
-  // Field 4: name_iid
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, name_iid_, msg);
+void TraceConfig_TraceFilter_StringFilterChain::Serialize(::protozero::Message* msg) const {
+  // Field 1: rules
+  for (auto& it : rules_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-HistogramName::HistogramName() = default;
-HistogramName::~HistogramName() = default;
-HistogramName::HistogramName(const HistogramName&) = default;
-HistogramName& HistogramName::operator=(const HistogramName&) = default;
-HistogramName::HistogramName(HistogramName&&) noexcept = default;
-HistogramName& HistogramName::operator=(HistogramName&&) = default;
+TraceConfig_TraceFilter_StringFilterRule::TraceConfig_TraceFilter_StringFilterRule() = default;
+TraceConfig_TraceFilter_StringFilterRule::~TraceConfig_TraceFilter_StringFilterRule() = default;
+TraceConfig_TraceFilter_StringFilterRule::TraceConfig_TraceFilter_StringFilterRule(const TraceConfig_TraceFilter_StringFilterRule&) = default;
+TraceConfig_TraceFilter_StringFilterRule& TraceConfig_TraceFilter_StringFilterRule::operator=(const TraceConfig_TraceFilter_StringFilterRule&) = default;
+TraceConfig_TraceFilter_StringFilterRule::TraceConfig_TraceFilter_StringFilterRule(TraceConfig_TraceFilter_StringFilterRule&&) noexcept = default;
+TraceConfig_TraceFilter_StringFilterRule& TraceConfig_TraceFilter_StringFilterRule::operator=(TraceConfig_TraceFilter_StringFilterRule&&) = default;

-bool HistogramName::operator==(const HistogramName& other) const {
+bool TraceConfig_TraceFilter_StringFilterRule::operator==(const TraceConfig_TraceFilter_StringFilterRule& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+   && ::protozero::internal::gen_helpers::EqualsField(policy_, other.policy_)
+   && ::protozero::internal::gen_helpers::EqualsField(regex_pattern_, other.regex_pattern_)
+   && ::protozero::internal::gen_helpers::EqualsField(atrace_payload_starts_with_, other.atrace_payload_starts_with_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(semantic_type_, other.semantic_type_);
 }

-bool HistogramName::ParseFromArray(const void* raw, size_t size) {
+bool TraceConfig_TraceFilter_StringFilterRule::ParseFromArray(const void* raw, size_t size) {
+  semantic_type_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -34242,12 +34447,22 @@ bool HistogramName::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* iid */:
-        field.get(&iid_);
+      case 1 /* policy */:
+        field.get(&policy_);
         break;
-      case 2 /* name */:
+      case 2 /* regex_pattern */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &regex_pattern_);
+        break;
+      case 3 /* atrace_payload_starts_with */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &atrace_payload_starts_with_);
+        break;
+      case 4 /* name */:
         ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
+      case 5 /* semantic_type */:
+        semantic_type_.emplace_back();
+        field.get(&semantic_type_.back());
+        break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
         break;
@@ -34256,156 +34471,65 @@ bool HistogramName::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string HistogramName::SerializeAsString() const {
+std::string TraceConfig_TraceFilter_StringFilterRule::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> HistogramName::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_TraceFilter_StringFilterRule::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void HistogramName::Serialize(::protozero::Message* msg) const {
-  // Field 1: iid
+void TraceConfig_TraceFilter_StringFilterRule::Serialize(::protozero::Message* msg) const {
+  // Field 1: policy
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, policy_, msg);
   }

-  // Field 2: name
+  // Field 2: regex_pattern
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(2, regex_pattern_, msg);
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
-
-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_keyed_service.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_keyed_service.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {
-
-ChromeKeyedService::ChromeKeyedService() = default;
-ChromeKeyedService::~ChromeKeyedService() = default;
-ChromeKeyedService::ChromeKeyedService(const ChromeKeyedService&) = default;
-ChromeKeyedService& ChromeKeyedService::operator=(const ChromeKeyedService&) = default;
-ChromeKeyedService::ChromeKeyedService(ChromeKeyedService&&) noexcept = default;
-ChromeKeyedService& ChromeKeyedService::operator=(ChromeKeyedService&&) = default;
-
-bool ChromeKeyedService::operator==(const ChromeKeyedService& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
-}
-
-bool ChromeKeyedService::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
-
-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
+  // Field 3: atrace_payload_starts_with
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeString(3, atrace_payload_starts_with_, msg);
   }
-  return !packed_error && !dec.bytes_left();
-}
-
-std::string ChromeKeyedService::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}

-std::vector<uint8_t> ChromeKeyedService::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  // Field 4: name
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeString(4, name_, msg);
+  }

-void ChromeKeyedService::Serialize(::protozero::Message* msg) const {
-  // Field 1: name
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
+  // Field 5: semantic_type
+  for (auto& it : semantic_type_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, it, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_latency_info.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_latency_info.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-ChromeLatencyInfo::ChromeLatencyInfo() = default;
-ChromeLatencyInfo::~ChromeLatencyInfo() = default;
-ChromeLatencyInfo::ChromeLatencyInfo(const ChromeLatencyInfo&) = default;
-ChromeLatencyInfo& ChromeLatencyInfo::operator=(const ChromeLatencyInfo&) = default;
-ChromeLatencyInfo::ChromeLatencyInfo(ChromeLatencyInfo&&) noexcept = default;
-ChromeLatencyInfo& ChromeLatencyInfo::operator=(ChromeLatencyInfo&&) = default;
+TraceConfig_IncidentReportConfig::TraceConfig_IncidentReportConfig() = default;
+TraceConfig_IncidentReportConfig::~TraceConfig_IncidentReportConfig() = default;
+TraceConfig_IncidentReportConfig::TraceConfig_IncidentReportConfig(const TraceConfig_IncidentReportConfig&) = default;
+TraceConfig_IncidentReportConfig& TraceConfig_IncidentReportConfig::operator=(const TraceConfig_IncidentReportConfig&) = default;
+TraceConfig_IncidentReportConfig::TraceConfig_IncidentReportConfig(TraceConfig_IncidentReportConfig&&) noexcept = default;
+TraceConfig_IncidentReportConfig& TraceConfig_IncidentReportConfig::operator=(TraceConfig_IncidentReportConfig&&) = default;

-bool ChromeLatencyInfo::operator==(const ChromeLatencyInfo& other) const {
+bool TraceConfig_IncidentReportConfig::operator==(const TraceConfig_IncidentReportConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(trace_id_, other.trace_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(step_, other.step_)
-   && ::protozero::internal::gen_helpers::EqualsField(frame_tree_node_id_, other.frame_tree_node_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(component_info_, other.component_info_)
-   && ::protozero::internal::gen_helpers::EqualsField(is_coalesced_, other.is_coalesced_)
-   && ::protozero::internal::gen_helpers::EqualsField(gesture_scroll_id_, other.gesture_scroll_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(touch_id_, other.touch_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(input_type_, other.input_type_);
+   && ::protozero::internal::gen_helpers::EqualsField(destination_package_, other.destination_package_)
+   && ::protozero::internal::gen_helpers::EqualsField(destination_class_, other.destination_class_)
+   && ::protozero::internal::gen_helpers::EqualsField(privacy_level_, other.privacy_level_)
+   && ::protozero::internal::gen_helpers::EqualsField(skip_incidentd_, other.skip_incidentd_)
+   && ::protozero::internal::gen_helpers::EqualsField(skip_dropbox_, other.skip_dropbox_);
 }

-int ChromeLatencyInfo::component_info_size() const { return static_cast<int>(component_info_.size()); }
-void ChromeLatencyInfo::clear_component_info() { component_info_.clear(); }
-ChromeLatencyInfo_ComponentInfo* ChromeLatencyInfo::add_component_info() { component_info_.emplace_back(); return &component_info_.back(); }
-bool ChromeLatencyInfo::ParseFromArray(const void* raw, size_t size) {
-  component_info_.clear();
+bool TraceConfig_IncidentReportConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -34415,30 +34539,20 @@ bool ChromeLatencyInfo::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* trace_id */:
-        field.get(&trace_id_);
-        break;
-      case 2 /* step */:
-        field.get(&step_);
-        break;
-      case 3 /* frame_tree_node_id */:
-        field.get(&frame_tree_node_id_);
-        break;
-      case 4 /* component_info */:
-        component_info_.emplace_back();
-        component_info_.back().ParseFromArray(field.data(), field.size());
+      case 1 /* destination_package */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &destination_package_);
         break;
-      case 5 /* is_coalesced */:
-        field.get(&is_coalesced_);
+      case 2 /* destination_class */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &destination_class_);
         break;
-      case 6 /* gesture_scroll_id */:
-        field.get(&gesture_scroll_id_);
+      case 3 /* privacy_level */:
+        field.get(&privacy_level_);
         break;
-      case 7 /* touch_id */:
-        field.get(&touch_id_);
+      case 5 /* skip_incidentd */:
+        field.get(&skip_incidentd_);
         break;
-      case 8 /* input_type */:
-        field.get(&input_type_);
+      case 4 /* skip_dropbox */:
+        field.get(&skip_dropbox_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -34448,77 +34562,62 @@ bool ChromeLatencyInfo::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeLatencyInfo::SerializeAsString() const {
+std::string TraceConfig_IncidentReportConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeLatencyInfo::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_IncidentReportConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeLatencyInfo::Serialize(::protozero::Message* msg) const {
-  // Field 1: trace_id
+void TraceConfig_IncidentReportConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: destination_package
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, trace_id_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, destination_package_, msg);
   }

-  // Field 2: step
+  // Field 2: destination_class
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, step_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(2, destination_class_, msg);
   }

-  // Field 3: frame_tree_node_id
+  // Field 3: privacy_level
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, frame_tree_node_id_, msg);
-  }
-
-  // Field 4: component_info
-  for (auto& it : component_info_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, privacy_level_, msg);
   }

-  // Field 5: is_coalesced
+  // Field 5: skip_incidentd
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, is_coalesced_, msg);
-  }
-
-  // Field 6: gesture_scroll_id
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, gesture_scroll_id_, msg);
-  }
-
-  // Field 7: touch_id
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, touch_id_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, skip_incidentd_, msg);
   }

-  // Field 8: input_type
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, input_type_, msg);
+  // Field 4: skip_dropbox
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, skip_dropbox_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-ChromeLatencyInfo_ComponentInfo::ChromeLatencyInfo_ComponentInfo() = default;
-ChromeLatencyInfo_ComponentInfo::~ChromeLatencyInfo_ComponentInfo() = default;
-ChromeLatencyInfo_ComponentInfo::ChromeLatencyInfo_ComponentInfo(const ChromeLatencyInfo_ComponentInfo&) = default;
-ChromeLatencyInfo_ComponentInfo& ChromeLatencyInfo_ComponentInfo::operator=(const ChromeLatencyInfo_ComponentInfo&) = default;
-ChromeLatencyInfo_ComponentInfo::ChromeLatencyInfo_ComponentInfo(ChromeLatencyInfo_ComponentInfo&&) noexcept = default;
-ChromeLatencyInfo_ComponentInfo& ChromeLatencyInfo_ComponentInfo::operator=(ChromeLatencyInfo_ComponentInfo&&) = default;
+TraceConfig_CompressionConfig::TraceConfig_CompressionConfig() = default;
+TraceConfig_CompressionConfig::~TraceConfig_CompressionConfig() = default;
+TraceConfig_CompressionConfig::TraceConfig_CompressionConfig(const TraceConfig_CompressionConfig&) = default;
+TraceConfig_CompressionConfig& TraceConfig_CompressionConfig::operator=(const TraceConfig_CompressionConfig&) = default;
+TraceConfig_CompressionConfig::TraceConfig_CompressionConfig(TraceConfig_CompressionConfig&&) noexcept = default;
+TraceConfig_CompressionConfig& TraceConfig_CompressionConfig::operator=(TraceConfig_CompressionConfig&&) = default;

-bool ChromeLatencyInfo_ComponentInfo::operator==(const ChromeLatencyInfo_ComponentInfo& other) const {
+bool TraceConfig_CompressionConfig::operator==(const TraceConfig_CompressionConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(component_type_, other.component_type_)
-   && ::protozero::internal::gen_helpers::EqualsField(time_us_, other.time_us_);
+   && ::protozero::internal::gen_helpers::EqualsField(deflate_, other.deflate_)
+   && ::protozero::internal::gen_helpers::EqualsField(zstd_, other.zstd_);
 }

-bool ChromeLatencyInfo_ComponentInfo::ParseFromArray(const void* raw, size_t size) {
+bool TraceConfig_CompressionConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -34528,11 +34627,11 @@ bool ChromeLatencyInfo_ComponentInfo::ParseFromArray(const void* raw, size_t siz
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* component_type */:
-        field.get(&component_type_);
+      case 1 /* deflate */:
+        (*deflate_).ParseFromArray(field.data(), field.size());
         break;
-      case 2 /* time_us */:
-        field.get(&time_us_);
+      case 2 /* zstd */:
+        (*zstd_).ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -34542,69 +34641,46 @@ bool ChromeLatencyInfo_ComponentInfo::ParseFromArray(const void* raw, size_t siz
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeLatencyInfo_ComponentInfo::SerializeAsString() const {
+std::string TraceConfig_CompressionConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeLatencyInfo_ComponentInfo::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_CompressionConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeLatencyInfo_ComponentInfo::Serialize(::protozero::Message* msg) const {
-  // Field 1: component_type
+void TraceConfig_CompressionConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: deflate
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, component_type_, msg);
+    (*deflate_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 2: time_us
+  // Field 2: zstd
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, time_us_, msg);
+    (*zstd_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_legacy_ipc.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_legacy_ipc.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-ChromeLegacyIpc::ChromeLegacyIpc() = default;
-ChromeLegacyIpc::~ChromeLegacyIpc() = default;
-ChromeLegacyIpc::ChromeLegacyIpc(const ChromeLegacyIpc&) = default;
-ChromeLegacyIpc& ChromeLegacyIpc::operator=(const ChromeLegacyIpc&) = default;
-ChromeLegacyIpc::ChromeLegacyIpc(ChromeLegacyIpc&&) noexcept = default;
-ChromeLegacyIpc& ChromeLegacyIpc::operator=(ChromeLegacyIpc&&) = default;
+TraceConfig_CompressionConfig_Zstd::TraceConfig_CompressionConfig_Zstd() = default;
+TraceConfig_CompressionConfig_Zstd::~TraceConfig_CompressionConfig_Zstd() = default;
+TraceConfig_CompressionConfig_Zstd::TraceConfig_CompressionConfig_Zstd(const TraceConfig_CompressionConfig_Zstd&) = default;
+TraceConfig_CompressionConfig_Zstd& TraceConfig_CompressionConfig_Zstd::operator=(const TraceConfig_CompressionConfig_Zstd&) = default;
+TraceConfig_CompressionConfig_Zstd::TraceConfig_CompressionConfig_Zstd(TraceConfig_CompressionConfig_Zstd&&) noexcept = default;
+TraceConfig_CompressionConfig_Zstd& TraceConfig_CompressionConfig_Zstd::operator=(TraceConfig_CompressionConfig_Zstd&&) = default;

-bool ChromeLegacyIpc::operator==(const ChromeLegacyIpc& other) const {
+bool TraceConfig_CompressionConfig_Zstd::operator==(const TraceConfig_CompressionConfig_Zstd& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(message_class_, other.message_class_)
-   && ::protozero::internal::gen_helpers::EqualsField(message_line_, other.message_line_);
+   && ::protozero::internal::gen_helpers::EqualsField(level_, other.level_);
 }

-bool ChromeLegacyIpc::ParseFromArray(const void* raw, size_t size) {
+bool TraceConfig_CompressionConfig_Zstd::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -34614,11 +34690,8 @@ bool ChromeLegacyIpc::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* message_class */:
-        field.get(&message_class_);
-        break;
-      case 2 /* message_line */:
-        field.get(&message_line_);
+      case 1 /* level */:
+        field.get(&level_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -34628,69 +34701,40 @@ bool ChromeLegacyIpc::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeLegacyIpc::SerializeAsString() const {
+std::string TraceConfig_CompressionConfig_Zstd::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeLegacyIpc::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_CompressionConfig_Zstd::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeLegacyIpc::Serialize(::protozero::Message* msg) const {
-  // Field 1: message_class
+void TraceConfig_CompressionConfig_Zstd::Serialize(::protozero::Message* msg) const {
+  // Field 1: level
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, message_class_, msg);
-  }
-
-  // Field 2: message_line
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, message_line_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, level_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_message_pump.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_message_pump.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-ChromeMessagePump::ChromeMessagePump() = default;
-ChromeMessagePump::~ChromeMessagePump() = default;
-ChromeMessagePump::ChromeMessagePump(const ChromeMessagePump&) = default;
-ChromeMessagePump& ChromeMessagePump::operator=(const ChromeMessagePump&) = default;
-ChromeMessagePump::ChromeMessagePump(ChromeMessagePump&&) noexcept = default;
-ChromeMessagePump& ChromeMessagePump::operator=(ChromeMessagePump&&) = default;
+TraceConfig_CompressionConfig_Deflate::TraceConfig_CompressionConfig_Deflate() = default;
+TraceConfig_CompressionConfig_Deflate::~TraceConfig_CompressionConfig_Deflate() = default;
+TraceConfig_CompressionConfig_Deflate::TraceConfig_CompressionConfig_Deflate(const TraceConfig_CompressionConfig_Deflate&) = default;
+TraceConfig_CompressionConfig_Deflate& TraceConfig_CompressionConfig_Deflate::operator=(const TraceConfig_CompressionConfig_Deflate&) = default;
+TraceConfig_CompressionConfig_Deflate::TraceConfig_CompressionConfig_Deflate(TraceConfig_CompressionConfig_Deflate&&) noexcept = default;
+TraceConfig_CompressionConfig_Deflate& TraceConfig_CompressionConfig_Deflate::operator=(TraceConfig_CompressionConfig_Deflate&&) = default;

-bool ChromeMessagePump::operator==(const ChromeMessagePump& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(sent_messages_in_queue_, other.sent_messages_in_queue_)
-   && ::protozero::internal::gen_helpers::EqualsField(io_handler_location_iid_, other.io_handler_location_iid_);
+bool TraceConfig_CompressionConfig_Deflate::operator==(const TraceConfig_CompressionConfig_Deflate& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
 }

-bool ChromeMessagePump::ParseFromArray(const void* raw, size_t size) {
+bool TraceConfig_CompressionConfig_Deflate::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -34700,12 +34744,6 @@ bool ChromeMessagePump::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* sent_messages_in_queue */:
-        field.get(&sent_messages_in_queue_);
-        break;
-      case 2 /* io_handler_location_iid */:
-        field.get(&io_handler_location_iid_);
-        break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
         break;
@@ -34714,74 +34752,36 @@ bool ChromeMessagePump::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeMessagePump::SerializeAsString() const {
+std::string TraceConfig_CompressionConfig_Deflate::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeMessagePump::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_CompressionConfig_Deflate::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeMessagePump::Serialize(::protozero::Message* msg) const {
-  // Field 1: sent_messages_in_queue
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, sent_messages_in_queue_, msg);
-  }
-
-  // Field 2: io_handler_location_iid
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, io_handler_location_iid_, msg);
-  }
-
+void TraceConfig_CompressionConfig_Deflate::Serialize(::protozero::Message* msg) const {
   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_mojo_event_info.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_mojo_event_info.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-ChromeMojoEventInfo::ChromeMojoEventInfo() = default;
-ChromeMojoEventInfo::~ChromeMojoEventInfo() = default;
-ChromeMojoEventInfo::ChromeMojoEventInfo(const ChromeMojoEventInfo&) = default;
-ChromeMojoEventInfo& ChromeMojoEventInfo::operator=(const ChromeMojoEventInfo&) = default;
-ChromeMojoEventInfo::ChromeMojoEventInfo(ChromeMojoEventInfo&&) noexcept = default;
-ChromeMojoEventInfo& ChromeMojoEventInfo::operator=(ChromeMojoEventInfo&&) = default;
+TraceConfig_IncrementalStateConfig::TraceConfig_IncrementalStateConfig() = default;
+TraceConfig_IncrementalStateConfig::~TraceConfig_IncrementalStateConfig() = default;
+TraceConfig_IncrementalStateConfig::TraceConfig_IncrementalStateConfig(const TraceConfig_IncrementalStateConfig&) = default;
+TraceConfig_IncrementalStateConfig& TraceConfig_IncrementalStateConfig::operator=(const TraceConfig_IncrementalStateConfig&) = default;
+TraceConfig_IncrementalStateConfig::TraceConfig_IncrementalStateConfig(TraceConfig_IncrementalStateConfig&&) noexcept = default;
+TraceConfig_IncrementalStateConfig& TraceConfig_IncrementalStateConfig::operator=(TraceConfig_IncrementalStateConfig&&) = default;

-bool ChromeMojoEventInfo::operator==(const ChromeMojoEventInfo& other) const {
+bool TraceConfig_IncrementalStateConfig::operator==(const TraceConfig_IncrementalStateConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(watcher_notify_interface_tag_, other.watcher_notify_interface_tag_)
-   && ::protozero::internal::gen_helpers::EqualsField(ipc_hash_, other.ipc_hash_)
-   && ::protozero::internal::gen_helpers::EqualsField(mojo_interface_tag_, other.mojo_interface_tag_)
-   && ::protozero::internal::gen_helpers::EqualsField(mojo_interface_method_iid_, other.mojo_interface_method_iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(is_reply_, other.is_reply_)
-   && ::protozero::internal::gen_helpers::EqualsField(payload_size_, other.payload_size_)
-   && ::protozero::internal::gen_helpers::EqualsField(data_num_bytes_, other.data_num_bytes_);
+   && ::protozero::internal::gen_helpers::EqualsField(clear_period_ms_, other.clear_period_ms_);
 }

-bool ChromeMojoEventInfo::ParseFromArray(const void* raw, size_t size) {
+bool TraceConfig_IncrementalStateConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -34791,26 +34791,8 @@ bool ChromeMojoEventInfo::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* watcher_notify_interface_tag */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &watcher_notify_interface_tag_);
-        break;
-      case 2 /* ipc_hash */:
-        field.get(&ipc_hash_);
-        break;
-      case 3 /* mojo_interface_tag */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &mojo_interface_tag_);
-        break;
-      case 4 /* mojo_interface_method_iid */:
-        field.get(&mojo_interface_method_iid_);
-        break;
-      case 5 /* is_reply */:
-        field.get(&is_reply_);
-        break;
-      case 6 /* payload_size */:
-        field.get(&payload_size_);
-        break;
-      case 7 /* data_num_bytes */:
-        field.get(&data_num_bytes_);
+      case 1 /* clear_period_ms */:
+        field.get(&clear_period_ms_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -34820,97 +34802,48 @@ bool ChromeMojoEventInfo::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeMojoEventInfo::SerializeAsString() const {
+std::string TraceConfig_IncrementalStateConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeMojoEventInfo::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_IncrementalStateConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeMojoEventInfo::Serialize(::protozero::Message* msg) const {
-  // Field 1: watcher_notify_interface_tag
+void TraceConfig_IncrementalStateConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: clear_period_ms
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, watcher_notify_interface_tag_, msg);
-  }
-
-  // Field 2: ipc_hash
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, ipc_hash_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, clear_period_ms_, msg);
   }

-  // Field 3: mojo_interface_tag
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeString(3, mojo_interface_tag_, msg);
-  }
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}

-  // Field 4: mojo_interface_method_iid
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, mojo_interface_method_iid_, msg);
-  }

-  // Field 5: is_reply
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, is_reply_, msg);
-  }
-
-  // Field 6: payload_size
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, payload_size_, msg);
-  }
-
-  // Field 7: data_num_bytes
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, data_num_bytes_, msg);
-  }
-
-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
-
-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_process_descriptor.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_process_descriptor.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {
-
-ChromeProcessDescriptor::ChromeProcessDescriptor() = default;
-ChromeProcessDescriptor::~ChromeProcessDescriptor() = default;
-ChromeProcessDescriptor::ChromeProcessDescriptor(const ChromeProcessDescriptor&) = default;
-ChromeProcessDescriptor& ChromeProcessDescriptor::operator=(const ChromeProcessDescriptor&) = default;
-ChromeProcessDescriptor::ChromeProcessDescriptor(ChromeProcessDescriptor&&) noexcept = default;
-ChromeProcessDescriptor& ChromeProcessDescriptor::operator=(ChromeProcessDescriptor&&) = default;
+TraceConfig_TriggerConfig::TraceConfig_TriggerConfig() = default;
+TraceConfig_TriggerConfig::~TraceConfig_TriggerConfig() = default;
+TraceConfig_TriggerConfig::TraceConfig_TriggerConfig(const TraceConfig_TriggerConfig&) = default;
+TraceConfig_TriggerConfig& TraceConfig_TriggerConfig::operator=(const TraceConfig_TriggerConfig&) = default;
+TraceConfig_TriggerConfig::TraceConfig_TriggerConfig(TraceConfig_TriggerConfig&&) noexcept = default;
+TraceConfig_TriggerConfig& TraceConfig_TriggerConfig::operator=(TraceConfig_TriggerConfig&&) = default;

-bool ChromeProcessDescriptor::operator==(const ChromeProcessDescriptor& other) const {
+bool TraceConfig_TriggerConfig::operator==(const TraceConfig_TriggerConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(process_type_, other.process_type_)
-   && ::protozero::internal::gen_helpers::EqualsField(process_priority_, other.process_priority_)
-   && ::protozero::internal::gen_helpers::EqualsField(legacy_sort_index_, other.legacy_sort_index_)
-   && ::protozero::internal::gen_helpers::EqualsField(host_app_package_name_, other.host_app_package_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(crash_trace_id_, other.crash_trace_id_);
+   && ::protozero::internal::gen_helpers::EqualsField(trigger_mode_, other.trigger_mode_)
+   && ::protozero::internal::gen_helpers::EqualsField(use_clone_snapshot_if_available_, other.use_clone_snapshot_if_available_)
+   && ::protozero::internal::gen_helpers::EqualsField(triggers_, other.triggers_)
+   && ::protozero::internal::gen_helpers::EqualsField(trigger_timeout_ms_, other.trigger_timeout_ms_);
 }

-bool ChromeProcessDescriptor::ParseFromArray(const void* raw, size_t size) {
+int TraceConfig_TriggerConfig::triggers_size() const { return static_cast<int>(triggers_.size()); }
+void TraceConfig_TriggerConfig::clear_triggers() { triggers_.clear(); }
+TraceConfig_TriggerConfig_Trigger* TraceConfig_TriggerConfig::add_triggers() { triggers_.emplace_back(); return &triggers_.back(); }
+bool TraceConfig_TriggerConfig::ParseFromArray(const void* raw, size_t size) {
+  triggers_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -34920,20 +34853,18 @@ bool ChromeProcessDescriptor::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* process_type */:
-        field.get(&process_type_);
-        break;
-      case 2 /* process_priority */:
-        field.get(&process_priority_);
+      case 1 /* trigger_mode */:
+        field.get(&trigger_mode_);
         break;
-      case 3 /* legacy_sort_index */:
-        field.get(&legacy_sort_index_);
+      case 5 /* use_clone_snapshot_if_available */:
+        field.get(&use_clone_snapshot_if_available_);
         break;
-      case 4 /* host_app_package_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &host_app_package_name_);
+      case 2 /* triggers */:
+        triggers_.emplace_back();
+        triggers_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 5 /* crash_trace_id */:
-        field.get(&crash_trace_id_);
+      case 3 /* trigger_timeout_ms */:
+        field.get(&trigger_timeout_ms_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -34943,85 +34874,60 @@ bool ChromeProcessDescriptor::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeProcessDescriptor::SerializeAsString() const {
+std::string TraceConfig_TriggerConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeProcessDescriptor::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_TriggerConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeProcessDescriptor::Serialize(::protozero::Message* msg) const {
-  // Field 1: process_type
+void TraceConfig_TriggerConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: trigger_mode
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, process_type_, msg);
-  }
-
-  // Field 2: process_priority
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, process_priority_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, trigger_mode_, msg);
   }

-  // Field 3: legacy_sort_index
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, legacy_sort_index_, msg);
+  // Field 5: use_clone_snapshot_if_available
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, use_clone_snapshot_if_available_, msg);
   }

-  // Field 4: host_app_package_name
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeString(4, host_app_package_name_, msg);
+  // Field 2: triggers
+  for (auto& it : triggers_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

-  // Field 5: crash_trace_id
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, crash_trace_id_, msg);
+  // Field 3: trigger_timeout_ms
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, trigger_timeout_ms_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-ChromeRendererSchedulerState::ChromeRendererSchedulerState() = default;
-ChromeRendererSchedulerState::~ChromeRendererSchedulerState() = default;
-ChromeRendererSchedulerState::ChromeRendererSchedulerState(const ChromeRendererSchedulerState&) = default;
-ChromeRendererSchedulerState& ChromeRendererSchedulerState::operator=(const ChromeRendererSchedulerState&) = default;
-ChromeRendererSchedulerState::ChromeRendererSchedulerState(ChromeRendererSchedulerState&&) noexcept = default;
-ChromeRendererSchedulerState& ChromeRendererSchedulerState::operator=(ChromeRendererSchedulerState&&) = default;
+TraceConfig_TriggerConfig_Trigger::TraceConfig_TriggerConfig_Trigger() = default;
+TraceConfig_TriggerConfig_Trigger::~TraceConfig_TriggerConfig_Trigger() = default;
+TraceConfig_TriggerConfig_Trigger::TraceConfig_TriggerConfig_Trigger(const TraceConfig_TriggerConfig_Trigger&) = default;
+TraceConfig_TriggerConfig_Trigger& TraceConfig_TriggerConfig_Trigger::operator=(const TraceConfig_TriggerConfig_Trigger&) = default;
+TraceConfig_TriggerConfig_Trigger::TraceConfig_TriggerConfig_Trigger(TraceConfig_TriggerConfig_Trigger&&) noexcept = default;
+TraceConfig_TriggerConfig_Trigger& TraceConfig_TriggerConfig_Trigger::operator=(TraceConfig_TriggerConfig_Trigger&&) = default;

-bool ChromeRendererSchedulerState::operator==(const ChromeRendererSchedulerState& other) const {
+bool TraceConfig_TriggerConfig_Trigger::operator==(const TraceConfig_TriggerConfig_Trigger& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(rail_mode_, other.rail_mode_)
-   && ::protozero::internal::gen_helpers::EqualsField(is_backgrounded_, other.is_backgrounded_)
-   && ::protozero::internal::gen_helpers::EqualsField(is_hidden_, other.is_hidden_);
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(producer_name_regex_, other.producer_name_regex_)
+   && ::protozero::internal::gen_helpers::EqualsField(stop_delay_ms_, other.stop_delay_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_per_24_h_, other.max_per_24_h_)
+   && ::protozero::internal::gen_helpers::EqualsField(skip_probability_, other.skip_probability_);
 }

-bool ChromeRendererSchedulerState::ParseFromArray(const void* raw, size_t size) {
+bool TraceConfig_TriggerConfig_Trigger::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -35031,14 +34937,20 @@ bool ChromeRendererSchedulerState::ParseFromArray(const void* raw, size_t size)
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* rail_mode */:
-        field.get(&rail_mode_);
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 2 /* is_backgrounded */:
-        field.get(&is_backgrounded_);
+      case 2 /* producer_name_regex */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &producer_name_regex_);
         break;
-      case 3 /* is_hidden */:
-        field.get(&is_hidden_);
+      case 3 /* stop_delay_ms */:
+        field.get(&stop_delay_ms_);
+        break;
+      case 4 /* max_per_24_h */:
+        field.get(&max_per_24_h_);
+        break;
+      case 5 /* skip_probability */:
+        field.get(&skip_probability_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -35048,75 +34960,62 @@ bool ChromeRendererSchedulerState::ParseFromArray(const void* raw, size_t size)
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeRendererSchedulerState::SerializeAsString() const {
+std::string TraceConfig_TriggerConfig_Trigger::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeRendererSchedulerState::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_TriggerConfig_Trigger::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeRendererSchedulerState::Serialize(::protozero::Message* msg) const {
-  // Field 1: rail_mode
+void TraceConfig_TriggerConfig_Trigger::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, rail_mode_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

-  // Field 2: is_backgrounded
+  // Field 2: producer_name_regex
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, is_backgrounded_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(2, producer_name_regex_, msg);
   }

-  // Field 3: is_hidden
+  // Field 3: stop_delay_ms
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, is_hidden_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, stop_delay_ms_, msg);
+  }
+
+  // Field 4: max_per_24_h
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, max_per_24_h_, msg);
+  }
+
+  // Field 5: skip_probability
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeFixed(5, skip_probability_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_thread_descriptor.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_thread_descriptor.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-ChromeThreadDescriptor::ChromeThreadDescriptor() = default;
-ChromeThreadDescriptor::~ChromeThreadDescriptor() = default;
-ChromeThreadDescriptor::ChromeThreadDescriptor(const ChromeThreadDescriptor&) = default;
-ChromeThreadDescriptor& ChromeThreadDescriptor::operator=(const ChromeThreadDescriptor&) = default;
-ChromeThreadDescriptor::ChromeThreadDescriptor(ChromeThreadDescriptor&&) noexcept = default;
-ChromeThreadDescriptor& ChromeThreadDescriptor::operator=(ChromeThreadDescriptor&&) = default;
+TraceConfig_GuardrailOverrides::TraceConfig_GuardrailOverrides() = default;
+TraceConfig_GuardrailOverrides::~TraceConfig_GuardrailOverrides() = default;
+TraceConfig_GuardrailOverrides::TraceConfig_GuardrailOverrides(const TraceConfig_GuardrailOverrides&) = default;
+TraceConfig_GuardrailOverrides& TraceConfig_GuardrailOverrides::operator=(const TraceConfig_GuardrailOverrides&) = default;
+TraceConfig_GuardrailOverrides::TraceConfig_GuardrailOverrides(TraceConfig_GuardrailOverrides&&) noexcept = default;
+TraceConfig_GuardrailOverrides& TraceConfig_GuardrailOverrides::operator=(TraceConfig_GuardrailOverrides&&) = default;

-bool ChromeThreadDescriptor::operator==(const ChromeThreadDescriptor& other) const {
+bool TraceConfig_GuardrailOverrides::operator==(const TraceConfig_GuardrailOverrides& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(thread_type_, other.thread_type_)
-   && ::protozero::internal::gen_helpers::EqualsField(legacy_sort_index_, other.legacy_sort_index_)
-   && ::protozero::internal::gen_helpers::EqualsField(is_sandboxed_tid_, other.is_sandboxed_tid_);
+   && ::protozero::internal::gen_helpers::EqualsField(max_upload_per_day_bytes_, other.max_upload_per_day_bytes_)
+   && ::protozero::internal::gen_helpers::EqualsField(max_tracing_buffer_size_kb_, other.max_tracing_buffer_size_kb_);
 }

-bool ChromeThreadDescriptor::ParseFromArray(const void* raw, size_t size) {
+bool TraceConfig_GuardrailOverrides::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -35126,14 +35025,11 @@ bool ChromeThreadDescriptor::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* thread_type */:
-        field.get(&thread_type_);
-        break;
-      case 2 /* legacy_sort_index */:
-        field.get(&legacy_sort_index_);
+      case 1 /* max_upload_per_day_bytes */:
+        field.get(&max_upload_per_day_bytes_);
         break;
-      case 3 /* is_sandboxed_tid */:
-        field.get(&is_sandboxed_tid_);
+      case 2 /* max_tracing_buffer_size_kb */:
+        field.get(&max_tracing_buffer_size_kb_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -35143,74 +35039,49 @@ bool ChromeThreadDescriptor::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeThreadDescriptor::SerializeAsString() const {
+std::string TraceConfig_GuardrailOverrides::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeThreadDescriptor::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_GuardrailOverrides::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeThreadDescriptor::Serialize(::protozero::Message* msg) const {
-  // Field 1: thread_type
+void TraceConfig_GuardrailOverrides::Serialize(::protozero::Message* msg) const {
+  // Field 1: max_upload_per_day_bytes
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, thread_type_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, max_upload_per_day_bytes_, msg);
   }

-  // Field 2: legacy_sort_index
+  // Field 2: max_tracing_buffer_size_kb
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, legacy_sort_index_, msg);
-  }
-
-  // Field 3: is_sandboxed_tid
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, is_sandboxed_tid_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, max_tracing_buffer_size_kb_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_user_event.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_user_event.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-ChromeUserEvent::ChromeUserEvent() = default;
-ChromeUserEvent::~ChromeUserEvent() = default;
-ChromeUserEvent::ChromeUserEvent(const ChromeUserEvent&) = default;
-ChromeUserEvent& ChromeUserEvent::operator=(const ChromeUserEvent&) = default;
-ChromeUserEvent::ChromeUserEvent(ChromeUserEvent&&) noexcept = default;
-ChromeUserEvent& ChromeUserEvent::operator=(ChromeUserEvent&&) = default;
+TraceConfig_StatsdMetadata::TraceConfig_StatsdMetadata() = default;
+TraceConfig_StatsdMetadata::~TraceConfig_StatsdMetadata() = default;
+TraceConfig_StatsdMetadata::TraceConfig_StatsdMetadata(const TraceConfig_StatsdMetadata&) = default;
+TraceConfig_StatsdMetadata& TraceConfig_StatsdMetadata::operator=(const TraceConfig_StatsdMetadata&) = default;
+TraceConfig_StatsdMetadata::TraceConfig_StatsdMetadata(TraceConfig_StatsdMetadata&&) noexcept = default;
+TraceConfig_StatsdMetadata& TraceConfig_StatsdMetadata::operator=(TraceConfig_StatsdMetadata&&) = default;

-bool ChromeUserEvent::operator==(const ChromeUserEvent& other) const {
+bool TraceConfig_StatsdMetadata::operator==(const TraceConfig_StatsdMetadata& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(action_, other.action_)
-   && ::protozero::internal::gen_helpers::EqualsField(action_hash_, other.action_hash_);
+   && ::protozero::internal::gen_helpers::EqualsField(triggering_alert_id_, other.triggering_alert_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(triggering_config_uid_, other.triggering_config_uid_)
+   && ::protozero::internal::gen_helpers::EqualsField(triggering_config_id_, other.triggering_config_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(triggering_subscription_id_, other.triggering_subscription_id_);
 }

-bool ChromeUserEvent::ParseFromArray(const void* raw, size_t size) {
+bool TraceConfig_StatsdMetadata::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -35220,11 +35091,17 @@ bool ChromeUserEvent::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* action */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &action_);
+      case 1 /* triggering_alert_id */:
+        field.get(&triggering_alert_id_);
         break;
-      case 2 /* action_hash */:
-        field.get(&action_hash_);
+      case 2 /* triggering_config_uid */:
+        field.get(&triggering_config_uid_);
+        break;
+      case 3 /* triggering_config_id */:
+        field.get(&triggering_config_id_);
+        break;
+      case 4 /* triggering_subscription_id */:
+        field.get(&triggering_subscription_id_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -35234,70 +35111,58 @@ bool ChromeUserEvent::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeUserEvent::SerializeAsString() const {
+std::string TraceConfig_StatsdMetadata::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeUserEvent::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_StatsdMetadata::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeUserEvent::Serialize(::protozero::Message* msg) const {
-  // Field 1: action
+void TraceConfig_StatsdMetadata::Serialize(::protozero::Message* msg) const {
+  // Field 1: triggering_alert_id
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, action_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, triggering_alert_id_, msg);
   }

-  // Field 2: action_hash
+  // Field 2: triggering_config_uid
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, action_hash_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, triggering_config_uid_, msg);
+  }
+
+  // Field 3: triggering_config_id
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, triggering_config_id_, msg);
+  }
+
+  // Field 4: triggering_subscription_id
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, triggering_subscription_id_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_window_handle_event_info.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_window_handle_event_info.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-ChromeWindowHandleEventInfo::ChromeWindowHandleEventInfo() = default;
-ChromeWindowHandleEventInfo::~ChromeWindowHandleEventInfo() = default;
-ChromeWindowHandleEventInfo::ChromeWindowHandleEventInfo(const ChromeWindowHandleEventInfo&) = default;
-ChromeWindowHandleEventInfo& ChromeWindowHandleEventInfo::operator=(const ChromeWindowHandleEventInfo&) = default;
-ChromeWindowHandleEventInfo::ChromeWindowHandleEventInfo(ChromeWindowHandleEventInfo&&) noexcept = default;
-ChromeWindowHandleEventInfo& ChromeWindowHandleEventInfo::operator=(ChromeWindowHandleEventInfo&&) = default;
+TraceConfig_ProducerConfig::TraceConfig_ProducerConfig() = default;
+TraceConfig_ProducerConfig::~TraceConfig_ProducerConfig() = default;
+TraceConfig_ProducerConfig::TraceConfig_ProducerConfig(const TraceConfig_ProducerConfig&) = default;
+TraceConfig_ProducerConfig& TraceConfig_ProducerConfig::operator=(const TraceConfig_ProducerConfig&) = default;
+TraceConfig_ProducerConfig::TraceConfig_ProducerConfig(TraceConfig_ProducerConfig&&) noexcept = default;
+TraceConfig_ProducerConfig& TraceConfig_ProducerConfig::operator=(TraceConfig_ProducerConfig&&) = default;

-bool ChromeWindowHandleEventInfo::operator==(const ChromeWindowHandleEventInfo& other) const {
+bool TraceConfig_ProducerConfig::operator==(const TraceConfig_ProducerConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(dpi_, other.dpi_)
-   && ::protozero::internal::gen_helpers::EqualsField(message_id_, other.message_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(hwnd_ptr_, other.hwnd_ptr_);
+   && ::protozero::internal::gen_helpers::EqualsField(producer_name_, other.producer_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(shm_size_kb_, other.shm_size_kb_)
+   && ::protozero::internal::gen_helpers::EqualsField(page_size_kb_, other.page_size_kb_);
 }

-bool ChromeWindowHandleEventInfo::ParseFromArray(const void* raw, size_t size) {
+bool TraceConfig_ProducerConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -35307,14 +35172,14 @@ bool ChromeWindowHandleEventInfo::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* dpi */:
-        field.get(&dpi_);
+      case 1 /* producer_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &producer_name_);
         break;
-      case 2 /* message_id */:
-        field.get(&message_id_);
+      case 2 /* shm_size_kb */:
+        field.get(&shm_size_kb_);
         break;
-      case 3 /* hwnd_ptr */:
-        field.get(&hwnd_ptr_);
+      case 3 /* page_size_kb */:
+        field.get(&page_size_kb_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -35324,80 +35189,60 @@ bool ChromeWindowHandleEventInfo::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ChromeWindowHandleEventInfo::SerializeAsString() const {
+std::string TraceConfig_ProducerConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ChromeWindowHandleEventInfo::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_ProducerConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ChromeWindowHandleEventInfo::Serialize(::protozero::Message* msg) const {
-  // Field 1: dpi
+void TraceConfig_ProducerConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: producer_name
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, dpi_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(1, producer_name_, msg);
   }

-  // Field 2: message_id
+  // Field 2: shm_size_kb
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, message_id_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, shm_size_kb_, msg);
   }

-  // Field 3: hwnd_ptr
+  // Field 3: page_size_kb
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(3, hwnd_ptr_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, page_size_kb_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/counter_descriptor.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/counter_descriptor.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-CounterDescriptor::CounterDescriptor() = default;
-CounterDescriptor::~CounterDescriptor() = default;
-CounterDescriptor::CounterDescriptor(const CounterDescriptor&) = default;
-CounterDescriptor& CounterDescriptor::operator=(const CounterDescriptor&) = default;
-CounterDescriptor::CounterDescriptor(CounterDescriptor&&) noexcept = default;
-CounterDescriptor& CounterDescriptor::operator=(CounterDescriptor&&) = default;
+TraceConfig_BuiltinDataSource::TraceConfig_BuiltinDataSource() = default;
+TraceConfig_BuiltinDataSource::~TraceConfig_BuiltinDataSource() = default;
+TraceConfig_BuiltinDataSource::TraceConfig_BuiltinDataSource(const TraceConfig_BuiltinDataSource&) = default;
+TraceConfig_BuiltinDataSource& TraceConfig_BuiltinDataSource::operator=(const TraceConfig_BuiltinDataSource&) = default;
+TraceConfig_BuiltinDataSource::TraceConfig_BuiltinDataSource(TraceConfig_BuiltinDataSource&&) noexcept = default;
+TraceConfig_BuiltinDataSource& TraceConfig_BuiltinDataSource::operator=(TraceConfig_BuiltinDataSource&&) = default;

-bool CounterDescriptor::operator==(const CounterDescriptor& other) const {
+bool TraceConfig_BuiltinDataSource::operator==(const TraceConfig_BuiltinDataSource& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
-   && ::protozero::internal::gen_helpers::EqualsField(categories_, other.categories_)
-   && ::protozero::internal::gen_helpers::EqualsField(unit_, other.unit_)
-   && ::protozero::internal::gen_helpers::EqualsField(unit_name_, other.unit_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(unit_multiplier_, other.unit_multiplier_)
-   && ::protozero::internal::gen_helpers::EqualsField(is_incremental_, other.is_incremental_)
-   && ::protozero::internal::gen_helpers::EqualsField(y_axis_share_key_, other.y_axis_share_key_);
+   && ::protozero::internal::gen_helpers::EqualsField(disable_clock_snapshotting_, other.disable_clock_snapshotting_)
+   && ::protozero::internal::gen_helpers::EqualsField(disable_trace_config_, other.disable_trace_config_)
+   && ::protozero::internal::gen_helpers::EqualsField(disable_system_info_, other.disable_system_info_)
+   && ::protozero::internal::gen_helpers::EqualsField(disable_service_events_, other.disable_service_events_)
+   && ::protozero::internal::gen_helpers::EqualsField(primary_trace_clock_, other.primary_trace_clock_)
+   && ::protozero::internal::gen_helpers::EqualsField(snapshot_interval_ms_, other.snapshot_interval_ms_)
+   && ::protozero::internal::gen_helpers::EqualsField(prefer_suspend_clock_for_snapshot_, other.prefer_suspend_clock_for_snapshot_)
+   && ::protozero::internal::gen_helpers::EqualsField(disable_chunk_usage_histograms_, other.disable_chunk_usage_histograms_)
+   && ::protozero::internal::gen_helpers::EqualsField(disable_extension_descriptors_, other.disable_extension_descriptors_)
+   && ::protozero::internal::gen_helpers::EqualsField(enable_concurrent_session_events_, other.enable_concurrent_session_events_);
 }

-bool CounterDescriptor::ParseFromArray(const void* raw, size_t size) {
-  categories_.clear();
+bool TraceConfig_BuiltinDataSource::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -35407,27 +35252,35 @@ bool CounterDescriptor::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* type */:
-        field.get(&type_);
-        break;
-      case 2 /* categories */:
-        categories_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &categories_.back());
+      case 1 /* disable_clock_snapshotting */:
+        field.get(&disable_clock_snapshotting_);
         break;
-      case 3 /* unit */:
-        field.get(&unit_);
+      case 2 /* disable_trace_config */:
+        field.get(&disable_trace_config_);
         break;
-      case 6 /* unit_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &unit_name_);
+      case 3 /* disable_system_info */:
+        field.get(&disable_system_info_);
         break;
-      case 4 /* unit_multiplier */:
-        field.get(&unit_multiplier_);
+      case 4 /* disable_service_events */:
+        field.get(&disable_service_events_);
         break;
-      case 5 /* is_incremental */:
-        field.get(&is_incremental_);
+      case 5 /* primary_trace_clock */:
+        field.get(&primary_trace_clock_);
         break;
-      case 7 /* y_axis_share_key */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &y_axis_share_key_);
+      case 6 /* snapshot_interval_ms */:
+        field.get(&snapshot_interval_ms_);
+        break;
+      case 7 /* prefer_suspend_clock_for_snapshot */:
+        field.get(&prefer_suspend_clock_for_snapshot_);
+        break;
+      case 8 /* disable_chunk_usage_histograms */:
+        field.get(&disable_chunk_usage_histograms_);
+        break;
+      case 9 /* disable_extension_descriptors */:
+        field.get(&disable_extension_descriptors_);
+        break;
+      case 10 /* enable_concurrent_session_events */:
+        field.get(&enable_concurrent_session_events_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -35437,158 +35290,92 @@ bool CounterDescriptor::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string CounterDescriptor::SerializeAsString() const {
+std::string TraceConfig_BuiltinDataSource::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> CounterDescriptor::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_BuiltinDataSource::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void CounterDescriptor::Serialize(::protozero::Message* msg) const {
-  // Field 1: type
+void TraceConfig_BuiltinDataSource::Serialize(::protozero::Message* msg) const {
+  // Field 1: disable_clock_snapshotting
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, type_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, disable_clock_snapshotting_, msg);
   }

-  // Field 2: categories
-  for (auto& it : categories_) {
-    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
+  // Field 2: disable_trace_config
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, disable_trace_config_, msg);
   }

-  // Field 3: unit
+  // Field 3: disable_system_info
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, unit_, msg);
-  }
-
-  // Field 6: unit_name
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeString(6, unit_name_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, disable_system_info_, msg);
   }

-  // Field 4: unit_multiplier
+  // Field 4: disable_service_events
   if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, unit_multiplier_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, disable_service_events_, msg);
   }

-  // Field 5: is_incremental
+  // Field 5: primary_trace_clock
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, is_incremental_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, primary_trace_clock_, msg);
   }

-  // Field 7: y_axis_share_key
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeString(7, y_axis_share_key_, msg);
+  // Field 6: snapshot_interval_ms
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, snapshot_interval_ms_, msg);
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
-
-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/debug_annotation.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/debug_annotation.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {
-
-DebugAnnotationValueTypeName::DebugAnnotationValueTypeName() = default;
-DebugAnnotationValueTypeName::~DebugAnnotationValueTypeName() = default;
-DebugAnnotationValueTypeName::DebugAnnotationValueTypeName(const DebugAnnotationValueTypeName&) = default;
-DebugAnnotationValueTypeName& DebugAnnotationValueTypeName::operator=(const DebugAnnotationValueTypeName&) = default;
-DebugAnnotationValueTypeName::DebugAnnotationValueTypeName(DebugAnnotationValueTypeName&&) noexcept = default;
-DebugAnnotationValueTypeName& DebugAnnotationValueTypeName::operator=(DebugAnnotationValueTypeName&&) = default;
-
-bool DebugAnnotationValueTypeName::operator==(const DebugAnnotationValueTypeName& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
-}
-
-bool DebugAnnotationValueTypeName::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
-
-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* iid */:
-        field.get(&iid_);
-        break;
-      case 2 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
+  // Field 7: prefer_suspend_clock_for_snapshot
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, prefer_suspend_clock_for_snapshot_, msg);
   }
-  return !packed_error && !dec.bytes_left();
-}
-
-std::string DebugAnnotationValueTypeName::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}

-std::vector<uint8_t> DebugAnnotationValueTypeName::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
+  // Field 8: disable_chunk_usage_histograms
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, disable_chunk_usage_histograms_, msg);
+  }

-void DebugAnnotationValueTypeName::Serialize(::protozero::Message* msg) const {
-  // Field 1: iid
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
+  // Field 9: disable_extension_descriptors
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, disable_extension_descriptors_, msg);
   }

-  // Field 2: name
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
+  // Field 10: enable_concurrent_session_events
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(10, enable_concurrent_session_events_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-DebugAnnotationName::DebugAnnotationName() = default;
-DebugAnnotationName::~DebugAnnotationName() = default;
-DebugAnnotationName::DebugAnnotationName(const DebugAnnotationName&) = default;
-DebugAnnotationName& DebugAnnotationName::operator=(const DebugAnnotationName&) = default;
-DebugAnnotationName::DebugAnnotationName(DebugAnnotationName&&) noexcept = default;
-DebugAnnotationName& DebugAnnotationName::operator=(DebugAnnotationName&&) = default;
+TraceConfig_DataSource::TraceConfig_DataSource() = default;
+TraceConfig_DataSource::~TraceConfig_DataSource() = default;
+TraceConfig_DataSource::TraceConfig_DataSource(const TraceConfig_DataSource&) = default;
+TraceConfig_DataSource& TraceConfig_DataSource::operator=(const TraceConfig_DataSource&) = default;
+TraceConfig_DataSource::TraceConfig_DataSource(TraceConfig_DataSource&&) noexcept = default;
+TraceConfig_DataSource& TraceConfig_DataSource::operator=(TraceConfig_DataSource&&) = default;

-bool DebugAnnotationName::operator==(const DebugAnnotationName& other) const {
+bool TraceConfig_DataSource::operator==(const TraceConfig_DataSource& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+   && ::protozero::internal::gen_helpers::EqualsField(config_, other.config_)
+   && ::protozero::internal::gen_helpers::EqualsField(producer_name_filter_, other.producer_name_filter_)
+   && ::protozero::internal::gen_helpers::EqualsField(producer_name_regex_filter_, other.producer_name_regex_filter_)
+   && ::protozero::internal::gen_helpers::EqualsField(machine_name_filter_, other.machine_name_filter_);
 }

-bool DebugAnnotationName::ParseFromArray(const void* raw, size_t size) {
+bool TraceConfig_DataSource::ParseFromArray(const void* raw, size_t size) {
+  producer_name_filter_.clear();
+  producer_name_regex_filter_.clear();
+  machine_name_filter_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -35598,11 +35385,20 @@ bool DebugAnnotationName::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* iid */:
-        field.get(&iid_);
+      case 1 /* config */:
+        (*config_).ParseFromArray(field.data(), field.size());
         break;
-      case 2 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+      case 2 /* producer_name_filter */:
+        producer_name_filter_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &producer_name_filter_.back());
+        break;
+      case 3 /* producer_name_regex_filter */:
+        producer_name_regex_filter_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &producer_name_regex_filter_.back());
+        break;
+      case 4 /* machine_name_filter */:
+        machine_name_filter_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &machine_name_filter_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -35612,69 +35408,61 @@ bool DebugAnnotationName::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string DebugAnnotationName::SerializeAsString() const {
+std::string TraceConfig_DataSource::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> DebugAnnotationName::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_DataSource::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void DebugAnnotationName::Serialize(::protozero::Message* msg) const {
-  // Field 1: iid
+void TraceConfig_DataSource::Serialize(::protozero::Message* msg) const {
+  // Field 1: config
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
+    (*config_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 2: name
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
+  // Field 2: producer_name_filter
+  for (auto& it : producer_name_filter_) {
+    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
+  }
+
+  // Field 3: producer_name_regex_filter
+  for (auto& it : producer_name_regex_filter_) {
+    ::protozero::internal::gen_helpers::SerializeString(3, it, msg);
+  }
+
+  // Field 4: machine_name_filter
+  for (auto& it : machine_name_filter_) {
+    ::protozero::internal::gen_helpers::SerializeString(4, it, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-DebugAnnotation::DebugAnnotation() = default;
-DebugAnnotation::~DebugAnnotation() = default;
-DebugAnnotation::DebugAnnotation(const DebugAnnotation&) = default;
-DebugAnnotation& DebugAnnotation::operator=(const DebugAnnotation&) = default;
-DebugAnnotation::DebugAnnotation(DebugAnnotation&&) noexcept = default;
-DebugAnnotation& DebugAnnotation::operator=(DebugAnnotation&&) = default;
+TraceConfig_BufferConfig::TraceConfig_BufferConfig() = default;
+TraceConfig_BufferConfig::~TraceConfig_BufferConfig() = default;
+TraceConfig_BufferConfig::TraceConfig_BufferConfig(const TraceConfig_BufferConfig&) = default;
+TraceConfig_BufferConfig& TraceConfig_BufferConfig::operator=(const TraceConfig_BufferConfig&) = default;
+TraceConfig_BufferConfig::TraceConfig_BufferConfig(TraceConfig_BufferConfig&&) noexcept = default;
+TraceConfig_BufferConfig& TraceConfig_BufferConfig::operator=(TraceConfig_BufferConfig&&) = default;

-bool DebugAnnotation::operator==(const DebugAnnotation& other) const {
+bool TraceConfig_BufferConfig::operator==(const TraceConfig_BufferConfig& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_iid_, other.name_iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(size_kb_, other.size_kb_)
+   && ::protozero::internal::gen_helpers::EqualsField(fill_policy_, other.fill_policy_)
+   && ::protozero::internal::gen_helpers::EqualsField(transfer_on_clone_, other.transfer_on_clone_)
+   && ::protozero::internal::gen_helpers::EqualsField(clear_before_clone_, other.clear_before_clone_)
    && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(bool_value_, other.bool_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(uint_value_, other.uint_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(int_value_, other.int_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(double_value_, other.double_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(pointer_value_, other.pointer_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(nested_value_, other.nested_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(legacy_json_value_, other.legacy_json_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(string_value_, other.string_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(string_value_iid_, other.string_value_iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(proto_type_name_, other.proto_type_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(proto_type_name_iid_, other.proto_type_name_iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(proto_value_, other.proto_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(dict_entries_, other.dict_entries_)
-   && ::protozero::internal::gen_helpers::EqualsField(array_values_, other.array_values_);
+   && ::protozero::internal::gen_helpers::EqualsField(experimental_mode_, other.experimental_mode_);
 }

-int DebugAnnotation::dict_entries_size() const { return static_cast<int>(dict_entries_.size()); }
-void DebugAnnotation::clear_dict_entries() { dict_entries_.clear(); }
-DebugAnnotation* DebugAnnotation::add_dict_entries() { dict_entries_.emplace_back(); return &dict_entries_.back(); }
-int DebugAnnotation::array_values_size() const { return static_cast<int>(array_values_.size()); }
-void DebugAnnotation::clear_array_values() { array_values_.clear(); }
-DebugAnnotation* DebugAnnotation::add_array_values() { array_values_.emplace_back(); return &array_values_.back(); }
-bool DebugAnnotation::ParseFromArray(const void* raw, size_t size) {
-  dict_entries_.clear();
-  array_values_.clear();
+bool TraceConfig_BufferConfig::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -35684,55 +35472,23 @@ bool DebugAnnotation::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* name_iid */:
-        field.get(&name_iid_);
-        break;
-      case 10 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 2 /* bool_value */:
-        field.get(&bool_value_);
-        break;
-      case 3 /* uint_value */:
-        field.get(&uint_value_);
-        break;
-      case 4 /* int_value */:
-        field.get(&int_value_);
-        break;
-      case 5 /* double_value */:
-        field.get(&double_value_);
-        break;
-      case 7 /* pointer_value */:
-        field.get(&pointer_value_);
-        break;
-      case 8 /* nested_value */:
-        (*nested_value_).ParseFromArray(field.data(), field.size());
-        break;
-      case 9 /* legacy_json_value */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &legacy_json_value_);
-        break;
-      case 6 /* string_value */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &string_value_);
-        break;
-      case 17 /* string_value_iid */:
-        field.get(&string_value_iid_);
+      case 1 /* size_kb */:
+        field.get(&size_kb_);
         break;
-      case 16 /* proto_type_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &proto_type_name_);
+      case 4 /* fill_policy */:
+        field.get(&fill_policy_);
         break;
-      case 13 /* proto_type_name_iid */:
-        field.get(&proto_type_name_iid_);
+      case 5 /* transfer_on_clone */:
+        field.get(&transfer_on_clone_);
         break;
-      case 14 /* proto_value */:
-        field.get(&proto_value_);
+      case 6 /* clear_before_clone */:
+        field.get(&clear_before_clone_);
         break;
-      case 11 /* dict_entries */:
-        dict_entries_.emplace_back();
-        dict_entries_.back().ParseFromArray(field.data(), field.size());
+      case 7 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
         break;
-      case 12 /* array_values */:
-        array_values_.emplace_back();
-        array_values_.back().ParseFromArray(field.data(), field.size());
+      case 8 /* experimental_mode */:
+        field.get(&experimental_mode_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -35742,227 +35498,47 @@ bool DebugAnnotation::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string DebugAnnotation::SerializeAsString() const {
+std::string TraceConfig_BufferConfig::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> DebugAnnotation::SerializeAsArray() const {
+std::vector<uint8_t> TraceConfig_BufferConfig::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void DebugAnnotation::Serialize(::protozero::Message* msg) const {
-  // Field 1: name_iid
+void TraceConfig_BufferConfig::Serialize(::protozero::Message* msg) const {
+  // Field 1: size_kb
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, name_iid_, msg);
-  }
-
-  // Field 10: name
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeString(10, name_, msg);
-  }
-
-  // Field 2: bool_value
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, bool_value_, msg);
-  }
-
-  // Field 3: uint_value
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, uint_value_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, size_kb_, msg);
   }

-  // Field 4: int_value
+  // Field 4: fill_policy
   if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, int_value_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, fill_policy_, msg);
   }

-  // Field 5: double_value
+  // Field 5: transfer_on_clone
   if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(5, double_value_, msg);
-  }
-
-  // Field 7: pointer_value
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, pointer_value_, msg);
-  }
-
-  // Field 8: nested_value
-  if (_has_field_[8]) {
-    (*nested_value_).Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
-  }
-
-  // Field 9: legacy_json_value
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeString(9, legacy_json_value_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, transfer_on_clone_, msg);
   }

-  // Field 6: string_value
+  // Field 6: clear_before_clone
   if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeString(6, string_value_, msg);
-  }
-
-  // Field 17: string_value_iid
-  if (_has_field_[17]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(17, string_value_iid_, msg);
-  }
-
-  // Field 16: proto_type_name
-  if (_has_field_[16]) {
-    ::protozero::internal::gen_helpers::SerializeString(16, proto_type_name_, msg);
-  }
-
-  // Field 13: proto_type_name_iid
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(13, proto_type_name_iid_, msg);
-  }
-
-  // Field 14: proto_value
-  if (_has_field_[14]) {
-    ::protozero::internal::gen_helpers::SerializeString(14, proto_value_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(6, clear_before_clone_, msg);
   }

-  // Field 11: dict_entries
-  for (auto& it : dict_entries_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(11));
+  // Field 7: name
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeString(7, name_, msg);
   }

-  // Field 12: array_values
-  for (auto& it : array_values_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(12));
-  }
-
-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
-
-
-DebugAnnotation_NestedValue::DebugAnnotation_NestedValue() = default;
-DebugAnnotation_NestedValue::~DebugAnnotation_NestedValue() = default;
-DebugAnnotation_NestedValue::DebugAnnotation_NestedValue(const DebugAnnotation_NestedValue&) = default;
-DebugAnnotation_NestedValue& DebugAnnotation_NestedValue::operator=(const DebugAnnotation_NestedValue&) = default;
-DebugAnnotation_NestedValue::DebugAnnotation_NestedValue(DebugAnnotation_NestedValue&&) noexcept = default;
-DebugAnnotation_NestedValue& DebugAnnotation_NestedValue::operator=(DebugAnnotation_NestedValue&&) = default;
-
-bool DebugAnnotation_NestedValue::operator==(const DebugAnnotation_NestedValue& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(nested_type_, other.nested_type_)
-   && ::protozero::internal::gen_helpers::EqualsField(dict_keys_, other.dict_keys_)
-   && ::protozero::internal::gen_helpers::EqualsField(dict_values_, other.dict_values_)
-   && ::protozero::internal::gen_helpers::EqualsField(array_values_, other.array_values_)
-   && ::protozero::internal::gen_helpers::EqualsField(int_value_, other.int_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(double_value_, other.double_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(bool_value_, other.bool_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(string_value_, other.string_value_);
-}
-
-int DebugAnnotation_NestedValue::dict_values_size() const { return static_cast<int>(dict_values_.size()); }
-void DebugAnnotation_NestedValue::clear_dict_values() { dict_values_.clear(); }
-DebugAnnotation_NestedValue* DebugAnnotation_NestedValue::add_dict_values() { dict_values_.emplace_back(); return &dict_values_.back(); }
-int DebugAnnotation_NestedValue::array_values_size() const { return static_cast<int>(array_values_.size()); }
-void DebugAnnotation_NestedValue::clear_array_values() { array_values_.clear(); }
-DebugAnnotation_NestedValue* DebugAnnotation_NestedValue::add_array_values() { array_values_.emplace_back(); return &array_values_.back(); }
-bool DebugAnnotation_NestedValue::ParseFromArray(const void* raw, size_t size) {
-  dict_keys_.clear();
-  dict_values_.clear();
-  array_values_.clear();
-  unknown_fields_.clear();
-  bool packed_error = false;
-
-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* nested_type */:
-        field.get(&nested_type_);
-        break;
-      case 2 /* dict_keys */:
-        dict_keys_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &dict_keys_.back());
-        break;
-      case 3 /* dict_values */:
-        dict_values_.emplace_back();
-        dict_values_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 4 /* array_values */:
-        array_values_.emplace_back();
-        array_values_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 5 /* int_value */:
-        field.get(&int_value_);
-        break;
-      case 6 /* double_value */:
-        field.get(&double_value_);
-        break;
-      case 7 /* bool_value */:
-        field.get(&bool_value_);
-        break;
-      case 8 /* string_value */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &string_value_);
-        break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
-        break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
-}
-
-std::string DebugAnnotation_NestedValue::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
-
-std::vector<uint8_t> DebugAnnotation_NestedValue::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
-
-void DebugAnnotation_NestedValue::Serialize(::protozero::Message* msg) const {
-  // Field 1: nested_type
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, nested_type_, msg);
-  }
-
-  // Field 2: dict_keys
-  for (auto& it : dict_keys_) {
-    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
-  }
-
-  // Field 3: dict_values
-  for (auto& it : dict_values_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
-  }
-
-  // Field 4: array_values
-  for (auto& it : array_values_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
-  }
-
-  // Field 5: int_value
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, int_value_, msg);
-  }
-
-  // Field 6: double_value
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(6, double_value_, msg);
-  }
-
-  // Field 7: bool_value
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, bool_value_, msg);
-  }
-
-  // Field 8: string_value
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeString(8, string_value_, msg);
+  // Field 8: experimental_mode
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, experimental_mode_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -35974,7 +35550,163 @@ void DebugAnnotation_NestedValue::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/log_message.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/protovm/vm_program.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/android_energy_consumer_descriptor.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/android_log_constants.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/builtin_clock.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/commit_data_request.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/data_source_descriptor.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/descriptor.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/ftrace_descriptor.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/gpu_counter_descriptor.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/interceptor_descriptor.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/observable_events.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/perf_events.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/sys_stats_counters.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/system_info.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/trace_attributes.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/trace_stats.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/tracing_service_capabilities.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/tracing_service_state.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/common/track_event_descriptor.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/android/android_aflags.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/android/android_game_intervention_list.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/android/android_log.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/android/android_system_property.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/android/cpu_per_uid_data.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/android/graphics_frame_event.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/android/initial_display_state.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/android/kernel_wakelock_data.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/android/packages_list.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/android/pixel_modem_events.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/android/recovered_trace_info.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/android/user_list.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/chrome/chrome_benchmark_metadata.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/chrome/chrome_metadata.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/chrome/chrome_trigger.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/chrome/v8.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/gpu/gpu_counter_event.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/gpu/gpu_log.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/gpu/gpu_mem_event.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/gpu/gpu_render_stage_event.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/gpu/vulkan_api_event.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/gpu/vulkan_memory_event.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/profiling/inline_callstack.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/profiling/art_process_metadata.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/profiling/deobfuscation.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/profiling/profile_common.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/profiling/profile_packet.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/profiling/smaps.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/profiling/stack_sample.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_active_processes.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_application_state_info.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_content_settings_event_info.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_frame_reporter.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_histogram_sample.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_keyed_service.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_latency_info.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_legacy_ipc.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_message_pump.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_mojo_event_info.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_thread_descriptor.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_user_event.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_window_handle_event_info.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/counter_descriptor.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/debug_annotation.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/log_message.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/process_descriptor.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/range_of_interest.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/screenshot.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/source_location.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/state_descriptor.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/task_execution.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/thread_descriptor.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/track_descriptor.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/track_event.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/interned_data/interned_data.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_active_processes.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -35985,26 +35717,26 @@ void DebugAnnotation_NestedValue::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/log_message.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_active_processes.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-LogMessageBody::LogMessageBody() = default;
-LogMessageBody::~LogMessageBody() = default;
-LogMessageBody::LogMessageBody(const LogMessageBody&) = default;
-LogMessageBody& LogMessageBody::operator=(const LogMessageBody&) = default;
-LogMessageBody::LogMessageBody(LogMessageBody&&) noexcept = default;
-LogMessageBody& LogMessageBody::operator=(LogMessageBody&&) = default;
+ChromeActiveProcesses::ChromeActiveProcesses() = default;
+ChromeActiveProcesses::~ChromeActiveProcesses() = default;
+ChromeActiveProcesses::ChromeActiveProcesses(const ChromeActiveProcesses&) = default;
+ChromeActiveProcesses& ChromeActiveProcesses::operator=(const ChromeActiveProcesses&) = default;
+ChromeActiveProcesses::ChromeActiveProcesses(ChromeActiveProcesses&&) noexcept = default;
+ChromeActiveProcesses& ChromeActiveProcesses::operator=(ChromeActiveProcesses&&) = default;

-bool LogMessageBody::operator==(const LogMessageBody& other) const {
+bool ChromeActiveProcesses::operator==(const ChromeActiveProcesses& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(body_, other.body_);
+   && ::protozero::internal::gen_helpers::EqualsField(pid_, other.pid_);
 }

-bool LogMessageBody::ParseFromArray(const void* raw, size_t size) {
+bool ChromeActiveProcesses::ParseFromArray(const void* raw, size_t size) {
+  pid_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -36014,11 +35746,9 @@ bool LogMessageBody::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* iid */:
-        field.get(&iid_);
-        break;
-      case 2 /* body */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &body_);
+      case 1 /* pid */:
+        pid_.emplace_back();
+        field.get(&pid_.back());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -36028,48 +35758,63 @@ bool LogMessageBody::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string LogMessageBody::SerializeAsString() const {
+std::string ChromeActiveProcesses::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> LogMessageBody::SerializeAsArray() const {
+std::vector<uint8_t> ChromeActiveProcesses::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void LogMessageBody::Serialize(::protozero::Message* msg) const {
-  // Field 1: iid
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
-  }
-
-  // Field 2: body
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, body_, msg);
+void ChromeActiveProcesses::Serialize(::protozero::Message* msg) const {
+  // Field 1: pid
+  for (auto& it : pid_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, it, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_application_state_info.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_application_state_info.gen.h"

-LogMessage::LogMessage() = default;
-LogMessage::~LogMessage() = default;
-LogMessage::LogMessage(const LogMessage&) = default;
-LogMessage& LogMessage::operator=(const LogMessage&) = default;
-LogMessage::LogMessage(LogMessage&&) noexcept = default;
-LogMessage& LogMessage::operator=(LogMessage&&) = default;
+namespace perfetto {
+namespace protos {
+namespace gen {

-bool LogMessage::operator==(const LogMessage& other) const {
+ChromeApplicationStateInfo::ChromeApplicationStateInfo() = default;
+ChromeApplicationStateInfo::~ChromeApplicationStateInfo() = default;
+ChromeApplicationStateInfo::ChromeApplicationStateInfo(const ChromeApplicationStateInfo&) = default;
+ChromeApplicationStateInfo& ChromeApplicationStateInfo::operator=(const ChromeApplicationStateInfo&) = default;
+ChromeApplicationStateInfo::ChromeApplicationStateInfo(ChromeApplicationStateInfo&&) noexcept = default;
+ChromeApplicationStateInfo& ChromeApplicationStateInfo::operator=(ChromeApplicationStateInfo&&) = default;
+
+bool ChromeApplicationStateInfo::operator==(const ChromeApplicationStateInfo& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(source_location_iid_, other.source_location_iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(body_iid_, other.body_iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(prio_, other.prio_);
+   && ::protozero::internal::gen_helpers::EqualsField(application_state_, other.application_state_);
 }

-bool LogMessage::ParseFromArray(const void* raw, size_t size) {
+bool ChromeApplicationStateInfo::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -36079,14 +35824,8 @@ bool LogMessage::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* source_location_iid */:
-        field.get(&source_location_iid_);
-        break;
-      case 2 /* body_iid */:
-        field.get(&body_iid_);
-        break;
-      case 3 /* prio */:
-        field.get(&prio_);
+      case 1 /* application_state */:
+        field.get(&application_state_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -36096,32 +35835,22 @@ bool LogMessage::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string LogMessage::SerializeAsString() const {
+std::string ChromeApplicationStateInfo::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> LogMessage::SerializeAsArray() const {
+std::vector<uint8_t> ChromeApplicationStateInfo::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void LogMessage::Serialize(::protozero::Message* msg) const {
-  // Field 1: source_location_iid
+void ChromeApplicationStateInfo::Serialize(::protozero::Message* msg) const {
+  // Field 1: application_state
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, source_location_iid_, msg);
-  }
-
-  // Field 2: body_iid
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, body_iid_, msg);
-  }
-
-  // Field 3: prio
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, prio_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, application_state_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -36133,7 +35862,7 @@ void LogMessage::Serialize(::protozero::Message* msg) const {
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/process_descriptor.gen.cc
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.gen.cc
 // gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/message.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
@@ -36144,34 +35873,32 @@ void LogMessage::Serialize(::protozero::Message* msg) const {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
 #endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/process_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/source_location.gen.h"

 namespace perfetto {
 namespace protos {
 namespace gen {

-ProcessDescriptor::ProcessDescriptor() = default;
-ProcessDescriptor::~ProcessDescriptor() = default;
-ProcessDescriptor::ProcessDescriptor(const ProcessDescriptor&) = default;
-ProcessDescriptor& ProcessDescriptor::operator=(const ProcessDescriptor&) = default;
-ProcessDescriptor::ProcessDescriptor(ProcessDescriptor&&) noexcept = default;
-ProcessDescriptor& ProcessDescriptor::operator=(ProcessDescriptor&&) = default;
+CompositorTimingHistory::CompositorTimingHistory() = default;
+CompositorTimingHistory::~CompositorTimingHistory() = default;
+CompositorTimingHistory::CompositorTimingHistory(const CompositorTimingHistory&) = default;
+CompositorTimingHistory& CompositorTimingHistory::operator=(const CompositorTimingHistory&) = default;
+CompositorTimingHistory::CompositorTimingHistory(CompositorTimingHistory&&) noexcept = default;
+CompositorTimingHistory& CompositorTimingHistory::operator=(CompositorTimingHistory&&) = default;

-bool ProcessDescriptor::operator==(const ProcessDescriptor& other) const {
+bool CompositorTimingHistory::operator==(const CompositorTimingHistory& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(pid_, other.pid_)
-   && ::protozero::internal::gen_helpers::EqualsField(cmdline_, other.cmdline_)
-   && ::protozero::internal::gen_helpers::EqualsField(process_name_, other.process_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(process_priority_, other.process_priority_)
-   && ::protozero::internal::gen_helpers::EqualsField(start_timestamp_ns_, other.start_timestamp_ns_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_process_type_, other.chrome_process_type_)
-   && ::protozero::internal::gen_helpers::EqualsField(legacy_sort_index_, other.legacy_sort_index_)
-   && ::protozero::internal::gen_helpers::EqualsField(process_labels_, other.process_labels_);
+   && ::protozero::internal::gen_helpers::EqualsField(begin_main_frame_queue_critical_estimate_delta_us_, other.begin_main_frame_queue_critical_estimate_delta_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(begin_main_frame_queue_not_critical_estimate_delta_us_, other.begin_main_frame_queue_not_critical_estimate_delta_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(begin_main_frame_start_to_ready_to_commit_estimate_delta_us_, other.begin_main_frame_start_to_ready_to_commit_estimate_delta_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(commit_to_ready_to_activate_estimate_delta_us_, other.commit_to_ready_to_activate_estimate_delta_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(prepare_tiles_estimate_delta_us_, other.prepare_tiles_estimate_delta_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(activate_estimate_delta_us_, other.activate_estimate_delta_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(draw_estimate_delta_us_, other.draw_estimate_delta_us_);
 }

-bool ProcessDescriptor::ParseFromArray(const void* raw, size_t size) {
-  cmdline_.clear();
-  process_labels_.clear();
+bool CompositorTimingHistory::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -36181,31 +35908,26 @@ bool ProcessDescriptor::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* pid */:
-        field.get(&pid_);
-        break;
-      case 2 /* cmdline */:
-        cmdline_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &cmdline_.back());
+      case 1 /* begin_main_frame_queue_critical_estimate_delta_us */:
+        field.get(&begin_main_frame_queue_critical_estimate_delta_us_);
         break;
-      case 6 /* process_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &process_name_);
+      case 2 /* begin_main_frame_queue_not_critical_estimate_delta_us */:
+        field.get(&begin_main_frame_queue_not_critical_estimate_delta_us_);
         break;
-      case 5 /* process_priority */:
-        field.get(&process_priority_);
+      case 3 /* begin_main_frame_start_to_ready_to_commit_estimate_delta_us */:
+        field.get(&begin_main_frame_start_to_ready_to_commit_estimate_delta_us_);
         break;
-      case 7 /* start_timestamp_ns */:
-        field.get(&start_timestamp_ns_);
+      case 4 /* commit_to_ready_to_activate_estimate_delta_us */:
+        field.get(&commit_to_ready_to_activate_estimate_delta_us_);
         break;
-      case 4 /* chrome_process_type */:
-        field.get(&chrome_process_type_);
+      case 5 /* prepare_tiles_estimate_delta_us */:
+        field.get(&prepare_tiles_estimate_delta_us_);
         break;
-      case 3 /* legacy_sort_index */:
-        field.get(&legacy_sort_index_);
+      case 6 /* activate_estimate_delta_us */:
+        field.get(&activate_estimate_delta_us_);
         break;
-      case 8 /* process_labels */:
-        process_labels_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &process_labels_.back());
+      case 7 /* draw_estimate_delta_us */:
+        field.get(&draw_estimate_delta_us_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -36215,98 +35937,74 @@ bool ProcessDescriptor::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string ProcessDescriptor::SerializeAsString() const {
+std::string CompositorTimingHistory::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> ProcessDescriptor::SerializeAsArray() const {
+std::vector<uint8_t> CompositorTimingHistory::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void ProcessDescriptor::Serialize(::protozero::Message* msg) const {
-  // Field 1: pid
+void CompositorTimingHistory::Serialize(::protozero::Message* msg) const {
+  // Field 1: begin_main_frame_queue_critical_estimate_delta_us
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, pid_, msg);
-  }
-
-  // Field 2: cmdline
-  for (auto& it : cmdline_) {
-    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, begin_main_frame_queue_critical_estimate_delta_us_, msg);
   }

-  // Field 6: process_name
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeString(6, process_name_, msg);
+  // Field 2: begin_main_frame_queue_not_critical_estimate_delta_us
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, begin_main_frame_queue_not_critical_estimate_delta_us_, msg);
   }

-  // Field 5: process_priority
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, process_priority_, msg);
+  // Field 3: begin_main_frame_start_to_ready_to_commit_estimate_delta_us
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, begin_main_frame_start_to_ready_to_commit_estimate_delta_us_, msg);
   }

-  // Field 7: start_timestamp_ns
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, start_timestamp_ns_, msg);
+  // Field 4: commit_to_ready_to_activate_estimate_delta_us
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, commit_to_ready_to_activate_estimate_delta_us_, msg);
   }

-  // Field 4: chrome_process_type
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, chrome_process_type_, msg);
+  // Field 5: prepare_tiles_estimate_delta_us
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, prepare_tiles_estimate_delta_us_, msg);
   }

-  // Field 3: legacy_sort_index
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, legacy_sort_index_, msg);
+  // Field 6: activate_estimate_delta_us
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, activate_estimate_delta_us_, msg);
   }

-  // Field 8: process_labels
-  for (auto& it : process_labels_) {
-    ::protozero::internal::gen_helpers::SerializeString(8, it, msg);
+  // Field 7: draw_estimate_delta_us
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, draw_estimate_delta_us_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/range_of_interest.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/range_of_interest.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-TrackEventRangeOfInterest::TrackEventRangeOfInterest() = default;
-TrackEventRangeOfInterest::~TrackEventRangeOfInterest() = default;
-TrackEventRangeOfInterest::TrackEventRangeOfInterest(const TrackEventRangeOfInterest&) = default;
-TrackEventRangeOfInterest& TrackEventRangeOfInterest::operator=(const TrackEventRangeOfInterest&) = default;
-TrackEventRangeOfInterest::TrackEventRangeOfInterest(TrackEventRangeOfInterest&&) noexcept = default;
-TrackEventRangeOfInterest& TrackEventRangeOfInterest::operator=(TrackEventRangeOfInterest&&) = default;
+BeginFrameSourceState::BeginFrameSourceState() = default;
+BeginFrameSourceState::~BeginFrameSourceState() = default;
+BeginFrameSourceState::BeginFrameSourceState(const BeginFrameSourceState&) = default;
+BeginFrameSourceState& BeginFrameSourceState::operator=(const BeginFrameSourceState&) = default;
+BeginFrameSourceState::BeginFrameSourceState(BeginFrameSourceState&&) noexcept = default;
+BeginFrameSourceState& BeginFrameSourceState::operator=(BeginFrameSourceState&&) = default;

-bool TrackEventRangeOfInterest::operator==(const TrackEventRangeOfInterest& other) const {
+bool BeginFrameSourceState::operator==(const BeginFrameSourceState& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(start_us_, other.start_us_);
+   && ::protozero::internal::gen_helpers::EqualsField(source_id_, other.source_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(paused_, other.paused_)
+   && ::protozero::internal::gen_helpers::EqualsField(num_observers_, other.num_observers_)
+   && ::protozero::internal::gen_helpers::EqualsField(last_begin_frame_args_, other.last_begin_frame_args_);
 }

-bool TrackEventRangeOfInterest::ParseFromArray(const void* raw, size_t size) {
+bool BeginFrameSourceState::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -36316,8 +36014,17 @@ bool TrackEventRangeOfInterest::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* start_us */:
-        field.get(&start_us_);
+      case 1 /* source_id */:
+        field.get(&source_id_);
+        break;
+      case 2 /* paused */:
+        field.get(&paused_);
+        break;
+      case 3 /* num_observers */:
+        field.get(&num_observers_);
+        break;
+      case 4 /* last_begin_frame_args */:
+        (*last_begin_frame_args_).ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -36327,65 +36034,66 @@ bool TrackEventRangeOfInterest::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TrackEventRangeOfInterest::SerializeAsString() const {
+std::string BeginFrameSourceState::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TrackEventRangeOfInterest::SerializeAsArray() const {
+std::vector<uint8_t> BeginFrameSourceState::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TrackEventRangeOfInterest::Serialize(::protozero::Message* msg) const {
-  // Field 1: start_us
+void BeginFrameSourceState::Serialize(::protozero::Message* msg) const {
+  // Field 1: source_id
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, start_us_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, source_id_, msg);
+  }
+
+  // Field 2: paused
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, paused_, msg);
+  }
+
+  // Field 3: num_observers
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, num_observers_, msg);
+  }
+
+  // Field 4: last_begin_frame_args
+  if (_has_field_[4]) {
+    (*last_begin_frame_args_).Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/screenshot.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/screenshot.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-Screenshot::Screenshot() = default;
-Screenshot::~Screenshot() = default;
-Screenshot::Screenshot(const Screenshot&) = default;
-Screenshot& Screenshot::operator=(const Screenshot&) = default;
-Screenshot::Screenshot(Screenshot&&) noexcept = default;
-Screenshot& Screenshot::operator=(Screenshot&&) = default;
+BeginFrameArgs::BeginFrameArgs() = default;
+BeginFrameArgs::~BeginFrameArgs() = default;
+BeginFrameArgs::BeginFrameArgs(const BeginFrameArgs&) = default;
+BeginFrameArgs& BeginFrameArgs::operator=(const BeginFrameArgs&) = default;
+BeginFrameArgs::BeginFrameArgs(BeginFrameArgs&&) noexcept = default;
+BeginFrameArgs& BeginFrameArgs::operator=(BeginFrameArgs&&) = default;

-bool Screenshot::operator==(const Screenshot& other) const {
+bool BeginFrameArgs::operator==(const BeginFrameArgs& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(jpg_image_, other.jpg_image_)
-   && ::protozero::internal::gen_helpers::EqualsField(pam_image_, other.pam_image_)
-   && ::protozero::internal::gen_helpers::EqualsField(ppm_image_, other.ppm_image_);
+   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
+   && ::protozero::internal::gen_helpers::EqualsField(source_id_, other.source_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(sequence_number_, other.sequence_number_)
+   && ::protozero::internal::gen_helpers::EqualsField(frame_time_us_, other.frame_time_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(deadline_us_, other.deadline_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(interval_delta_us_, other.interval_delta_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(on_critical_path_, other.on_critical_path_)
+   && ::protozero::internal::gen_helpers::EqualsField(animate_only_, other.animate_only_)
+   && ::protozero::internal::gen_helpers::EqualsField(source_location_iid_, other.source_location_iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(source_location_, other.source_location_)
+   && ::protozero::internal::gen_helpers::EqualsField(frames_throttled_since_last_, other.frames_throttled_since_last_);
 }

-bool Screenshot::ParseFromArray(const void* raw, size_t size) {
+bool BeginFrameArgs::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -36395,14 +36103,38 @@ bool Screenshot::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* jpg_image */:
-        field.get(&jpg_image_);
+      case 1 /* type */:
+        field.get(&type_);
         break;
-      case 2 /* pam_image */:
-        field.get(&pam_image_);
+      case 2 /* source_id */:
+        field.get(&source_id_);
         break;
-      case 3 /* ppm_image */:
-        field.get(&ppm_image_);
+      case 3 /* sequence_number */:
+        field.get(&sequence_number_);
+        break;
+      case 4 /* frame_time_us */:
+        field.get(&frame_time_us_);
+        break;
+      case 5 /* deadline_us */:
+        field.get(&deadline_us_);
+        break;
+      case 6 /* interval_delta_us */:
+        field.get(&interval_delta_us_);
+        break;
+      case 7 /* on_critical_path */:
+        field.get(&on_critical_path_);
+        break;
+      case 8 /* animate_only */:
+        field.get(&animate_only_);
+        break;
+      case 9 /* source_location_iid */:
+        field.get(&source_location_iid_);
+        break;
+      case 10 /* source_location */:
+        (*source_location_).ParseFromArray(field.data(), field.size());
+        break;
+      case 12 /* frames_throttled_since_last */:
+        field.get(&frames_throttled_since_last_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -36412,76 +36144,92 @@ bool Screenshot::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string Screenshot::SerializeAsString() const {
+std::string BeginFrameArgs::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> Screenshot::SerializeAsArray() const {
+std::vector<uint8_t> BeginFrameArgs::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void Screenshot::Serialize(::protozero::Message* msg) const {
-  // Field 1: jpg_image
+void BeginFrameArgs::Serialize(::protozero::Message* msg) const {
+  // Field 1: type
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, jpg_image_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, type_, msg);
   }

-  // Field 2: pam_image
+  // Field 2: source_id
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, pam_image_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, source_id_, msg);
   }

-  // Field 3: ppm_image
+  // Field 3: sequence_number
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeString(3, ppm_image_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, sequence_number_, msg);
+  }
+
+  // Field 4: frame_time_us
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, frame_time_us_, msg);
+  }
+
+  // Field 5: deadline_us
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, deadline_us_, msg);
+  }
+
+  // Field 6: interval_delta_us
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, interval_delta_us_, msg);
+  }
+
+  // Field 7: on_critical_path
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, on_critical_path_, msg);
+  }
+
+  // Field 8: animate_only
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, animate_only_, msg);
+  }
+
+  // Field 9: source_location_iid
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(9, source_location_iid_, msg);
+  }
+
+  // Field 10: source_location
+  if (_has_field_[10]) {
+    (*source_location_).Serialize(msg->BeginNestedMessage<::protozero::Message>(10));
+  }
+
+  // Field 12: frames_throttled_since_last
+  if (_has_field_[12]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(12, frames_throttled_since_last_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/source_location.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/source_location.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-SourceLocation::SourceLocation() = default;
-SourceLocation::~SourceLocation() = default;
-SourceLocation::SourceLocation(const SourceLocation&) = default;
-SourceLocation& SourceLocation::operator=(const SourceLocation&) = default;
-SourceLocation::SourceLocation(SourceLocation&&) noexcept = default;
-SourceLocation& SourceLocation::operator=(SourceLocation&&) = default;
+BeginFrameObserverState::BeginFrameObserverState() = default;
+BeginFrameObserverState::~BeginFrameObserverState() = default;
+BeginFrameObserverState::BeginFrameObserverState(const BeginFrameObserverState&) = default;
+BeginFrameObserverState& BeginFrameObserverState::operator=(const BeginFrameObserverState&) = default;
+BeginFrameObserverState::BeginFrameObserverState(BeginFrameObserverState&&) noexcept = default;
+BeginFrameObserverState& BeginFrameObserverState::operator=(BeginFrameObserverState&&) = default;

-bool SourceLocation::operator==(const SourceLocation& other) const {
+bool BeginFrameObserverState::operator==(const BeginFrameObserverState& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(file_name_, other.file_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(function_name_, other.function_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(line_number_, other.line_number_);
+   && ::protozero::internal::gen_helpers::EqualsField(dropped_begin_frame_args_, other.dropped_begin_frame_args_)
+   && ::protozero::internal::gen_helpers::EqualsField(last_begin_frame_args_, other.last_begin_frame_args_);
 }

-bool SourceLocation::ParseFromArray(const void* raw, size_t size) {
+bool BeginFrameObserverState::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -36491,17 +36239,11 @@ bool SourceLocation::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* iid */:
-        field.get(&iid_);
-        break;
-      case 2 /* file_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &file_name_);
-        break;
-      case 3 /* function_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &function_name_);
+      case 1 /* dropped_begin_frame_args */:
+        field.get(&dropped_begin_frame_args_);
         break;
-      case 4 /* line_number */:
-        field.get(&line_number_);
+      case 2 /* last_begin_frame_args */:
+        (*last_begin_frame_args_).ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -36511,58 +36253,51 @@ bool SourceLocation::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string SourceLocation::SerializeAsString() const {
+std::string BeginFrameObserverState::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> SourceLocation::SerializeAsArray() const {
+std::vector<uint8_t> BeginFrameObserverState::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void SourceLocation::Serialize(::protozero::Message* msg) const {
-  // Field 1: iid
+void BeginFrameObserverState::Serialize(::protozero::Message* msg) const {
+  // Field 1: dropped_begin_frame_args
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, dropped_begin_frame_args_, msg);
   }

-  // Field 2: file_name
+  // Field 2: last_begin_frame_args
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, file_name_, msg);
-  }
-
-  // Field 3: function_name
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeString(3, function_name_, msg);
-  }
-
-  // Field 4: line_number
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, line_number_, msg);
+    (*last_begin_frame_args_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-UnsymbolizedSourceLocation::UnsymbolizedSourceLocation() = default;
-UnsymbolizedSourceLocation::~UnsymbolizedSourceLocation() = default;
-UnsymbolizedSourceLocation::UnsymbolizedSourceLocation(const UnsymbolizedSourceLocation&) = default;
-UnsymbolizedSourceLocation& UnsymbolizedSourceLocation::operator=(const UnsymbolizedSourceLocation&) = default;
-UnsymbolizedSourceLocation::UnsymbolizedSourceLocation(UnsymbolizedSourceLocation&&) noexcept = default;
-UnsymbolizedSourceLocation& UnsymbolizedSourceLocation::operator=(UnsymbolizedSourceLocation&&) = default;
+BeginImplFrameArgs::BeginImplFrameArgs() = default;
+BeginImplFrameArgs::~BeginImplFrameArgs() = default;
+BeginImplFrameArgs::BeginImplFrameArgs(const BeginImplFrameArgs&) = default;
+BeginImplFrameArgs& BeginImplFrameArgs::operator=(const BeginImplFrameArgs&) = default;
+BeginImplFrameArgs::BeginImplFrameArgs(BeginImplFrameArgs&&) noexcept = default;
+BeginImplFrameArgs& BeginImplFrameArgs::operator=(BeginImplFrameArgs&&) = default;

-bool UnsymbolizedSourceLocation::operator==(const UnsymbolizedSourceLocation& other) const {
+bool BeginImplFrameArgs::operator==(const BeginImplFrameArgs& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(mapping_id_, other.mapping_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(rel_pc_, other.rel_pc_);
+   && ::protozero::internal::gen_helpers::EqualsField(updated_at_us_, other.updated_at_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(finished_at_us_, other.finished_at_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(state_, other.state_)
+   && ::protozero::internal::gen_helpers::EqualsField(current_args_, other.current_args_)
+   && ::protozero::internal::gen_helpers::EqualsField(last_args_, other.last_args_)
+   && ::protozero::internal::gen_helpers::EqualsField(timestamps_in_us_, other.timestamps_in_us_);
 }

-bool UnsymbolizedSourceLocation::ParseFromArray(const void* raw, size_t size) {
+bool BeginImplFrameArgs::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -36572,14 +36307,23 @@ bool UnsymbolizedSourceLocation::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* iid */:
-        field.get(&iid_);
+      case 1 /* updated_at_us */:
+        field.get(&updated_at_us_);
         break;
-      case 2 /* mapping_id */:
-        field.get(&mapping_id_);
+      case 2 /* finished_at_us */:
+        field.get(&finished_at_us_);
         break;
-      case 3 /* rel_pc */:
-        field.get(&rel_pc_);
+      case 3 /* state */:
+        field.get(&state_);
+        break;
+      case 4 /* current_args */:
+        (*current_args_).ParseFromArray(field.data(), field.size());
+        break;
+      case 5 /* last_args */:
+        (*last_args_).ParseFromArray(field.data(), field.size());
+        break;
+      case 6 /* timestamps_in_us */:
+        (*timestamps_in_us_).ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -36589,72 +36333,72 @@ bool UnsymbolizedSourceLocation::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string UnsymbolizedSourceLocation::SerializeAsString() const {
+std::string BeginImplFrameArgs::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> UnsymbolizedSourceLocation::SerializeAsArray() const {
+std::vector<uint8_t> BeginImplFrameArgs::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void UnsymbolizedSourceLocation::Serialize(::protozero::Message* msg) const {
-  // Field 1: iid
+void BeginImplFrameArgs::Serialize(::protozero::Message* msg) const {
+  // Field 1: updated_at_us
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, updated_at_us_, msg);
   }

-  // Field 2: mapping_id
+  // Field 2: finished_at_us
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, mapping_id_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, finished_at_us_, msg);
   }

-  // Field 3: rel_pc
+  // Field 3: state
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, rel_pc_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, state_, msg);
+  }
+
+  // Field 4: current_args
+  if (_has_field_[4]) {
+    (*current_args_).Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
+  }
+
+  // Field 5: last_args
+  if (_has_field_[5]) {
+    (*last_args_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
+  }
+
+  // Field 6: timestamps_in_us
+  if (_has_field_[6]) {
+    (*timestamps_in_us_).Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/state_descriptor.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/state_descriptor.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-StateDescriptor::StateDescriptor() = default;
-StateDescriptor::~StateDescriptor() = default;
-StateDescriptor::StateDescriptor(const StateDescriptor&) = default;
-StateDescriptor& StateDescriptor::operator=(const StateDescriptor&) = default;
-StateDescriptor::StateDescriptor(StateDescriptor&&) noexcept = default;
-StateDescriptor& StateDescriptor::operator=(StateDescriptor&&) = default;
+BeginImplFrameArgs_TimestampsInUs::BeginImplFrameArgs_TimestampsInUs() = default;
+BeginImplFrameArgs_TimestampsInUs::~BeginImplFrameArgs_TimestampsInUs() = default;
+BeginImplFrameArgs_TimestampsInUs::BeginImplFrameArgs_TimestampsInUs(const BeginImplFrameArgs_TimestampsInUs&) = default;
+BeginImplFrameArgs_TimestampsInUs& BeginImplFrameArgs_TimestampsInUs::operator=(const BeginImplFrameArgs_TimestampsInUs&) = default;
+BeginImplFrameArgs_TimestampsInUs::BeginImplFrameArgs_TimestampsInUs(BeginImplFrameArgs_TimestampsInUs&&) noexcept = default;
+BeginImplFrameArgs_TimestampsInUs& BeginImplFrameArgs_TimestampsInUs::operator=(BeginImplFrameArgs_TimestampsInUs&&) = default;

-bool StateDescriptor::operator==(const StateDescriptor& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
+bool BeginImplFrameArgs_TimestampsInUs::operator==(const BeginImplFrameArgs_TimestampsInUs& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(interval_delta_, other.interval_delta_)
+   && ::protozero::internal::gen_helpers::EqualsField(now_to_deadline_delta_, other.now_to_deadline_delta_)
+   && ::protozero::internal::gen_helpers::EqualsField(frame_time_to_now_delta_, other.frame_time_to_now_delta_)
+   && ::protozero::internal::gen_helpers::EqualsField(frame_time_to_deadline_delta_, other.frame_time_to_deadline_delta_)
+   && ::protozero::internal::gen_helpers::EqualsField(now_, other.now_)
+   && ::protozero::internal::gen_helpers::EqualsField(frame_time_, other.frame_time_)
+   && ::protozero::internal::gen_helpers::EqualsField(deadline_, other.deadline_);
 }

-bool StateDescriptor::ParseFromArray(const void* raw, size_t size) {
+bool BeginImplFrameArgs_TimestampsInUs::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -36664,6 +36408,27 @@ bool StateDescriptor::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
+      case 1 /* interval_delta */:
+        field.get(&interval_delta_);
+        break;
+      case 2 /* now_to_deadline_delta */:
+        field.get(&now_to_deadline_delta_);
+        break;
+      case 3 /* frame_time_to_now_delta */:
+        field.get(&frame_time_to_now_delta_);
+        break;
+      case 4 /* frame_time_to_deadline_delta */:
+        field.get(&frame_time_to_deadline_delta_);
+        break;
+      case 5 /* now */:
+        field.get(&now_);
+        break;
+      case 6 /* frame_time */:
+        field.get(&frame_time_);
+        break;
+      case 7 /* deadline */:
+        field.get(&deadline_);
+        break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
         break;
@@ -36672,58 +36437,72 @@ bool StateDescriptor::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string StateDescriptor::SerializeAsString() const {
+std::string BeginImplFrameArgs_TimestampsInUs::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> StateDescriptor::SerializeAsArray() const {
+std::vector<uint8_t> BeginImplFrameArgs_TimestampsInUs::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void StateDescriptor::Serialize(::protozero::Message* msg) const {
+void BeginImplFrameArgs_TimestampsInUs::Serialize(::protozero::Message* msg) const {
+  // Field 1: interval_delta
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, interval_delta_, msg);
+  }
+
+  // Field 2: now_to_deadline_delta
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, now_to_deadline_delta_, msg);
+  }
+
+  // Field 3: frame_time_to_now_delta
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, frame_time_to_now_delta_, msg);
+  }
+
+  // Field 4: frame_time_to_deadline_delta
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, frame_time_to_deadline_delta_, msg);
+  }
+
+  // Field 5: now
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, now_, msg);
+  }
+
+  // Field 6: frame_time
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, frame_time_, msg);
+  }
+
+  // Field 7: deadline
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, deadline_, msg);
+  }
+
   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/task_execution.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/task_execution.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-TaskExecution::TaskExecution() = default;
-TaskExecution::~TaskExecution() = default;
-TaskExecution::TaskExecution(const TaskExecution&) = default;
-TaskExecution& TaskExecution::operator=(const TaskExecution&) = default;
-TaskExecution::TaskExecution(TaskExecution&&) noexcept = default;
-TaskExecution& TaskExecution::operator=(TaskExecution&&) = default;
+ChromeCompositorStateMachine::ChromeCompositorStateMachine() = default;
+ChromeCompositorStateMachine::~ChromeCompositorStateMachine() = default;
+ChromeCompositorStateMachine::ChromeCompositorStateMachine(const ChromeCompositorStateMachine&) = default;
+ChromeCompositorStateMachine& ChromeCompositorStateMachine::operator=(const ChromeCompositorStateMachine&) = default;
+ChromeCompositorStateMachine::ChromeCompositorStateMachine(ChromeCompositorStateMachine&&) noexcept = default;
+ChromeCompositorStateMachine& ChromeCompositorStateMachine::operator=(ChromeCompositorStateMachine&&) = default;

-bool TaskExecution::operator==(const TaskExecution& other) const {
+bool ChromeCompositorStateMachine::operator==(const ChromeCompositorStateMachine& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(posted_from_iid_, other.posted_from_iid_);
+   && ::protozero::internal::gen_helpers::EqualsField(major_state_, other.major_state_)
+   && ::protozero::internal::gen_helpers::EqualsField(minor_state_, other.minor_state_);
 }

-bool TaskExecution::ParseFromArray(const void* raw, size_t size) {
+bool ChromeCompositorStateMachine::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -36733,8 +36512,11 @@ bool TaskExecution::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* posted_from_iid */:
-        field.get(&posted_from_iid_);
+      case 1 /* major_state */:
+        (*major_state_).ParseFromArray(field.data(), field.size());
+        break;
+      case 2 /* minor_state */:
+        (*minor_state_).ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -36744,70 +36526,90 @@ bool TaskExecution::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TaskExecution::SerializeAsString() const {
+std::string ChromeCompositorStateMachine::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TaskExecution::SerializeAsArray() const {
+std::vector<uint8_t> ChromeCompositorStateMachine::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TaskExecution::Serialize(::protozero::Message* msg) const {
-  // Field 1: posted_from_iid
+void ChromeCompositorStateMachine::Serialize(::protozero::Message* msg) const {
+  // Field 1: major_state
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, posted_from_iid_, msg);
+    (*major_state_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+  }
+
+  // Field 2: minor_state
+  if (_has_field_[2]) {
+    (*minor_state_).Serialize(msg->BeginNestedMessage<::protozero::Message>(2));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/thread_descriptor.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/thread_descriptor.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {

-ThreadDescriptor::ThreadDescriptor() = default;
-ThreadDescriptor::~ThreadDescriptor() = default;
-ThreadDescriptor::ThreadDescriptor(const ThreadDescriptor&) = default;
-ThreadDescriptor& ThreadDescriptor::operator=(const ThreadDescriptor&) = default;
-ThreadDescriptor::ThreadDescriptor(ThreadDescriptor&&) noexcept = default;
-ThreadDescriptor& ThreadDescriptor::operator=(ThreadDescriptor&&) = default;
+ChromeCompositorStateMachine_MinorState::ChromeCompositorStateMachine_MinorState() = default;
+ChromeCompositorStateMachine_MinorState::~ChromeCompositorStateMachine_MinorState() = default;
+ChromeCompositorStateMachine_MinorState::ChromeCompositorStateMachine_MinorState(const ChromeCompositorStateMachine_MinorState&) = default;
+ChromeCompositorStateMachine_MinorState& ChromeCompositorStateMachine_MinorState::operator=(const ChromeCompositorStateMachine_MinorState&) = default;
+ChromeCompositorStateMachine_MinorState::ChromeCompositorStateMachine_MinorState(ChromeCompositorStateMachine_MinorState&&) noexcept = default;
+ChromeCompositorStateMachine_MinorState& ChromeCompositorStateMachine_MinorState::operator=(ChromeCompositorStateMachine_MinorState&&) = default;

-bool ThreadDescriptor::operator==(const ThreadDescriptor& other) const {
+bool ChromeCompositorStateMachine_MinorState::operator==(const ChromeCompositorStateMachine_MinorState& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(pid_, other.pid_)
-   && ::protozero::internal::gen_helpers::EqualsField(tid_, other.tid_)
-   && ::protozero::internal::gen_helpers::EqualsField(thread_name_, other.thread_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_thread_type_, other.chrome_thread_type_)
-   && ::protozero::internal::gen_helpers::EqualsField(reference_timestamp_us_, other.reference_timestamp_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(reference_thread_time_us_, other.reference_thread_time_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(reference_thread_instruction_count_, other.reference_thread_instruction_count_)
-   && ::protozero::internal::gen_helpers::EqualsField(legacy_sort_index_, other.legacy_sort_index_);
+   && ::protozero::internal::gen_helpers::EqualsField(commit_count_, other.commit_count_)
+   && ::protozero::internal::gen_helpers::EqualsField(current_frame_number_, other.current_frame_number_)
+   && ::protozero::internal::gen_helpers::EqualsField(last_frame_number_submit_performed_, other.last_frame_number_submit_performed_)
+   && ::protozero::internal::gen_helpers::EqualsField(last_frame_number_draw_performed_, other.last_frame_number_draw_performed_)
+   && ::protozero::internal::gen_helpers::EqualsField(last_frame_number_begin_main_frame_sent_, other.last_frame_number_begin_main_frame_sent_)
+   && ::protozero::internal::gen_helpers::EqualsField(did_draw_, other.did_draw_)
+   && ::protozero::internal::gen_helpers::EqualsField(did_send_begin_main_frame_for_current_frame_, other.did_send_begin_main_frame_for_current_frame_)
+   && ::protozero::internal::gen_helpers::EqualsField(did_notify_begin_main_frame_not_expected_until_, other.did_notify_begin_main_frame_not_expected_until_)
+   && ::protozero::internal::gen_helpers::EqualsField(did_notify_begin_main_frame_not_expected_soon_, other.did_notify_begin_main_frame_not_expected_soon_)
+   && ::protozero::internal::gen_helpers::EqualsField(wants_begin_main_frame_not_expected_, other.wants_begin_main_frame_not_expected_)
+   && ::protozero::internal::gen_helpers::EqualsField(did_commit_during_frame_, other.did_commit_during_frame_)
+   && ::protozero::internal::gen_helpers::EqualsField(did_invalidate_layer_tree_frame_sink_, other.did_invalidate_layer_tree_frame_sink_)
+   && ::protozero::internal::gen_helpers::EqualsField(did_perform_impl_side_invalidaion_, other.did_perform_impl_side_invalidaion_)
+   && ::protozero::internal::gen_helpers::EqualsField(did_prepare_tiles_, other.did_prepare_tiles_)
+   && ::protozero::internal::gen_helpers::EqualsField(consecutive_checkerboard_animations_, other.consecutive_checkerboard_animations_)
+   && ::protozero::internal::gen_helpers::EqualsField(pending_submit_frames_, other.pending_submit_frames_)
+   && ::protozero::internal::gen_helpers::EqualsField(submit_frames_with_current_layer_tree_frame_sink_, other.submit_frames_with_current_layer_tree_frame_sink_)
+   && ::protozero::internal::gen_helpers::EqualsField(needs_redraw_, other.needs_redraw_)
+   && ::protozero::internal::gen_helpers::EqualsField(needs_prepare_tiles_, other.needs_prepare_tiles_)
+   && ::protozero::internal::gen_helpers::EqualsField(needs_begin_main_frame_, other.needs_begin_main_frame_)
+   && ::protozero::internal::gen_helpers::EqualsField(needs_one_begin_impl_frame_, other.needs_one_begin_impl_frame_)
+   && ::protozero::internal::gen_helpers::EqualsField(visible_, other.visible_)
+   && ::protozero::internal::gen_helpers::EqualsField(begin_frame_source_paused_, other.begin_frame_source_paused_)
+   && ::protozero::internal::gen_helpers::EqualsField(can_draw_, other.can_draw_)
+   && ::protozero::internal::gen_helpers::EqualsField(resourceless_draw_, other.resourceless_draw_)
+   && ::protozero::internal::gen_helpers::EqualsField(has_pending_tree_, other.has_pending_tree_)
+   && ::protozero::internal::gen_helpers::EqualsField(pending_tree_is_ready_for_activation_, other.pending_tree_is_ready_for_activation_)
+   && ::protozero::internal::gen_helpers::EqualsField(active_tree_needs_first_draw_, other.active_tree_needs_first_draw_)
+   && ::protozero::internal::gen_helpers::EqualsField(active_tree_is_ready_to_draw_, other.active_tree_is_ready_to_draw_)
+   && ::protozero::internal::gen_helpers::EqualsField(did_create_and_initialize_first_layer_tree_frame_sink_, other.did_create_and_initialize_first_layer_tree_frame_sink_)
+   && ::protozero::internal::gen_helpers::EqualsField(tree_priority_, other.tree_priority_)
+   && ::protozero::internal::gen_helpers::EqualsField(scroll_handler_state_, other.scroll_handler_state_)
+   && ::protozero::internal::gen_helpers::EqualsField(critical_begin_main_frame_to_activate_is_fast_, other.critical_begin_main_frame_to_activate_is_fast_)
+   && ::protozero::internal::gen_helpers::EqualsField(main_thread_missed_last_deadline_, other.main_thread_missed_last_deadline_)
+   && ::protozero::internal::gen_helpers::EqualsField(video_needs_begin_frames_, other.video_needs_begin_frames_)
+   && ::protozero::internal::gen_helpers::EqualsField(defer_begin_main_frame_, other.defer_begin_main_frame_)
+   && ::protozero::internal::gen_helpers::EqualsField(last_commit_had_no_updates_, other.last_commit_had_no_updates_)
+   && ::protozero::internal::gen_helpers::EqualsField(did_draw_in_last_frame_, other.did_draw_in_last_frame_)
+   && ::protozero::internal::gen_helpers::EqualsField(did_submit_in_last_frame_, other.did_submit_in_last_frame_)
+   && ::protozero::internal::gen_helpers::EqualsField(needs_impl_side_invalidation_, other.needs_impl_side_invalidation_)
+   && ::protozero::internal::gen_helpers::EqualsField(current_pending_tree_is_impl_side_, other.current_pending_tree_is_impl_side_)
+   && ::protozero::internal::gen_helpers::EqualsField(previous_pending_tree_was_impl_side_, other.previous_pending_tree_was_impl_side_)
+   && ::protozero::internal::gen_helpers::EqualsField(processing_animation_worklets_for_active_tree_, other.processing_animation_worklets_for_active_tree_)
+   && ::protozero::internal::gen_helpers::EqualsField(processing_animation_worklets_for_pending_tree_, other.processing_animation_worklets_for_pending_tree_)
+   && ::protozero::internal::gen_helpers::EqualsField(processing_paint_worklets_for_pending_tree_, other.processing_paint_worklets_for_pending_tree_);
 }

-bool ThreadDescriptor::ParseFromArray(const void* raw, size_t size) {
+bool ChromeCompositorStateMachine_MinorState::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -36817,223 +36619,140 @@ bool ThreadDescriptor::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* pid */:
-        field.get(&pid_);
+      case 1 /* commit_count */:
+        field.get(&commit_count_);
         break;
-      case 2 /* tid */:
-        field.get(&tid_);
+      case 2 /* current_frame_number */:
+        field.get(&current_frame_number_);
         break;
-      case 5 /* thread_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &thread_name_);
+      case 3 /* last_frame_number_submit_performed */:
+        field.get(&last_frame_number_submit_performed_);
         break;
-      case 4 /* chrome_thread_type */:
-        field.get(&chrome_thread_type_);
+      case 4 /* last_frame_number_draw_performed */:
+        field.get(&last_frame_number_draw_performed_);
         break;
-      case 6 /* reference_timestamp_us */:
-        field.get(&reference_timestamp_us_);
+      case 5 /* last_frame_number_begin_main_frame_sent */:
+        field.get(&last_frame_number_begin_main_frame_sent_);
         break;
-      case 7 /* reference_thread_time_us */:
-        field.get(&reference_thread_time_us_);
+      case 6 /* did_draw */:
+        field.get(&did_draw_);
         break;
-      case 8 /* reference_thread_instruction_count */:
-        field.get(&reference_thread_instruction_count_);
+      case 7 /* did_send_begin_main_frame_for_current_frame */:
+        field.get(&did_send_begin_main_frame_for_current_frame_);
         break;
-      case 3 /* legacy_sort_index */:
-        field.get(&legacy_sort_index_);
+      case 8 /* did_notify_begin_main_frame_not_expected_until */:
+        field.get(&did_notify_begin_main_frame_not_expected_until_);
         break;
-      default:
-        field.SerializeAndAppendTo(&unknown_fields_);
+      case 9 /* did_notify_begin_main_frame_not_expected_soon */:
+        field.get(&did_notify_begin_main_frame_not_expected_soon_);
         break;
-    }
-  }
-  return !packed_error && !dec.bytes_left();
-}
-
-std::string ThreadDescriptor::SerializeAsString() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsString();
-}
-
-std::vector<uint8_t> ThreadDescriptor::SerializeAsArray() const {
-  ::protozero::internal::gen_helpers::MessageSerializer msg;
-  Serialize(msg.get());
-  return msg.SerializeAsArray();
-}
-
-void ThreadDescriptor::Serialize(::protozero::Message* msg) const {
-  // Field 1: pid
-  if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, pid_, msg);
-  }
-
-  // Field 2: tid
-  if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, tid_, msg);
-  }
-
-  // Field 5: thread_name
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeString(5, thread_name_, msg);
-  }
-
-  // Field 4: chrome_thread_type
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, chrome_thread_type_, msg);
-  }
-
-  // Field 6: reference_timestamp_us
-  if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, reference_timestamp_us_, msg);
-  }
-
-  // Field 7: reference_thread_time_us
-  if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(7, reference_thread_time_us_, msg);
-  }
-
-  // Field 8: reference_thread_instruction_count
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, reference_thread_instruction_count_, msg);
-  }
-
-  // Field 3: legacy_sort_index
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, legacy_sort_index_, msg);
-  }
-
-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
-
-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/track_descriptor.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/track_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/state_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/counter_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/thread_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/process_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_thread_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_process_descriptor.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {
-
-TrackDescriptor::TrackDescriptor() = default;
-TrackDescriptor::~TrackDescriptor() = default;
-TrackDescriptor::TrackDescriptor(const TrackDescriptor&) = default;
-TrackDescriptor& TrackDescriptor::operator=(const TrackDescriptor&) = default;
-TrackDescriptor::TrackDescriptor(TrackDescriptor&&) noexcept = default;
-TrackDescriptor& TrackDescriptor::operator=(TrackDescriptor&&) = default;
-
-bool TrackDescriptor::operator==(const TrackDescriptor& other) const {
-  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(uuid_, other.uuid_)
-   && ::protozero::internal::gen_helpers::EqualsField(parent_uuid_, other.parent_uuid_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(static_name_, other.static_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(atrace_name_, other.atrace_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(description_, other.description_)
-   && ::protozero::internal::gen_helpers::EqualsField(process_, other.process_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_process_, other.chrome_process_)
-   && ::protozero::internal::gen_helpers::EqualsField(thread_, other.thread_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_thread_, other.chrome_thread_)
-   && ::protozero::internal::gen_helpers::EqualsField(counter_, other.counter_)
-   && ::protozero::internal::gen_helpers::EqualsField(state_, other.state_)
-   && ::protozero::internal::gen_helpers::EqualsField(disallow_merging_with_system_tracks_, other.disallow_merging_with_system_tracks_)
-   && ::protozero::internal::gen_helpers::EqualsField(child_ordering_, other.child_ordering_)
-   && ::protozero::internal::gen_helpers::EqualsField(sibling_order_rank_, other.sibling_order_rank_)
-   && ::protozero::internal::gen_helpers::EqualsField(sibling_merge_behavior_, other.sibling_merge_behavior_)
-   && ::protozero::internal::gen_helpers::EqualsField(sibling_merge_key_, other.sibling_merge_key_)
-   && ::protozero::internal::gen_helpers::EqualsField(sibling_merge_key_int_, other.sibling_merge_key_int_)
-   && ::protozero::internal::gen_helpers::EqualsField(process_ordering_, other.process_ordering_)
-   && ::protozero::internal::gen_helpers::EqualsField(thread_ordering_, other.thread_ordering_);
-}
-
-bool TrackDescriptor::ParseFromArray(const void* raw, size_t size) {
-  unknown_fields_.clear();
-  bool packed_error = false;
-
-  ::protozero::ProtoDecoder dec(raw, size);
-  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
-    if (field.id() < _has_field_.size()) {
-      _has_field_.set(field.id());
-    }
-    switch (field.id()) {
-      case 1 /* uuid */:
-        field.get(&uuid_);
+      case 10 /* wants_begin_main_frame_not_expected */:
+        field.get(&wants_begin_main_frame_not_expected_);
         break;
-      case 5 /* parent_uuid */:
-        field.get(&parent_uuid_);
+      case 11 /* did_commit_during_frame */:
+        field.get(&did_commit_during_frame_);
         break;
-      case 2 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+      case 12 /* did_invalidate_layer_tree_frame_sink */:
+        field.get(&did_invalidate_layer_tree_frame_sink_);
         break;
-      case 10 /* static_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &static_name_);
+      case 13 /* did_perform_impl_side_invalidaion */:
+        field.get(&did_perform_impl_side_invalidaion_);
         break;
-      case 13 /* atrace_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &atrace_name_);
+      case 14 /* did_prepare_tiles */:
+        field.get(&did_prepare_tiles_);
         break;
-      case 14 /* description */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &description_);
+      case 15 /* consecutive_checkerboard_animations */:
+        field.get(&consecutive_checkerboard_animations_);
         break;
-      case 3 /* process */:
-        (*process_).ParseFromArray(field.data(), field.size());
+      case 16 /* pending_submit_frames */:
+        field.get(&pending_submit_frames_);
         break;
-      case 6 /* chrome_process */:
-        (*chrome_process_).ParseFromArray(field.data(), field.size());
+      case 17 /* submit_frames_with_current_layer_tree_frame_sink */:
+        field.get(&submit_frames_with_current_layer_tree_frame_sink_);
         break;
-      case 4 /* thread */:
-        (*thread_).ParseFromArray(field.data(), field.size());
+      case 18 /* needs_redraw */:
+        field.get(&needs_redraw_);
         break;
-      case 7 /* chrome_thread */:
-        (*chrome_thread_).ParseFromArray(field.data(), field.size());
+      case 19 /* needs_prepare_tiles */:
+        field.get(&needs_prepare_tiles_);
         break;
-      case 8 /* counter */:
-        (*counter_).ParseFromArray(field.data(), field.size());
+      case 20 /* needs_begin_main_frame */:
+        field.get(&needs_begin_main_frame_);
         break;
-      case 18 /* state */:
-        (*state_).ParseFromArray(field.data(), field.size());
+      case 21 /* needs_one_begin_impl_frame */:
+        field.get(&needs_one_begin_impl_frame_);
         break;
-      case 9 /* disallow_merging_with_system_tracks */:
-        field.get(&disallow_merging_with_system_tracks_);
+      case 22 /* visible */:
+        field.get(&visible_);
         break;
-      case 11 /* child_ordering */:
-        field.get(&child_ordering_);
+      case 23 /* begin_frame_source_paused */:
+        field.get(&begin_frame_source_paused_);
         break;
-      case 12 /* sibling_order_rank */:
-        field.get(&sibling_order_rank_);
+      case 24 /* can_draw */:
+        field.get(&can_draw_);
         break;
-      case 15 /* sibling_merge_behavior */:
-        field.get(&sibling_merge_behavior_);
+      case 25 /* resourceless_draw */:
+        field.get(&resourceless_draw_);
         break;
-      case 16 /* sibling_merge_key */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &sibling_merge_key_);
+      case 26 /* has_pending_tree */:
+        field.get(&has_pending_tree_);
         break;
-      case 17 /* sibling_merge_key_int */:
-        field.get(&sibling_merge_key_int_);
+      case 27 /* pending_tree_is_ready_for_activation */:
+        field.get(&pending_tree_is_ready_for_activation_);
         break;
-      case 19 /* process_ordering */:
-        field.get(&process_ordering_);
+      case 28 /* active_tree_needs_first_draw */:
+        field.get(&active_tree_needs_first_draw_);
         break;
-      case 20 /* thread_ordering */:
-        field.get(&thread_ordering_);
+      case 29 /* active_tree_is_ready_to_draw */:
+        field.get(&active_tree_is_ready_to_draw_);
+        break;
+      case 30 /* did_create_and_initialize_first_layer_tree_frame_sink */:
+        field.get(&did_create_and_initialize_first_layer_tree_frame_sink_);
+        break;
+      case 31 /* tree_priority */:
+        field.get(&tree_priority_);
+        break;
+      case 32 /* scroll_handler_state */:
+        field.get(&scroll_handler_state_);
+        break;
+      case 33 /* critical_begin_main_frame_to_activate_is_fast */:
+        field.get(&critical_begin_main_frame_to_activate_is_fast_);
+        break;
+      case 34 /* main_thread_missed_last_deadline */:
+        field.get(&main_thread_missed_last_deadline_);
+        break;
+      case 36 /* video_needs_begin_frames */:
+        field.get(&video_needs_begin_frames_);
+        break;
+      case 37 /* defer_begin_main_frame */:
+        field.get(&defer_begin_main_frame_);
+        break;
+      case 38 /* last_commit_had_no_updates */:
+        field.get(&last_commit_had_no_updates_);
+        break;
+      case 39 /* did_draw_in_last_frame */:
+        field.get(&did_draw_in_last_frame_);
+        break;
+      case 40 /* did_submit_in_last_frame */:
+        field.get(&did_submit_in_last_frame_);
+        break;
+      case 41 /* needs_impl_side_invalidation */:
+        field.get(&needs_impl_side_invalidation_);
+        break;
+      case 42 /* current_pending_tree_is_impl_side */:
+        field.get(&current_pending_tree_is_impl_side_);
+        break;
+      case 43 /* previous_pending_tree_was_impl_side */:
+        field.get(&previous_pending_tree_was_impl_side_);
+        break;
+      case 44 /* processing_animation_worklets_for_active_tree */:
+        field.get(&processing_animation_worklets_for_active_tree_);
+        break;
+      case 45 /* processing_animation_worklets_for_pending_tree */:
+        field.get(&processing_animation_worklets_for_pending_tree_);
+        break;
+      case 46 /* processing_paint_worklets_for_pending_tree */:
+        field.get(&processing_paint_worklets_for_pending_tree_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -37043,178 +36762,265 @@ bool TrackDescriptor::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TrackDescriptor::SerializeAsString() const {
+std::string ChromeCompositorStateMachine_MinorState::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TrackDescriptor::SerializeAsArray() const {
+std::vector<uint8_t> ChromeCompositorStateMachine_MinorState::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TrackDescriptor::Serialize(::protozero::Message* msg) const {
-  // Field 1: uuid
+void ChromeCompositorStateMachine_MinorState::Serialize(::protozero::Message* msg) const {
+  // Field 1: commit_count
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, uuid_, msg);
-  }
-
-  // Field 5: parent_uuid
-  if (_has_field_[5]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(5, parent_uuid_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, commit_count_, msg);
   }

-  // Field 2: name
+  // Field 2: current_frame_number
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
-  }
-
-  // Field 10: static_name
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeString(10, static_name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, current_frame_number_, msg);
   }

-  // Field 13: atrace_name
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeString(13, atrace_name_, msg);
+  // Field 3: last_frame_number_submit_performed
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, last_frame_number_submit_performed_, msg);
   }

-  // Field 14: description
-  if (_has_field_[14]) {
-    ::protozero::internal::gen_helpers::SerializeString(14, description_, msg);
+  // Field 4: last_frame_number_draw_performed
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, last_frame_number_draw_performed_, msg);
   }

-  // Field 3: process
-  if (_has_field_[3]) {
-    (*process_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+  // Field 5: last_frame_number_begin_main_frame_sent
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, last_frame_number_begin_main_frame_sent_, msg);
   }

-  // Field 6: chrome_process
+  // Field 6: did_draw
   if (_has_field_[6]) {
-    (*chrome_process_).Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
-  }
-
-  // Field 4: thread
-  if (_has_field_[4]) {
-    (*thread_).Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(6, did_draw_, msg);
   }

-  // Field 7: chrome_thread
+  // Field 7: did_send_begin_main_frame_for_current_frame
   if (_has_field_[7]) {
-    (*chrome_thread_).Serialize(msg->BeginNestedMessage<::protozero::Message>(7));
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, did_send_begin_main_frame_for_current_frame_, msg);
   }

-  // Field 8: counter
+  // Field 8: did_notify_begin_main_frame_not_expected_until
   if (_has_field_[8]) {
-    (*counter_).Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, did_notify_begin_main_frame_not_expected_until_, msg);
   }

-  // Field 18: state
-  if (_has_field_[18]) {
-    (*state_).Serialize(msg->BeginNestedMessage<::protozero::Message>(18));
+  // Field 9: did_notify_begin_main_frame_not_expected_soon
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, did_notify_begin_main_frame_not_expected_soon_, msg);
   }

-  // Field 9: disallow_merging_with_system_tracks
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, disallow_merging_with_system_tracks_, msg);
+  // Field 10: wants_begin_main_frame_not_expected
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(10, wants_begin_main_frame_not_expected_, msg);
   }

-  // Field 11: child_ordering
+  // Field 11: did_commit_during_frame
   if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(11, child_ordering_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(11, did_commit_during_frame_, msg);
   }

-  // Field 12: sibling_order_rank
+  // Field 12: did_invalidate_layer_tree_frame_sink
   if (_has_field_[12]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(12, sibling_order_rank_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(12, did_invalidate_layer_tree_frame_sink_, msg);
   }

-  // Field 15: sibling_merge_behavior
+  // Field 13: did_perform_impl_side_invalidaion
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(13, did_perform_impl_side_invalidaion_, msg);
+  }
+
+  // Field 14: did_prepare_tiles
+  if (_has_field_[14]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(14, did_prepare_tiles_, msg);
+  }
+
+  // Field 15: consecutive_checkerboard_animations
   if (_has_field_[15]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(15, sibling_merge_behavior_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(15, consecutive_checkerboard_animations_, msg);
   }

-  // Field 16: sibling_merge_key
+  // Field 16: pending_submit_frames
   if (_has_field_[16]) {
-    ::protozero::internal::gen_helpers::SerializeString(16, sibling_merge_key_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(16, pending_submit_frames_, msg);
   }

-  // Field 17: sibling_merge_key_int
+  // Field 17: submit_frames_with_current_layer_tree_frame_sink
   if (_has_field_[17]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(17, sibling_merge_key_int_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(17, submit_frames_with_current_layer_tree_frame_sink_, msg);
   }

-  // Field 19: process_ordering
+  // Field 18: needs_redraw
+  if (_has_field_[18]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(18, needs_redraw_, msg);
+  }
+
+  // Field 19: needs_prepare_tiles
   if (_has_field_[19]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(19, process_ordering_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(19, needs_prepare_tiles_, msg);
   }

-  // Field 20: thread_ordering
+  // Field 20: needs_begin_main_frame
   if (_has_field_[20]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(20, thread_ordering_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(20, needs_begin_main_frame_, msg);
   }

-  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
-}
+  // Field 21: needs_one_begin_impl_frame
+  if (_has_field_[21]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(21, needs_one_begin_impl_frame_, msg);
+  }

-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/track_event.gen.cc
-// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/track_event.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/source_location.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/screenshot.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_window_handle_event_info.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_user_event.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_mojo_event_info.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_message_pump.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_legacy_ipc.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_latency_info.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_keyed_service.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_histogram_sample.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_frame_reporter.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_content_settings_event_info.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_application_state_info.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_active_processes.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/task_execution.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/log_message.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/debug_annotation.gen.h"
+  // Field 22: visible
+  if (_has_field_[22]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(22, visible_, msg);
+  }

-namespace perfetto {
-namespace protos {
-namespace gen {
+  // Field 23: begin_frame_source_paused
+  if (_has_field_[23]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(23, begin_frame_source_paused_, msg);
+  }

-EventName::EventName() = default;
-EventName::~EventName() = default;
-EventName::EventName(const EventName&) = default;
-EventName& EventName::operator=(const EventName&) = default;
-EventName::EventName(EventName&&) noexcept = default;
-EventName& EventName::operator=(EventName&&) = default;
+  // Field 24: can_draw
+  if (_has_field_[24]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(24, can_draw_, msg);
+  }

-bool EventName::operator==(const EventName& other) const {
+  // Field 25: resourceless_draw
+  if (_has_field_[25]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(25, resourceless_draw_, msg);
+  }
+
+  // Field 26: has_pending_tree
+  if (_has_field_[26]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(26, has_pending_tree_, msg);
+  }
+
+  // Field 27: pending_tree_is_ready_for_activation
+  if (_has_field_[27]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(27, pending_tree_is_ready_for_activation_, msg);
+  }
+
+  // Field 28: active_tree_needs_first_draw
+  if (_has_field_[28]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(28, active_tree_needs_first_draw_, msg);
+  }
+
+  // Field 29: active_tree_is_ready_to_draw
+  if (_has_field_[29]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(29, active_tree_is_ready_to_draw_, msg);
+  }
+
+  // Field 30: did_create_and_initialize_first_layer_tree_frame_sink
+  if (_has_field_[30]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(30, did_create_and_initialize_first_layer_tree_frame_sink_, msg);
+  }
+
+  // Field 31: tree_priority
+  if (_has_field_[31]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(31, tree_priority_, msg);
+  }
+
+  // Field 32: scroll_handler_state
+  if (_has_field_[32]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(32, scroll_handler_state_, msg);
+  }
+
+  // Field 33: critical_begin_main_frame_to_activate_is_fast
+  if (_has_field_[33]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(33, critical_begin_main_frame_to_activate_is_fast_, msg);
+  }
+
+  // Field 34: main_thread_missed_last_deadline
+  if (_has_field_[34]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(34, main_thread_missed_last_deadline_, msg);
+  }
+
+  // Field 36: video_needs_begin_frames
+  if (_has_field_[36]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(36, video_needs_begin_frames_, msg);
+  }
+
+  // Field 37: defer_begin_main_frame
+  if (_has_field_[37]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(37, defer_begin_main_frame_, msg);
+  }
+
+  // Field 38: last_commit_had_no_updates
+  if (_has_field_[38]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(38, last_commit_had_no_updates_, msg);
+  }
+
+  // Field 39: did_draw_in_last_frame
+  if (_has_field_[39]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(39, did_draw_in_last_frame_, msg);
+  }
+
+  // Field 40: did_submit_in_last_frame
+  if (_has_field_[40]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(40, did_submit_in_last_frame_, msg);
+  }
+
+  // Field 41: needs_impl_side_invalidation
+  if (_has_field_[41]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(41, needs_impl_side_invalidation_, msg);
+  }
+
+  // Field 42: current_pending_tree_is_impl_side
+  if (_has_field_[42]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(42, current_pending_tree_is_impl_side_, msg);
+  }
+
+  // Field 43: previous_pending_tree_was_impl_side
+  if (_has_field_[43]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(43, previous_pending_tree_was_impl_side_, msg);
+  }
+
+  // Field 44: processing_animation_worklets_for_active_tree
+  if (_has_field_[44]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(44, processing_animation_worklets_for_active_tree_, msg);
+  }
+
+  // Field 45: processing_animation_worklets_for_pending_tree
+  if (_has_field_[45]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(45, processing_animation_worklets_for_pending_tree_, msg);
+  }
+
+  // Field 46: processing_paint_worklets_for_pending_tree
+  if (_has_field_[46]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(46, processing_paint_worklets_for_pending_tree_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+ChromeCompositorStateMachine_MajorState::ChromeCompositorStateMachine_MajorState() = default;
+ChromeCompositorStateMachine_MajorState::~ChromeCompositorStateMachine_MajorState() = default;
+ChromeCompositorStateMachine_MajorState::ChromeCompositorStateMachine_MajorState(const ChromeCompositorStateMachine_MajorState&) = default;
+ChromeCompositorStateMachine_MajorState& ChromeCompositorStateMachine_MajorState::operator=(const ChromeCompositorStateMachine_MajorState&) = default;
+ChromeCompositorStateMachine_MajorState::ChromeCompositorStateMachine_MajorState(ChromeCompositorStateMachine_MajorState&&) noexcept = default;
+ChromeCompositorStateMachine_MajorState& ChromeCompositorStateMachine_MajorState::operator=(ChromeCompositorStateMachine_MajorState&&) = default;
+
+bool ChromeCompositorStateMachine_MajorState::operator==(const ChromeCompositorStateMachine_MajorState& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+   && ::protozero::internal::gen_helpers::EqualsField(next_action_, other.next_action_)
+   && ::protozero::internal::gen_helpers::EqualsField(begin_impl_frame_state_, other.begin_impl_frame_state_)
+   && ::protozero::internal::gen_helpers::EqualsField(begin_main_frame_state_, other.begin_main_frame_state_)
+   && ::protozero::internal::gen_helpers::EqualsField(layer_tree_frame_sink_state_, other.layer_tree_frame_sink_state_)
+   && ::protozero::internal::gen_helpers::EqualsField(forced_redraw_state_, other.forced_redraw_state_);
 }

-bool EventName::ParseFromArray(const void* raw, size_t size) {
+bool ChromeCompositorStateMachine_MajorState::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -37224,11 +37030,20 @@ bool EventName::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* iid */:
-        field.get(&iid_);
+      case 1 /* next_action */:
+        field.get(&next_action_);
         break;
-      case 2 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+      case 2 /* begin_impl_frame_state */:
+        field.get(&begin_impl_frame_state_);
+        break;
+      case 3 /* begin_main_frame_state */:
+        field.get(&begin_main_frame_state_);
+        break;
+      case 4 /* layer_tree_frame_sink_state */:
+        field.get(&layer_tree_frame_sink_state_);
+        break;
+      case 5 /* forced_redraw_state */:
+        field.get(&forced_redraw_state_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -37238,47 +37053,76 @@ bool EventName::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string EventName::SerializeAsString() const {
+std::string ChromeCompositorStateMachine_MajorState::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> EventName::SerializeAsArray() const {
+std::vector<uint8_t> ChromeCompositorStateMachine_MajorState::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void EventName::Serialize(::protozero::Message* msg) const {
-  // Field 1: iid
+void ChromeCompositorStateMachine_MajorState::Serialize(::protozero::Message* msg) const {
+  // Field 1: next_action
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, next_action_, msg);
   }

-  // Field 2: name
+  // Field 2: begin_impl_frame_state
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, begin_impl_frame_state_, msg);
+  }
+
+  // Field 3: begin_main_frame_state
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, begin_main_frame_state_, msg);
+  }
+
+  // Field 4: layer_tree_frame_sink_state
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, layer_tree_frame_sink_state_, msg);
+  }
+
+  // Field 5: forced_redraw_state
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, forced_redraw_state_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-EventCategory::EventCategory() = default;
-EventCategory::~EventCategory() = default;
-EventCategory::EventCategory(const EventCategory&) = default;
-EventCategory& EventCategory::operator=(const EventCategory&) = default;
-EventCategory::EventCategory(EventCategory&&) noexcept = default;
-EventCategory& EventCategory::operator=(EventCategory&&) = default;
+ChromeCompositorSchedulerState::ChromeCompositorSchedulerState() = default;
+ChromeCompositorSchedulerState::~ChromeCompositorSchedulerState() = default;
+ChromeCompositorSchedulerState::ChromeCompositorSchedulerState(const ChromeCompositorSchedulerState&) = default;
+ChromeCompositorSchedulerState& ChromeCompositorSchedulerState::operator=(const ChromeCompositorSchedulerState&) = default;
+ChromeCompositorSchedulerState::ChromeCompositorSchedulerState(ChromeCompositorSchedulerState&&) noexcept = default;
+ChromeCompositorSchedulerState& ChromeCompositorSchedulerState::operator=(ChromeCompositorSchedulerState&&) = default;

-bool EventCategory::operator==(const EventCategory& other) const {
+bool ChromeCompositorSchedulerState::operator==(const ChromeCompositorSchedulerState& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+   && ::protozero::internal::gen_helpers::EqualsField(state_machine_, other.state_machine_)
+   && ::protozero::internal::gen_helpers::EqualsField(observing_begin_frame_source_, other.observing_begin_frame_source_)
+   && ::protozero::internal::gen_helpers::EqualsField(begin_impl_frame_deadline_task_, other.begin_impl_frame_deadline_task_)
+   && ::protozero::internal::gen_helpers::EqualsField(pending_begin_frame_task_, other.pending_begin_frame_task_)
+   && ::protozero::internal::gen_helpers::EqualsField(skipped_last_frame_missed_exceeded_deadline_, other.skipped_last_frame_missed_exceeded_deadline_)
+   && ::protozero::internal::gen_helpers::EqualsField(inside_action_, other.inside_action_)
+   && ::protozero::internal::gen_helpers::EqualsField(deadline_mode_, other.deadline_mode_)
+   && ::protozero::internal::gen_helpers::EqualsField(deadline_us_, other.deadline_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(deadline_scheduled_at_us_, other.deadline_scheduled_at_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(now_us_, other.now_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(now_to_deadline_delta_us_, other.now_to_deadline_delta_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(now_to_deadline_scheduled_at_delta_us_, other.now_to_deadline_scheduled_at_delta_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(begin_impl_frame_args_, other.begin_impl_frame_args_)
+   && ::protozero::internal::gen_helpers::EqualsField(begin_frame_observer_state_, other.begin_frame_observer_state_)
+   && ::protozero::internal::gen_helpers::EqualsField(begin_frame_source_state_, other.begin_frame_source_state_)
+   && ::protozero::internal::gen_helpers::EqualsField(compositor_timing_history_, other.compositor_timing_history_);
 }

-bool EventCategory::ParseFromArray(const void* raw, size_t size) {
+bool ChromeCompositorSchedulerState::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -37288,11 +37132,53 @@ bool EventCategory::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* iid */:
-        field.get(&iid_);
+      case 1 /* state_machine */:
+        (*state_machine_).ParseFromArray(field.data(), field.size());
         break;
-      case 2 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+      case 2 /* observing_begin_frame_source */:
+        field.get(&observing_begin_frame_source_);
+        break;
+      case 3 /* begin_impl_frame_deadline_task */:
+        field.get(&begin_impl_frame_deadline_task_);
+        break;
+      case 4 /* pending_begin_frame_task */:
+        field.get(&pending_begin_frame_task_);
+        break;
+      case 5 /* skipped_last_frame_missed_exceeded_deadline */:
+        field.get(&skipped_last_frame_missed_exceeded_deadline_);
+        break;
+      case 7 /* inside_action */:
+        field.get(&inside_action_);
+        break;
+      case 8 /* deadline_mode */:
+        field.get(&deadline_mode_);
+        break;
+      case 9 /* deadline_us */:
+        field.get(&deadline_us_);
+        break;
+      case 10 /* deadline_scheduled_at_us */:
+        field.get(&deadline_scheduled_at_us_);
+        break;
+      case 11 /* now_us */:
+        field.get(&now_us_);
+        break;
+      case 12 /* now_to_deadline_delta_us */:
+        field.get(&now_to_deadline_delta_us_);
+        break;
+      case 13 /* now_to_deadline_scheduled_at_delta_us */:
+        field.get(&now_to_deadline_scheduled_at_delta_us_);
+        break;
+      case 14 /* begin_impl_frame_args */:
+        (*begin_impl_frame_args_).ParseFromArray(field.data(), field.size());
+        break;
+      case 15 /* begin_frame_observer_state */:
+        (*begin_frame_observer_state_).ParseFromArray(field.data(), field.size());
+        break;
+      case 16 /* begin_frame_source_state */:
+        (*begin_frame_source_state_).ParseFromArray(field.data(), field.size());
+        break;
+      case 17 /* compositor_timing_history */:
+        (*compositor_timing_history_).ParseFromArray(field.data(), field.size());
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -37302,50 +37188,138 @@ bool EventCategory::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string EventCategory::SerializeAsString() const {
+std::string ChromeCompositorSchedulerState::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> EventCategory::SerializeAsArray() const {
+std::vector<uint8_t> ChromeCompositorSchedulerState::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void EventCategory::Serialize(::protozero::Message* msg) const {
-  // Field 1: iid
+void ChromeCompositorSchedulerState::Serialize(::protozero::Message* msg) const {
+  // Field 1: state_machine
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
+    (*state_machine_).Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
   }

-  // Field 2: name
+  // Field 2: observing_begin_frame_source
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, observing_begin_frame_source_, msg);
+  }
+
+  // Field 3: begin_impl_frame_deadline_task
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, begin_impl_frame_deadline_task_, msg);
+  }
+
+  // Field 4: pending_begin_frame_task
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(4, pending_begin_frame_task_, msg);
+  }
+
+  // Field 5: skipped_last_frame_missed_exceeded_deadline
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, skipped_last_frame_missed_exceeded_deadline_, msg);
+  }
+
+  // Field 7: inside_action
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, inside_action_, msg);
+  }
+
+  // Field 8: deadline_mode
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, deadline_mode_, msg);
+  }
+
+  // Field 9: deadline_us
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(9, deadline_us_, msg);
+  }
+
+  // Field 10: deadline_scheduled_at_us
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(10, deadline_scheduled_at_us_, msg);
+  }
+
+  // Field 11: now_us
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(11, now_us_, msg);
+  }
+
+  // Field 12: now_to_deadline_delta_us
+  if (_has_field_[12]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(12, now_to_deadline_delta_us_, msg);
+  }
+
+  // Field 13: now_to_deadline_scheduled_at_delta_us
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(13, now_to_deadline_scheduled_at_delta_us_, msg);
+  }
+
+  // Field 14: begin_impl_frame_args
+  if (_has_field_[14]) {
+    (*begin_impl_frame_args_).Serialize(msg->BeginNestedMessage<::protozero::Message>(14));
+  }
+
+  // Field 15: begin_frame_observer_state
+  if (_has_field_[15]) {
+    (*begin_frame_observer_state_).Serialize(msg->BeginNestedMessage<::protozero::Message>(15));
+  }
+
+  // Field 16: begin_frame_source_state
+  if (_has_field_[16]) {
+    (*begin_frame_source_state_).Serialize(msg->BeginNestedMessage<::protozero::Message>(16));
+  }
+
+  // Field 17: compositor_timing_history
+  if (_has_field_[17]) {
+    (*compositor_timing_history_).Serialize(msg->BeginNestedMessage<::protozero::Message>(17));
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_content_settings_event_info.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_content_settings_event_info.gen.h"

-TrackEventDefaults::TrackEventDefaults() = default;
-TrackEventDefaults::~TrackEventDefaults() = default;
-TrackEventDefaults::TrackEventDefaults(const TrackEventDefaults&) = default;
-TrackEventDefaults& TrackEventDefaults::operator=(const TrackEventDefaults&) = default;
-TrackEventDefaults::TrackEventDefaults(TrackEventDefaults&&) noexcept = default;
-TrackEventDefaults& TrackEventDefaults::operator=(TrackEventDefaults&&) = default;
+namespace perfetto {
+namespace protos {
+namespace gen {

-bool TrackEventDefaults::operator==(const TrackEventDefaults& other) const {
+ChromeContentSettingsEventInfo::ChromeContentSettingsEventInfo() = default;
+ChromeContentSettingsEventInfo::~ChromeContentSettingsEventInfo() = default;
+ChromeContentSettingsEventInfo::ChromeContentSettingsEventInfo(const ChromeContentSettingsEventInfo&) = default;
+ChromeContentSettingsEventInfo& ChromeContentSettingsEventInfo::operator=(const ChromeContentSettingsEventInfo&) = default;
+ChromeContentSettingsEventInfo::ChromeContentSettingsEventInfo(ChromeContentSettingsEventInfo&&) noexcept = default;
+ChromeContentSettingsEventInfo& ChromeContentSettingsEventInfo::operator=(ChromeContentSettingsEventInfo&&) = default;
+
+bool ChromeContentSettingsEventInfo::operator==(const ChromeContentSettingsEventInfo& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(track_uuid_, other.track_uuid_)
-   && ::protozero::internal::gen_helpers::EqualsField(extra_counter_track_uuids_, other.extra_counter_track_uuids_)
-   && ::protozero::internal::gen_helpers::EqualsField(extra_double_counter_track_uuids_, other.extra_double_counter_track_uuids_);
+   && ::protozero::internal::gen_helpers::EqualsField(number_of_exceptions_, other.number_of_exceptions_);
 }

-bool TrackEventDefaults::ParseFromArray(const void* raw, size_t size) {
-  extra_counter_track_uuids_.clear();
-  extra_double_counter_track_uuids_.clear();
+bool ChromeContentSettingsEventInfo::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -37355,16 +37329,8 @@ bool TrackEventDefaults::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 11 /* track_uuid */:
-        field.get(&track_uuid_);
-        break;
-      case 31 /* extra_counter_track_uuids */:
-        extra_counter_track_uuids_.emplace_back();
-        field.get(&extra_counter_track_uuids_.back());
-        break;
-      case 45 /* extra_double_counter_track_uuids */:
-        extra_double_counter_track_uuids_.emplace_back();
-        field.get(&extra_double_counter_track_uuids_.back());
+      case 1 /* number_of_exceptions */:
+        field.get(&number_of_exceptions_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -37374,113 +37340,81 @@ bool TrackEventDefaults::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TrackEventDefaults::SerializeAsString() const {
+std::string ChromeContentSettingsEventInfo::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TrackEventDefaults::SerializeAsArray() const {
+std::vector<uint8_t> ChromeContentSettingsEventInfo::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TrackEventDefaults::Serialize(::protozero::Message* msg) const {
-  // Field 11: track_uuid
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(11, track_uuid_, msg);
-  }
-
-  // Field 31: extra_counter_track_uuids
-  for (auto& it : extra_counter_track_uuids_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(31, it, msg);
-  }
-
-  // Field 45: extra_double_counter_track_uuids
-  for (auto& it : extra_double_counter_track_uuids_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(45, it, msg);
+void ChromeContentSettingsEventInfo::Serialize(::protozero::Message* msg) const {
+  // Field 1: number_of_exceptions
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, number_of_exceptions_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_frame_reporter.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_frame_reporter.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-TrackEvent::TrackEvent() = default;
-TrackEvent::~TrackEvent() = default;
-TrackEvent::TrackEvent(const TrackEvent&) = default;
-TrackEvent& TrackEvent::operator=(const TrackEvent&) = default;
-TrackEvent::TrackEvent(TrackEvent&&) noexcept = default;
-TrackEvent& TrackEvent::operator=(TrackEvent&&) = default;
+ChromeFrameReporter::ChromeFrameReporter() = default;
+ChromeFrameReporter::~ChromeFrameReporter() = default;
+ChromeFrameReporter::ChromeFrameReporter(const ChromeFrameReporter&) = default;
+ChromeFrameReporter& ChromeFrameReporter::operator=(const ChromeFrameReporter&) = default;
+ChromeFrameReporter::ChromeFrameReporter(ChromeFrameReporter&&) noexcept = default;
+ChromeFrameReporter& ChromeFrameReporter::operator=(ChromeFrameReporter&&) = default;

-bool TrackEvent::operator==(const TrackEvent& other) const {
+bool ChromeFrameReporter::operator==(const ChromeFrameReporter& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(category_iids_, other.category_iids_)
-   && ::protozero::internal::gen_helpers::EqualsField(categories_, other.categories_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_iid_, other.name_iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
-   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
-   && ::protozero::internal::gen_helpers::EqualsField(track_uuid_, other.track_uuid_)
-   && ::protozero::internal::gen_helpers::EqualsField(counter_value_, other.counter_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(double_counter_value_, other.double_counter_value_)
-   && ::protozero::internal::gen_helpers::EqualsField(extra_counter_track_uuids_, other.extra_counter_track_uuids_)
-   && ::protozero::internal::gen_helpers::EqualsField(extra_counter_values_, other.extra_counter_values_)
-   && ::protozero::internal::gen_helpers::EqualsField(extra_double_counter_track_uuids_, other.extra_double_counter_track_uuids_)
-   && ::protozero::internal::gen_helpers::EqualsField(extra_double_counter_values_, other.extra_double_counter_values_)
-   && ::protozero::internal::gen_helpers::EqualsField(flow_ids_old_, other.flow_ids_old_)
-   && ::protozero::internal::gen_helpers::EqualsField(flow_ids_, other.flow_ids_)
-   && ::protozero::internal::gen_helpers::EqualsField(terminating_flow_ids_old_, other.terminating_flow_ids_old_)
-   && ::protozero::internal::gen_helpers::EqualsField(terminating_flow_ids_, other.terminating_flow_ids_)
-   && ::protozero::internal::gen_helpers::EqualsField(correlation_id_, other.correlation_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(correlation_id_str_, other.correlation_id_str_)
-   && ::protozero::internal::gen_helpers::EqualsField(correlation_id_str_iid_, other.correlation_id_str_iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(callstack_, other.callstack_)
-   && ::protozero::internal::gen_helpers::EqualsField(callstack_iid_, other.callstack_iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(callstack_weight_, other.callstack_weight_)
-   && ::protozero::internal::gen_helpers::EqualsField(debug_annotations_, other.debug_annotations_)
-   && ::protozero::internal::gen_helpers::EqualsField(task_execution_, other.task_execution_)
-   && ::protozero::internal::gen_helpers::EqualsField(log_message_, other.log_message_)
-   && ::protozero::internal::gen_helpers::EqualsField(cc_scheduler_state_, other.cc_scheduler_state_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_user_event_, other.chrome_user_event_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_keyed_service_, other.chrome_keyed_service_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_legacy_ipc_, other.chrome_legacy_ipc_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_histogram_sample_, other.chrome_histogram_sample_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_latency_info_, other.chrome_latency_info_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_frame_reporter_, other.chrome_frame_reporter_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_application_state_info_, other.chrome_application_state_info_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_renderer_scheduler_state_, other.chrome_renderer_scheduler_state_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_window_handle_event_info_, other.chrome_window_handle_event_info_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_content_settings_event_info_, other.chrome_content_settings_event_info_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_active_processes_, other.chrome_active_processes_)
-   && ::protozero::internal::gen_helpers::EqualsField(screenshot_, other.screenshot_)
-   && ::protozero::internal::gen_helpers::EqualsField(source_location_, other.source_location_)
-   && ::protozero::internal::gen_helpers::EqualsField(source_location_iid_, other.source_location_iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_message_pump_, other.chrome_message_pump_)
-   && ::protozero::internal::gen_helpers::EqualsField(chrome_mojo_event_info_, other.chrome_mojo_event_info_)
-   && ::protozero::internal::gen_helpers::EqualsField(timestamp_delta_us_, other.timestamp_delta_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(timestamp_absolute_us_, other.timestamp_absolute_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(thread_time_delta_us_, other.thread_time_delta_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(thread_time_absolute_us_, other.thread_time_absolute_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(thread_instruction_count_delta_, other.thread_instruction_count_delta_)
-   && ::protozero::internal::gen_helpers::EqualsField(thread_instruction_count_absolute_, other.thread_instruction_count_absolute_)
-   && ::protozero::internal::gen_helpers::EqualsField(legacy_event_, other.legacy_event_);
+   && ::protozero::internal::gen_helpers::EqualsField(state_, other.state_)
+   && ::protozero::internal::gen_helpers::EqualsField(reason_, other.reason_)
+   && ::protozero::internal::gen_helpers::EqualsField(frame_source_, other.frame_source_)
+   && ::protozero::internal::gen_helpers::EqualsField(frame_sequence_, other.frame_sequence_)
+   && ::protozero::internal::gen_helpers::EqualsField(affects_smoothness_, other.affects_smoothness_)
+   && ::protozero::internal::gen_helpers::EqualsField(scroll_state_, other.scroll_state_)
+   && ::protozero::internal::gen_helpers::EqualsField(has_main_animation_, other.has_main_animation_)
+   && ::protozero::internal::gen_helpers::EqualsField(has_compositor_animation_, other.has_compositor_animation_)
+   && ::protozero::internal::gen_helpers::EqualsField(has_smooth_input_main_, other.has_smooth_input_main_)
+   && ::protozero::internal::gen_helpers::EqualsField(has_missing_content_, other.has_missing_content_)
+   && ::protozero::internal::gen_helpers::EqualsField(layer_tree_host_id_, other.layer_tree_host_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(has_high_latency_, other.has_high_latency_)
+   && ::protozero::internal::gen_helpers::EqualsField(frame_type_, other.frame_type_)
+   && ::protozero::internal::gen_helpers::EqualsField(high_latency_contribution_stage_, other.high_latency_contribution_stage_)
+   && ::protozero::internal::gen_helpers::EqualsField(checkerboarded_needs_raster_, other.checkerboarded_needs_raster_)
+   && ::protozero::internal::gen_helpers::EqualsField(checkerboarded_needs_record_, other.checkerboarded_needs_record_)
+   && ::protozero::internal::gen_helpers::EqualsField(surface_frame_trace_id_, other.surface_frame_trace_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(display_trace_id_, other.display_trace_id_);
 }

-int TrackEvent::debug_annotations_size() const { return static_cast<int>(debug_annotations_.size()); }
-void TrackEvent::clear_debug_annotations() { debug_annotations_.clear(); }
-DebugAnnotation* TrackEvent::add_debug_annotations() { debug_annotations_.emplace_back(); return &debug_annotations_.back(); }
-bool TrackEvent::ParseFromArray(const void* raw, size_t size) {
-  category_iids_.clear();
-  categories_.clear();
-  extra_counter_track_uuids_.clear();
-  extra_counter_values_.clear();
-  extra_double_counter_track_uuids_.clear();
-  extra_double_counter_values_.clear();
-  flow_ids_old_.clear();
-  flow_ids_.clear();
-  terminating_flow_ids_old_.clear();
-  terminating_flow_ids_.clear();
-  debug_annotations_.clear();
+bool ChromeFrameReporter::ParseFromArray(const void* raw, size_t size) {
+  high_latency_contribution_stage_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -37490,163 +37424,60 @@ bool TrackEvent::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 3 /* category_iids */:
-        category_iids_.emplace_back();
-        field.get(&category_iids_.back());
-        break;
-      case 22 /* categories */:
-        categories_.emplace_back();
-        ::protozero::internal::gen_helpers::DeserializeString(field, &categories_.back());
-        break;
-      case 10 /* name_iid */:
-        field.get(&name_iid_);
-        break;
-      case 23 /* name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
-        break;
-      case 9 /* type */:
-        field.get(&type_);
-        break;
-      case 11 /* track_uuid */:
-        field.get(&track_uuid_);
-        break;
-      case 30 /* counter_value */:
-        field.get(&counter_value_);
-        break;
-      case 44 /* double_counter_value */:
-        field.get(&double_counter_value_);
-        break;
-      case 31 /* extra_counter_track_uuids */:
-        extra_counter_track_uuids_.emplace_back();
-        field.get(&extra_counter_track_uuids_.back());
-        break;
-      case 12 /* extra_counter_values */:
-        extra_counter_values_.emplace_back();
-        field.get(&extra_counter_values_.back());
-        break;
-      case 45 /* extra_double_counter_track_uuids */:
-        extra_double_counter_track_uuids_.emplace_back();
-        field.get(&extra_double_counter_track_uuids_.back());
-        break;
-      case 46 /* extra_double_counter_values */:
-        extra_double_counter_values_.emplace_back();
-        field.get(&extra_double_counter_values_.back());
-        break;
-      case 36 /* flow_ids_old */:
-        flow_ids_old_.emplace_back();
-        field.get(&flow_ids_old_.back());
-        break;
-      case 47 /* flow_ids */:
-        flow_ids_.emplace_back();
-        field.get(&flow_ids_.back());
-        break;
-      case 42 /* terminating_flow_ids_old */:
-        terminating_flow_ids_old_.emplace_back();
-        field.get(&terminating_flow_ids_old_.back());
-        break;
-      case 48 /* terminating_flow_ids */:
-        terminating_flow_ids_.emplace_back();
-        field.get(&terminating_flow_ids_.back());
-        break;
-      case 52 /* correlation_id */:
-        field.get(&correlation_id_);
-        break;
-      case 53 /* correlation_id_str */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &correlation_id_str_);
-        break;
-      case 54 /* correlation_id_str_iid */:
-        field.get(&correlation_id_str_iid_);
-        break;
-      case 55 /* callstack */:
-        (*callstack_).ParseFromArray(field.data(), field.size());
-        break;
-      case 56 /* callstack_iid */:
-        field.get(&callstack_iid_);
-        break;
-      case 57 /* callstack_weight */:
-        field.get(&callstack_weight_);
-        break;
-      case 4 /* debug_annotations */:
-        debug_annotations_.emplace_back();
-        debug_annotations_.back().ParseFromArray(field.data(), field.size());
-        break;
-      case 5 /* task_execution */:
-        (*task_execution_).ParseFromArray(field.data(), field.size());
-        break;
-      case 21 /* log_message */:
-        (*log_message_).ParseFromArray(field.data(), field.size());
-        break;
-      case 24 /* cc_scheduler_state */:
-        (*cc_scheduler_state_).ParseFromArray(field.data(), field.size());
-        break;
-      case 25 /* chrome_user_event */:
-        (*chrome_user_event_).ParseFromArray(field.data(), field.size());
-        break;
-      case 26 /* chrome_keyed_service */:
-        (*chrome_keyed_service_).ParseFromArray(field.data(), field.size());
-        break;
-      case 27 /* chrome_legacy_ipc */:
-        (*chrome_legacy_ipc_).ParseFromArray(field.data(), field.size());
-        break;
-      case 28 /* chrome_histogram_sample */:
-        (*chrome_histogram_sample_).ParseFromArray(field.data(), field.size());
-        break;
-      case 29 /* chrome_latency_info */:
-        (*chrome_latency_info_).ParseFromArray(field.data(), field.size());
-        break;
-      case 32 /* chrome_frame_reporter */:
-        (*chrome_frame_reporter_).ParseFromArray(field.data(), field.size());
+      case 1 /* state */:
+        field.get(&state_);
         break;
-      case 39 /* chrome_application_state_info */:
-        (*chrome_application_state_info_).ParseFromArray(field.data(), field.size());
+      case 2 /* reason */:
+        field.get(&reason_);
         break;
-      case 40 /* chrome_renderer_scheduler_state */:
-        (*chrome_renderer_scheduler_state_).ParseFromArray(field.data(), field.size());
+      case 3 /* frame_source */:
+        field.get(&frame_source_);
         break;
-      case 41 /* chrome_window_handle_event_info */:
-        (*chrome_window_handle_event_info_).ParseFromArray(field.data(), field.size());
+      case 4 /* frame_sequence */:
+        field.get(&frame_sequence_);
         break;
-      case 43 /* chrome_content_settings_event_info */:
-        (*chrome_content_settings_event_info_).ParseFromArray(field.data(), field.size());
+      case 5 /* affects_smoothness */:
+        field.get(&affects_smoothness_);
         break;
-      case 49 /* chrome_active_processes */:
-        (*chrome_active_processes_).ParseFromArray(field.data(), field.size());
+      case 6 /* scroll_state */:
+        field.get(&scroll_state_);
         break;
-      case 50 /* screenshot */:
-        (*screenshot_).ParseFromArray(field.data(), field.size());
+      case 7 /* has_main_animation */:
+        field.get(&has_main_animation_);
         break;
-      case 33 /* source_location */:
-        (*source_location_).ParseFromArray(field.data(), field.size());
+      case 8 /* has_compositor_animation */:
+        field.get(&has_compositor_animation_);
         break;
-      case 34 /* source_location_iid */:
-        field.get(&source_location_iid_);
+      case 9 /* has_smooth_input_main */:
+        field.get(&has_smooth_input_main_);
         break;
-      case 35 /* chrome_message_pump */:
-        (*chrome_message_pump_).ParseFromArray(field.data(), field.size());
+      case 10 /* has_missing_content */:
+        field.get(&has_missing_content_);
         break;
-      case 38 /* chrome_mojo_event_info */:
-        (*chrome_mojo_event_info_).ParseFromArray(field.data(), field.size());
+      case 11 /* layer_tree_host_id */:
+        field.get(&layer_tree_host_id_);
         break;
-      case 1 /* timestamp_delta_us */:
-        field.get(&timestamp_delta_us_);
+      case 12 /* has_high_latency */:
+        field.get(&has_high_latency_);
         break;
-      case 16 /* timestamp_absolute_us */:
-        field.get(&timestamp_absolute_us_);
+      case 13 /* frame_type */:
+        field.get(&frame_type_);
         break;
-      case 2 /* thread_time_delta_us */:
-        field.get(&thread_time_delta_us_);
+      case 14 /* high_latency_contribution_stage */:
+        high_latency_contribution_stage_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &high_latency_contribution_stage_.back());
         break;
-      case 17 /* thread_time_absolute_us */:
-        field.get(&thread_time_absolute_us_);
+      case 15 /* checkerboarded_needs_raster */:
+        field.get(&checkerboarded_needs_raster_);
         break;
-      case 8 /* thread_instruction_count_delta */:
-        field.get(&thread_instruction_count_delta_);
+      case 16 /* checkerboarded_needs_record */:
+        field.get(&checkerboarded_needs_record_);
         break;
-      case 20 /* thread_instruction_count_absolute */:
-        field.get(&thread_instruction_count_absolute_);
+      case 17 /* surface_frame_trace_id */:
+        field.get(&surface_frame_trace_id_);
         break;
-      case 6 /* legacy_event */:
-        (*legacy_event_).ParseFromArray(field.data(), field.size());
+      case 18 /* display_trace_id */:
+        field.get(&display_trace_id_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -37656,296 +37487,404 @@ bool TrackEvent::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TrackEvent::SerializeAsString() const {
+std::string ChromeFrameReporter::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TrackEvent::SerializeAsArray() const {
+std::vector<uint8_t> ChromeFrameReporter::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TrackEvent::Serialize(::protozero::Message* msg) const {
-  // Field 3: category_iids
-  for (auto& it : category_iids_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, it, msg);
-  }
-
-  // Field 22: categories
-  for (auto& it : categories_) {
-    ::protozero::internal::gen_helpers::SerializeString(22, it, msg);
-  }
-
-  // Field 10: name_iid
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(10, name_iid_, msg);
+void ChromeFrameReporter::Serialize(::protozero::Message* msg) const {
+  // Field 1: state
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, state_, msg);
   }

-  // Field 23: name
-  if (_has_field_[23]) {
-    ::protozero::internal::gen_helpers::SerializeString(23, name_, msg);
+  // Field 2: reason
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, reason_, msg);
   }

-  // Field 9: type
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(9, type_, msg);
+  // Field 3: frame_source
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, frame_source_, msg);
   }

-  // Field 11: track_uuid
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(11, track_uuid_, msg);
+  // Field 4: frame_sequence
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, frame_sequence_, msg);
   }

-  // Field 30: counter_value
-  if (_has_field_[30]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(30, counter_value_, msg);
+  // Field 5: affects_smoothness
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, affects_smoothness_, msg);
   }

-  // Field 44: double_counter_value
-  if (_has_field_[44]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(44, double_counter_value_, msg);
+  // Field 6: scroll_state
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, scroll_state_, msg);
   }

-  // Field 31: extra_counter_track_uuids
-  for (auto& it : extra_counter_track_uuids_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(31, it, msg);
+  // Field 7: has_main_animation
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, has_main_animation_, msg);
   }

-  // Field 12: extra_counter_values
-  for (auto& it : extra_counter_values_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(12, it, msg);
+  // Field 8: has_compositor_animation
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(8, has_compositor_animation_, msg);
   }

-  // Field 45: extra_double_counter_track_uuids
-  for (auto& it : extra_double_counter_track_uuids_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(45, it, msg);
+  // Field 9: has_smooth_input_main
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, has_smooth_input_main_, msg);
   }

-  // Field 46: extra_double_counter_values
-  for (auto& it : extra_double_counter_values_) {
-    ::protozero::internal::gen_helpers::SerializeFixed(46, it, msg);
+  // Field 10: has_missing_content
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(10, has_missing_content_, msg);
   }

-  // Field 36: flow_ids_old
-  for (auto& it : flow_ids_old_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(36, it, msg);
+  // Field 11: layer_tree_host_id
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(11, layer_tree_host_id_, msg);
   }

-  // Field 47: flow_ids
-  for (auto& it : flow_ids_) {
-    ::protozero::internal::gen_helpers::SerializeFixed(47, it, msg);
+  // Field 12: has_high_latency
+  if (_has_field_[12]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(12, has_high_latency_, msg);
   }

-  // Field 42: terminating_flow_ids_old
-  for (auto& it : terminating_flow_ids_old_) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(42, it, msg);
+  // Field 13: frame_type
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(13, frame_type_, msg);
   }

-  // Field 48: terminating_flow_ids
-  for (auto& it : terminating_flow_ids_) {
-    ::protozero::internal::gen_helpers::SerializeFixed(48, it, msg);
+  // Field 14: high_latency_contribution_stage
+  for (auto& it : high_latency_contribution_stage_) {
+    ::protozero::internal::gen_helpers::SerializeString(14, it, msg);
   }

-  // Field 52: correlation_id
-  if (_has_field_[52]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(52, correlation_id_, msg);
+  // Field 15: checkerboarded_needs_raster
+  if (_has_field_[15]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(15, checkerboarded_needs_raster_, msg);
   }

-  // Field 53: correlation_id_str
-  if (_has_field_[53]) {
-    ::protozero::internal::gen_helpers::SerializeString(53, correlation_id_str_, msg);
+  // Field 16: checkerboarded_needs_record
+  if (_has_field_[16]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(16, checkerboarded_needs_record_, msg);
   }

-  // Field 54: correlation_id_str_iid
-  if (_has_field_[54]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(54, correlation_id_str_iid_, msg);
+  // Field 17: surface_frame_trace_id
+  if (_has_field_[17]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(17, surface_frame_trace_id_, msg);
   }

-  // Field 55: callstack
-  if (_has_field_[55]) {
-    (*callstack_).Serialize(msg->BeginNestedMessage<::protozero::Message>(55));
+  // Field 18: display_trace_id
+  if (_has_field_[18]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(18, display_trace_id_, msg);
   }

-  // Field 56: callstack_iid
-  if (_has_field_[56]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(56, callstack_iid_, msg);
-  }
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}

-  // Field 57: callstack_weight
-  if (_has_field_[57]) {
-    ::protozero::internal::gen_helpers::SerializeFixed(57, callstack_weight_, msg);
-  }
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_histogram_sample.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_histogram_sample.gen.h"

-  // Field 4: debug_annotations
-  for (auto& it : debug_annotations_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
-  }
+namespace perfetto {
+namespace protos {
+namespace gen {

-  // Field 5: task_execution
-  if (_has_field_[5]) {
-    (*task_execution_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
-  }
+ChromeHistogramSample::ChromeHistogramSample() = default;
+ChromeHistogramSample::~ChromeHistogramSample() = default;
+ChromeHistogramSample::ChromeHistogramSample(const ChromeHistogramSample&) = default;
+ChromeHistogramSample& ChromeHistogramSample::operator=(const ChromeHistogramSample&) = default;
+ChromeHistogramSample::ChromeHistogramSample(ChromeHistogramSample&&) noexcept = default;
+ChromeHistogramSample& ChromeHistogramSample::operator=(ChromeHistogramSample&&) = default;

-  // Field 21: log_message
-  if (_has_field_[21]) {
-    (*log_message_).Serialize(msg->BeginNestedMessage<::protozero::Message>(21));
-  }
+bool ChromeHistogramSample::operator==(const ChromeHistogramSample& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_hash_, other.name_hash_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(sample_, other.sample_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_iid_, other.name_iid_);
+}

-  // Field 24: cc_scheduler_state
-  if (_has_field_[24]) {
-    (*cc_scheduler_state_).Serialize(msg->BeginNestedMessage<::protozero::Message>(24));
-  }
+bool ChromeHistogramSample::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;

-  // Field 25: chrome_user_event
-  if (_has_field_[25]) {
-    (*chrome_user_event_).Serialize(msg->BeginNestedMessage<::protozero::Message>(25));
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* name_hash */:
+        field.get(&name_hash_);
+        break;
+      case 2 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      case 3 /* sample */:
+        field.get(&sample_);
+        break;
+      case 4 /* name_iid */:
+        field.get(&name_iid_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 26: chrome_keyed_service
-  if (_has_field_[26]) {
-    (*chrome_keyed_service_).Serialize(msg->BeginNestedMessage<::protozero::Message>(26));
-  }
+std::string ChromeHistogramSample::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}

-  // Field 27: chrome_legacy_ipc
-  if (_has_field_[27]) {
-    (*chrome_legacy_ipc_).Serialize(msg->BeginNestedMessage<::protozero::Message>(27));
-  }
+std::vector<uint8_t> ChromeHistogramSample::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}

-  // Field 28: chrome_histogram_sample
-  if (_has_field_[28]) {
-    (*chrome_histogram_sample_).Serialize(msg->BeginNestedMessage<::protozero::Message>(28));
+void ChromeHistogramSample::Serialize(::protozero::Message* msg) const {
+  // Field 1: name_hash
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, name_hash_, msg);
   }

-  // Field 29: chrome_latency_info
-  if (_has_field_[29]) {
-    (*chrome_latency_info_).Serialize(msg->BeginNestedMessage<::protozero::Message>(29));
+  // Field 2: name
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
   }

-  // Field 32: chrome_frame_reporter
-  if (_has_field_[32]) {
-    (*chrome_frame_reporter_).Serialize(msg->BeginNestedMessage<::protozero::Message>(32));
+  // Field 3: sample
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, sample_, msg);
   }

-  // Field 39: chrome_application_state_info
-  if (_has_field_[39]) {
-    (*chrome_application_state_info_).Serialize(msg->BeginNestedMessage<::protozero::Message>(39));
+  // Field 4: name_iid
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, name_iid_, msg);
   }

-  // Field 40: chrome_renderer_scheduler_state
-  if (_has_field_[40]) {
-    (*chrome_renderer_scheduler_state_).Serialize(msg->BeginNestedMessage<::protozero::Message>(40));
-  }
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}

-  // Field 41: chrome_window_handle_event_info
-  if (_has_field_[41]) {
-    (*chrome_window_handle_event_info_).Serialize(msg->BeginNestedMessage<::protozero::Message>(41));
-  }

-  // Field 43: chrome_content_settings_event_info
-  if (_has_field_[43]) {
-    (*chrome_content_settings_event_info_).Serialize(msg->BeginNestedMessage<::protozero::Message>(43));
-  }
+HistogramName::HistogramName() = default;
+HistogramName::~HistogramName() = default;
+HistogramName::HistogramName(const HistogramName&) = default;
+HistogramName& HistogramName::operator=(const HistogramName&) = default;
+HistogramName::HistogramName(HistogramName&&) noexcept = default;
+HistogramName& HistogramName::operator=(HistogramName&&) = default;

-  // Field 49: chrome_active_processes
-  if (_has_field_[49]) {
-    (*chrome_active_processes_).Serialize(msg->BeginNestedMessage<::protozero::Message>(49));
-  }
+bool HistogramName::operator==(const HistogramName& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+}

-  // Field 50: screenshot
-  if (_has_field_[50]) {
-    (*screenshot_).Serialize(msg->BeginNestedMessage<::protozero::Message>(50));
-  }
+bool HistogramName::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;

-  // Field 33: source_location
-  if (_has_field_[33]) {
-    (*source_location_).Serialize(msg->BeginNestedMessage<::protozero::Message>(33));
-  }
-
-  // Field 34: source_location_iid
-  if (_has_field_[34]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(34, source_location_iid_, msg);
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* iid */:
+        field.get(&iid_);
+        break;
+      case 2 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 35: chrome_message_pump
-  if (_has_field_[35]) {
-    (*chrome_message_pump_).Serialize(msg->BeginNestedMessage<::protozero::Message>(35));
-  }
+std::string HistogramName::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}

-  // Field 38: chrome_mojo_event_info
-  if (_has_field_[38]) {
-    (*chrome_mojo_event_info_).Serialize(msg->BeginNestedMessage<::protozero::Message>(38));
-  }
+std::vector<uint8_t> HistogramName::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}

-  // Field 1: timestamp_delta_us
+void HistogramName::Serialize(::protozero::Message* msg) const {
+  // Field 1: iid
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, timestamp_delta_us_, msg);
-  }
-
-  // Field 16: timestamp_absolute_us
-  if (_has_field_[16]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(16, timestamp_absolute_us_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
   }

-  // Field 2: thread_time_delta_us
+  // Field 2: name
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, thread_time_delta_us_, msg);
+    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
   }

-  // Field 17: thread_time_absolute_us
-  if (_has_field_[17]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(17, thread_time_absolute_us_, msg);
-  }
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}

-  // Field 8: thread_instruction_count_delta
-  if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, thread_instruction_count_delta_, msg);
-  }
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_keyed_service.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_keyed_service.gen.h"

-  // Field 20: thread_instruction_count_absolute
-  if (_has_field_[20]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(20, thread_instruction_count_absolute_, msg);
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+ChromeKeyedService::ChromeKeyedService() = default;
+ChromeKeyedService::~ChromeKeyedService() = default;
+ChromeKeyedService::ChromeKeyedService(const ChromeKeyedService&) = default;
+ChromeKeyedService& ChromeKeyedService::operator=(const ChromeKeyedService&) = default;
+ChromeKeyedService::ChromeKeyedService(ChromeKeyedService&&) noexcept = default;
+ChromeKeyedService& ChromeKeyedService::operator=(ChromeKeyedService&&) = default;
+
+bool ChromeKeyedService::operator==(const ChromeKeyedService& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+}
+
+bool ChromeKeyedService::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
   }
+  return !packed_error && !dec.bytes_left();
+}

-  // Field 6: legacy_event
-  if (_has_field_[6]) {
-    (*legacy_event_).Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
+std::string ChromeKeyedService::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ChromeKeyedService::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ChromeKeyedService::Serialize(::protozero::Message* msg) const {
+  // Field 1: name
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, name_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_latency_info.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_latency_info.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {

-TrackEvent_LegacyEvent::TrackEvent_LegacyEvent() = default;
-TrackEvent_LegacyEvent::~TrackEvent_LegacyEvent() = default;
-TrackEvent_LegacyEvent::TrackEvent_LegacyEvent(const TrackEvent_LegacyEvent&) = default;
-TrackEvent_LegacyEvent& TrackEvent_LegacyEvent::operator=(const TrackEvent_LegacyEvent&) = default;
-TrackEvent_LegacyEvent::TrackEvent_LegacyEvent(TrackEvent_LegacyEvent&&) noexcept = default;
-TrackEvent_LegacyEvent& TrackEvent_LegacyEvent::operator=(TrackEvent_LegacyEvent&&) = default;
+ChromeLatencyInfo::ChromeLatencyInfo() = default;
+ChromeLatencyInfo::~ChromeLatencyInfo() = default;
+ChromeLatencyInfo::ChromeLatencyInfo(const ChromeLatencyInfo&) = default;
+ChromeLatencyInfo& ChromeLatencyInfo::operator=(const ChromeLatencyInfo&) = default;
+ChromeLatencyInfo::ChromeLatencyInfo(ChromeLatencyInfo&&) noexcept = default;
+ChromeLatencyInfo& ChromeLatencyInfo::operator=(ChromeLatencyInfo&&) = default;

-bool TrackEvent_LegacyEvent::operator==(const TrackEvent_LegacyEvent& other) const {
+bool ChromeLatencyInfo::operator==(const ChromeLatencyInfo& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(name_iid_, other.name_iid_)
-   && ::protozero::internal::gen_helpers::EqualsField(phase_, other.phase_)
-   && ::protozero::internal::gen_helpers::EqualsField(duration_us_, other.duration_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(thread_duration_us_, other.thread_duration_us_)
-   && ::protozero::internal::gen_helpers::EqualsField(thread_instruction_delta_, other.thread_instruction_delta_)
-   && ::protozero::internal::gen_helpers::EqualsField(unscoped_id_, other.unscoped_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(local_id_, other.local_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(global_id_, other.global_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(id_scope_, other.id_scope_)
-   && ::protozero::internal::gen_helpers::EqualsField(use_async_tts_, other.use_async_tts_)
-   && ::protozero::internal::gen_helpers::EqualsField(bind_id_, other.bind_id_)
-   && ::protozero::internal::gen_helpers::EqualsField(bind_to_enclosing_, other.bind_to_enclosing_)
-   && ::protozero::internal::gen_helpers::EqualsField(flow_direction_, other.flow_direction_)
-   && ::protozero::internal::gen_helpers::EqualsField(instant_event_scope_, other.instant_event_scope_)
-   && ::protozero::internal::gen_helpers::EqualsField(pid_override_, other.pid_override_)
-   && ::protozero::internal::gen_helpers::EqualsField(tid_override_, other.tid_override_);
+   && ::protozero::internal::gen_helpers::EqualsField(trace_id_, other.trace_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(step_, other.step_)
+   && ::protozero::internal::gen_helpers::EqualsField(frame_tree_node_id_, other.frame_tree_node_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(component_info_, other.component_info_)
+   && ::protozero::internal::gen_helpers::EqualsField(is_coalesced_, other.is_coalesced_)
+   && ::protozero::internal::gen_helpers::EqualsField(gesture_scroll_id_, other.gesture_scroll_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(touch_id_, other.touch_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(input_type_, other.input_type_);
 }

-bool TrackEvent_LegacyEvent::ParseFromArray(const void* raw, size_t size) {
+int ChromeLatencyInfo::component_info_size() const { return static_cast<int>(component_info_.size()); }
+void ChromeLatencyInfo::clear_component_info() { component_info_.clear(); }
+ChromeLatencyInfo_ComponentInfo* ChromeLatencyInfo::add_component_info() { component_info_.emplace_back(); return &component_info_.back(); }
+bool ChromeLatencyInfo::ParseFromArray(const void* raw, size_t size) {
+  component_info_.clear();
   unknown_fields_.clear();
   bool packed_error = false;

@@ -37955,53 +37894,30 @@ bool TrackEvent_LegacyEvent::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* name_iid */:
-        field.get(&name_iid_);
-        break;
-      case 2 /* phase */:
-        field.get(&phase_);
-        break;
-      case 3 /* duration_us */:
-        field.get(&duration_us_);
-        break;
-      case 4 /* thread_duration_us */:
-        field.get(&thread_duration_us_);
-        break;
-      case 15 /* thread_instruction_delta */:
-        field.get(&thread_instruction_delta_);
-        break;
-      case 6 /* unscoped_id */:
-        field.get(&unscoped_id_);
-        break;
-      case 10 /* local_id */:
-        field.get(&local_id_);
-        break;
-      case 11 /* global_id */:
-        field.get(&global_id_);
-        break;
-      case 7 /* id_scope */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &id_scope_);
+      case 1 /* trace_id */:
+        field.get(&trace_id_);
         break;
-      case 9 /* use_async_tts */:
-        field.get(&use_async_tts_);
+      case 2 /* step */:
+        field.get(&step_);
         break;
-      case 8 /* bind_id */:
-        field.get(&bind_id_);
+      case 3 /* frame_tree_node_id */:
+        field.get(&frame_tree_node_id_);
         break;
-      case 12 /* bind_to_enclosing */:
-        field.get(&bind_to_enclosing_);
+      case 4 /* component_info */:
+        component_info_.emplace_back();
+        component_info_.back().ParseFromArray(field.data(), field.size());
         break;
-      case 13 /* flow_direction */:
-        field.get(&flow_direction_);
+      case 5 /* is_coalesced */:
+        field.get(&is_coalesced_);
         break;
-      case 14 /* instant_event_scope */:
-        field.get(&instant_event_scope_);
+      case 6 /* gesture_scroll_id */:
+        field.get(&gesture_scroll_id_);
         break;
-      case 18 /* pid_override */:
-        field.get(&pid_override_);
+      case 7 /* touch_id */:
+        field.get(&touch_id_);
         break;
-      case 19 /* tid_override */:
-        field.get(&tid_override_);
+      case 8 /* input_type */:
+        field.get(&input_type_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -38011,120 +37927,77 @@ bool TrackEvent_LegacyEvent::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TrackEvent_LegacyEvent::SerializeAsString() const {
+std::string ChromeLatencyInfo::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TrackEvent_LegacyEvent::SerializeAsArray() const {
+std::vector<uint8_t> ChromeLatencyInfo::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TrackEvent_LegacyEvent::Serialize(::protozero::Message* msg) const {
-  // Field 1: name_iid
+void ChromeLatencyInfo::Serialize(::protozero::Message* msg) const {
+  // Field 1: trace_id
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(1, name_iid_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, trace_id_, msg);
   }

-  // Field 2: phase
+  // Field 2: step
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(2, phase_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, step_, msg);
   }

-  // Field 3: duration_us
+  // Field 3: frame_tree_node_id
   if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, duration_us_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, frame_tree_node_id_, msg);
   }

-  // Field 4: thread_duration_us
-  if (_has_field_[4]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(4, thread_duration_us_, msg);
+  // Field 4: component_info
+  for (auto& it : component_info_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
   }

-  // Field 15: thread_instruction_delta
-  if (_has_field_[15]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(15, thread_instruction_delta_, msg);
+  // Field 5: is_coalesced
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, is_coalesced_, msg);
   }

-  // Field 6: unscoped_id
+  // Field 6: gesture_scroll_id
   if (_has_field_[6]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(6, unscoped_id_, msg);
-  }
-
-  // Field 10: local_id
-  if (_has_field_[10]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(10, local_id_, msg);
-  }
-
-  // Field 11: global_id
-  if (_has_field_[11]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(11, global_id_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, gesture_scroll_id_, msg);
   }

-  // Field 7: id_scope
+  // Field 7: touch_id
   if (_has_field_[7]) {
-    ::protozero::internal::gen_helpers::SerializeString(7, id_scope_, msg);
-  }
-
-  // Field 9: use_async_tts
-  if (_has_field_[9]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, use_async_tts_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, touch_id_, msg);
   }

-  // Field 8: bind_id
+  // Field 8: input_type
   if (_has_field_[8]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(8, bind_id_, msg);
-  }
-
-  // Field 12: bind_to_enclosing
-  if (_has_field_[12]) {
-    ::protozero::internal::gen_helpers::SerializeTinyVarInt(12, bind_to_enclosing_, msg);
-  }
-
-  // Field 13: flow_direction
-  if (_has_field_[13]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(13, flow_direction_, msg);
-  }
-
-  // Field 14: instant_event_scope
-  if (_has_field_[14]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(14, instant_event_scope_, msg);
-  }
-
-  // Field 18: pid_override
-  if (_has_field_[18]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(18, pid_override_, msg);
-  }
-
-  // Field 19: tid_override
-  if (_has_field_[19]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(19, tid_override_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, input_type_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }


-TrackEvent_InlineCallstack::TrackEvent_InlineCallstack() = default;
-TrackEvent_InlineCallstack::~TrackEvent_InlineCallstack() = default;
-TrackEvent_InlineCallstack::TrackEvent_InlineCallstack(const TrackEvent_InlineCallstack&) = default;
-TrackEvent_InlineCallstack& TrackEvent_InlineCallstack::operator=(const TrackEvent_InlineCallstack&) = default;
-TrackEvent_InlineCallstack::TrackEvent_InlineCallstack(TrackEvent_InlineCallstack&&) noexcept = default;
-TrackEvent_InlineCallstack& TrackEvent_InlineCallstack::operator=(TrackEvent_InlineCallstack&&) = default;
+ChromeLatencyInfo_ComponentInfo::ChromeLatencyInfo_ComponentInfo() = default;
+ChromeLatencyInfo_ComponentInfo::~ChromeLatencyInfo_ComponentInfo() = default;
+ChromeLatencyInfo_ComponentInfo::ChromeLatencyInfo_ComponentInfo(const ChromeLatencyInfo_ComponentInfo&) = default;
+ChromeLatencyInfo_ComponentInfo& ChromeLatencyInfo_ComponentInfo::operator=(const ChromeLatencyInfo_ComponentInfo&) = default;
+ChromeLatencyInfo_ComponentInfo::ChromeLatencyInfo_ComponentInfo(ChromeLatencyInfo_ComponentInfo&&) noexcept = default;
+ChromeLatencyInfo_ComponentInfo& ChromeLatencyInfo_ComponentInfo::operator=(ChromeLatencyInfo_ComponentInfo&&) = default;

-bool TrackEvent_InlineCallstack::operator==(const TrackEvent_InlineCallstack& other) const {
+bool ChromeLatencyInfo_ComponentInfo::operator==(const ChromeLatencyInfo_ComponentInfo& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(frames_, other.frames_);
+   && ::protozero::internal::gen_helpers::EqualsField(component_type_, other.component_type_)
+   && ::protozero::internal::gen_helpers::EqualsField(time_us_, other.time_us_);
 }

-int TrackEvent_InlineCallstack::frames_size() const { return static_cast<int>(frames_.size()); }
-void TrackEvent_InlineCallstack::clear_frames() { frames_.clear(); }
-TrackEvent_InlineCallstack_Frame* TrackEvent_InlineCallstack::add_frames() { frames_.emplace_back(); return &frames_.back(); }
-bool TrackEvent_InlineCallstack::ParseFromArray(const void* raw, size_t size) {
-  frames_.clear();
+bool ChromeLatencyInfo_ComponentInfo::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -38134,9 +38007,11 @@ bool TrackEvent_InlineCallstack::ParseFromArray(const void* raw, size_t size) {
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* frames */:
-        frames_.emplace_back();
-        frames_.back().ParseFromArray(field.data(), field.size());
+      case 1 /* component_type */:
+        field.get(&component_type_);
+        break;
+      case 2 /* time_us */:
+        field.get(&time_us_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -38146,43 +38021,69 @@ bool TrackEvent_InlineCallstack::ParseFromArray(const void* raw, size_t size) {
   return !packed_error && !dec.bytes_left();
 }

-std::string TrackEvent_InlineCallstack::SerializeAsString() const {
+std::string ChromeLatencyInfo_ComponentInfo::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TrackEvent_InlineCallstack::SerializeAsArray() const {
+std::vector<uint8_t> ChromeLatencyInfo_ComponentInfo::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TrackEvent_InlineCallstack::Serialize(::protozero::Message* msg) const {
-  // Field 1: frames
-  for (auto& it : frames_) {
-    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+void ChromeLatencyInfo_ComponentInfo::Serialize(::protozero::Message* msg) const {
+  // Field 1: component_type
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, component_type_, msg);
+  }
+
+  // Field 2: time_us
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, time_us_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_legacy_ipc.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_legacy_ipc.gen.h"

-TrackEvent_InlineCallstack_Frame::TrackEvent_InlineCallstack_Frame() = default;
-TrackEvent_InlineCallstack_Frame::~TrackEvent_InlineCallstack_Frame() = default;
-TrackEvent_InlineCallstack_Frame::TrackEvent_InlineCallstack_Frame(const TrackEvent_InlineCallstack_Frame&) = default;
-TrackEvent_InlineCallstack_Frame& TrackEvent_InlineCallstack_Frame::operator=(const TrackEvent_InlineCallstack_Frame&) = default;
-TrackEvent_InlineCallstack_Frame::TrackEvent_InlineCallstack_Frame(TrackEvent_InlineCallstack_Frame&&) noexcept = default;
-TrackEvent_InlineCallstack_Frame& TrackEvent_InlineCallstack_Frame::operator=(TrackEvent_InlineCallstack_Frame&&) = default;
+namespace perfetto {
+namespace protos {
+namespace gen {

-bool TrackEvent_InlineCallstack_Frame::operator==(const TrackEvent_InlineCallstack_Frame& other) const {
+ChromeLegacyIpc::ChromeLegacyIpc() = default;
+ChromeLegacyIpc::~ChromeLegacyIpc() = default;
+ChromeLegacyIpc::ChromeLegacyIpc(const ChromeLegacyIpc&) = default;
+ChromeLegacyIpc& ChromeLegacyIpc::operator=(const ChromeLegacyIpc&) = default;
+ChromeLegacyIpc::ChromeLegacyIpc(ChromeLegacyIpc&&) noexcept = default;
+ChromeLegacyIpc& ChromeLegacyIpc::operator=(ChromeLegacyIpc&&) = default;
+
+bool ChromeLegacyIpc::operator==(const ChromeLegacyIpc& other) const {
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
-   && ::protozero::internal::gen_helpers::EqualsField(function_name_, other.function_name_)
-   && ::protozero::internal::gen_helpers::EqualsField(source_file_, other.source_file_)
-   && ::protozero::internal::gen_helpers::EqualsField(line_number_, other.line_number_);
+   && ::protozero::internal::gen_helpers::EqualsField(message_class_, other.message_class_)
+   && ::protozero::internal::gen_helpers::EqualsField(message_line_, other.message_line_);
 }

-bool TrackEvent_InlineCallstack_Frame::ParseFromArray(const void* raw, size_t size) {
+bool ChromeLegacyIpc::ParseFromArray(const void* raw, size_t size) {
   unknown_fields_.clear();
   bool packed_error = false;

@@ -38192,14 +38093,11 @@ bool TrackEvent_InlineCallstack_Frame::ParseFromArray(const void* raw, size_t si
       _has_field_.set(field.id());
     }
     switch (field.id()) {
-      case 1 /* function_name */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &function_name_);
-        break;
-      case 2 /* source_file */:
-        ::protozero::internal::gen_helpers::DeserializeString(field, &source_file_);
+      case 1 /* message_class */:
+        field.get(&message_class_);
         break;
-      case 3 /* line_number */:
-        field.get(&line_number_);
+      case 2 /* message_line */:
+        field.get(&message_line_);
         break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
@@ -38209,32 +38107,27 @@ bool TrackEvent_InlineCallstack_Frame::ParseFromArray(const void* raw, size_t si
   return !packed_error && !dec.bytes_left();
 }

-std::string TrackEvent_InlineCallstack_Frame::SerializeAsString() const {
+std::string ChromeLegacyIpc::SerializeAsString() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsString();
 }

-std::vector<uint8_t> TrackEvent_InlineCallstack_Frame::SerializeAsArray() const {
+std::vector<uint8_t> ChromeLegacyIpc::SerializeAsArray() const {
   ::protozero::internal::gen_helpers::MessageSerializer msg;
   Serialize(msg.get());
   return msg.SerializeAsArray();
 }

-void TrackEvent_InlineCallstack_Frame::Serialize(::protozero::Message* msg) const {
-  // Field 1: function_name
+void ChromeLegacyIpc::Serialize(::protozero::Message* msg) const {
+  // Field 1: message_class
   if (_has_field_[1]) {
-    ::protozero::internal::gen_helpers::SerializeString(1, function_name_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, message_class_, msg);
   }

-  // Field 2: source_file
+  // Field 2: message_line
   if (_has_field_[2]) {
-    ::protozero::internal::gen_helpers::SerializeString(2, source_file_, msg);
-  }
-
-  // Field 3: line_number
-  if (_has_field_[3]) {
-    ::protozero::internal::gen_helpers::SerializeVarInt(3, line_number_, msg);
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, message_line_, msg);
   }

   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
@@ -38246,49 +38139,3637 @@ void TrackEvent_InlineCallstack_Frame::Serialize(::protozero::Message* msg) cons
 #if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
-// gen_amalgamated begin source: gen/protos/perfetto/common/semantic_type.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_aflags_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_game_intervention_list_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_input_event_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_log_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_polled_state_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_sdk_sysprop_guard_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_system_property_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/app_wakelock_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/cpu_per_uid_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/display_video_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/inputmethod_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/kernel_wakelocks_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/network_trace_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/packages_list_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/pixel_modem_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/protolog_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/surfaceflinger_layers_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/surfaceflinger_transactions_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/user_list_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/android/windowmanager_config.pbzero.cc
-// Intentionally empty (crbug.com/998165)
-// gen_amalgamated begin source: gen/protos/perfetto/config/ftrace/frozen_ftrace_config.pbzero.cc
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_message_pump.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_message_pump.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+ChromeMessagePump::ChromeMessagePump() = default;
+ChromeMessagePump::~ChromeMessagePump() = default;
+ChromeMessagePump::ChromeMessagePump(const ChromeMessagePump&) = default;
+ChromeMessagePump& ChromeMessagePump::operator=(const ChromeMessagePump&) = default;
+ChromeMessagePump::ChromeMessagePump(ChromeMessagePump&&) noexcept = default;
+ChromeMessagePump& ChromeMessagePump::operator=(ChromeMessagePump&&) = default;
+
+bool ChromeMessagePump::operator==(const ChromeMessagePump& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(sent_messages_in_queue_, other.sent_messages_in_queue_)
+   && ::protozero::internal::gen_helpers::EqualsField(io_handler_location_iid_, other.io_handler_location_iid_);
+}
+
+bool ChromeMessagePump::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* sent_messages_in_queue */:
+        field.get(&sent_messages_in_queue_);
+        break;
+      case 2 /* io_handler_location_iid */:
+        field.get(&io_handler_location_iid_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string ChromeMessagePump::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ChromeMessagePump::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ChromeMessagePump::Serialize(::protozero::Message* msg) const {
+  // Field 1: sent_messages_in_queue
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(1, sent_messages_in_queue_, msg);
+  }
+
+  // Field 2: io_handler_location_iid
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, io_handler_location_iid_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_mojo_event_info.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_mojo_event_info.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+ChromeMojoEventInfo::ChromeMojoEventInfo() = default;
+ChromeMojoEventInfo::~ChromeMojoEventInfo() = default;
+ChromeMojoEventInfo::ChromeMojoEventInfo(const ChromeMojoEventInfo&) = default;
+ChromeMojoEventInfo& ChromeMojoEventInfo::operator=(const ChromeMojoEventInfo&) = default;
+ChromeMojoEventInfo::ChromeMojoEventInfo(ChromeMojoEventInfo&&) noexcept = default;
+ChromeMojoEventInfo& ChromeMojoEventInfo::operator=(ChromeMojoEventInfo&&) = default;
+
+bool ChromeMojoEventInfo::operator==(const ChromeMojoEventInfo& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(watcher_notify_interface_tag_, other.watcher_notify_interface_tag_)
+   && ::protozero::internal::gen_helpers::EqualsField(ipc_hash_, other.ipc_hash_)
+   && ::protozero::internal::gen_helpers::EqualsField(mojo_interface_tag_, other.mojo_interface_tag_)
+   && ::protozero::internal::gen_helpers::EqualsField(mojo_interface_method_iid_, other.mojo_interface_method_iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(is_reply_, other.is_reply_)
+   && ::protozero::internal::gen_helpers::EqualsField(payload_size_, other.payload_size_)
+   && ::protozero::internal::gen_helpers::EqualsField(data_num_bytes_, other.data_num_bytes_);
+}
+
+bool ChromeMojoEventInfo::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* watcher_notify_interface_tag */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &watcher_notify_interface_tag_);
+        break;
+      case 2 /* ipc_hash */:
+        field.get(&ipc_hash_);
+        break;
+      case 3 /* mojo_interface_tag */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &mojo_interface_tag_);
+        break;
+      case 4 /* mojo_interface_method_iid */:
+        field.get(&mojo_interface_method_iid_);
+        break;
+      case 5 /* is_reply */:
+        field.get(&is_reply_);
+        break;
+      case 6 /* payload_size */:
+        field.get(&payload_size_);
+        break;
+      case 7 /* data_num_bytes */:
+        field.get(&data_num_bytes_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string ChromeMojoEventInfo::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ChromeMojoEventInfo::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ChromeMojoEventInfo::Serialize(::protozero::Message* msg) const {
+  // Field 1: watcher_notify_interface_tag
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, watcher_notify_interface_tag_, msg);
+  }
+
+  // Field 2: ipc_hash
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, ipc_hash_, msg);
+  }
+
+  // Field 3: mojo_interface_tag
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeString(3, mojo_interface_tag_, msg);
+  }
+
+  // Field 4: mojo_interface_method_iid
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, mojo_interface_method_iid_, msg);
+  }
+
+  // Field 5: is_reply
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, is_reply_, msg);
+  }
+
+  // Field 6: payload_size
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, payload_size_, msg);
+  }
+
+  // Field 7: data_num_bytes
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, data_num_bytes_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_process_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_process_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+ChromeProcessDescriptor::ChromeProcessDescriptor() = default;
+ChromeProcessDescriptor::~ChromeProcessDescriptor() = default;
+ChromeProcessDescriptor::ChromeProcessDescriptor(const ChromeProcessDescriptor&) = default;
+ChromeProcessDescriptor& ChromeProcessDescriptor::operator=(const ChromeProcessDescriptor&) = default;
+ChromeProcessDescriptor::ChromeProcessDescriptor(ChromeProcessDescriptor&&) noexcept = default;
+ChromeProcessDescriptor& ChromeProcessDescriptor::operator=(ChromeProcessDescriptor&&) = default;
+
+bool ChromeProcessDescriptor::operator==(const ChromeProcessDescriptor& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(process_type_, other.process_type_)
+   && ::protozero::internal::gen_helpers::EqualsField(process_priority_, other.process_priority_)
+   && ::protozero::internal::gen_helpers::EqualsField(legacy_sort_index_, other.legacy_sort_index_)
+   && ::protozero::internal::gen_helpers::EqualsField(host_app_package_name_, other.host_app_package_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(crash_trace_id_, other.crash_trace_id_);
+}
+
+bool ChromeProcessDescriptor::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* process_type */:
+        field.get(&process_type_);
+        break;
+      case 2 /* process_priority */:
+        field.get(&process_priority_);
+        break;
+      case 3 /* legacy_sort_index */:
+        field.get(&legacy_sort_index_);
+        break;
+      case 4 /* host_app_package_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &host_app_package_name_);
+        break;
+      case 5 /* crash_trace_id */:
+        field.get(&crash_trace_id_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string ChromeProcessDescriptor::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ChromeProcessDescriptor::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ChromeProcessDescriptor::Serialize(::protozero::Message* msg) const {
+  // Field 1: process_type
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, process_type_, msg);
+  }
+
+  // Field 2: process_priority
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, process_priority_, msg);
+  }
+
+  // Field 3: legacy_sort_index
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, legacy_sort_index_, msg);
+  }
+
+  // Field 4: host_app_package_name
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeString(4, host_app_package_name_, msg);
+  }
+
+  // Field 5: crash_trace_id
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, crash_trace_id_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+ChromeRendererSchedulerState::ChromeRendererSchedulerState() = default;
+ChromeRendererSchedulerState::~ChromeRendererSchedulerState() = default;
+ChromeRendererSchedulerState::ChromeRendererSchedulerState(const ChromeRendererSchedulerState&) = default;
+ChromeRendererSchedulerState& ChromeRendererSchedulerState::operator=(const ChromeRendererSchedulerState&) = default;
+ChromeRendererSchedulerState::ChromeRendererSchedulerState(ChromeRendererSchedulerState&&) noexcept = default;
+ChromeRendererSchedulerState& ChromeRendererSchedulerState::operator=(ChromeRendererSchedulerState&&) = default;
+
+bool ChromeRendererSchedulerState::operator==(const ChromeRendererSchedulerState& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(rail_mode_, other.rail_mode_)
+   && ::protozero::internal::gen_helpers::EqualsField(is_backgrounded_, other.is_backgrounded_)
+   && ::protozero::internal::gen_helpers::EqualsField(is_hidden_, other.is_hidden_);
+}
+
+bool ChromeRendererSchedulerState::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* rail_mode */:
+        field.get(&rail_mode_);
+        break;
+      case 2 /* is_backgrounded */:
+        field.get(&is_backgrounded_);
+        break;
+      case 3 /* is_hidden */:
+        field.get(&is_hidden_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string ChromeRendererSchedulerState::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ChromeRendererSchedulerState::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ChromeRendererSchedulerState::Serialize(::protozero::Message* msg) const {
+  // Field 1: rail_mode
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, rail_mode_, msg);
+  }
+
+  // Field 2: is_backgrounded
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, is_backgrounded_, msg);
+  }
+
+  // Field 3: is_hidden
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, is_hidden_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_thread_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_thread_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+ChromeThreadDescriptor::ChromeThreadDescriptor() = default;
+ChromeThreadDescriptor::~ChromeThreadDescriptor() = default;
+ChromeThreadDescriptor::ChromeThreadDescriptor(const ChromeThreadDescriptor&) = default;
+ChromeThreadDescriptor& ChromeThreadDescriptor::operator=(const ChromeThreadDescriptor&) = default;
+ChromeThreadDescriptor::ChromeThreadDescriptor(ChromeThreadDescriptor&&) noexcept = default;
+ChromeThreadDescriptor& ChromeThreadDescriptor::operator=(ChromeThreadDescriptor&&) = default;
+
+bool ChromeThreadDescriptor::operator==(const ChromeThreadDescriptor& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(thread_type_, other.thread_type_)
+   && ::protozero::internal::gen_helpers::EqualsField(legacy_sort_index_, other.legacy_sort_index_)
+   && ::protozero::internal::gen_helpers::EqualsField(is_sandboxed_tid_, other.is_sandboxed_tid_);
+}
+
+bool ChromeThreadDescriptor::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* thread_type */:
+        field.get(&thread_type_);
+        break;
+      case 2 /* legacy_sort_index */:
+        field.get(&legacy_sort_index_);
+        break;
+      case 3 /* is_sandboxed_tid */:
+        field.get(&is_sandboxed_tid_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string ChromeThreadDescriptor::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ChromeThreadDescriptor::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ChromeThreadDescriptor::Serialize(::protozero::Message* msg) const {
+  // Field 1: thread_type
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, thread_type_, msg);
+  }
+
+  // Field 2: legacy_sort_index
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, legacy_sort_index_, msg);
+  }
+
+  // Field 3: is_sandboxed_tid
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, is_sandboxed_tid_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_user_event.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_user_event.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+ChromeUserEvent::ChromeUserEvent() = default;
+ChromeUserEvent::~ChromeUserEvent() = default;
+ChromeUserEvent::ChromeUserEvent(const ChromeUserEvent&) = default;
+ChromeUserEvent& ChromeUserEvent::operator=(const ChromeUserEvent&) = default;
+ChromeUserEvent::ChromeUserEvent(ChromeUserEvent&&) noexcept = default;
+ChromeUserEvent& ChromeUserEvent::operator=(ChromeUserEvent&&) = default;
+
+bool ChromeUserEvent::operator==(const ChromeUserEvent& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(action_, other.action_)
+   && ::protozero::internal::gen_helpers::EqualsField(action_hash_, other.action_hash_);
+}
+
+bool ChromeUserEvent::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* action */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &action_);
+        break;
+      case 2 /* action_hash */:
+        field.get(&action_hash_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string ChromeUserEvent::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ChromeUserEvent::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ChromeUserEvent::Serialize(::protozero::Message* msg) const {
+  // Field 1: action
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, action_, msg);
+  }
+
+  // Field 2: action_hash
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, action_hash_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/chrome_window_handle_event_info.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_window_handle_event_info.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+ChromeWindowHandleEventInfo::ChromeWindowHandleEventInfo() = default;
+ChromeWindowHandleEventInfo::~ChromeWindowHandleEventInfo() = default;
+ChromeWindowHandleEventInfo::ChromeWindowHandleEventInfo(const ChromeWindowHandleEventInfo&) = default;
+ChromeWindowHandleEventInfo& ChromeWindowHandleEventInfo::operator=(const ChromeWindowHandleEventInfo&) = default;
+ChromeWindowHandleEventInfo::ChromeWindowHandleEventInfo(ChromeWindowHandleEventInfo&&) noexcept = default;
+ChromeWindowHandleEventInfo& ChromeWindowHandleEventInfo::operator=(ChromeWindowHandleEventInfo&&) = default;
+
+bool ChromeWindowHandleEventInfo::operator==(const ChromeWindowHandleEventInfo& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(dpi_, other.dpi_)
+   && ::protozero::internal::gen_helpers::EqualsField(message_id_, other.message_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(hwnd_ptr_, other.hwnd_ptr_);
+}
+
+bool ChromeWindowHandleEventInfo::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* dpi */:
+        field.get(&dpi_);
+        break;
+      case 2 /* message_id */:
+        field.get(&message_id_);
+        break;
+      case 3 /* hwnd_ptr */:
+        field.get(&hwnd_ptr_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string ChromeWindowHandleEventInfo::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ChromeWindowHandleEventInfo::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ChromeWindowHandleEventInfo::Serialize(::protozero::Message* msg) const {
+  // Field 1: dpi
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, dpi_, msg);
+  }
+
+  // Field 2: message_id
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, message_id_, msg);
+  }
+
+  // Field 3: hwnd_ptr
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeFixed(3, hwnd_ptr_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/counter_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/counter_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+CounterDescriptor::CounterDescriptor() = default;
+CounterDescriptor::~CounterDescriptor() = default;
+CounterDescriptor::CounterDescriptor(const CounterDescriptor&) = default;
+CounterDescriptor& CounterDescriptor::operator=(const CounterDescriptor&) = default;
+CounterDescriptor::CounterDescriptor(CounterDescriptor&&) noexcept = default;
+CounterDescriptor& CounterDescriptor::operator=(CounterDescriptor&&) = default;
+
+bool CounterDescriptor::operator==(const CounterDescriptor& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
+   && ::protozero::internal::gen_helpers::EqualsField(categories_, other.categories_)
+   && ::protozero::internal::gen_helpers::EqualsField(unit_, other.unit_)
+   && ::protozero::internal::gen_helpers::EqualsField(unit_name_, other.unit_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(unit_multiplier_, other.unit_multiplier_)
+   && ::protozero::internal::gen_helpers::EqualsField(is_incremental_, other.is_incremental_)
+   && ::protozero::internal::gen_helpers::EqualsField(y_axis_share_key_, other.y_axis_share_key_);
+}
+
+bool CounterDescriptor::ParseFromArray(const void* raw, size_t size) {
+  categories_.clear();
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* type */:
+        field.get(&type_);
+        break;
+      case 2 /* categories */:
+        categories_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &categories_.back());
+        break;
+      case 3 /* unit */:
+        field.get(&unit_);
+        break;
+      case 6 /* unit_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &unit_name_);
+        break;
+      case 4 /* unit_multiplier */:
+        field.get(&unit_multiplier_);
+        break;
+      case 5 /* is_incremental */:
+        field.get(&is_incremental_);
+        break;
+      case 7 /* y_axis_share_key */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &y_axis_share_key_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string CounterDescriptor::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> CounterDescriptor::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void CounterDescriptor::Serialize(::protozero::Message* msg) const {
+  // Field 1: type
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, type_, msg);
+  }
+
+  // Field 2: categories
+  for (auto& it : categories_) {
+    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
+  }
+
+  // Field 3: unit
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, unit_, msg);
+  }
+
+  // Field 6: unit_name
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeString(6, unit_name_, msg);
+  }
+
+  // Field 4: unit_multiplier
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, unit_multiplier_, msg);
+  }
+
+  // Field 5: is_incremental
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(5, is_incremental_, msg);
+  }
+
+  // Field 7: y_axis_share_key
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeString(7, y_axis_share_key_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/debug_annotation.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/debug_annotation.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+DebugAnnotationValueTypeName::DebugAnnotationValueTypeName() = default;
+DebugAnnotationValueTypeName::~DebugAnnotationValueTypeName() = default;
+DebugAnnotationValueTypeName::DebugAnnotationValueTypeName(const DebugAnnotationValueTypeName&) = default;
+DebugAnnotationValueTypeName& DebugAnnotationValueTypeName::operator=(const DebugAnnotationValueTypeName&) = default;
+DebugAnnotationValueTypeName::DebugAnnotationValueTypeName(DebugAnnotationValueTypeName&&) noexcept = default;
+DebugAnnotationValueTypeName& DebugAnnotationValueTypeName::operator=(DebugAnnotationValueTypeName&&) = default;
+
+bool DebugAnnotationValueTypeName::operator==(const DebugAnnotationValueTypeName& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+}
+
+bool DebugAnnotationValueTypeName::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* iid */:
+        field.get(&iid_);
+        break;
+      case 2 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string DebugAnnotationValueTypeName::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> DebugAnnotationValueTypeName::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void DebugAnnotationValueTypeName::Serialize(::protozero::Message* msg) const {
+  // Field 1: iid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
+  }
+
+  // Field 2: name
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+DebugAnnotationName::DebugAnnotationName() = default;
+DebugAnnotationName::~DebugAnnotationName() = default;
+DebugAnnotationName::DebugAnnotationName(const DebugAnnotationName&) = default;
+DebugAnnotationName& DebugAnnotationName::operator=(const DebugAnnotationName&) = default;
+DebugAnnotationName::DebugAnnotationName(DebugAnnotationName&&) noexcept = default;
+DebugAnnotationName& DebugAnnotationName::operator=(DebugAnnotationName&&) = default;
+
+bool DebugAnnotationName::operator==(const DebugAnnotationName& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+}
+
+bool DebugAnnotationName::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* iid */:
+        field.get(&iid_);
+        break;
+      case 2 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string DebugAnnotationName::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> DebugAnnotationName::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void DebugAnnotationName::Serialize(::protozero::Message* msg) const {
+  // Field 1: iid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
+  }
+
+  // Field 2: name
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+DebugAnnotation::DebugAnnotation() = default;
+DebugAnnotation::~DebugAnnotation() = default;
+DebugAnnotation::DebugAnnotation(const DebugAnnotation&) = default;
+DebugAnnotation& DebugAnnotation::operator=(const DebugAnnotation&) = default;
+DebugAnnotation::DebugAnnotation(DebugAnnotation&&) noexcept = default;
+DebugAnnotation& DebugAnnotation::operator=(DebugAnnotation&&) = default;
+
+bool DebugAnnotation::operator==(const DebugAnnotation& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_iid_, other.name_iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(bool_value_, other.bool_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(uint_value_, other.uint_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(int_value_, other.int_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(double_value_, other.double_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(pointer_value_, other.pointer_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(nested_value_, other.nested_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(legacy_json_value_, other.legacy_json_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(string_value_, other.string_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(string_value_iid_, other.string_value_iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(proto_type_name_, other.proto_type_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(proto_type_name_iid_, other.proto_type_name_iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(proto_value_, other.proto_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(dict_entries_, other.dict_entries_)
+   && ::protozero::internal::gen_helpers::EqualsField(array_values_, other.array_values_);
+}
+
+int DebugAnnotation::dict_entries_size() const { return static_cast<int>(dict_entries_.size()); }
+void DebugAnnotation::clear_dict_entries() { dict_entries_.clear(); }
+DebugAnnotation* DebugAnnotation::add_dict_entries() { dict_entries_.emplace_back(); return &dict_entries_.back(); }
+int DebugAnnotation::array_values_size() const { return static_cast<int>(array_values_.size()); }
+void DebugAnnotation::clear_array_values() { array_values_.clear(); }
+DebugAnnotation* DebugAnnotation::add_array_values() { array_values_.emplace_back(); return &array_values_.back(); }
+bool DebugAnnotation::ParseFromArray(const void* raw, size_t size) {
+  dict_entries_.clear();
+  array_values_.clear();
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* name_iid */:
+        field.get(&name_iid_);
+        break;
+      case 10 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      case 2 /* bool_value */:
+        field.get(&bool_value_);
+        break;
+      case 3 /* uint_value */:
+        field.get(&uint_value_);
+        break;
+      case 4 /* int_value */:
+        field.get(&int_value_);
+        break;
+      case 5 /* double_value */:
+        field.get(&double_value_);
+        break;
+      case 7 /* pointer_value */:
+        field.get(&pointer_value_);
+        break;
+      case 8 /* nested_value */:
+        (*nested_value_).ParseFromArray(field.data(), field.size());
+        break;
+      case 9 /* legacy_json_value */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &legacy_json_value_);
+        break;
+      case 6 /* string_value */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &string_value_);
+        break;
+      case 17 /* string_value_iid */:
+        field.get(&string_value_iid_);
+        break;
+      case 16 /* proto_type_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &proto_type_name_);
+        break;
+      case 13 /* proto_type_name_iid */:
+        field.get(&proto_type_name_iid_);
+        break;
+      case 14 /* proto_value */:
+        field.get(&proto_value_);
+        break;
+      case 11 /* dict_entries */:
+        dict_entries_.emplace_back();
+        dict_entries_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 12 /* array_values */:
+        array_values_.emplace_back();
+        array_values_.back().ParseFromArray(field.data(), field.size());
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string DebugAnnotation::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> DebugAnnotation::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void DebugAnnotation::Serialize(::protozero::Message* msg) const {
+  // Field 1: name_iid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, name_iid_, msg);
+  }
+
+  // Field 10: name
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeString(10, name_, msg);
+  }
+
+  // Field 2: bool_value
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(2, bool_value_, msg);
+  }
+
+  // Field 3: uint_value
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, uint_value_, msg);
+  }
+
+  // Field 4: int_value
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, int_value_, msg);
+  }
+
+  // Field 5: double_value
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeFixed(5, double_value_, msg);
+  }
+
+  // Field 7: pointer_value
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, pointer_value_, msg);
+  }
+
+  // Field 8: nested_value
+  if (_has_field_[8]) {
+    (*nested_value_).Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
+  }
+
+  // Field 9: legacy_json_value
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeString(9, legacy_json_value_, msg);
+  }
+
+  // Field 6: string_value
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeString(6, string_value_, msg);
+  }
+
+  // Field 17: string_value_iid
+  if (_has_field_[17]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(17, string_value_iid_, msg);
+  }
+
+  // Field 16: proto_type_name
+  if (_has_field_[16]) {
+    ::protozero::internal::gen_helpers::SerializeString(16, proto_type_name_, msg);
+  }
+
+  // Field 13: proto_type_name_iid
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(13, proto_type_name_iid_, msg);
+  }
+
+  // Field 14: proto_value
+  if (_has_field_[14]) {
+    ::protozero::internal::gen_helpers::SerializeString(14, proto_value_, msg);
+  }
+
+  // Field 11: dict_entries
+  for (auto& it : dict_entries_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(11));
+  }
+
+  // Field 12: array_values
+  for (auto& it : array_values_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(12));
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+DebugAnnotation_NestedValue::DebugAnnotation_NestedValue() = default;
+DebugAnnotation_NestedValue::~DebugAnnotation_NestedValue() = default;
+DebugAnnotation_NestedValue::DebugAnnotation_NestedValue(const DebugAnnotation_NestedValue&) = default;
+DebugAnnotation_NestedValue& DebugAnnotation_NestedValue::operator=(const DebugAnnotation_NestedValue&) = default;
+DebugAnnotation_NestedValue::DebugAnnotation_NestedValue(DebugAnnotation_NestedValue&&) noexcept = default;
+DebugAnnotation_NestedValue& DebugAnnotation_NestedValue::operator=(DebugAnnotation_NestedValue&&) = default;
+
+bool DebugAnnotation_NestedValue::operator==(const DebugAnnotation_NestedValue& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(nested_type_, other.nested_type_)
+   && ::protozero::internal::gen_helpers::EqualsField(dict_keys_, other.dict_keys_)
+   && ::protozero::internal::gen_helpers::EqualsField(dict_values_, other.dict_values_)
+   && ::protozero::internal::gen_helpers::EqualsField(array_values_, other.array_values_)
+   && ::protozero::internal::gen_helpers::EqualsField(int_value_, other.int_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(double_value_, other.double_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(bool_value_, other.bool_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(string_value_, other.string_value_);
+}
+
+int DebugAnnotation_NestedValue::dict_values_size() const { return static_cast<int>(dict_values_.size()); }
+void DebugAnnotation_NestedValue::clear_dict_values() { dict_values_.clear(); }
+DebugAnnotation_NestedValue* DebugAnnotation_NestedValue::add_dict_values() { dict_values_.emplace_back(); return &dict_values_.back(); }
+int DebugAnnotation_NestedValue::array_values_size() const { return static_cast<int>(array_values_.size()); }
+void DebugAnnotation_NestedValue::clear_array_values() { array_values_.clear(); }
+DebugAnnotation_NestedValue* DebugAnnotation_NestedValue::add_array_values() { array_values_.emplace_back(); return &array_values_.back(); }
+bool DebugAnnotation_NestedValue::ParseFromArray(const void* raw, size_t size) {
+  dict_keys_.clear();
+  dict_values_.clear();
+  array_values_.clear();
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* nested_type */:
+        field.get(&nested_type_);
+        break;
+      case 2 /* dict_keys */:
+        dict_keys_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &dict_keys_.back());
+        break;
+      case 3 /* dict_values */:
+        dict_values_.emplace_back();
+        dict_values_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 4 /* array_values */:
+        array_values_.emplace_back();
+        array_values_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 5 /* int_value */:
+        field.get(&int_value_);
+        break;
+      case 6 /* double_value */:
+        field.get(&double_value_);
+        break;
+      case 7 /* bool_value */:
+        field.get(&bool_value_);
+        break;
+      case 8 /* string_value */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &string_value_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string DebugAnnotation_NestedValue::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> DebugAnnotation_NestedValue::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void DebugAnnotation_NestedValue::Serialize(::protozero::Message* msg) const {
+  // Field 1: nested_type
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, nested_type_, msg);
+  }
+
+  // Field 2: dict_keys
+  for (auto& it : dict_keys_) {
+    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
+  }
+
+  // Field 3: dict_values
+  for (auto& it : dict_values_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+  }
+
+  // Field 4: array_values
+  for (auto& it : array_values_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
+  }
+
+  // Field 5: int_value
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, int_value_, msg);
+  }
+
+  // Field 6: double_value
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeFixed(6, double_value_, msg);
+  }
+
+  // Field 7: bool_value
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(7, bool_value_, msg);
+  }
+
+  // Field 8: string_value
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeString(8, string_value_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/log_message.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/log_message.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+LogMessageBody::LogMessageBody() = default;
+LogMessageBody::~LogMessageBody() = default;
+LogMessageBody::LogMessageBody(const LogMessageBody&) = default;
+LogMessageBody& LogMessageBody::operator=(const LogMessageBody&) = default;
+LogMessageBody::LogMessageBody(LogMessageBody&&) noexcept = default;
+LogMessageBody& LogMessageBody::operator=(LogMessageBody&&) = default;
+
+bool LogMessageBody::operator==(const LogMessageBody& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(body_, other.body_);
+}
+
+bool LogMessageBody::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* iid */:
+        field.get(&iid_);
+        break;
+      case 2 /* body */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &body_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string LogMessageBody::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> LogMessageBody::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void LogMessageBody::Serialize(::protozero::Message* msg) const {
+  // Field 1: iid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
+  }
+
+  // Field 2: body
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, body_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+LogMessage::LogMessage() = default;
+LogMessage::~LogMessage() = default;
+LogMessage::LogMessage(const LogMessage&) = default;
+LogMessage& LogMessage::operator=(const LogMessage&) = default;
+LogMessage::LogMessage(LogMessage&&) noexcept = default;
+LogMessage& LogMessage::operator=(LogMessage&&) = default;
+
+bool LogMessage::operator==(const LogMessage& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(source_location_iid_, other.source_location_iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(body_iid_, other.body_iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(prio_, other.prio_);
+}
+
+bool LogMessage::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* source_location_iid */:
+        field.get(&source_location_iid_);
+        break;
+      case 2 /* body_iid */:
+        field.get(&body_iid_);
+        break;
+      case 3 /* prio */:
+        field.get(&prio_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string LogMessage::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> LogMessage::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void LogMessage::Serialize(::protozero::Message* msg) const {
+  // Field 1: source_location_iid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, source_location_iid_, msg);
+  }
+
+  // Field 2: body_iid
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, body_iid_, msg);
+  }
+
+  // Field 3: prio
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, prio_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/process_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/process_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+ProcessDescriptor::ProcessDescriptor() = default;
+ProcessDescriptor::~ProcessDescriptor() = default;
+ProcessDescriptor::ProcessDescriptor(const ProcessDescriptor&) = default;
+ProcessDescriptor& ProcessDescriptor::operator=(const ProcessDescriptor&) = default;
+ProcessDescriptor::ProcessDescriptor(ProcessDescriptor&&) noexcept = default;
+ProcessDescriptor& ProcessDescriptor::operator=(ProcessDescriptor&&) = default;
+
+bool ProcessDescriptor::operator==(const ProcessDescriptor& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(pid_, other.pid_)
+   && ::protozero::internal::gen_helpers::EqualsField(cmdline_, other.cmdline_)
+   && ::protozero::internal::gen_helpers::EqualsField(process_name_, other.process_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(process_priority_, other.process_priority_)
+   && ::protozero::internal::gen_helpers::EqualsField(start_timestamp_ns_, other.start_timestamp_ns_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_process_type_, other.chrome_process_type_)
+   && ::protozero::internal::gen_helpers::EqualsField(legacy_sort_index_, other.legacy_sort_index_)
+   && ::protozero::internal::gen_helpers::EqualsField(process_labels_, other.process_labels_);
+}
+
+bool ProcessDescriptor::ParseFromArray(const void* raw, size_t size) {
+  cmdline_.clear();
+  process_labels_.clear();
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* pid */:
+        field.get(&pid_);
+        break;
+      case 2 /* cmdline */:
+        cmdline_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &cmdline_.back());
+        break;
+      case 6 /* process_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &process_name_);
+        break;
+      case 5 /* process_priority */:
+        field.get(&process_priority_);
+        break;
+      case 7 /* start_timestamp_ns */:
+        field.get(&start_timestamp_ns_);
+        break;
+      case 4 /* chrome_process_type */:
+        field.get(&chrome_process_type_);
+        break;
+      case 3 /* legacy_sort_index */:
+        field.get(&legacy_sort_index_);
+        break;
+      case 8 /* process_labels */:
+        process_labels_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &process_labels_.back());
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string ProcessDescriptor::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ProcessDescriptor::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ProcessDescriptor::Serialize(::protozero::Message* msg) const {
+  // Field 1: pid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, pid_, msg);
+  }
+
+  // Field 2: cmdline
+  for (auto& it : cmdline_) {
+    ::protozero::internal::gen_helpers::SerializeString(2, it, msg);
+  }
+
+  // Field 6: process_name
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeString(6, process_name_, msg);
+  }
+
+  // Field 5: process_priority
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, process_priority_, msg);
+  }
+
+  // Field 7: start_timestamp_ns
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, start_timestamp_ns_, msg);
+  }
+
+  // Field 4: chrome_process_type
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, chrome_process_type_, msg);
+  }
+
+  // Field 3: legacy_sort_index
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, legacy_sort_index_, msg);
+  }
+
+  // Field 8: process_labels
+  for (auto& it : process_labels_) {
+    ::protozero::internal::gen_helpers::SerializeString(8, it, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/range_of_interest.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/range_of_interest.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+TrackEventRangeOfInterest::TrackEventRangeOfInterest() = default;
+TrackEventRangeOfInterest::~TrackEventRangeOfInterest() = default;
+TrackEventRangeOfInterest::TrackEventRangeOfInterest(const TrackEventRangeOfInterest&) = default;
+TrackEventRangeOfInterest& TrackEventRangeOfInterest::operator=(const TrackEventRangeOfInterest&) = default;
+TrackEventRangeOfInterest::TrackEventRangeOfInterest(TrackEventRangeOfInterest&&) noexcept = default;
+TrackEventRangeOfInterest& TrackEventRangeOfInterest::operator=(TrackEventRangeOfInterest&&) = default;
+
+bool TrackEventRangeOfInterest::operator==(const TrackEventRangeOfInterest& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(start_us_, other.start_us_);
+}
+
+bool TrackEventRangeOfInterest::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* start_us */:
+        field.get(&start_us_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string TrackEventRangeOfInterest::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> TrackEventRangeOfInterest::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void TrackEventRangeOfInterest::Serialize(::protozero::Message* msg) const {
+  // Field 1: start_us
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, start_us_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/screenshot.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/screenshot.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+Screenshot::Screenshot() = default;
+Screenshot::~Screenshot() = default;
+Screenshot::Screenshot(const Screenshot&) = default;
+Screenshot& Screenshot::operator=(const Screenshot&) = default;
+Screenshot::Screenshot(Screenshot&&) noexcept = default;
+Screenshot& Screenshot::operator=(Screenshot&&) = default;
+
+bool Screenshot::operator==(const Screenshot& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(jpg_image_, other.jpg_image_)
+   && ::protozero::internal::gen_helpers::EqualsField(pam_image_, other.pam_image_)
+   && ::protozero::internal::gen_helpers::EqualsField(ppm_image_, other.ppm_image_);
+}
+
+bool Screenshot::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* jpg_image */:
+        field.get(&jpg_image_);
+        break;
+      case 2 /* pam_image */:
+        field.get(&pam_image_);
+        break;
+      case 3 /* ppm_image */:
+        field.get(&ppm_image_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string Screenshot::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> Screenshot::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void Screenshot::Serialize(::protozero::Message* msg) const {
+  // Field 1: jpg_image
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, jpg_image_, msg);
+  }
+
+  // Field 2: pam_image
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, pam_image_, msg);
+  }
+
+  // Field 3: ppm_image
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeString(3, ppm_image_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/source_location.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/source_location.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+SourceLocation::SourceLocation() = default;
+SourceLocation::~SourceLocation() = default;
+SourceLocation::SourceLocation(const SourceLocation&) = default;
+SourceLocation& SourceLocation::operator=(const SourceLocation&) = default;
+SourceLocation::SourceLocation(SourceLocation&&) noexcept = default;
+SourceLocation& SourceLocation::operator=(SourceLocation&&) = default;
+
+bool SourceLocation::operator==(const SourceLocation& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(file_name_, other.file_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(function_name_, other.function_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(line_number_, other.line_number_);
+}
+
+bool SourceLocation::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* iid */:
+        field.get(&iid_);
+        break;
+      case 2 /* file_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &file_name_);
+        break;
+      case 3 /* function_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &function_name_);
+        break;
+      case 4 /* line_number */:
+        field.get(&line_number_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string SourceLocation::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> SourceLocation::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void SourceLocation::Serialize(::protozero::Message* msg) const {
+  // Field 1: iid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
+  }
+
+  // Field 2: file_name
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, file_name_, msg);
+  }
+
+  // Field 3: function_name
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeString(3, function_name_, msg);
+  }
+
+  // Field 4: line_number
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, line_number_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+UnsymbolizedSourceLocation::UnsymbolizedSourceLocation() = default;
+UnsymbolizedSourceLocation::~UnsymbolizedSourceLocation() = default;
+UnsymbolizedSourceLocation::UnsymbolizedSourceLocation(const UnsymbolizedSourceLocation&) = default;
+UnsymbolizedSourceLocation& UnsymbolizedSourceLocation::operator=(const UnsymbolizedSourceLocation&) = default;
+UnsymbolizedSourceLocation::UnsymbolizedSourceLocation(UnsymbolizedSourceLocation&&) noexcept = default;
+UnsymbolizedSourceLocation& UnsymbolizedSourceLocation::operator=(UnsymbolizedSourceLocation&&) = default;
+
+bool UnsymbolizedSourceLocation::operator==(const UnsymbolizedSourceLocation& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(mapping_id_, other.mapping_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(rel_pc_, other.rel_pc_);
+}
+
+bool UnsymbolizedSourceLocation::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* iid */:
+        field.get(&iid_);
+        break;
+      case 2 /* mapping_id */:
+        field.get(&mapping_id_);
+        break;
+      case 3 /* rel_pc */:
+        field.get(&rel_pc_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string UnsymbolizedSourceLocation::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> UnsymbolizedSourceLocation::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void UnsymbolizedSourceLocation::Serialize(::protozero::Message* msg) const {
+  // Field 1: iid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
+  }
+
+  // Field 2: mapping_id
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, mapping_id_, msg);
+  }
+
+  // Field 3: rel_pc
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, rel_pc_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/state_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/state_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+StateDescriptor::StateDescriptor() = default;
+StateDescriptor::~StateDescriptor() = default;
+StateDescriptor::StateDescriptor(const StateDescriptor&) = default;
+StateDescriptor& StateDescriptor::operator=(const StateDescriptor&) = default;
+StateDescriptor::StateDescriptor(StateDescriptor&&) noexcept = default;
+StateDescriptor& StateDescriptor::operator=(StateDescriptor&&) = default;
+
+bool StateDescriptor::operator==(const StateDescriptor& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
+}
+
+bool StateDescriptor::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string StateDescriptor::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> StateDescriptor::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void StateDescriptor::Serialize(::protozero::Message* msg) const {
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/task_execution.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/task_execution.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+TaskExecution::TaskExecution() = default;
+TaskExecution::~TaskExecution() = default;
+TaskExecution::TaskExecution(const TaskExecution&) = default;
+TaskExecution& TaskExecution::operator=(const TaskExecution&) = default;
+TaskExecution::TaskExecution(TaskExecution&&) noexcept = default;
+TaskExecution& TaskExecution::operator=(TaskExecution&&) = default;
+
+bool TaskExecution::operator==(const TaskExecution& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(posted_from_iid_, other.posted_from_iid_);
+}
+
+bool TaskExecution::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* posted_from_iid */:
+        field.get(&posted_from_iid_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string TaskExecution::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> TaskExecution::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void TaskExecution::Serialize(::protozero::Message* msg) const {
+  // Field 1: posted_from_iid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, posted_from_iid_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/thread_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/thread_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+ThreadDescriptor::ThreadDescriptor() = default;
+ThreadDescriptor::~ThreadDescriptor() = default;
+ThreadDescriptor::ThreadDescriptor(const ThreadDescriptor&) = default;
+ThreadDescriptor& ThreadDescriptor::operator=(const ThreadDescriptor&) = default;
+ThreadDescriptor::ThreadDescriptor(ThreadDescriptor&&) noexcept = default;
+ThreadDescriptor& ThreadDescriptor::operator=(ThreadDescriptor&&) = default;
+
+bool ThreadDescriptor::operator==(const ThreadDescriptor& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(pid_, other.pid_)
+   && ::protozero::internal::gen_helpers::EqualsField(tid_, other.tid_)
+   && ::protozero::internal::gen_helpers::EqualsField(thread_name_, other.thread_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_thread_type_, other.chrome_thread_type_)
+   && ::protozero::internal::gen_helpers::EqualsField(reference_timestamp_us_, other.reference_timestamp_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(reference_thread_time_us_, other.reference_thread_time_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(reference_thread_instruction_count_, other.reference_thread_instruction_count_)
+   && ::protozero::internal::gen_helpers::EqualsField(legacy_sort_index_, other.legacy_sort_index_);
+}
+
+bool ThreadDescriptor::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* pid */:
+        field.get(&pid_);
+        break;
+      case 2 /* tid */:
+        field.get(&tid_);
+        break;
+      case 5 /* thread_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &thread_name_);
+        break;
+      case 4 /* chrome_thread_type */:
+        field.get(&chrome_thread_type_);
+        break;
+      case 6 /* reference_timestamp_us */:
+        field.get(&reference_timestamp_us_);
+        break;
+      case 7 /* reference_thread_time_us */:
+        field.get(&reference_thread_time_us_);
+        break;
+      case 8 /* reference_thread_instruction_count */:
+        field.get(&reference_thread_instruction_count_);
+        break;
+      case 3 /* legacy_sort_index */:
+        field.get(&legacy_sort_index_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string ThreadDescriptor::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> ThreadDescriptor::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void ThreadDescriptor::Serialize(::protozero::Message* msg) const {
+  // Field 1: pid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, pid_, msg);
+  }
+
+  // Field 2: tid
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, tid_, msg);
+  }
+
+  // Field 5: thread_name
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeString(5, thread_name_, msg);
+  }
+
+  // Field 4: chrome_thread_type
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, chrome_thread_type_, msg);
+  }
+
+  // Field 6: reference_timestamp_us
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, reference_timestamp_us_, msg);
+  }
+
+  // Field 7: reference_thread_time_us
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(7, reference_thread_time_us_, msg);
+  }
+
+  // Field 8: reference_thread_instruction_count
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, reference_thread_instruction_count_, msg);
+  }
+
+  // Field 3: legacy_sort_index
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, legacy_sort_index_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/track_descriptor.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/track_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/state_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/counter_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/thread_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/process_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_thread_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_process_descriptor.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+TrackDescriptor::TrackDescriptor() = default;
+TrackDescriptor::~TrackDescriptor() = default;
+TrackDescriptor::TrackDescriptor(const TrackDescriptor&) = default;
+TrackDescriptor& TrackDescriptor::operator=(const TrackDescriptor&) = default;
+TrackDescriptor::TrackDescriptor(TrackDescriptor&&) noexcept = default;
+TrackDescriptor& TrackDescriptor::operator=(TrackDescriptor&&) = default;
+
+bool TrackDescriptor::operator==(const TrackDescriptor& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(uuid_, other.uuid_)
+   && ::protozero::internal::gen_helpers::EqualsField(parent_uuid_, other.parent_uuid_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(static_name_, other.static_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(atrace_name_, other.atrace_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(description_, other.description_)
+   && ::protozero::internal::gen_helpers::EqualsField(process_, other.process_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_process_, other.chrome_process_)
+   && ::protozero::internal::gen_helpers::EqualsField(thread_, other.thread_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_thread_, other.chrome_thread_)
+   && ::protozero::internal::gen_helpers::EqualsField(counter_, other.counter_)
+   && ::protozero::internal::gen_helpers::EqualsField(state_, other.state_)
+   && ::protozero::internal::gen_helpers::EqualsField(disallow_merging_with_system_tracks_, other.disallow_merging_with_system_tracks_)
+   && ::protozero::internal::gen_helpers::EqualsField(child_ordering_, other.child_ordering_)
+   && ::protozero::internal::gen_helpers::EqualsField(sibling_order_rank_, other.sibling_order_rank_)
+   && ::protozero::internal::gen_helpers::EqualsField(sibling_merge_behavior_, other.sibling_merge_behavior_)
+   && ::protozero::internal::gen_helpers::EqualsField(sibling_merge_key_, other.sibling_merge_key_)
+   && ::protozero::internal::gen_helpers::EqualsField(sibling_merge_key_int_, other.sibling_merge_key_int_)
+   && ::protozero::internal::gen_helpers::EqualsField(process_ordering_, other.process_ordering_)
+   && ::protozero::internal::gen_helpers::EqualsField(thread_ordering_, other.thread_ordering_);
+}
+
+bool TrackDescriptor::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* uuid */:
+        field.get(&uuid_);
+        break;
+      case 5 /* parent_uuid */:
+        field.get(&parent_uuid_);
+        break;
+      case 2 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      case 10 /* static_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &static_name_);
+        break;
+      case 13 /* atrace_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &atrace_name_);
+        break;
+      case 14 /* description */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &description_);
+        break;
+      case 3 /* process */:
+        (*process_).ParseFromArray(field.data(), field.size());
+        break;
+      case 6 /* chrome_process */:
+        (*chrome_process_).ParseFromArray(field.data(), field.size());
+        break;
+      case 4 /* thread */:
+        (*thread_).ParseFromArray(field.data(), field.size());
+        break;
+      case 7 /* chrome_thread */:
+        (*chrome_thread_).ParseFromArray(field.data(), field.size());
+        break;
+      case 8 /* counter */:
+        (*counter_).ParseFromArray(field.data(), field.size());
+        break;
+      case 18 /* state */:
+        (*state_).ParseFromArray(field.data(), field.size());
+        break;
+      case 9 /* disallow_merging_with_system_tracks */:
+        field.get(&disallow_merging_with_system_tracks_);
+        break;
+      case 11 /* child_ordering */:
+        field.get(&child_ordering_);
+        break;
+      case 12 /* sibling_order_rank */:
+        field.get(&sibling_order_rank_);
+        break;
+      case 15 /* sibling_merge_behavior */:
+        field.get(&sibling_merge_behavior_);
+        break;
+      case 16 /* sibling_merge_key */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &sibling_merge_key_);
+        break;
+      case 17 /* sibling_merge_key_int */:
+        field.get(&sibling_merge_key_int_);
+        break;
+      case 19 /* process_ordering */:
+        field.get(&process_ordering_);
+        break;
+      case 20 /* thread_ordering */:
+        field.get(&thread_ordering_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string TrackDescriptor::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> TrackDescriptor::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void TrackDescriptor::Serialize(::protozero::Message* msg) const {
+  // Field 1: uuid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, uuid_, msg);
+  }
+
+  // Field 5: parent_uuid
+  if (_has_field_[5]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(5, parent_uuid_, msg);
+  }
+
+  // Field 2: name
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
+  }
+
+  // Field 10: static_name
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeString(10, static_name_, msg);
+  }
+
+  // Field 13: atrace_name
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeString(13, atrace_name_, msg);
+  }
+
+  // Field 14: description
+  if (_has_field_[14]) {
+    ::protozero::internal::gen_helpers::SerializeString(14, description_, msg);
+  }
+
+  // Field 3: process
+  if (_has_field_[3]) {
+    (*process_).Serialize(msg->BeginNestedMessage<::protozero::Message>(3));
+  }
+
+  // Field 6: chrome_process
+  if (_has_field_[6]) {
+    (*chrome_process_).Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
+  }
+
+  // Field 4: thread
+  if (_has_field_[4]) {
+    (*thread_).Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
+  }
+
+  // Field 7: chrome_thread
+  if (_has_field_[7]) {
+    (*chrome_thread_).Serialize(msg->BeginNestedMessage<::protozero::Message>(7));
+  }
+
+  // Field 8: counter
+  if (_has_field_[8]) {
+    (*counter_).Serialize(msg->BeginNestedMessage<::protozero::Message>(8));
+  }
+
+  // Field 18: state
+  if (_has_field_[18]) {
+    (*state_).Serialize(msg->BeginNestedMessage<::protozero::Message>(18));
+  }
+
+  // Field 9: disallow_merging_with_system_tracks
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, disallow_merging_with_system_tracks_, msg);
+  }
+
+  // Field 11: child_ordering
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(11, child_ordering_, msg);
+  }
+
+  // Field 12: sibling_order_rank
+  if (_has_field_[12]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(12, sibling_order_rank_, msg);
+  }
+
+  // Field 15: sibling_merge_behavior
+  if (_has_field_[15]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(15, sibling_merge_behavior_, msg);
+  }
+
+  // Field 16: sibling_merge_key
+  if (_has_field_[16]) {
+    ::protozero::internal::gen_helpers::SerializeString(16, sibling_merge_key_, msg);
+  }
+
+  // Field 17: sibling_merge_key_int
+  if (_has_field_[17]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(17, sibling_merge_key_int_, msg);
+  }
+
+  // Field 19: process_ordering
+  if (_has_field_[19]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(19, process_ordering_, msg);
+  }
+
+  // Field 20: thread_ordering
+  if (_has_field_[20]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(20, thread_ordering_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/trace/track_event/track_event.gen.cc
+// gen_amalgamated expanded: #include "perfetto/protozero/gen_field_helpers.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/track_event.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/source_location.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/screenshot.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_window_handle_event_info.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_user_event.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_mojo_event_info.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_message_pump.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_legacy_ipc.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_latency_info.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_keyed_service.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_histogram_sample.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_frame_reporter.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_content_settings_event_info.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_application_state_info.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/chrome_active_processes.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/task_execution.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/log_message.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/debug_annotation.gen.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+EventName::EventName() = default;
+EventName::~EventName() = default;
+EventName::EventName(const EventName&) = default;
+EventName& EventName::operator=(const EventName&) = default;
+EventName::EventName(EventName&&) noexcept = default;
+EventName& EventName::operator=(EventName&&) = default;
+
+bool EventName::operator==(const EventName& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+}
+
+bool EventName::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* iid */:
+        field.get(&iid_);
+        break;
+      case 2 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string EventName::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> EventName::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void EventName::Serialize(::protozero::Message* msg) const {
+  // Field 1: iid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
+  }
+
+  // Field 2: name
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+EventCategory::EventCategory() = default;
+EventCategory::~EventCategory() = default;
+EventCategory::EventCategory(const EventCategory&) = default;
+EventCategory& EventCategory::operator=(const EventCategory&) = default;
+EventCategory::EventCategory(EventCategory&&) noexcept = default;
+EventCategory& EventCategory::operator=(EventCategory&&) = default;
+
+bool EventCategory::operator==(const EventCategory& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(iid_, other.iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_);
+}
+
+bool EventCategory::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* iid */:
+        field.get(&iid_);
+        break;
+      case 2 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string EventCategory::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> EventCategory::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void EventCategory::Serialize(::protozero::Message* msg) const {
+  // Field 1: iid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, iid_, msg);
+  }
+
+  // Field 2: name
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, name_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+TrackEventDefaults::TrackEventDefaults() = default;
+TrackEventDefaults::~TrackEventDefaults() = default;
+TrackEventDefaults::TrackEventDefaults(const TrackEventDefaults&) = default;
+TrackEventDefaults& TrackEventDefaults::operator=(const TrackEventDefaults&) = default;
+TrackEventDefaults::TrackEventDefaults(TrackEventDefaults&&) noexcept = default;
+TrackEventDefaults& TrackEventDefaults::operator=(TrackEventDefaults&&) = default;
+
+bool TrackEventDefaults::operator==(const TrackEventDefaults& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(track_uuid_, other.track_uuid_)
+   && ::protozero::internal::gen_helpers::EqualsField(extra_counter_track_uuids_, other.extra_counter_track_uuids_)
+   && ::protozero::internal::gen_helpers::EqualsField(extra_double_counter_track_uuids_, other.extra_double_counter_track_uuids_);
+}
+
+bool TrackEventDefaults::ParseFromArray(const void* raw, size_t size) {
+  extra_counter_track_uuids_.clear();
+  extra_double_counter_track_uuids_.clear();
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 11 /* track_uuid */:
+        field.get(&track_uuid_);
+        break;
+      case 31 /* extra_counter_track_uuids */:
+        extra_counter_track_uuids_.emplace_back();
+        field.get(&extra_counter_track_uuids_.back());
+        break;
+      case 45 /* extra_double_counter_track_uuids */:
+        extra_double_counter_track_uuids_.emplace_back();
+        field.get(&extra_double_counter_track_uuids_.back());
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string TrackEventDefaults::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> TrackEventDefaults::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void TrackEventDefaults::Serialize(::protozero::Message* msg) const {
+  // Field 11: track_uuid
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(11, track_uuid_, msg);
+  }
+
+  // Field 31: extra_counter_track_uuids
+  for (auto& it : extra_counter_track_uuids_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(31, it, msg);
+  }
+
+  // Field 45: extra_double_counter_track_uuids
+  for (auto& it : extra_double_counter_track_uuids_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(45, it, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+TrackEvent::TrackEvent() = default;
+TrackEvent::~TrackEvent() = default;
+TrackEvent::TrackEvent(const TrackEvent&) = default;
+TrackEvent& TrackEvent::operator=(const TrackEvent&) = default;
+TrackEvent::TrackEvent(TrackEvent&&) noexcept = default;
+TrackEvent& TrackEvent::operator=(TrackEvent&&) = default;
+
+bool TrackEvent::operator==(const TrackEvent& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(category_iids_, other.category_iids_)
+   && ::protozero::internal::gen_helpers::EqualsField(categories_, other.categories_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_iid_, other.name_iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_, other.name_)
+   && ::protozero::internal::gen_helpers::EqualsField(type_, other.type_)
+   && ::protozero::internal::gen_helpers::EqualsField(track_uuid_, other.track_uuid_)
+   && ::protozero::internal::gen_helpers::EqualsField(counter_value_, other.counter_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(double_counter_value_, other.double_counter_value_)
+   && ::protozero::internal::gen_helpers::EqualsField(extra_counter_track_uuids_, other.extra_counter_track_uuids_)
+   && ::protozero::internal::gen_helpers::EqualsField(extra_counter_values_, other.extra_counter_values_)
+   && ::protozero::internal::gen_helpers::EqualsField(extra_double_counter_track_uuids_, other.extra_double_counter_track_uuids_)
+   && ::protozero::internal::gen_helpers::EqualsField(extra_double_counter_values_, other.extra_double_counter_values_)
+   && ::protozero::internal::gen_helpers::EqualsField(flow_ids_old_, other.flow_ids_old_)
+   && ::protozero::internal::gen_helpers::EqualsField(flow_ids_, other.flow_ids_)
+   && ::protozero::internal::gen_helpers::EqualsField(terminating_flow_ids_old_, other.terminating_flow_ids_old_)
+   && ::protozero::internal::gen_helpers::EqualsField(terminating_flow_ids_, other.terminating_flow_ids_)
+   && ::protozero::internal::gen_helpers::EqualsField(correlation_id_, other.correlation_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(correlation_id_str_, other.correlation_id_str_)
+   && ::protozero::internal::gen_helpers::EqualsField(correlation_id_str_iid_, other.correlation_id_str_iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(callstack_, other.callstack_)
+   && ::protozero::internal::gen_helpers::EqualsField(callstack_iid_, other.callstack_iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(callstack_weight_, other.callstack_weight_)
+   && ::protozero::internal::gen_helpers::EqualsField(debug_annotations_, other.debug_annotations_)
+   && ::protozero::internal::gen_helpers::EqualsField(task_execution_, other.task_execution_)
+   && ::protozero::internal::gen_helpers::EqualsField(log_message_, other.log_message_)
+   && ::protozero::internal::gen_helpers::EqualsField(cc_scheduler_state_, other.cc_scheduler_state_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_user_event_, other.chrome_user_event_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_keyed_service_, other.chrome_keyed_service_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_legacy_ipc_, other.chrome_legacy_ipc_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_histogram_sample_, other.chrome_histogram_sample_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_latency_info_, other.chrome_latency_info_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_frame_reporter_, other.chrome_frame_reporter_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_application_state_info_, other.chrome_application_state_info_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_renderer_scheduler_state_, other.chrome_renderer_scheduler_state_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_window_handle_event_info_, other.chrome_window_handle_event_info_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_content_settings_event_info_, other.chrome_content_settings_event_info_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_active_processes_, other.chrome_active_processes_)
+   && ::protozero::internal::gen_helpers::EqualsField(screenshot_, other.screenshot_)
+   && ::protozero::internal::gen_helpers::EqualsField(source_location_, other.source_location_)
+   && ::protozero::internal::gen_helpers::EqualsField(source_location_iid_, other.source_location_iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_message_pump_, other.chrome_message_pump_)
+   && ::protozero::internal::gen_helpers::EqualsField(chrome_mojo_event_info_, other.chrome_mojo_event_info_)
+   && ::protozero::internal::gen_helpers::EqualsField(timestamp_delta_us_, other.timestamp_delta_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(timestamp_absolute_us_, other.timestamp_absolute_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(thread_time_delta_us_, other.thread_time_delta_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(thread_time_absolute_us_, other.thread_time_absolute_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(thread_instruction_count_delta_, other.thread_instruction_count_delta_)
+   && ::protozero::internal::gen_helpers::EqualsField(thread_instruction_count_absolute_, other.thread_instruction_count_absolute_)
+   && ::protozero::internal::gen_helpers::EqualsField(legacy_event_, other.legacy_event_);
+}
+
+int TrackEvent::debug_annotations_size() const { return static_cast<int>(debug_annotations_.size()); }
+void TrackEvent::clear_debug_annotations() { debug_annotations_.clear(); }
+DebugAnnotation* TrackEvent::add_debug_annotations() { debug_annotations_.emplace_back(); return &debug_annotations_.back(); }
+bool TrackEvent::ParseFromArray(const void* raw, size_t size) {
+  category_iids_.clear();
+  categories_.clear();
+  extra_counter_track_uuids_.clear();
+  extra_counter_values_.clear();
+  extra_double_counter_track_uuids_.clear();
+  extra_double_counter_values_.clear();
+  flow_ids_old_.clear();
+  flow_ids_.clear();
+  terminating_flow_ids_old_.clear();
+  terminating_flow_ids_.clear();
+  debug_annotations_.clear();
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 3 /* category_iids */:
+        category_iids_.emplace_back();
+        field.get(&category_iids_.back());
+        break;
+      case 22 /* categories */:
+        categories_.emplace_back();
+        ::protozero::internal::gen_helpers::DeserializeString(field, &categories_.back());
+        break;
+      case 10 /* name_iid */:
+        field.get(&name_iid_);
+        break;
+      case 23 /* name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &name_);
+        break;
+      case 9 /* type */:
+        field.get(&type_);
+        break;
+      case 11 /* track_uuid */:
+        field.get(&track_uuid_);
+        break;
+      case 30 /* counter_value */:
+        field.get(&counter_value_);
+        break;
+      case 44 /* double_counter_value */:
+        field.get(&double_counter_value_);
+        break;
+      case 31 /* extra_counter_track_uuids */:
+        extra_counter_track_uuids_.emplace_back();
+        field.get(&extra_counter_track_uuids_.back());
+        break;
+      case 12 /* extra_counter_values */:
+        extra_counter_values_.emplace_back();
+        field.get(&extra_counter_values_.back());
+        break;
+      case 45 /* extra_double_counter_track_uuids */:
+        extra_double_counter_track_uuids_.emplace_back();
+        field.get(&extra_double_counter_track_uuids_.back());
+        break;
+      case 46 /* extra_double_counter_values */:
+        extra_double_counter_values_.emplace_back();
+        field.get(&extra_double_counter_values_.back());
+        break;
+      case 36 /* flow_ids_old */:
+        flow_ids_old_.emplace_back();
+        field.get(&flow_ids_old_.back());
+        break;
+      case 47 /* flow_ids */:
+        flow_ids_.emplace_back();
+        field.get(&flow_ids_.back());
+        break;
+      case 42 /* terminating_flow_ids_old */:
+        terminating_flow_ids_old_.emplace_back();
+        field.get(&terminating_flow_ids_old_.back());
+        break;
+      case 48 /* terminating_flow_ids */:
+        terminating_flow_ids_.emplace_back();
+        field.get(&terminating_flow_ids_.back());
+        break;
+      case 52 /* correlation_id */:
+        field.get(&correlation_id_);
+        break;
+      case 53 /* correlation_id_str */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &correlation_id_str_);
+        break;
+      case 54 /* correlation_id_str_iid */:
+        field.get(&correlation_id_str_iid_);
+        break;
+      case 55 /* callstack */:
+        (*callstack_).ParseFromArray(field.data(), field.size());
+        break;
+      case 56 /* callstack_iid */:
+        field.get(&callstack_iid_);
+        break;
+      case 57 /* callstack_weight */:
+        field.get(&callstack_weight_);
+        break;
+      case 4 /* debug_annotations */:
+        debug_annotations_.emplace_back();
+        debug_annotations_.back().ParseFromArray(field.data(), field.size());
+        break;
+      case 5 /* task_execution */:
+        (*task_execution_).ParseFromArray(field.data(), field.size());
+        break;
+      case 21 /* log_message */:
+        (*log_message_).ParseFromArray(field.data(), field.size());
+        break;
+      case 24 /* cc_scheduler_state */:
+        (*cc_scheduler_state_).ParseFromArray(field.data(), field.size());
+        break;
+      case 25 /* chrome_user_event */:
+        (*chrome_user_event_).ParseFromArray(field.data(), field.size());
+        break;
+      case 26 /* chrome_keyed_service */:
+        (*chrome_keyed_service_).ParseFromArray(field.data(), field.size());
+        break;
+      case 27 /* chrome_legacy_ipc */:
+        (*chrome_legacy_ipc_).ParseFromArray(field.data(), field.size());
+        break;
+      case 28 /* chrome_histogram_sample */:
+        (*chrome_histogram_sample_).ParseFromArray(field.data(), field.size());
+        break;
+      case 29 /* chrome_latency_info */:
+        (*chrome_latency_info_).ParseFromArray(field.data(), field.size());
+        break;
+      case 32 /* chrome_frame_reporter */:
+        (*chrome_frame_reporter_).ParseFromArray(field.data(), field.size());
+        break;
+      case 39 /* chrome_application_state_info */:
+        (*chrome_application_state_info_).ParseFromArray(field.data(), field.size());
+        break;
+      case 40 /* chrome_renderer_scheduler_state */:
+        (*chrome_renderer_scheduler_state_).ParseFromArray(field.data(), field.size());
+        break;
+      case 41 /* chrome_window_handle_event_info */:
+        (*chrome_window_handle_event_info_).ParseFromArray(field.data(), field.size());
+        break;
+      case 43 /* chrome_content_settings_event_info */:
+        (*chrome_content_settings_event_info_).ParseFromArray(field.data(), field.size());
+        break;
+      case 49 /* chrome_active_processes */:
+        (*chrome_active_processes_).ParseFromArray(field.data(), field.size());
+        break;
+      case 50 /* screenshot */:
+        (*screenshot_).ParseFromArray(field.data(), field.size());
+        break;
+      case 33 /* source_location */:
+        (*source_location_).ParseFromArray(field.data(), field.size());
+        break;
+      case 34 /* source_location_iid */:
+        field.get(&source_location_iid_);
+        break;
+      case 35 /* chrome_message_pump */:
+        (*chrome_message_pump_).ParseFromArray(field.data(), field.size());
+        break;
+      case 38 /* chrome_mojo_event_info */:
+        (*chrome_mojo_event_info_).ParseFromArray(field.data(), field.size());
+        break;
+      case 1 /* timestamp_delta_us */:
+        field.get(&timestamp_delta_us_);
+        break;
+      case 16 /* timestamp_absolute_us */:
+        field.get(&timestamp_absolute_us_);
+        break;
+      case 2 /* thread_time_delta_us */:
+        field.get(&thread_time_delta_us_);
+        break;
+      case 17 /* thread_time_absolute_us */:
+        field.get(&thread_time_absolute_us_);
+        break;
+      case 8 /* thread_instruction_count_delta */:
+        field.get(&thread_instruction_count_delta_);
+        break;
+      case 20 /* thread_instruction_count_absolute */:
+        field.get(&thread_instruction_count_absolute_);
+        break;
+      case 6 /* legacy_event */:
+        (*legacy_event_).ParseFromArray(field.data(), field.size());
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string TrackEvent::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> TrackEvent::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void TrackEvent::Serialize(::protozero::Message* msg) const {
+  // Field 3: category_iids
+  for (auto& it : category_iids_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, it, msg);
+  }
+
+  // Field 22: categories
+  for (auto& it : categories_) {
+    ::protozero::internal::gen_helpers::SerializeString(22, it, msg);
+  }
+
+  // Field 10: name_iid
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(10, name_iid_, msg);
+  }
+
+  // Field 23: name
+  if (_has_field_[23]) {
+    ::protozero::internal::gen_helpers::SerializeString(23, name_, msg);
+  }
+
+  // Field 9: type
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(9, type_, msg);
+  }
+
+  // Field 11: track_uuid
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(11, track_uuid_, msg);
+  }
+
+  // Field 30: counter_value
+  if (_has_field_[30]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(30, counter_value_, msg);
+  }
+
+  // Field 44: double_counter_value
+  if (_has_field_[44]) {
+    ::protozero::internal::gen_helpers::SerializeFixed(44, double_counter_value_, msg);
+  }
+
+  // Field 31: extra_counter_track_uuids
+  for (auto& it : extra_counter_track_uuids_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(31, it, msg);
+  }
+
+  // Field 12: extra_counter_values
+  for (auto& it : extra_counter_values_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(12, it, msg);
+  }
+
+  // Field 45: extra_double_counter_track_uuids
+  for (auto& it : extra_double_counter_track_uuids_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(45, it, msg);
+  }
+
+  // Field 46: extra_double_counter_values
+  for (auto& it : extra_double_counter_values_) {
+    ::protozero::internal::gen_helpers::SerializeFixed(46, it, msg);
+  }
+
+  // Field 36: flow_ids_old
+  for (auto& it : flow_ids_old_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(36, it, msg);
+  }
+
+  // Field 47: flow_ids
+  for (auto& it : flow_ids_) {
+    ::protozero::internal::gen_helpers::SerializeFixed(47, it, msg);
+  }
+
+  // Field 42: terminating_flow_ids_old
+  for (auto& it : terminating_flow_ids_old_) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(42, it, msg);
+  }
+
+  // Field 48: terminating_flow_ids
+  for (auto& it : terminating_flow_ids_) {
+    ::protozero::internal::gen_helpers::SerializeFixed(48, it, msg);
+  }
+
+  // Field 52: correlation_id
+  if (_has_field_[52]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(52, correlation_id_, msg);
+  }
+
+  // Field 53: correlation_id_str
+  if (_has_field_[53]) {
+    ::protozero::internal::gen_helpers::SerializeString(53, correlation_id_str_, msg);
+  }
+
+  // Field 54: correlation_id_str_iid
+  if (_has_field_[54]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(54, correlation_id_str_iid_, msg);
+  }
+
+  // Field 55: callstack
+  if (_has_field_[55]) {
+    (*callstack_).Serialize(msg->BeginNestedMessage<::protozero::Message>(55));
+  }
+
+  // Field 56: callstack_iid
+  if (_has_field_[56]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(56, callstack_iid_, msg);
+  }
+
+  // Field 57: callstack_weight
+  if (_has_field_[57]) {
+    ::protozero::internal::gen_helpers::SerializeFixed(57, callstack_weight_, msg);
+  }
+
+  // Field 4: debug_annotations
+  for (auto& it : debug_annotations_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(4));
+  }
+
+  // Field 5: task_execution
+  if (_has_field_[5]) {
+    (*task_execution_).Serialize(msg->BeginNestedMessage<::protozero::Message>(5));
+  }
+
+  // Field 21: log_message
+  if (_has_field_[21]) {
+    (*log_message_).Serialize(msg->BeginNestedMessage<::protozero::Message>(21));
+  }
+
+  // Field 24: cc_scheduler_state
+  if (_has_field_[24]) {
+    (*cc_scheduler_state_).Serialize(msg->BeginNestedMessage<::protozero::Message>(24));
+  }
+
+  // Field 25: chrome_user_event
+  if (_has_field_[25]) {
+    (*chrome_user_event_).Serialize(msg->BeginNestedMessage<::protozero::Message>(25));
+  }
+
+  // Field 26: chrome_keyed_service
+  if (_has_field_[26]) {
+    (*chrome_keyed_service_).Serialize(msg->BeginNestedMessage<::protozero::Message>(26));
+  }
+
+  // Field 27: chrome_legacy_ipc
+  if (_has_field_[27]) {
+    (*chrome_legacy_ipc_).Serialize(msg->BeginNestedMessage<::protozero::Message>(27));
+  }
+
+  // Field 28: chrome_histogram_sample
+  if (_has_field_[28]) {
+    (*chrome_histogram_sample_).Serialize(msg->BeginNestedMessage<::protozero::Message>(28));
+  }
+
+  // Field 29: chrome_latency_info
+  if (_has_field_[29]) {
+    (*chrome_latency_info_).Serialize(msg->BeginNestedMessage<::protozero::Message>(29));
+  }
+
+  // Field 32: chrome_frame_reporter
+  if (_has_field_[32]) {
+    (*chrome_frame_reporter_).Serialize(msg->BeginNestedMessage<::protozero::Message>(32));
+  }
+
+  // Field 39: chrome_application_state_info
+  if (_has_field_[39]) {
+    (*chrome_application_state_info_).Serialize(msg->BeginNestedMessage<::protozero::Message>(39));
+  }
+
+  // Field 40: chrome_renderer_scheduler_state
+  if (_has_field_[40]) {
+    (*chrome_renderer_scheduler_state_).Serialize(msg->BeginNestedMessage<::protozero::Message>(40));
+  }
+
+  // Field 41: chrome_window_handle_event_info
+  if (_has_field_[41]) {
+    (*chrome_window_handle_event_info_).Serialize(msg->BeginNestedMessage<::protozero::Message>(41));
+  }
+
+  // Field 43: chrome_content_settings_event_info
+  if (_has_field_[43]) {
+    (*chrome_content_settings_event_info_).Serialize(msg->BeginNestedMessage<::protozero::Message>(43));
+  }
+
+  // Field 49: chrome_active_processes
+  if (_has_field_[49]) {
+    (*chrome_active_processes_).Serialize(msg->BeginNestedMessage<::protozero::Message>(49));
+  }
+
+  // Field 50: screenshot
+  if (_has_field_[50]) {
+    (*screenshot_).Serialize(msg->BeginNestedMessage<::protozero::Message>(50));
+  }
+
+  // Field 33: source_location
+  if (_has_field_[33]) {
+    (*source_location_).Serialize(msg->BeginNestedMessage<::protozero::Message>(33));
+  }
+
+  // Field 34: source_location_iid
+  if (_has_field_[34]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(34, source_location_iid_, msg);
+  }
+
+  // Field 35: chrome_message_pump
+  if (_has_field_[35]) {
+    (*chrome_message_pump_).Serialize(msg->BeginNestedMessage<::protozero::Message>(35));
+  }
+
+  // Field 38: chrome_mojo_event_info
+  if (_has_field_[38]) {
+    (*chrome_mojo_event_info_).Serialize(msg->BeginNestedMessage<::protozero::Message>(38));
+  }
+
+  // Field 1: timestamp_delta_us
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, timestamp_delta_us_, msg);
+  }
+
+  // Field 16: timestamp_absolute_us
+  if (_has_field_[16]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(16, timestamp_absolute_us_, msg);
+  }
+
+  // Field 2: thread_time_delta_us
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, thread_time_delta_us_, msg);
+  }
+
+  // Field 17: thread_time_absolute_us
+  if (_has_field_[17]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(17, thread_time_absolute_us_, msg);
+  }
+
+  // Field 8: thread_instruction_count_delta
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, thread_instruction_count_delta_, msg);
+  }
+
+  // Field 20: thread_instruction_count_absolute
+  if (_has_field_[20]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(20, thread_instruction_count_absolute_, msg);
+  }
+
+  // Field 6: legacy_event
+  if (_has_field_[6]) {
+    (*legacy_event_).Serialize(msg->BeginNestedMessage<::protozero::Message>(6));
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+TrackEvent_LegacyEvent::TrackEvent_LegacyEvent() = default;
+TrackEvent_LegacyEvent::~TrackEvent_LegacyEvent() = default;
+TrackEvent_LegacyEvent::TrackEvent_LegacyEvent(const TrackEvent_LegacyEvent&) = default;
+TrackEvent_LegacyEvent& TrackEvent_LegacyEvent::operator=(const TrackEvent_LegacyEvent&) = default;
+TrackEvent_LegacyEvent::TrackEvent_LegacyEvent(TrackEvent_LegacyEvent&&) noexcept = default;
+TrackEvent_LegacyEvent& TrackEvent_LegacyEvent::operator=(TrackEvent_LegacyEvent&&) = default;
+
+bool TrackEvent_LegacyEvent::operator==(const TrackEvent_LegacyEvent& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(name_iid_, other.name_iid_)
+   && ::protozero::internal::gen_helpers::EqualsField(phase_, other.phase_)
+   && ::protozero::internal::gen_helpers::EqualsField(duration_us_, other.duration_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(thread_duration_us_, other.thread_duration_us_)
+   && ::protozero::internal::gen_helpers::EqualsField(thread_instruction_delta_, other.thread_instruction_delta_)
+   && ::protozero::internal::gen_helpers::EqualsField(unscoped_id_, other.unscoped_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(local_id_, other.local_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(global_id_, other.global_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(id_scope_, other.id_scope_)
+   && ::protozero::internal::gen_helpers::EqualsField(use_async_tts_, other.use_async_tts_)
+   && ::protozero::internal::gen_helpers::EqualsField(bind_id_, other.bind_id_)
+   && ::protozero::internal::gen_helpers::EqualsField(bind_to_enclosing_, other.bind_to_enclosing_)
+   && ::protozero::internal::gen_helpers::EqualsField(flow_direction_, other.flow_direction_)
+   && ::protozero::internal::gen_helpers::EqualsField(instant_event_scope_, other.instant_event_scope_)
+   && ::protozero::internal::gen_helpers::EqualsField(pid_override_, other.pid_override_)
+   && ::protozero::internal::gen_helpers::EqualsField(tid_override_, other.tid_override_);
+}
+
+bool TrackEvent_LegacyEvent::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* name_iid */:
+        field.get(&name_iid_);
+        break;
+      case 2 /* phase */:
+        field.get(&phase_);
+        break;
+      case 3 /* duration_us */:
+        field.get(&duration_us_);
+        break;
+      case 4 /* thread_duration_us */:
+        field.get(&thread_duration_us_);
+        break;
+      case 15 /* thread_instruction_delta */:
+        field.get(&thread_instruction_delta_);
+        break;
+      case 6 /* unscoped_id */:
+        field.get(&unscoped_id_);
+        break;
+      case 10 /* local_id */:
+        field.get(&local_id_);
+        break;
+      case 11 /* global_id */:
+        field.get(&global_id_);
+        break;
+      case 7 /* id_scope */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &id_scope_);
+        break;
+      case 9 /* use_async_tts */:
+        field.get(&use_async_tts_);
+        break;
+      case 8 /* bind_id */:
+        field.get(&bind_id_);
+        break;
+      case 12 /* bind_to_enclosing */:
+        field.get(&bind_to_enclosing_);
+        break;
+      case 13 /* flow_direction */:
+        field.get(&flow_direction_);
+        break;
+      case 14 /* instant_event_scope */:
+        field.get(&instant_event_scope_);
+        break;
+      case 18 /* pid_override */:
+        field.get(&pid_override_);
+        break;
+      case 19 /* tid_override */:
+        field.get(&tid_override_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string TrackEvent_LegacyEvent::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> TrackEvent_LegacyEvent::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void TrackEvent_LegacyEvent::Serialize(::protozero::Message* msg) const {
+  // Field 1: name_iid
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, name_iid_, msg);
+  }
+
+  // Field 2: phase
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(2, phase_, msg);
+  }
+
+  // Field 3: duration_us
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, duration_us_, msg);
+  }
+
+  // Field 4: thread_duration_us
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, thread_duration_us_, msg);
+  }
+
+  // Field 15: thread_instruction_delta
+  if (_has_field_[15]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(15, thread_instruction_delta_, msg);
+  }
+
+  // Field 6: unscoped_id
+  if (_has_field_[6]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(6, unscoped_id_, msg);
+  }
+
+  // Field 10: local_id
+  if (_has_field_[10]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(10, local_id_, msg);
+  }
+
+  // Field 11: global_id
+  if (_has_field_[11]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(11, global_id_, msg);
+  }
+
+  // Field 7: id_scope
+  if (_has_field_[7]) {
+    ::protozero::internal::gen_helpers::SerializeString(7, id_scope_, msg);
+  }
+
+  // Field 9: use_async_tts
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(9, use_async_tts_, msg);
+  }
+
+  // Field 8: bind_id
+  if (_has_field_[8]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(8, bind_id_, msg);
+  }
+
+  // Field 12: bind_to_enclosing
+  if (_has_field_[12]) {
+    ::protozero::internal::gen_helpers::SerializeTinyVarInt(12, bind_to_enclosing_, msg);
+  }
+
+  // Field 13: flow_direction
+  if (_has_field_[13]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(13, flow_direction_, msg);
+  }
+
+  // Field 14: instant_event_scope
+  if (_has_field_[14]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(14, instant_event_scope_, msg);
+  }
+
+  // Field 18: pid_override
+  if (_has_field_[18]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(18, pid_override_, msg);
+  }
+
+  // Field 19: tid_override
+  if (_has_field_[19]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(19, tid_override_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+TrackEvent_InlineCallstack::TrackEvent_InlineCallstack() = default;
+TrackEvent_InlineCallstack::~TrackEvent_InlineCallstack() = default;
+TrackEvent_InlineCallstack::TrackEvent_InlineCallstack(const TrackEvent_InlineCallstack&) = default;
+TrackEvent_InlineCallstack& TrackEvent_InlineCallstack::operator=(const TrackEvent_InlineCallstack&) = default;
+TrackEvent_InlineCallstack::TrackEvent_InlineCallstack(TrackEvent_InlineCallstack&&) noexcept = default;
+TrackEvent_InlineCallstack& TrackEvent_InlineCallstack::operator=(TrackEvent_InlineCallstack&&) = default;
+
+bool TrackEvent_InlineCallstack::operator==(const TrackEvent_InlineCallstack& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(frames_, other.frames_);
+}
+
+int TrackEvent_InlineCallstack::frames_size() const { return static_cast<int>(frames_.size()); }
+void TrackEvent_InlineCallstack::clear_frames() { frames_.clear(); }
+TrackEvent_InlineCallstack_Frame* TrackEvent_InlineCallstack::add_frames() { frames_.emplace_back(); return &frames_.back(); }
+bool TrackEvent_InlineCallstack::ParseFromArray(const void* raw, size_t size) {
+  frames_.clear();
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* frames */:
+        frames_.emplace_back();
+        frames_.back().ParseFromArray(field.data(), field.size());
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string TrackEvent_InlineCallstack::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> TrackEvent_InlineCallstack::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void TrackEvent_InlineCallstack::Serialize(::protozero::Message* msg) const {
+  // Field 1: frames
+  for (auto& it : frames_) {
+    it.Serialize(msg->BeginNestedMessage<::protozero::Message>(1));
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+TrackEvent_InlineCallstack_Frame::TrackEvent_InlineCallstack_Frame() = default;
+TrackEvent_InlineCallstack_Frame::~TrackEvent_InlineCallstack_Frame() = default;
+TrackEvent_InlineCallstack_Frame::TrackEvent_InlineCallstack_Frame(const TrackEvent_InlineCallstack_Frame&) = default;
+TrackEvent_InlineCallstack_Frame& TrackEvent_InlineCallstack_Frame::operator=(const TrackEvent_InlineCallstack_Frame&) = default;
+TrackEvent_InlineCallstack_Frame::TrackEvent_InlineCallstack_Frame(TrackEvent_InlineCallstack_Frame&&) noexcept = default;
+TrackEvent_InlineCallstack_Frame& TrackEvent_InlineCallstack_Frame::operator=(TrackEvent_InlineCallstack_Frame&&) = default;
+
+bool TrackEvent_InlineCallstack_Frame::operator==(const TrackEvent_InlineCallstack_Frame& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(function_name_, other.function_name_)
+   && ::protozero::internal::gen_helpers::EqualsField(source_file_, other.source_file_)
+   && ::protozero::internal::gen_helpers::EqualsField(line_number_, other.line_number_);
+}
+
+bool TrackEvent_InlineCallstack_Frame::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* function_name */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &function_name_);
+        break;
+      case 2 /* source_file */:
+        ::protozero::internal::gen_helpers::DeserializeString(field, &source_file_);
+        break;
+      case 3 /* line_number */:
+        field.get(&line_number_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string TrackEvent_InlineCallstack_Frame::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> TrackEvent_InlineCallstack_Frame::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void TrackEvent_InlineCallstack_Frame::Serialize(::protozero::Message* msg) const {
+  // Field 1: function_name
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeString(1, function_name_, msg);
+  }
+
+  // Field 2: source_file
+  if (_has_field_[2]) {
+    ::protozero::internal::gen_helpers::SerializeString(2, source_file_, msg);
+  }
+
+  // Field 3: line_number
+  if (_has_field_[3]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(3, line_number_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+// gen_amalgamated begin source: gen/protos/perfetto/common/semantic_type.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_aflags_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_game_intervention_list_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_input_event_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_log_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_polled_state_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_process_state_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_sdk_sysprop_guard_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/android_system_property_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/app_wakelock_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/cpu_per_uid_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/display_video_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/inputmethod_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/kernel_wakelocks_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/network_trace_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/packages_list_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/pixel_modem_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/protolog_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/surfaceflinger_layers_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/surfaceflinger_transactions_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/user_list_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/android/windowmanager_config.pbzero.cc
+// Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/ftrace/frozen_ftrace_config.pbzero.cc
 // Intentionally empty (crbug.com/998165)
 // gen_amalgamated begin source: gen/protos/perfetto/config/ftrace/ftrace_config.pbzero.cc
 // Intentionally empty (crbug.com/998165)
@@ -38336,8 +41817,12 @@ void TrackEvent_InlineCallstack_Frame::Serialize(::protozero::Message* msg) cons
 // Intentionally empty (crbug.com/998165)
 // gen_amalgamated begin source: gen/protos/perfetto/config/chrome/histogram_samples.pbzero.cc
 // Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/chrome/sampling_heap_profiler.pbzero.cc
+// Intentionally empty (crbug.com/998165)
 // gen_amalgamated begin source: gen/protos/perfetto/config/chrome/scenario_config.pbzero.cc
 // Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/config/chrome/stack_sampling_profiler.pbzero.cc
+// Intentionally empty (crbug.com/998165)
 // gen_amalgamated begin source: gen/protos/perfetto/config/chrome/system_metrics.pbzero.cc
 // Intentionally empty (crbug.com/998165)
 // gen_amalgamated begin source: gen/protos/perfetto/config/chrome/v8_config.pbzero.cc
@@ -38478,6 +41963,8 @@ void TrackEvent_InlineCallstack_Frame::Serialize(::protozero::Message* msg) cons
 // Intentionally empty (crbug.com/998165)
 // gen_amalgamated begin source: gen/protos/perfetto/trace/ftrace/perf_trace_counters.pbzero.cc
 // Intentionally empty (crbug.com/998165)
+// gen_amalgamated begin source: gen/protos/perfetto/trace/ftrace/pixel_metrics.pbzero.cc
+// Intentionally empty (crbug.com/998165)
 // gen_amalgamated begin source: gen/protos/perfetto/trace/ftrace/pixel_mm.pbzero.cc
 // Intentionally empty (crbug.com/998165)
 // gen_amalgamated begin source: gen/protos/perfetto/trace/ftrace/power.pbzero.cc
@@ -39301,7 +42788,7 @@ namespace perfetto {
 // The SMB is *not* the ultimate logging buffer seen by the Consumer. That one
 // is larger (~MBs) and not shared with Producers.
 // Each SMB is small, typically few KB. Its size is configurable by the producer
-// within a max limit of ~MB (see kMaxShmSize in tracing_service_impl.cc).
+// within a max limit of ~MB (see kMaxShmSize in tracing_service.h).
 // The SMB is partitioned into fixed-size Page(s). The size of the Pages are
 // determined by each Producer at connection time and cannot be changed.
 // Hence, different producers can have SMB(s) that have a different Page size
@@ -40512,6 +43999,55 @@ class ClientIdentity;
 // TODO: for the moment this assumes that all the calls happen on the same
 // thread/sequence. Not sure this will be the case long term in Chrome.

+// A shared memory layout and the operations that send its data to the service.
+// Each version is independent. Support for v2 does not imply support for v1.
+//
+// Each value is a single bit. Peers exchange and store a set of versions as a
+// uint32_t mask, e.g. kProtocolAbiV1 | kProtocolAbiV2.
+// A new version must take the next unused bit: 1 << 2, then 1 << 3.
+//
+// At connection setup:
+// - The producer sends the mask of versions it supports in
+//   InitializeConnection.
+// - The service returns the mask of versions that both sides support.
+// - An old peer sends no mask. This means v1 only.
+// - No common version means the connection fails.
+// The proto enum in protos/perfetto/ipc/producer_port.proto has the same
+// values.
+//
+// The common mask stays fixed for the connection. A writer must use one of
+// these versions that its data source config and target buffer permit.
+//
+// A layout change that an old peer cannot read needs a new version.
+enum ProtocolAbiVersion : uint32_t {
+  // The original protocol.
+  // - The producer and the service share one shared memory buffer (SMB) of
+  //   pages and chunks.
+  // - Writers fill chunks. The producer tells the service which chunks are
+  //   complete with CommitData() IPCs.
+  // - Lengths of nested messages that cross a chunk are patched later, usually
+  //   through CommitData().
+  // Layout: include/perfetto/ext/tracing/core/shared_memory_abi.h.
+  // Docs: https://perfetto.dev/docs/design-docs/api-and-abi#shmem-abi.
+  kProtocolAbiV1 = 1 << 0,
+
+  // Tracing v2. It is still under development and its ABI is not stable yet.
+  //
+  // The producer allocates a ring buffer:
+  // - AttachV2RingBuffer() shares it with the service, once.
+  // - Writers append chunks and never patch them. Nested messages use proto
+  //   group encoding, so no length needs a patch.
+  // - DrainV2RingBuffer() asks the service to read it. There is no CommitData()
+  //   for these chunks.
+  // A connection can use both writer types only if both versions are in the
+  // common mask. A writer sequence must use one format throughout its life.
+  // Layout: src/tracing/v2/shared_ring_buffer_abi.h.
+  // Design: RFC-0014, https://github.com/google/perfetto/discussions/4508.
+  // Ring buffer ABI: RFC-0046,
+  // https://github.com/google/perfetto/discussions/7120.
+  kProtocolAbiV2 = 1 << 1,
+};
+
 // The API for the Producer port of the Service.
 // Subclassed by:
 // 1. The tracing_service_impl.cc business logic when returning it in response
@@ -40560,6 +44096,59 @@ class PERFETTO_EXPORT_COMPONENT ProducerEndpoint {

   virtual SharedMemory* shared_memory() const = 0;

+  // Attempts to allocate and attach the producer's tracing v2 ring buffer.
+  // Call on the endpoint thread after tracing setup, if the caller selects v2.
+  // Complete this call before allowing writer threads to call
+  // CreateTraceWriterV2().
+  // - Uses |config|'s chunk size options and drain occupancy percentage.
+  //   Pass the config of the first instance that selects v2.
+  // - After the first attempt, later calls on the same connection do nothing,
+  //   even if the config differs or sizing, allocation or attachment failed.
+  //   This keeps the arbiter pointer stable for writer threads, which read it
+  //   without a lock.
+  // - Does not wait for the service's attach reply. Writers can publish while
+  //   the reply is pending.
+  // - Does nothing if the endpoint or the connection has no tracing v2.
+  virtual void InitializeV2RingBuffer(const DataSourceConfig& config);
+
+  // Attaches the producer's tracing v2 ring buffer to the service, which
+  // installs a reader for it. The producer calls this on the endpoint
+  // sequence, before any DrainV2RingBuffer().
+  // - The producer owns |memory| and keeps it mapped for its writers.
+  // - Over IPC, ProducerIPCClientImpl sends the descriptor of |memory| in the
+  //   AttachV2RingBuffer IPC. ProducerIPCService maps it and calls this on the
+  //   service's ProducerEndpointImpl.
+  // - The ring buffer must be new: the reader starts at position 0. A ring
+  //   buffer that another reader drained fails on the first drain.
+  //
+  // Results:
+  // - Accepted: the layout is valid and the reader exists. Data that the
+  //   producer published before the call is kept. The service takes a
+  //   reference to |memory| and keeps it until the producer disconnects.
+  // - Rejected: the connection and any existing attachment stay unchanged.
+  //   The service keeps no reference to |memory|.
+  //   If no ring buffer is attached, the producer can retry with a valid
+  //   mapping.
+  // - |callback| runs on the endpoint sequence, possibly inline.
+  // - |memory| refers to the caller's shared_ptr, and |callback| may reset it.
+  //   An implementation that needs the mapping after running |callback| must
+  //   copy |memory| first.
+  virtual void AttachV2RingBuffer(const std::shared_ptr<SharedMemory>& memory,
+                                  uint32_t chunk_size_bytes,
+                                  std::function<void(bool)> callback);
+
+  // Requests a drain of the tracing v2 ring buffer, the equivalent of
+  // CommitData() for the v2 protocol.
+  //
+  // - Called on the producer endpoint sequence. The call has no reply.
+  // - Without an accepted ring buffer, the call does nothing.
+  // - The service processes requests from each producer in order. Flush relies
+  //   on the drain completing before later requests, such as
+  //   NotifyDataSourceStopped().
+  //
+  // See RFC 0046: https://github.com/google/perfetto/discussions/7120.
+  virtual void DrainV2RingBuffer();
+
   // Size of shared memory buffer pages. It's always a multiple of 4K.
   // See shared_memory_abi.h
   virtual size_t shared_buffer_page_size_kb() const = 0;
@@ -40575,10 +44164,24 @@ class PERFETTO_EXPORT_COMPONENT ProducerEndpoint {
   // writer should be stored by the tracing service. This value is passed
   // upon creation of the data source (StartDataSource()) in the
   // DataSourceConfig.target_buffer().
+  //
+  // This method always creates a v1 writer, which writes into the
+  // shared memory buffer (SMB).
+  // If v1 is not in the connection's common mask, it returns a
+  // NullTraceWriter, which discards all packets.
   virtual std::unique_ptr<TraceWriter> CreateTraceWriter(
       BufferID target_buffer,
       BufferExhaustedPolicy buffer_exhausted_policy) = 0;

+  // |CreateTraceWriter| equivalent for tracing service v2.
+  // Creates a ring buffer writer, or a NullTraceWriter on failure.
+  // - Any thread can call it after InitializeV2RingBuffer() returns.
+  // - Does not initialize the ring buffer, and never falls back to v1.
+  // - Endpoints without tracing v2 support return a NullTraceWriter by default.
+  virtual std::unique_ptr<TraceWriter> CreateTraceWriterV2(
+      BufferID,
+      BufferExhaustedPolicy);
+
   // TODO(eseckler): Also expose CreateStartupTraceWriter() ?

   // In some cases you can access the producer's SharedMemoryArbiter (for
@@ -40827,6 +44430,8 @@ class PERFETTO_EXPORT_COMPONENT TracingService {
   // Default sizes used by the service implementation and client library.
   static constexpr size_t kDefaultShmPageSize = 4096ul;
   static constexpr size_t kDefaultShmSize = 256 * 1024ul;
+  // Maximum size in bytes for service created or producer provided SMBs.
+  static constexpr size_t kMaxShmSize = 32 * 1024 * 1024ul;

   enum class ProducerSMBScrapingMode {
     // Use service's default setting for SMB scraping. Currently, the default
@@ -40859,8 +44464,9 @@ class PERFETTO_EXPORT_COMPONENT TracingService {
   // specifically:
   // 1) The Service will call Producer::* methods on the Service's task runner.
   // 2) The Producer should call ProducerEndpoint::* methods only on the
-  //    service's task runner, except for ProducerEndpoint::CreateTraceWriter(),
-  //    which can be called on any thread. To disconnect just destroy the
+  //    service's task runner, except for CreateTraceWriter() and
+  //    CreateTraceWriterV2(), which can be called on any thread.
+  //    To disconnect just destroy the
   //    returned ProducerEndpoint object. It is safe to destroy the Producer
   //    once the Producer::OnDisconnect() has been invoked.
   //
@@ -40871,7 +44477,7 @@ class PERFETTO_EXPORT_COMPONENT TracingService {
   // (e.g., if the hints are unreasonably large or other sizes were configured
   // in a tracing session's config). |in_process| enables the ProducerEndpoint
   // to manage its own shared memory and enables use of
-  // |ProducerEndpoint::CreateTraceWriter|.
+  // |ProducerEndpoint::CreateTraceWriter| and |CreateTraceWriterV2|.
   //
   // The producer can optionally provide a non-null |shm|, which the service
   // will adopt for the connection to the producer, provided it is correctly
@@ -40888,6 +44494,9 @@ class PERFETTO_EXPORT_COMPONENT TracingService {
   // Producer::StartDataSource(). The |shm| will also be rejected when
   // connecting to a service that is too old (pre Android-11).
   //
+  // |protocol_abi_versions| is a bitmask of the ProtocolAbiVersion values that
+  // both peers support. It must not be zero. The default is v1 only.
+  //
   // Can return null in the unlikely event that service has too many producers
   // connected.
   virtual std::unique_ptr<ProducerEndpoint> ConnectProducer(
@@ -40901,7 +44510,8 @@ class PERFETTO_EXPORT_COMPONENT TracingService {
       size_t shared_memory_page_size_hint_bytes = 0,
       std::unique_ptr<SharedMemory> shm = nullptr,
       const std::string& sdk_version = {},
-      const std::string& machine_name = {}) = 0;
+      const std::string& machine_name = {},
+      uint32_t protocol_abi_versions = kProtocolAbiV1) = 0;

   // Connects a Consumer instance and obtains a ConsumerEndpoint, which is
   // essentially a 1:1 channel between one Consumer and the Service.
@@ -41120,6 +44730,23 @@ class PERFETTO_EXPORT_COMPONENT SharedMemoryArbiter {
   // true.
   virtual bool TryShutdown() = 0;

+  // WriterIDs for tracing v2 ring buffer writers. Any thread can call these.
+  //
+  // Why v2 writers take their ID from this arbiter:
+  // - The service keys each sequence by (producer, WriterID). v1 and v2
+  //   writers of one producer share that key space, so they share one pool.
+  // - A live ID makes TryShutdown() fail. This keeps the endpoint and the
+  //   ring buffer alive until the last v2 writer is destroyed.
+  //
+  // Unlike CreateTraceWriter(), these do not call RegisterTraceWriter(): each
+  // ring buffer chunk names its own target buffer.
+  //
+  // Returns zero after shutdown or when all IDs are in use.
+  virtual WriterID AllocateTracingV2WriterID() = 0;
+  // Returns the ID to the pool. Call it after the writer's final publication:
+  // another writer can get the ID at once, and TryShutdown() can succeed.
+  virtual void ReleaseTracingV2WriterID(WriterID) = 0;
+
   // Create a bound arbiter instance. Args:
   // |SharedMemory|: the shared memory buffer to use.
   // |page_size|: a multiple of 4KB that defines the granularity of tracing
@@ -41409,6 +45036,8 @@ class SharedMemoryArbiterImpl : public SharedMemoryArbiter {
   void ScrapeEmulatedSharedMemoryBuffer(
       const std::map<WriterID, BufferID>& buffer_for_writers) override;
   bool TryShutdown() override;
+  WriterID AllocateTracingV2WriterID() override;
+  void ReleaseTracingV2WriterID(WriterID) override;

   base::TaskRunner* task_runner() const { return task_runner_; }
   size_t page_size() const { return shmem_abi_.page_size(); }
@@ -42421,6 +46050,12 @@ bool SharedMemoryArbiterImpl::TryDirectPatchLocked(
   for (auto ctm_it = chunks_to_move.rbegin(); ctm_it != chunks_to_move.rend();
        ++ctm_it) {
     uint32_t header_bitmap = shmem_abi_.GetPageHeaderBitmap(ctm_it->page());
+    // The page layout may have changed concurrently in shared memory.
+    // See b/513159433.
+    if (ctm_it->chunk() >=
+        SharedMemoryABI::GetNumChunksFromHeaderBitmap(header_bitmap)) {
+      continue;
+    }
     auto chunk_state = shmem_abi_.GetChunkStateFromHeaderBitmap(
         header_bitmap, ctm_it->chunk());
     // Note: the subset of |commit_data_req_| chunks that still need patching is
@@ -42552,6 +46187,12 @@ void SharedMemoryArbiterImpl::FlushPendingCommitDataRequests(
       // them to kChunkComplete - otherwise the service won't look at them.
       for (auto& ctm : *commit_data_req_->mutable_chunks_to_move()) {
         uint32_t header_bitmap = shmem_abi_.GetPageHeaderBitmap(ctm.page());
+        // The page layout may have changed concurrently in shared memory.
+        // See b/513159433.
+        if (ctm.chunk() >=
+            SharedMemoryABI::GetNumChunksFromHeaderBitmap(header_bitmap)) {
+          continue;
+        }
         auto chunk_state = shmem_abi_.GetChunkStateFromHeaderBitmap(
             header_bitmap, ctm.chunk());
         // Note: the subset of |commit_data_req_| chunks that still need
@@ -43021,6 +46662,16 @@ std::unique_ptr<TraceWriter> SharedMemoryArbiterImpl::CreateTraceWriterInternal(
       new TraceWriterImpl(this, id, target_buffer, buffer_exhausted_policy));
 }

+WriterID SharedMemoryArbiterImpl::AllocateTracingV2WriterID() {
+  std::lock_guard<base::MaybeRtMutex> lock(lock_);
+  return did_shutdown_ ? 0 : active_writer_ids_.Allocate();
+}
+
+void SharedMemoryArbiterImpl::ReleaseTracingV2WriterID(WriterID id) {
+  std::lock_guard<base::MaybeRtMutex> lock(lock_);
+  active_writer_ids_.Free(id);
+}
+
 void SharedMemoryArbiterImpl::ReleaseWriterID(WriterID id) {
   base::TaskRunner* task_runner = nullptr;
   base::WeakPtr<SharedMemoryArbiterImpl> weak_this;
@@ -43975,7 +47626,9 @@ class PERFETTO_EXPORT_COMPONENT Producer {
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/producer.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory_arbiter.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_writer.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/tracing_service.h"
+// gen_amalgamated expanded: #include "src/tracing/core/null_trace_writer.h"

 // gen_amalgamated expanded: #include "protos/perfetto/common/tracing_service_state.gen.h"

@@ -43999,6 +47652,26 @@ SharedMemoryArbiter::~SharedMemoryArbiter() = default;
 // TODO(primiano): make pure virtual after various 3way patches.
 void Consumer::OnSessionCloned(const OnSessionClonedArgs&) {}

+// Endpoints without tracing v2 support do not initialize a ring buffer.
+void ProducerEndpoint::InitializeV2RingBuffer(const DataSourceConfig&) {}
+
+// Endpoints without tracing v2 support reject ring buffers.
+void ProducerEndpoint::AttachV2RingBuffer(const std::shared_ptr<SharedMemory>&,
+                                          uint32_t,
+                                          std::function<void(bool)> callback) {
+  callback(false);
+}
+
+// Endpoints without tracing v2 support ignore drain requests.
+void ProducerEndpoint::DrainV2RingBuffer() {}
+
+// Endpoints without tracing v2 support discard v2 packets.
+std::unique_ptr<TraceWriter> ProducerEndpoint::CreateTraceWriterV2(
+    BufferID,
+    BufferExhaustedPolicy) {
+  return std::make_unique<NullTraceWriter>();
+}
+
 }  // namespace perfetto
 // gen_amalgamated begin source: src/tracing/console_interceptor.cc
 /*
@@ -44522,6 +48195,7 @@ bool DataSourceBase::CanAdoptStartupSession(
   DataSourceConfig service_config_stripped = service_config;

   startup_config_stripped.set_target_buffer(0);
+  startup_config_stripped.set_supports_tracing_v2(false);
   startup_config_stripped.set_tracing_session_id(0);
   startup_config_stripped.set_session_initiator(
       DataSourceConfig::SESSION_INITIATOR_UNSPECIFIED);
@@ -44530,6 +48204,7 @@ bool DataSourceBase::CanAdoptStartupSession(
   startup_config_stripped.set_enable_extra_guardrails(false);

   service_config_stripped.set_target_buffer(0);
+  service_config_stripped.set_supports_tracing_v2(false);
   service_config_stripped.set_tracing_session_id(0);
   service_config_stripped.set_session_initiator(
       DataSourceConfig::SESSION_INITIATOR_UNSPECIFIED);
@@ -44703,17 +48378,437 @@ struct PERFETTO_EXPORT_COMPONENT InternedDebugAnnotationValueTypeName
           SmallInternedDataTraits> {
   ~InternedDebugAnnotationValueTypeName() override;

-  static void Add(protos::pbzero::InternedData* interned_data,
-                  size_t iid,
-                  const char* value);
+  static void Add(protos::pbzero::InternedData* interned_data,
+                  size_t iid,
+                  const char* value);
+};
+
+}  // namespace internal
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_INTERNED_FIELDS_H_
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/tracing/event_context.h"
+
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_interned_fields.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
+
+namespace perfetto {
+
+EventContext::EventContext(
+    EventContext::TracePacketHandle trace_packet,
+    internal::TrackEventIncrementalState* incremental_state,
+    internal::TrackEventTlsState* tls_state)
+    : trace_packet_(std::move(trace_packet)),
+      event_(trace_packet_->set_track_event()),
+      incremental_state_(incremental_state),
+      tls_state_(tls_state) {}
+
+EventContext::~EventContext() {
+  if (!trace_packet_)
+    return;
+
+  // When the track event is finalized (i.e., the context is destroyed), we
+  // should flush any newly seen interned data to the trace. The data has
+  // earlier been written to a heap allocated protobuf message
+  // (|serialized_interned_data|). Here we just need to flush it to the main
+  // trace.
+  auto& serialized_interned_data = incremental_state_->serialized_interned_data;
+  if (PERFETTO_UNLIKELY(!serialized_interned_data.empty())) {
+    auto ranges = serialized_interned_data.GetRanges();
+    trace_packet_->AppendScatteredBytes(
+        perfetto::protos::pbzero::TracePacket::kInternedDataFieldNumber,
+        &ranges[0], ranges.size());
+
+    // Reset the message but keep one buffer allocated for future use.
+    serialized_interned_data.Reset();
+  }
+}
+
+protos::pbzero::DebugAnnotation* EventContext::AddDebugAnnotation(
+    const char* name) {
+  auto annotation = event()->add_debug_annotations();
+  annotation->set_name_iid(
+      internal::InternedDebugAnnotationName::Get(this, name));
+  return annotation;
+}
+
+protos::pbzero::DebugAnnotation* EventContext::AddDebugAnnotation(
+    ::perfetto::DynamicString name) {
+  auto annotation = event()->add_debug_annotations();
+  annotation->set_name(name.value);
+  return annotation;
+}
+
+TrackEventTlsStateUserData* EventContext::GetTlsUserData(const void* key) {
+  PERFETTO_CHECK(tls_state_);
+  PERFETTO_CHECK(key);
+  auto it = tls_state_->user_data.find(key);
+  if (it != tls_state_->user_data.end()) {
+    return it->second.get();
+  }
+  return nullptr;
+}
+
+void EventContext::SetTlsUserData(
+    const void* key,
+    std::unique_ptr<TrackEventTlsStateUserData> data) {
+  PERFETTO_CHECK(tls_state_);
+  PERFETTO_CHECK(key);
+  tls_state_->user_data[key] = std::move(data);
+}
+
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/interceptor.cc
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/tracing/interceptor.h"
+
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_muxer.h"
+
+namespace perfetto {
+
+InterceptorBase::~InterceptorBase() = default;
+InterceptorBase::ThreadLocalState::~ThreadLocalState() = default;
+
+// static
+void InterceptorBase::RegisterImpl(
+    const InterceptorDescriptor& descriptor,
+    std::function<std::unique_ptr<InterceptorBase>()> factory,
+    InterceptorBase::TLSFactory tls_factory,
+    InterceptorBase::TracePacketCallback on_trace_packet) {
+  auto* tracing_impl = internal::TracingMuxer::Get();
+  tracing_impl->RegisterInterceptor(descriptor, factory, tls_factory,
+                                    on_trace_packet);
+}
+
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/internal/checked_scope.cc
+/*
+ * Copyright (C) 2021 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.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/checked_scope.h"
+
+#include <utility>
+
+namespace perfetto {
+namespace internal {
+
+#if PERFETTO_DCHECK_IS_ON()
+CheckedScope::CheckedScope(CheckedScope* parent_scope)
+    : parent_scope_(parent_scope) {
+  if (parent_scope_) {
+    PERFETTO_DCHECK(parent_scope_->is_active());
+    parent_scope_->set_is_active(false);
+  }
+}
+
+CheckedScope::~CheckedScope() {
+  Reset();
+}
+
+void CheckedScope::Reset() {
+  if (!is_active_) {
+    // The only case when inactive scope could be destroyed is when Reset() was
+    // called explicitly or the contents of the object were moved away.
+    PERFETTO_DCHECK(deleted_);
+    return;
+  }
+  is_active_ = false;
+  deleted_ = true;
+  if (parent_scope_)
+    parent_scope_->set_is_active(true);
+}
+
+CheckedScope::CheckedScope(CheckedScope&& other) {
+  *this = std::move(other);
+}
+
+CheckedScope& CheckedScope::operator=(CheckedScope&& other) {
+  is_active_ = other.is_active_;
+  parent_scope_ = other.parent_scope_;
+  deleted_ = other.deleted_;
+
+  other.is_active_ = false;
+  other.parent_scope_ = nullptr;
+  other.deleted_ = true;
+
+  return *this;
+}
+#endif
+
+}  // namespace internal
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/internal/interceptor_trace_writer.cc
+// gen_amalgamated begin header: include/perfetto/tracing/internal/interceptor_trace_writer.h
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_TRACING_INTERNAL_INTERCEPTOR_TRACE_WRITER_H_
+#define INCLUDE_PERFETTO_TRACING_INTERNAL_INTERCEPTOR_TRACE_WRITER_H_
+
+#include <atomic>
+#include <cstdint>
+#include <functional>
+#include <memory>
+
+// gen_amalgamated expanded: #include "perfetto/protozero/message_handle.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/interceptor.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/data_source_internal.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/trace_writer_base.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+namespace internal {
+
+// A heap-backed trace writer used to reroute trace packets to an interceptor.
+class InterceptorTraceWriter : public TraceWriterBase {
+ public:
+  InterceptorTraceWriter(std::unique_ptr<InterceptorBase::ThreadLocalState> tls,
+                         InterceptorBase::TracePacketCallback packet_callback,
+                         DataSourceStaticState* static_state,
+                         uint32_t instance_index);
+  ~InterceptorTraceWriter() override;
+
+  // TraceWriterBase implementation.
+  protozero::MessageHandle<protos::pbzero::TracePacket> NewTracePacket()
+      override;
+  void FinishTracePacket() override;
+  void Flush(std::function<void()> callback = {}) override;
+  uint64_t written() const override;
+  uint64_t drop_count() const override;
+
+ private:
+  std::unique_ptr<InterceptorBase::ThreadLocalState> tls_;
+  InterceptorBase::TracePacketCallback packet_callback_;
+
+  protozero::HeapBuffered<protos::pbzero::TracePacket> cur_packet_;
+  uint64_t bytes_written_ = 0;
+
+  // Static state of the data source we are intercepting.
+  DataSourceStaticState* const static_state_;
+
+  // Index of the data source tracing session which we are intercepting
+  // (0...kMaxDataSourceInstances - 1). Used to look up this interceptor's
+  // session state (i.e., the Interceptor class instance) in the
+  // DataSourceStaticState::instances array.
+  const uint32_t instance_index_;
+
+  const uint32_t sequence_id_;
+
+  static std::atomic<uint32_t> next_sequence_id_;
+};
+
+}  // namespace internal
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_INTERCEPTOR_TRACE_WRITER_H_
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/interceptor_trace_writer.h"
+
+#include <atomic>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <utility>
+
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_writer.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message_handle.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/interceptor.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/data_source_internal.h"
+
+namespace perfetto {
+namespace internal {
+
+// static
+std::atomic<uint32_t> InterceptorTraceWriter::next_sequence_id_{};
+
+InterceptorTraceWriter::InterceptorTraceWriter(
+    std::unique_ptr<InterceptorBase::ThreadLocalState> tls,
+    InterceptorBase::TracePacketCallback packet_callback,
+    DataSourceStaticState* static_state,
+    uint32_t instance_index)
+    : tls_(std::move(tls)),
+      packet_callback_(std::move(packet_callback)),
+      static_state_(static_state),
+      instance_index_(instance_index),
+      sequence_id_(++next_sequence_id_) {}
+
+InterceptorTraceWriter::~InterceptorTraceWriter() = default;
+
+protozero::MessageHandle<protos::pbzero::TracePacket>
+InterceptorTraceWriter::NewTracePacket() {
+  Flush();
+  auto packet = TraceWriter::TracePacketHandle(cur_packet_.get());
+  packet->set_trusted_packet_sequence_id(sequence_id_);
+  return packet;
+}
+
+void InterceptorTraceWriter::Flush(std::function<void()> callback) {
+  if (!cur_packet_.empty()) {
+    InterceptorBase::TracePacketCallbackArgs args{};
+    args.static_state = static_state_;
+    args.instance_index = instance_index_;
+    args.tls = tls_.get();
+
+    const auto& slices = cur_packet_.GetSlices();
+    if (slices.size() == 1) {
+      // Fast path: the current packet fits into a single slice.
+      auto slice_range = slices.begin()->GetUsedRange();
+      args.packet_data = protozero::ConstBytes{
+          slice_range.begin,
+          static_cast<size_t>(slice_range.end - slice_range.begin)};
+      bytes_written_ += static_cast<uint64_t>(args.packet_data.size);
+      packet_callback_(std::move(args));
+    } else {
+      // Fallback: stitch together multiple slices.
+      auto stitched_data = cur_packet_.SerializeAsArray();
+      args.packet_data =
+          protozero::ConstBytes{stitched_data.data(), stitched_data.size()};
+      bytes_written_ += static_cast<uint64_t>(stitched_data.size());
+      packet_callback_(std::move(args));
+    }
+    cur_packet_.Reset();
+  }
+  if (callback)
+    callback();
+}
+
+void InterceptorTraceWriter::FinishTracePacket() {}
+
+uint64_t InterceptorTraceWriter::written() const {
+  return bytes_written_;
+}
+
+uint64_t InterceptorTraceWriter::drop_count() const {
+  return 0;
+}
+
+}  // namespace internal
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/internal/tracing_backend_fake.cc
+// gen_amalgamated begin header: include/perfetto/tracing/internal/tracing_backend_fake.h
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_TRACING_INTERNAL_TRACING_BACKEND_FAKE_H_
+#define INCLUDE_PERFETTO_TRACING_INTERNAL_TRACING_BACKEND_FAKE_H_
+
+// gen_amalgamated expanded: #include "perfetto/base/export.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/tracing_backend.h"
+
+namespace perfetto {
+namespace internal {
+
+// A built-in implementation of TracingBackend that fails any attempt to create
+// a tracing session.
+class PERFETTO_EXPORT_COMPONENT TracingBackendFake : public TracingBackend {
+ public:
+  static TracingBackend* GetInstance();
+
+  // TracingBackend implementation.
+  std::unique_ptr<ProducerEndpoint> ConnectProducer(
+      const ConnectProducerArgs&) override;
+  std::unique_ptr<ConsumerEndpoint> ConnectConsumer(
+      const ConnectConsumerArgs&) override;
+
+ private:
+  TracingBackendFake();
 };

 }  // namespace internal
 }  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_INTERNED_FIELDS_H_
+#endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACING_BACKEND_FAKE_H_
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -44728,81 +48823,163 @@ struct PERFETTO_EXPORT_COMPONENT InternedDebugAnnotationValueTypeName
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/tracing/event_context.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_backend_fake.h"

-// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_interned_fields.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
+// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/weak_ptr.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/consumer.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/producer.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_writer.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/tracing_service.h"

 namespace perfetto {
+namespace internal {

-EventContext::EventContext(
-    EventContext::TracePacketHandle trace_packet,
-    internal::TrackEventIncrementalState* incremental_state,
-    internal::TrackEventTlsState* tls_state)
-    : trace_packet_(std::move(trace_packet)),
-      event_(trace_packet_->set_track_event()),
-      incremental_state_(incremental_state),
-      tls_state_(tls_state) {}
+namespace {

-EventContext::~EventContext() {
-  if (!trace_packet_)
-    return;
+class UnsupportedProducerEndpoint : public ProducerEndpoint {
+ public:
+  UnsupportedProducerEndpoint(Producer* producer, base::TaskRunner* task_runner)
+      : producer_(producer), task_runner_(task_runner) {
+    // The SDK will attempt to reconnect the producer, so instead we allow it
+    // to connect successfully, but never start any sessions.
+    auto weak_ptr = weak_ptr_factory_.GetWeakPtr();
+    task_runner_->PostTask([weak_ptr] {
+      if (weak_ptr && weak_ptr->connected_)
+        weak_ptr->producer_->OnConnect();
+    });
+  }
+  ~UnsupportedProducerEndpoint() override { Disconnect(); }

-  // When the track event is finalized (i.e., the context is destroyed), we
-  // should flush any newly seen interned data to the trace. The data has
-  // earlier been written to a heap allocated protobuf message
-  // (|serialized_interned_data|). Here we just need to flush it to the main
-  // trace.
-  auto& serialized_interned_data = incremental_state_->serialized_interned_data;
-  if (PERFETTO_UNLIKELY(!serialized_interned_data.empty())) {
-    auto ranges = serialized_interned_data.GetRanges();
-    trace_packet_->AppendScatteredBytes(
-        perfetto::protos::pbzero::TracePacket::kInternedDataFieldNumber,
-        &ranges[0], ranges.size());
+  void Disconnect() override {
+    if (!connected_)
+      return;
+    connected_ = false;
+    producer_->OnDisconnect();
+  }

-    // Reset the message but keep one buffer allocated for future use.
-    serialized_interned_data.Reset();
+  void RegisterDataSource(const DataSourceDescriptor&) override {}
+  void UpdateDataSource(const DataSourceDescriptor&) override {}
+  void UnregisterDataSource(const std::string& /*name*/) override {}
+
+  void RegisterTraceWriter(uint32_t /*writer_id*/,
+                           uint32_t /*target_buffer*/) override {}
+  void UnregisterTraceWriter(uint32_t /*writer_id*/) override {}
+
+  void CommitData(const CommitDataRequest&,
+                  CommitDataCallback callback) override {
+    if (connected_) {
+      callback();
+    }
   }
-}

-protos::pbzero::DebugAnnotation* EventContext::AddDebugAnnotation(
-    const char* name) {
-  auto annotation = event()->add_debug_annotations();
-  annotation->set_name_iid(
-      internal::InternedDebugAnnotationName::Get(this, name));
-  return annotation;
-}
+  SharedMemory* shared_memory() const override { return nullptr; }
+  size_t shared_buffer_page_size_kb() const override { return 0; }

-protos::pbzero::DebugAnnotation* EventContext::AddDebugAnnotation(
-    ::perfetto::DynamicString name) {
-  auto annotation = event()->add_debug_annotations();
-  annotation->set_name(name.value);
-  return annotation;
-}
+  std::unique_ptr<TraceWriter> CreateTraceWriter(
+      BufferID /*target_buffer*/,
+      BufferExhaustedPolicy) override {
+    return nullptr;
+  }

-TrackEventTlsStateUserData* EventContext::GetTlsUserData(const void* key) {
-  PERFETTO_CHECK(tls_state_);
-  PERFETTO_CHECK(key);
-  auto it = tls_state_->user_data.find(key);
-  if (it != tls_state_->user_data.end()) {
-    return it->second.get();
+  SharedMemoryArbiter* MaybeSharedMemoryArbiter() override { return nullptr; }
+  bool IsShmemProvidedByProducer() const override { return false; }
+
+  void NotifyFlushComplete(FlushRequestID) override {}
+  void NotifyDataSourceStarted(DataSourceInstanceID) override {}
+  void NotifyDataSourceStopped(DataSourceInstanceID) override {}
+  void ActivateTriggers(const std::vector<std::string>&) override {}
+
+  void Sync(std::function<void()> callback) override {
+    if (connected_) {
+      callback();
+    }
   }
-  return nullptr;
+
+ private:
+  Producer* const producer_;
+  base::TaskRunner* const task_runner_;
+  bool connected_ = true;
+  base::WeakPtrFactory<UnsupportedProducerEndpoint> weak_ptr_factory_{
+      this};  // Keep last.
+};
+
+class UnsupportedConsumerEndpoint : public ConsumerEndpoint {
+ public:
+  UnsupportedConsumerEndpoint(Consumer* consumer, base::TaskRunner* task_runner)
+      : consumer_(consumer), task_runner_(task_runner) {
+    // The SDK will not to reconnect the consumer, so we just disconnect it
+    // immediately, which will cancel the tracing session.
+    auto weak_this = weak_ptr_factory_.GetWeakPtr();
+    task_runner_->PostTask([weak_this] {
+      if (weak_this)
+        weak_this->consumer_->OnDisconnect();
+    });
+  }
+  ~UnsupportedConsumerEndpoint() override = default;
+
+  void EnableTracing(const TraceConfig&, base::ScopedFile) override {}
+  void ChangeTraceConfig(const TraceConfig&) override {}
+
+  void StartTracing() override {}
+  void DisableTracing() override {}
+
+  void Flush(uint32_t /*timeout_ms*/,
+             FlushCallback callback,
+             FlushFlags) override {
+    callback(/*success=*/false);
+  }
+
+  void ReadBuffers() override {}
+  void FreeBuffers() override {}
+
+  void Detach(const std::string& /*key*/) override {}
+  void Attach(const std::string& /*key*/) override {}
+
+  void GetTraceStats() override {}
+  void ObserveEvents(uint32_t /*events_mask*/) override {}
+  void QueryServiceState(QueryServiceStateArgs,
+                         QueryServiceStateCallback) override {}
+  void QueryCapabilities(QueryCapabilitiesCallback) override {}
+
+  void SaveTraceForBugreport(SaveTraceForBugreportCallback) override {}
+  void CloneSession(CloneSessionArgs) override {}
+
+ private:
+  Consumer* const consumer_;
+  base::TaskRunner* const task_runner_;
+  base::WeakPtrFactory<UnsupportedConsumerEndpoint> weak_ptr_factory_{
+      this};  // Keep last.
+};
+
+}  // namespace
+
+// static
+TracingBackend* TracingBackendFake::GetInstance() {
+  static auto* instance = new TracingBackendFake();
+  return instance;
 }

-void EventContext::SetTlsUserData(
-    const void* key,
-    std::unique_ptr<TrackEventTlsStateUserData> data) {
-  PERFETTO_CHECK(tls_state_);
-  PERFETTO_CHECK(key);
-  tls_state_->user_data[key] = std::move(data);
+TracingBackendFake::TracingBackendFake() = default;
+
+std::unique_ptr<ProducerEndpoint> TracingBackendFake::ConnectProducer(
+    const ConnectProducerArgs& args) {
+  return std::unique_ptr<ProducerEndpoint>(
+      new UnsupportedProducerEndpoint(args.producer, args.task_runner));
+}
+
+std::unique_ptr<ConsumerEndpoint> TracingBackendFake::ConnectConsumer(
+    const ConnectConsumerArgs& args) {
+  return std::unique_ptr<ConsumerEndpoint>(
+      new UnsupportedConsumerEndpoint(args.consumer, args.task_runner));
 }

+}  // namespace internal
 }  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/interceptor.cc
+// gen_amalgamated begin source: src/tracing/internal/tracing_muxer_fake.cc
+// gen_amalgamated begin header: src/tracing/internal/tracing_muxer_fake.h
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -44817,28 +48994,65 @@ void EventContext::SetTlsUserData(
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/tracing/interceptor.h"
+#ifndef SRC_TRACING_INTERNAL_TRACING_MUXER_FAKE_H_
+#define SRC_TRACING_INTERNAL_TRACING_MUXER_FAKE_H_

+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
 // gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_muxer.h"

 namespace perfetto {
+namespace internal {

-InterceptorBase::~InterceptorBase() = default;
-InterceptorBase::ThreadLocalState::~ThreadLocalState() = default;
+// An always-fail implementation of TracingMuxer. Before tracing has been
+// initialized, all muxer operations will route here and fail with a helpful
+// error message. This is to avoid introducing null checks in
+// performance-critical parts of the codebase.
+class TracingMuxerFake : public TracingMuxer {
+  class FakePlatform : public Platform {
+   public:
+    ~FakePlatform() override;
+    ThreadLocalObject* GetOrCreateThreadLocalObject() override;
+    std::unique_ptr<base::TaskRunner> CreateTaskRunner(
+        const CreateTaskRunnerArgs&) override;
+    std::string GetCurrentProcessName() override;

-// static
-void InterceptorBase::RegisterImpl(
-    const InterceptorDescriptor& descriptor,
-    std::function<std::unique_ptr<InterceptorBase>()> factory,
-    InterceptorBase::TLSFactory tls_factory,
-    InterceptorBase::TracePacketCallback on_trace_packet) {
-  auto* tracing_impl = internal::TracingMuxer::Get();
-  tracing_impl->RegisterInterceptor(descriptor, factory, tls_factory,
-                                    on_trace_packet);
-}
+    static FakePlatform instance;
+  };
+
+ public:
+  TracingMuxerFake() : TracingMuxer(&FakePlatform::instance) {}
+  ~TracingMuxerFake() override;
+
+  static constexpr TracingMuxerFake* Get() { return &instance; }
+
+  // TracingMuxer implementation.
+  bool RegisterDataSource(const DataSourceDescriptor&,
+                          DataSourceFactory,
+                          DataSourceParams,
+                          bool,
+                          DataSourceStaticState*) override;
+  void UpdateDataSourceDescriptor(const DataSourceDescriptor&,
+                                  const DataSourceStaticState*) override;
+  std::unique_ptr<TraceWriterBase> CreateTraceWriter(
+      DataSourceStaticState*,
+      uint32_t data_source_instance_index,
+      DataSourceState*,
+      BufferExhaustedPolicy buffer_exhausted_policy) override;
+  void DestroyStoppedTraceWritersForCurrentThread() override;
+  void RegisterInterceptor(const InterceptorDescriptor&,
+                           InterceptorFactory,
+                           InterceptorBase::TLSFactory,
+                           InterceptorBase::TracePacketCallback) override;
+  void ActivateTriggers(const std::vector<std::string>&, uint32_t) override;
+
+ private:
+  static TracingMuxerFake instance;
+};

+}  // namespace internal
 }  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/internal/checked_scope.cc
+
+#endif  // SRC_TRACING_INTERNAL_TRACING_MUXER_FAKE_H_
 /*
  * Copyright (C) 2021 The Android Open Source Project
  *
@@ -44855,62 +49069,88 @@ void InterceptorBase::RegisterImpl(
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/tracing/internal/checked_scope.h"
-
-#include <utility>
+// gen_amalgamated expanded: #include "src/tracing/internal/tracing_muxer_fake.h"

 namespace perfetto {
 namespace internal {
+namespace {

-#if PERFETTO_DCHECK_IS_ON()
-CheckedScope::CheckedScope(CheckedScope* parent_scope)
-    : parent_scope_(parent_scope) {
-  if (parent_scope_) {
-    PERFETTO_DCHECK(parent_scope_->is_active());
-    parent_scope_->set_is_active(false);
-  }
+PERFETTO_NORETURN void FailUninitialized() {
+  PERFETTO_FATAL(
+      "Tracing not initialized. Call perfetto::Tracing::Initialize() first.");
 }

-CheckedScope::~CheckedScope() {
-  Reset();
+}  // namespace
+
+// static
+PERFETTO_NO_DESTROY TracingMuxerFake::FakePlatform
+    TracingMuxerFake::FakePlatform::instance{};
+// static
+PERFETTO_NO_DESTROY TracingMuxerFake TracingMuxerFake::instance{};
+
+TracingMuxerFake::~TracingMuxerFake() = default;
+
+TracingMuxerFake::FakePlatform::~FakePlatform() = default;
+
+Platform::ThreadLocalObject*
+TracingMuxerFake::FakePlatform::GetOrCreateThreadLocalObject() {
+  FailUninitialized();
 }

-void CheckedScope::Reset() {
-  if (!is_active_) {
-    // The only case when inactive scope could be destroyed is when Reset() was
-    // called explicitly or the contents of the object were moved away.
-    PERFETTO_DCHECK(deleted_);
-    return;
-  }
-  is_active_ = false;
-  deleted_ = true;
-  if (parent_scope_)
-    parent_scope_->set_is_active(true);
+std::unique_ptr<base::TaskRunner>
+TracingMuxerFake::FakePlatform::CreateTaskRunner(const CreateTaskRunnerArgs&) {
+  FailUninitialized();
 }

-CheckedScope::CheckedScope(CheckedScope&& other) {
-  *this = std::move(other);
+std::string TracingMuxerFake::FakePlatform::GetCurrentProcessName() {
+  FailUninitialized();
 }

-CheckedScope& CheckedScope::operator=(CheckedScope&& other) {
-  is_active_ = other.is_active_;
-  parent_scope_ = other.parent_scope_;
-  deleted_ = other.deleted_;
+bool TracingMuxerFake::RegisterDataSource(const DataSourceDescriptor&,
+                                          DataSourceFactory,
+                                          DataSourceParams,
+                                          bool,
+                                          DataSourceStaticState*) {
+  FailUninitialized();
+}

-  other.is_active_ = false;
-  other.parent_scope_ = nullptr;
-  other.deleted_ = true;
+void TracingMuxerFake::UpdateDataSourceDescriptor(
+    const DataSourceDescriptor&,
+    const DataSourceStaticState*) {
+  FailUninitialized();
+}

-  return *this;
+std::unique_ptr<TraceWriterBase> TracingMuxerFake::CreateTraceWriter(
+    DataSourceStaticState*,
+    uint32_t,
+    DataSourceState*,
+    BufferExhaustedPolicy) {
+  FailUninitialized();
+}
+
+void TracingMuxerFake::DestroyStoppedTraceWritersForCurrentThread() {
+  FailUninitialized();
+}
+
+void TracingMuxerFake::RegisterInterceptor(
+    const InterceptorDescriptor&,
+    InterceptorFactory,
+    InterceptorBase::TLSFactory,
+    InterceptorBase::TracePacketCallback) {
+  FailUninitialized();
+}
+
+void TracingMuxerFake::ActivateTriggers(const std::vector<std::string>&,
+                                        uint32_t) {
+  FailUninitialized();
 }
-#endif

 }  // namespace internal
 }  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/internal/interceptor_trace_writer.cc
-// gen_amalgamated begin header: include/perfetto/tracing/internal/interceptor_trace_writer.h
+// gen_amalgamated begin source: src/tracing/internal/tracing_muxer_impl.cc
+// gen_amalgamated begin header: src/tracing/internal/tracing_muxer_impl.h
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -44925,386 +49165,577 @@ CheckedScope& CheckedScope::operator=(CheckedScope&& other) {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_TRACING_INTERNAL_INTERCEPTOR_TRACE_WRITER_H_
-#define INCLUDE_PERFETTO_TRACING_INTERNAL_INTERCEPTOR_TRACE_WRITER_H_
+#ifndef SRC_TRACING_INTERNAL_TRACING_MUXER_IMPL_H_
+#define SRC_TRACING_INTERNAL_TRACING_MUXER_IMPL_H_

+#include <stddef.h>
+#include <stdint.h>
+
+#include <array>
 #include <atomic>
-#include <cstdint>
+#include <bitset>
 #include <functional>
+#include <list>
+#include <map>
 #include <memory>
+#include <set>
+#include <utility>
+#include <vector>

-// gen_amalgamated expanded: #include "perfetto/protozero/message_handle.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/interceptor.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/data_source_internal.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/trace_writer_base.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/trace_packet.pbzero.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/basic_types.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/consumer.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/producer.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/tracing_service.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/backend_type.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/core/data_source_descriptor.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/core/forward_decls.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/core/trace_config.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/basic_types.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_muxer.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/tracing.h"
+
+// gen_amalgamated expanded: #include "protos/perfetto/common/interceptor_descriptor.gen.h"
+
+namespace perfetto {
+
+class ConsumerEndpoint;
+class DataSourceBase;
+class ProducerEndpoint;
+class TraceWriterBase;
+class TracingBackend;
+class TracingSession;
+struct TracingInitArgs;
+
+namespace base {
+class TaskRunner;
+}
+
+namespace shlib {
+void ResetForTesting();
+}
+
+namespace test {
+class TracingMuxerImplInternalsForTest;
+}
+
+namespace internal {
+
+struct DataSourceStaticState;
+
+// This class acts as a bridge between the public API and the TracingBackend(s).
+// It exposes a simplified view of the world to the API methods handling all the
+// bookkeeping to map data source instances and trace writers to the various
+// backends. It deals with N data sources, M backends (1 backend == 1 tracing
+// service == 1 producer connection) and T concurrent tracing sessions.
+//
+// Handing data source registration and start/stop flows [producer side]:
+// ----------------------------------------------------------------------
+// 1. The API client subclasses perfetto::DataSource and calls
+//    DataSource::Register<MyDataSource>(). In turn this calls into the
+//    TracingMuxer.
+// 2. The tracing muxer iterates through all the backends (1 backend == 1
+//    service == 1 producer connection) and registers the data source on each
+//    backend.
+// 3. When any (services behind a) backend starts tracing and requests to start
+//    that specific data source, the TracingMuxerImpl constructs a new instance
+//    of MyDataSource and calls the OnStart() method.
+//
+// Controlling trace and retrieving trace data [consumer side]:
+// ------------------------------------------------------------
+// 1. The API client calls Tracing::NewTrace(), returns a RAII TracingSession
+//    object.
+// 2. NewTrace() calls into internal::TracingMuxer(Impl). TracingMuxer
+//    subclasses the TracingSession object (TracingSessionImpl) and returns it.
+// 3. The tracing muxer identifies the backend (according to the args passed to
+//    NewTrace), creates a new Consumer and connects to it.
+// 4. When the API client calls Start()/Stop()/ReadTrace() methods, the
+//    TracingMuxer forwards them to the consumer associated to the
+//    TracingSession. Likewise for callbacks coming from the consumer-side of
+//    the service.
+class TracingMuxerImpl : public TracingMuxer {
+ public:
+  // This is different than TracingSessionID because it's global across all
+  // backends. TracingSessionID is global only within the scope of one service.
+  using TracingSessionGlobalID = uint64_t;
+
+  struct RegisteredDataSource {
+    DataSourceDescriptor descriptor;
+    DataSourceFactory factory{};
+    DataSourceParams params;
+    bool no_flush = false;
+    DataSourceStaticState* static_state = nullptr;
+  };
+
+  static void InitializeInstance(const TracingInitArgs&);
+  static void ResetForTesting();
+  static void Shutdown();
+
+  // TracingMuxer implementation.
+  bool RegisterDataSource(const DataSourceDescriptor&,
+                          DataSourceFactory,
+                          DataSourceParams,
+                          bool no_flush,
+                          DataSourceStaticState*) override;
+  void UpdateDataSourceDescriptor(const DataSourceDescriptor&,
+                                  const DataSourceStaticState*) override;
+  std::unique_ptr<TraceWriterBase> CreateTraceWriter(
+      DataSourceStaticState*,
+      uint32_t data_source_instance_index,
+      DataSourceState*,
+      BufferExhaustedPolicy buffer_exhausted_policy) override;
+  void DestroyStoppedTraceWritersForCurrentThread() override;
+  void RegisterInterceptor(const InterceptorDescriptor&,
+                           InterceptorFactory,
+                           InterceptorBase::TLSFactory,
+                           InterceptorBase::TracePacketCallback) override;
+
+  void ActivateTriggers(const std::vector<std::string>&, uint32_t) override;
+
+  std::unique_ptr<TracingSession> CreateTracingSession(
+      BackendType,
+      TracingConsumerBackend* (*system_backend_factory)());
+  std::unique_ptr<StartupTracingSession> CreateStartupTracingSession(
+      const TraceConfig& config,
+      Tracing::SetupStartupTracingOpts);
+  std::unique_ptr<StartupTracingSession> CreateStartupTracingSessionBlocking(
+      const TraceConfig& config,
+      Tracing::SetupStartupTracingOpts);

-namespace perfetto {
-namespace internal {
+  // Producer-side bookkeeping methods.
+  void UpdateDataSourcesOnAllBackends();
+  void SetupDataSource(TracingBackendId,
+                       uint32_t backend_connection_id,
+                       DataSourceInstanceID,
+                       const DataSourceConfig&);
+  void StartDataSource(TracingBackendId, DataSourceInstanceID);
+  void StopDataSource_AsyncBegin(TracingBackendId, DataSourceInstanceID);
+  void ClearDataSourceIncrementalState(TracingBackendId, DataSourceInstanceID);
+  void SyncProducersForTesting();

-// A heap-backed trace writer used to reroute trace packets to an interceptor.
-class InterceptorTraceWriter : public TraceWriterBase {
- public:
-  InterceptorTraceWriter(std::unique_ptr<InterceptorBase::ThreadLocalState> tls,
-                         InterceptorBase::TracePacketCallback packet_callback,
-                         DataSourceStaticState* static_state,
-                         uint32_t instance_index);
-  ~InterceptorTraceWriter() override;
+  // Consumer-side bookkeeping methods.
+  void SetupTracingSession(TracingSessionGlobalID,
+                           const std::shared_ptr<TraceConfig>&,
+                           base::ScopedFile trace_fd = base::ScopedFile());
+  void StartTracingSession(TracingSessionGlobalID);
+  void CloneTracingSession(TracingSessionGlobalID,
+                           TracingSession::CloneTraceArgs,
+                           TracingSession::CloneTraceCallback);
+  void ChangeTracingSessionConfig(TracingSessionGlobalID, const TraceConfig&);
+  void StopTracingSession(TracingSessionGlobalID);
+  void DestroyTracingSession(TracingSessionGlobalID);
+  void FlushTracingSession(TracingSessionGlobalID,
+                           uint32_t,
+                           std::function<void(bool)>);
+  void ReadTracingSessionData(
+      TracingSessionGlobalID,
+      std::function<void(TracingSession::ReadTraceCallbackArgs)>);
+  void GetTraceStats(TracingSessionGlobalID,
+                     TracingSession::GetTraceStatsCallback);
+  void QueryServiceState(TracingSessionGlobalID,
+                         TracingSession::QueryServiceStateCallback);

-  // TraceWriterBase implementation.
-  protozero::MessageHandle<protos::pbzero::TracePacket> NewTracePacket()
-      override;
-  void FinishTracePacket() override;
-  void Flush(std::function<void()> callback = {}) override;
-  uint64_t written() const override;
-  uint64_t drop_count() const override;
+  // Sets the batching period to |batch_commits_duration_ms| on the backends
+  // with type |backend_type|.
+  void SetBatchCommitsDurationForTesting(uint32_t batch_commits_duration_ms,
+                                         BackendType backend_type);

- private:
-  std::unique_ptr<InterceptorBase::ThreadLocalState> tls_;
-  InterceptorBase::TracePacketCallback packet_callback_;
+  // Enables direct SMB patching on the backends with type |backend_type| (see
+  // SharedMemoryArbiter::EnableDirectSMBPatching). Returns true if the
+  // operation succeeded for all backends with type |backend_type|, false
+  // otherwise.
+  bool EnableDirectSMBPatchingForTesting(BackendType backend_type);

-  protozero::HeapBuffered<protos::pbzero::TracePacket> cur_packet_;
-  uint64_t bytes_written_ = 0;
+  void SetMaxProducerReconnectionsForTesting(uint32_t count);

-  // Static state of the data source we are intercepting.
-  DataSourceStaticState* const static_state_;
+ private:
+  friend class test::TracingMuxerImplInternalsForTest;
+  friend void shlib::ResetForTesting();

-  // Index of the data source tracing session which we are intercepting
-  // (0...kMaxDataSourceInstances - 1). Used to look up this interceptor's
-  // session state (i.e., the Interceptor class instance) in the
-  // DataSourceStaticState::instances array.
-  const uint32_t instance_index_;
+  // For each TracingBackend we create and register one ProducerImpl instance.
+  // This talks to the producer-side of the service, gets start/stop requests
+  // from it and routes them to the registered data sources.
+  // One ProducerImpl == one backend == one tracing service.
+  // This class is needed to disambiguate callbacks coming from different
+  // services. TracingMuxerImpl can't directly implement the Producer interface
+  // because the Producer virtual methods don't allow to identify the service.
+  class ProducerImpl : public Producer {
+   public:
+    ProducerImpl(TracingMuxerImpl*,
+                 TracingBackendId,
+                 uint32_t shmem_batch_commits_duration_ms,
+                 bool shmem_direct_patching_enabled);
+    ~ProducerImpl() override;

-  const uint32_t sequence_id_;
+    void Initialize(std::unique_ptr<ProducerEndpoint> endpoint);
+    void RegisterDataSource(const DataSourceDescriptor&,
+                            DataSourceFactory,
+                            DataSourceStaticState*);
+    void DisposeConnection();

-  static std::atomic<uint32_t> next_sequence_id_;
-};
+    // perfetto::Producer implementation.
+    void OnConnect() override;
+    void OnDisconnect() override;
+    void OnTracingSetup() override;
+    void OnStartupTracingSetup() override;
+    void SetupDataSource(DataSourceInstanceID,
+                         const DataSourceConfig&) override;
+    void StartDataSource(DataSourceInstanceID,
+                         const DataSourceConfig&) override;
+    void StopDataSource(DataSourceInstanceID) override;
+    void Flush(FlushRequestID,
+               const DataSourceInstanceID*,
+               size_t,
+               FlushFlags) override;
+    void ClearIncrementalState(const DataSourceInstanceID*, size_t) override;

-}  // namespace internal
-}  // namespace perfetto
+    bool SweepDeadServices();
+    void SendOnConnectTriggers();
+    void NotifyFlushForDataSourceDone(DataSourceInstanceID, FlushRequestID);

-#endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_INTERCEPTOR_TRACE_WRITER_H_
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+    PERFETTO_THREAD_CHECKER(thread_checker_)
+    TracingMuxerImpl* muxer_;
+    TracingBackendId const backend_id_;
+    bool connected_ = false;
+    bool did_setup_tracing_ = false;
+    bool did_setup_startup_tracing_ = false;
+    std::atomic<uint32_t> connection_id_{0};
+    uint16_t last_startup_target_buffer_reservation_ = 0;
+    bool is_producer_provided_smb_ = false;
+    bool producer_provided_smb_failed_ = false;

-// gen_amalgamated expanded: #include "perfetto/tracing/internal/interceptor_trace_writer.h"
+    const uint32_t shmem_batch_commits_duration_ms_ = 0;
+    const bool shmem_direct_patching_enabled_ = false;

-#include <atomic>
-#include <cstddef>
-#include <cstdint>
-#include <functional>
-#include <memory>
-#include <utility>
+    // Set of data sources that have been actually registered on this producer.
+    // This can be a subset of the global |data_sources_|, because data sources
+    // can register before the producer is fully connected.
+    std::bitset<kMaxDataSources> registered_data_sources_{};

-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_writer.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/message_handle.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/interceptor.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/data_source_internal.h"
+    // A collection of disconnected service endpoints. Since trace writers on
+    // arbitrary threads might continue writing data to disconnected services,
+    // we keep the old services around and periodically try to clean up ones
+    // that no longer have any writers (see SweepDeadServices).
+    std::list<std::shared_ptr<ProducerEndpoint>> dead_services_;

-namespace perfetto {
-namespace internal {
+    // Triggers that should be sent when the service connects (trigger_name,
+    // expiration).
+    std::list<std::pair<std::string, base::TimeMillis>> on_connect_triggers_;

-// static
-std::atomic<uint32_t> InterceptorTraceWriter::next_sequence_id_{};
+    std::map<FlushRequestID, std::set<DataSourceInstanceID>> pending_flushes_;

-InterceptorTraceWriter::InterceptorTraceWriter(
-    std::unique_ptr<InterceptorBase::ThreadLocalState> tls,
-    InterceptorBase::TracePacketCallback packet_callback,
-    DataSourceStaticState* static_state,
-    uint32_t instance_index)
-    : tls_(std::move(tls)),
-      packet_callback_(std::move(packet_callback)),
-      static_state_(static_state),
-      instance_index_(instance_index),
-      sequence_id_(++next_sequence_id_) {}
+    // The currently active service endpoint is maintained as an atomic shared
+    // pointer so it won't get deleted from underneath threads that are creating
+    // trace writers. At any given time one endpoint can be shared (and thus
+    // kept alive) by the |service_| pointer, an entry in |dead_services_| and
+    // as a pointer on the stack in CreateTraceWriter() (on an arbitrary
+    // thread). The endpoint is never shared outside ProducerImpl itself.
+    //
+    // WARNING: Any *write* access to this variable or any *read* access from a
+    // non-muxer thread must be done through std::atomic_{load,store} to avoid
+    // data races.
+    std::shared_ptr<ProducerEndpoint> service_;  // Keep last.
+  };

-InterceptorTraceWriter::~InterceptorTraceWriter() = default;
+  // For each TracingSession created by the API client (Tracing::NewTrace() we
+  // create and register one ConsumerImpl instance.
+  // This talks to the consumer-side of the service, gets end-of-trace and
+  // on-trace-data callbacks and routes them to the API client callbacks.
+  // This class is needed to disambiguate callbacks coming from different
+  // tracing sessions.
+  class ConsumerImpl : public Consumer {
+   public:
+    ConsumerImpl(TracingMuxerImpl*, BackendType, TracingSessionGlobalID);
+    ~ConsumerImpl() override;

-protozero::MessageHandle<protos::pbzero::TracePacket>
-InterceptorTraceWriter::NewTracePacket() {
-  Flush();
-  auto packet = TraceWriter::TracePacketHandle(cur_packet_.get());
-  packet->set_trusted_packet_sequence_id(sequence_id_);
-  return packet;
-}
+    void Initialize(std::unique_ptr<ConsumerEndpoint> endpoint);

-void InterceptorTraceWriter::Flush(std::function<void()> callback) {
-  if (!cur_packet_.empty()) {
-    InterceptorBase::TracePacketCallbackArgs args{};
-    args.static_state = static_state_;
-    args.instance_index = instance_index_;
-    args.tls = tls_.get();
+    // perfetto::Consumer implementation.
+    void OnConnect() override;
+    void OnDisconnect() override;
+    void OnTracingDisabled(const std::string& error) override;
+    void OnTraceData(std::vector<TracePacket>, bool has_more) override;
+    void OnDetach(bool success) override;
+    void OnAttach(bool success, const TraceConfig&) override;
+    void OnTraceStats(bool success, const TraceStats&) override;
+    void OnObservableEvents(const ObservableEvents&) override;
+    void OnSessionCloned(const OnSessionClonedArgs&) override;

-    const auto& slices = cur_packet_.GetSlices();
-    if (slices.size() == 1) {
-      // Fast path: the current packet fits into a single slice.
-      auto slice_range = slices.begin()->GetUsedRange();
-      args.packet_data = protozero::ConstBytes{
-          slice_range.begin,
-          static_cast<size_t>(slice_range.end - slice_range.begin)};
-      bytes_written_ += static_cast<uint64_t>(args.packet_data.size);
-      packet_callback_(std::move(args));
-    } else {
-      // Fallback: stitch together multiple slices.
-      auto stitched_data = cur_packet_.SerializeAsArray();
-      args.packet_data =
-          protozero::ConstBytes{stitched_data.data(), stitched_data.size()};
-      bytes_written_ += static_cast<uint64_t>(stitched_data.size());
-      packet_callback_(std::move(args));
-    }
-    cur_packet_.Reset();
-  }
-  if (callback)
-    callback();
-}
+    void NotifyStartComplete();
+    void NotifyError(const TracingError&);
+    void NotifyStopComplete();

-void InterceptorTraceWriter::FinishTracePacket() {}
+    // Will eventually inform the |muxer_| when it is safe to remove |this|.
+    void Disconnect();

-uint64_t InterceptorTraceWriter::written() const {
-  return bytes_written_;
-}
+    TracingMuxerImpl* muxer_;
+    BackendType const backend_type_;
+    TracingSessionGlobalID const session_id_;
+    bool connected_ = false;

-uint64_t InterceptorTraceWriter::drop_count() const {
-  return 0;
-}
+    // This is to handle the case where the Setup call from the API client
+    // arrives before the consumer has connected. In this case we keep around
+    // the config and check if we have it after connection.
+    bool start_pending_ = false;

-}  // namespace internal
-}  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/internal/tracing_backend_fake.cc
-// gen_amalgamated begin header: include/perfetto/tracing/internal/tracing_backend_fake.h
-/*
- * Copyright (C) 2021 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.
- */
+    // Similarly if the session is stopped before the consumer was connected, we
+    // need to wait until the session has started before stopping it.
+    bool stop_pending_ = false;

-#ifndef INCLUDE_PERFETTO_TRACING_INTERNAL_TRACING_BACKEND_FAKE_H_
-#define INCLUDE_PERFETTO_TRACING_INTERNAL_TRACING_BACKEND_FAKE_H_
+    // Similarly we need to buffer a call to get trace statistics if the
+    // consumer wasn't connected yet.
+    bool get_trace_stats_pending_ = false;

-// gen_amalgamated expanded: #include "perfetto/base/export.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/tracing_backend.h"
+    // Similarly we need to buffer a session cloning args if the session is
+    // cloning another sesison before the consumer was connected.
+    std::optional<ConsumerEndpoint::CloneSessionArgs> session_to_clone_;

-namespace perfetto {
-namespace internal {
+    // Whether this session was already stopped. This will happen in response to
+    // Stop{,Blocking}, but also if the service stops the session for us
+    // automatically (e.g., when there are no data sources).
+    bool stopped_ = false;

-// A built-in implementation of TracingBackend that fails any attempt to create
-// a tracing session.
-class PERFETTO_EXPORT_COMPONENT TracingBackendFake : public TracingBackend {
- public:
-  static TracingBackend* GetInstance();
+    // shared_ptr because it's posted across threads. This is to avoid copying
+    // it more than once.
+    std::shared_ptr<TraceConfig> trace_config_;
+    base::ScopedFile trace_fd_;

-  // TracingBackend implementation.
-  std::unique_ptr<ProducerEndpoint> ConnectProducer(
-      const ConnectProducerArgs&) override;
-  std::unique_ptr<ConsumerEndpoint> ConnectConsumer(
-      const ConnectConsumerArgs&) override;
+    // If the API client passes a callback to start, we should invoke this when
+    // NotifyStartComplete() is invoked.
+    std::function<void()> start_complete_callback_;

- private:
-  TracingBackendFake();
-};
+    // An internal callback used to implement StartBlocking().
+    std::function<void()> blocking_start_complete_callback_;

-}  // namespace internal
-}  // namespace perfetto
+    // If the API client passes a callback to get notification about the
+    // errors, we should invoke this when NotifyError() is invoked.
+    std::function<void(TracingError)> error_callback_;

-#endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACING_BACKEND_FAKE_H_
-/*
- * Copyright (C) 2021 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.
- */
+    // If the API client passes a callback to stop, we should invoke this when
+    // OnTracingDisabled() is invoked.
+    std::function<void()> stop_complete_callback_;

-// gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_backend_fake.h"
+    // An internal callback used to implement StopBlocking().
+    std::function<void()> blocking_stop_complete_callback_;

-// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/weak_ptr.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/consumer.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/producer.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_writer.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/tracing_service.h"
+    // Callback for a pending call to CloneTrace().
+    TracingSession::CloneTraceCallback clone_trace_callback_;

-namespace perfetto {
-namespace internal {
+    // Callback passed to ReadTrace().
+    std::function<void(TracingSession::ReadTraceCallbackArgs)>
+        read_trace_callback_;

-namespace {
+    // Callback passed to GetTraceStats().
+    TracingSession::GetTraceStatsCallback get_trace_stats_callback_;

-class UnsupportedProducerEndpoint : public ProducerEndpoint {
- public:
-  UnsupportedProducerEndpoint(Producer* producer, base::TaskRunner* task_runner)
-      : producer_(producer), task_runner_(task_runner) {
-    // The SDK will attempt to reconnect the producer, so instead we allow it
-    // to connect successfully, but never start any sessions.
-    auto weak_ptr = weak_ptr_factory_.GetWeakPtr();
-    task_runner_->PostTask([weak_ptr] {
-      if (weak_ptr && weak_ptr->connected_)
-        weak_ptr->producer_->OnConnect();
-    });
-  }
-  ~UnsupportedProducerEndpoint() override { Disconnect(); }
+    // Callback for a pending call to QueryServiceState().
+    TracingSession::QueryServiceStateCallback query_service_state_callback_;

-  void Disconnect() override {
-    if (!connected_)
-      return;
-    connected_ = false;
-    producer_->OnDisconnect();
-  }
+    // The states of all data sources in this tracing session. |true| means the
+    // data source has started tracing.
+    using DataSourceHandle = std::pair<std::string, std::string>;
+    std::map<DataSourceHandle, bool> data_source_states_;

-  void RegisterDataSource(const DataSourceDescriptor&) override {}
-  void UpdateDataSource(const DataSourceDescriptor&) override {}
-  void UnregisterDataSource(const std::string& /*name*/) override {}
+    std::unique_ptr<ConsumerEndpoint> service_;  // Keep before last.
+    PERFETTO_THREAD_CHECKER(thread_checker_)     // Keep last.
+  };

-  void RegisterTraceWriter(uint32_t /*writer_id*/,
-                           uint32_t /*target_buffer*/) override {}
-  void UnregisterTraceWriter(uint32_t /*writer_id*/) override {}
+  // This object is returned to API clients when they call
+  // Tracing::CreateTracingSession().
+  class TracingSessionImpl : public TracingSession {
+   public:
+    TracingSessionImpl(TracingMuxerImpl*, TracingSessionGlobalID, BackendType);
+    ~TracingSessionImpl() override;
+    void Setup(const TraceConfig&, int fd) override;
+    void Start() override;
+    void StartBlocking() override;
+    void CloneTrace(CloneTraceArgs args, CloneTraceCallback) override;
+    void SetOnStartCallback(std::function<void()>) override;
+    void SetOnErrorCallback(std::function<void(TracingError)>) override;
+    void Stop() override;
+    void StopBlocking() override;
+    void Flush(std::function<void(bool)>, uint32_t timeout_ms) override;
+    void ReadTrace(ReadTraceCallback) override;
+    void SetOnStopCallback(std::function<void()>) override;
+    void GetTraceStats(GetTraceStatsCallback) override;
+    void QueryServiceState(QueryServiceStateCallback) override;
+    void ChangeTraceConfig(const TraceConfig&) override;

-  void CommitData(const CommitDataRequest&,
-                  CommitDataCallback callback) override {
-    if (connected_) {
-      callback();
-    }
-  }
+   private:
+    TracingMuxerImpl* const muxer_;
+    TracingSessionGlobalID const session_id_;
+    BackendType const backend_type_;
+  };

-  SharedMemory* shared_memory() const override { return nullptr; }
-  size_t shared_buffer_page_size_kb() const override { return 0; }
+  // This object is returned to API clients when they call
+  // Tracing::SetupStartupTracing().
+  class StartupTracingSessionImpl : public StartupTracingSession {
+   public:
+    StartupTracingSessionImpl(TracingMuxerImpl*,
+                              TracingSessionGlobalID,
+                              BackendType);
+    ~StartupTracingSessionImpl() override;
+    void Abort() override;
+    void AbortBlocking() override;

-  std::unique_ptr<TraceWriter> CreateTraceWriter(
-      BufferID /*target_buffer*/,
-      BufferExhaustedPolicy) override {
-    return nullptr;
-  }
+   private:
+    TracingMuxerImpl* const muxer_;
+    TracingSessionGlobalID const session_id_;
+    BackendType backend_type_;
+  };

-  SharedMemoryArbiter* MaybeSharedMemoryArbiter() override { return nullptr; }
-  bool IsShmemProvidedByProducer() const override { return false; }
+  struct RegisteredInterceptor {
+    protos::gen::InterceptorDescriptor descriptor;
+    InterceptorFactory factory{};
+    InterceptorBase::TLSFactory tls_factory{};
+    InterceptorBase::TracePacketCallback packet_callback{};
+  };

-  void NotifyFlushComplete(FlushRequestID) override {}
-  void NotifyDataSourceStarted(DataSourceInstanceID) override {}
-  void NotifyDataSourceStopped(DataSourceInstanceID) override {}
-  void ActivateTriggers(const std::vector<std::string>&) override {}
+  struct RegisteredStartupSession {
+    TracingSessionID session_id = 0;
+    int num_unbound_data_sources = 0;

-  void Sync(std::function<void()> callback) override {
-    if (connected_) {
-      callback();
-    }
-  }
+    bool is_aborting = false;
+    int num_aborting_data_sources = 0;

- private:
-  Producer* const producer_;
-  base::TaskRunner* const task_runner_;
-  bool connected_ = true;
-  base::WeakPtrFactory<UnsupportedProducerEndpoint> weak_ptr_factory_{
-      this};  // Keep last.
-};
+    std::function<void()> on_aborted;
+    std::function<void()> on_adopted;
+  };

-class UnsupportedConsumerEndpoint : public ConsumerEndpoint {
- public:
-  UnsupportedConsumerEndpoint(Consumer* consumer, base::TaskRunner* task_runner)
-      : consumer_(consumer), task_runner_(task_runner) {
-    // The SDK will not to reconnect the consumer, so we just disconnect it
-    // immediately, which will cancel the tracing session.
-    auto weak_this = weak_ptr_factory_.GetWeakPtr();
-    task_runner_->PostTask([weak_this] {
-      if (weak_this)
-        weak_this->consumer_->OnDisconnect();
-    });
-  }
-  ~UnsupportedConsumerEndpoint() override = default;
+  struct RegisteredProducerBackend {
+    // Backends are supposed to have static lifetime.
+    TracingProducerBackend* backend = nullptr;
+    TracingBackendId id = 0;
+    BackendType type{};

-  void EnableTracing(const TraceConfig&, base::ScopedFile) override {}
-  void ChangeTraceConfig(const TraceConfig&) override {}
+    TracingBackend::ConnectProducerArgs producer_conn_args;
+    std::unique_ptr<ProducerImpl> producer;

-  void StartTracing() override {}
-  void DisableTracing() override {}
+    std::vector<RegisteredStartupSession> startup_sessions;
+  };

-  void Flush(uint32_t /*timeout_ms*/,
-             FlushCallback callback,
-             FlushFlags) override {
-    callback(/*success=*/false);
-  }
+  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;
+  };

-  void ReadBuffers() override {}
-  void FreeBuffers() override {}
+  void UpdateDataSourceOnAllBackends(RegisteredDataSource& rds,
+                                     bool is_changed);
+  explicit TracingMuxerImpl(const TracingInitArgs&);
+  void Initialize(const TracingInitArgs& args);
+  void AddBackends(const TracingInitArgs& args);
+  void AddConsumerBackend(TracingConsumerBackend* backend, BackendType type);
+  void AddProducerBackend(TracingProducerBackend* backend,
+                          BackendType type,
+                          const TracingInitArgs& args);
+  ConsumerImpl* FindConsumer(TracingSessionGlobalID session_id);
+  std::pair<ConsumerImpl*, RegisteredConsumerBackend*> FindConsumerAndBackend(
+      TracingSessionGlobalID session_id);
+  RegisteredProducerBackend* FindProducerBackendById(TracingBackendId id);
+  RegisteredProducerBackend* FindProducerBackendByType(BackendType type);
+  RegisteredConsumerBackend* FindConsumerBackendByType(BackendType type);
+  void InitializeConsumer(TracingSessionGlobalID session_id);
+  void OnConsumerDisconnected(ConsumerImpl* consumer);
+  void OnProducerDisconnected(ProducerImpl* producer);
+  void DestroyAllTraceWritersForCurrentThread();
+  // Test only method.
+  void SweepDeadBackends();

-  void Detach(const std::string& /*key*/) override {}
-  void Attach(const std::string& /*key*/) override {}
+  struct FindDataSourceRes {
+    FindDataSourceRes() = default;
+    FindDataSourceRes(DataSourceStaticState* a,
+                      DataSourceState* b,
+                      uint32_t c,
+                      bool d)
+        : static_state(a),
+          internal_state(b),
+          instance_idx(c),
+          requires_callbacks_under_lock(d) {}
+    explicit operator bool() const { return !!internal_state; }

-  void GetTraceStats() override {}
-  void ObserveEvents(uint32_t /*events_mask*/) override {}
-  void QueryServiceState(QueryServiceStateArgs,
-                         QueryServiceStateCallback) override {}
-  void QueryCapabilities(QueryCapabilitiesCallback) override {}
+    DataSourceStaticState* static_state = nullptr;
+    DataSourceState* internal_state = nullptr;
+    uint32_t instance_idx = 0;
+    bool requires_callbacks_under_lock = false;
+  };
+  FindDataSourceRes FindDataSource(TracingBackendId, DataSourceInstanceID);

-  void SaveTraceForBugreport(SaveTraceForBugreportCallback) override {}
-  void CloneSession(CloneSessionArgs) override {}
+  FindDataSourceRes SetupDataSourceImpl(
+      const RegisteredDataSource&,
+      TracingBackendId,
+      uint32_t backend_connection_id,
+      DataSourceInstanceID,
+      const DataSourceConfig&,
+      TracingSessionGlobalID startup_session_id);
+  void StartDataSourceImpl(const FindDataSourceRes&);
+  void StopDataSource_AsyncBeginImpl(const FindDataSourceRes&);
+  void StopDataSource_AsyncEnd(TracingBackendId,
+                               uint32_t backend_connection_id,
+                               DataSourceInstanceID,
+                               const FindDataSourceRes&);
+  bool FlushDataSource_AsyncBegin(TracingBackendId,
+                                  DataSourceInstanceID,
+                                  FlushRequestID,
+                                  FlushFlags);
+  void FlushDataSource_AsyncEnd(TracingBackendId,
+                                uint32_t backend_connection_id,
+                                DataSourceInstanceID,
+                                const FindDataSourceRes&,
+                                FlushRequestID);
+  void AbortStartupTracingSession(TracingSessionGlobalID, BackendType);
+  // When ResetForTesting() is executed, `cb` will be called on the calling
+  // thread and on the muxer thread.
+  void AppendResetForTestingCallback(std::function<void()> cb);

- private:
-  Consumer* const consumer_;
-  base::TaskRunner* const task_runner_;
-  base::WeakPtrFactory<UnsupportedConsumerEndpoint> weak_ptr_factory_{
-      this};  // Keep last.
-};
+  // WARNING: If you add new state here, be sure to update ResetForTesting.
+  std::unique_ptr<base::TaskRunner> task_runner_;
+  std::vector<RegisteredDataSource> data_sources_;
+  // These lists can only have one backend per BackendType. The elements are
+  // sorted by BackendType priority (see BackendTypePriority). They always
+  // contain a fake low-priority kUnspecifiedBackend at the end.
+  std::list<RegisteredProducerBackend> producer_backends_;
+  std::list<RegisteredConsumerBackend> consumer_backends_;
+  std::vector<RegisteredInterceptor> interceptors_;
+  TracingPolicy* policy_ = nullptr;

-}  // namespace
+  // Learn more at TracingInitArgs::supports_multiple_data_source_instances
+  bool supports_multiple_data_source_instances_ = true;

-// static
-TracingBackend* TracingBackendFake::GetInstance() {
-  static auto* instance = new TracingBackendFake();
-  return instance;
-}
+  std::atomic<TracingSessionGlobalID> next_tracing_session_id_{};
+  std::atomic<uint32_t> next_data_source_index_{};
+  uint32_t muxer_id_for_testing_{};

-TracingBackendFake::TracingBackendFake() = default;
+  // Maximum number of times we will try to reconnect producer backend.
+  // Should only be modified for testing purposes.
+  std::atomic<uint32_t> max_producer_reconnections_{100u};

-std::unique_ptr<ProducerEndpoint> TracingBackendFake::ConnectProducer(
-    const ConnectProducerArgs& args) {
-  return std::unique_ptr<ProducerEndpoint>(
-      new UnsupportedProducerEndpoint(args.producer, args.task_runner));
-}
+  // Test only member.
+  // After ResetForTesting() is called, holds tracing backends which needs to be
+  // kept alive until all inbound references have gone away. See
+  // SweepDeadBackends().
+  std::list<RegisteredProducerBackend> dead_backends_;

-std::unique_ptr<ConsumerEndpoint> TracingBackendFake::ConnectConsumer(
-    const ConnectConsumerArgs& args) {
-  return std::unique_ptr<ConsumerEndpoint>(
-      new UnsupportedConsumerEndpoint(args.consumer, args.task_runner));
-}
+  // Test only member.
+  // Executes these cleanup functions on the calling thread and on the muxer
+  // thread when ResetForTesting() is called.
+  std::list<std::function<void()>> reset_callbacks_;
+
+  PERFETTO_THREAD_CHECKER(thread_checker_)
+};

 }  // namespace internal
 }  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/internal/tracing_muxer_fake.cc
-// gen_amalgamated begin header: src/tracing/internal/tracing_muxer_fake.h
+
+#endif  // SRC_TRACING_INTERNAL_TRACING_MUXER_IMPL_H_
+// gen_amalgamated begin header: include/perfetto/ext/tracing/core/trace_stats.h
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -45319,67 +49750,21 @@ std::unique_ptr<ConsumerEndpoint> TracingBackendFake::ConnectConsumer(
  * limitations under the License.
  */

-#ifndef SRC_TRACING_INTERNAL_TRACING_MUXER_FAKE_H_
-#define SRC_TRACING_INTERNAL_TRACING_MUXER_FAKE_H_
-
-// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_muxer.h"
-
-namespace perfetto {
-namespace internal {
-
-// An always-fail implementation of TracingMuxer. Before tracing has been
-// initialized, all muxer operations will route here and fail with a helpful
-// error message. This is to avoid introducing null checks in
-// performance-critical parts of the codebase.
-class TracingMuxerFake : public TracingMuxer {
-  class FakePlatform : public Platform {
-   public:
-    ~FakePlatform() override;
-    ThreadLocalObject* GetOrCreateThreadLocalObject() override;
-    std::unique_ptr<base::TaskRunner> CreateTaskRunner(
-        const CreateTaskRunnerArgs&) override;
-    std::string GetCurrentProcessName() override;
-
-    static FakePlatform instance;
-  };
-
- public:
-  TracingMuxerFake() : TracingMuxer(&FakePlatform::instance) {}
-  ~TracingMuxerFake() override;
-
-  static constexpr TracingMuxerFake* Get() { return &instance; }
-
-  // TracingMuxer implementation.
-  bool RegisterDataSource(const DataSourceDescriptor&,
-                          DataSourceFactory,
-                          DataSourceParams,
-                          bool,
-                          DataSourceStaticState*) override;
-  void UpdateDataSourceDescriptor(const DataSourceDescriptor&,
-                                  const DataSourceStaticState*) override;
-  std::unique_ptr<TraceWriterBase> CreateTraceWriter(
-      DataSourceStaticState*,
-      uint32_t data_source_instance_index,
-      DataSourceState*,
-      BufferExhaustedPolicy buffer_exhausted_policy) override;
-  void DestroyStoppedTraceWritersForCurrentThread() override;
-  void RegisterInterceptor(const InterceptorDescriptor&,
-                           InterceptorFactory,
-                           InterceptorBase::TLSFactory,
-                           InterceptorBase::TracePacketCallback) override;
-  void ActivateTriggers(const std::vector<std::string>&, uint32_t) override;
+#ifndef INCLUDE_PERFETTO_EXT_TRACING_CORE_TRACE_STATS_H_
+#define INCLUDE_PERFETTO_EXT_TRACING_CORE_TRACE_STATS_H_

- private:
-  static TracingMuxerFake instance;
-};
+// Creates the aliases in the ::perfetto namespace, doing things like:
+// using ::perfetto::Foo = ::perfetto::protos::gen::Foo.
+// See comments in forward_decls.h for the historical reasons of this
+// indirection layer.
+// gen_amalgamated expanded: #include "perfetto/tracing/core/forward_decls.h"

-}  // namespace internal
-}  // namespace perfetto
+// gen_amalgamated expanded: #include "protos/perfetto/common/trace_stats.gen.h"

-#endif  // SRC_TRACING_INTERNAL_TRACING_MUXER_FAKE_H_
+#endif  // INCLUDE_PERFETTO_EXT_TRACING_CORE_TRACE_STATS_H_
+// gen_amalgamated begin header: include/perfetto/tracing/core/tracing_service_state.h
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -45394,86 +49779,18 @@ class TracingMuxerFake : public TracingMuxer {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/tracing/internal/tracing_muxer_fake.h"
-
-namespace perfetto {
-namespace internal {
-namespace {
-
-PERFETTO_NORETURN void FailUninitialized() {
-  PERFETTO_FATAL(
-      "Tracing not initialized. Call perfetto::Tracing::Initialize() first.");
-}
-
-}  // namespace
-
-// static
-PERFETTO_NO_DESTROY TracingMuxerFake::FakePlatform
-    TracingMuxerFake::FakePlatform::instance{};
-// static
-PERFETTO_NO_DESTROY TracingMuxerFake TracingMuxerFake::instance{};
-
-TracingMuxerFake::~TracingMuxerFake() = default;
-
-TracingMuxerFake::FakePlatform::~FakePlatform() = default;
-
-Platform::ThreadLocalObject*
-TracingMuxerFake::FakePlatform::GetOrCreateThreadLocalObject() {
-  FailUninitialized();
-}
-
-std::unique_ptr<base::TaskRunner>
-TracingMuxerFake::FakePlatform::CreateTaskRunner(const CreateTaskRunnerArgs&) {
-  FailUninitialized();
-}
-
-std::string TracingMuxerFake::FakePlatform::GetCurrentProcessName() {
-  FailUninitialized();
-}
-
-bool TracingMuxerFake::RegisterDataSource(const DataSourceDescriptor&,
-                                          DataSourceFactory,
-                                          DataSourceParams,
-                                          bool,
-                                          DataSourceStaticState*) {
-  FailUninitialized();
-}
-
-void TracingMuxerFake::UpdateDataSourceDescriptor(
-    const DataSourceDescriptor&,
-    const DataSourceStaticState*) {
-  FailUninitialized();
-}
-
-std::unique_ptr<TraceWriterBase> TracingMuxerFake::CreateTraceWriter(
-    DataSourceStaticState*,
-    uint32_t,
-    DataSourceState*,
-    BufferExhaustedPolicy) {
-  FailUninitialized();
-}
-
-void TracingMuxerFake::DestroyStoppedTraceWritersForCurrentThread() {
-  FailUninitialized();
-}
+#ifndef INCLUDE_PERFETTO_TRACING_CORE_TRACING_SERVICE_STATE_H_
+#define INCLUDE_PERFETTO_TRACING_CORE_TRACING_SERVICE_STATE_H_

-void TracingMuxerFake::RegisterInterceptor(
-    const InterceptorDescriptor&,
-    InterceptorFactory,
-    InterceptorBase::TLSFactory,
-    InterceptorBase::TracePacketCallback) {
-  FailUninitialized();
-}
+// Creates the aliases in the ::perfetto namespace, doing things like:
+// using ::perfetto::Foo = ::perfetto::protos::gen::Foo.
+// See comments in forward_decls.h for the historical reasons of this
+// indirection layer.
+// gen_amalgamated expanded: #include "perfetto/tracing/core/forward_decls.h"

-void TracingMuxerFake::ActivateTriggers(const std::vector<std::string>&,
-                                        uint32_t) {
-  FailUninitialized();
-}
+// gen_amalgamated expanded: #include "protos/perfetto/common/tracing_service_state.gen.h"

-}  // namespace internal
-}  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/internal/tracing_muxer_impl.cc
-// gen_amalgamated begin header: src/tracing/internal/tracing_muxer_impl.h
+#endif  // INCLUDE_PERFETTO_TRACING_CORE_TRACING_SERVICE_STATE_H_
 /*
  * Copyright (C) 2019 The Android Open Source Project
  *
@@ -45490,3476 +49807,4046 @@ void TracingMuxerFake::ActivateTriggers(const std::vector<std::string>&,
  * limitations under the License.
  */

-#ifndef SRC_TRACING_INTERNAL_TRACING_MUXER_IMPL_H_
-#define SRC_TRACING_INTERNAL_TRACING_MUXER_IMPL_H_
-
-#include <stddef.h>
-#include <stdint.h>
+// gen_amalgamated expanded: #include "src/tracing/internal/tracing_muxer_impl.h"

-#include <array>
+#include <algorithm>
 #include <atomic>
-#include <bitset>
-#include <functional>
-#include <list>
-#include <map>
-#include <memory>
-#include <set>
-#include <utility>
+#include <mutex>
+#include <optional>
 #include <vector>

+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
 // gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/fnv_hash.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/basic_types.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/consumer.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/producer.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/uuid.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/waitable_event.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory_arbiter.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_packet.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_stats.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_writer.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/tracing_service.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/backend_type.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/core/data_source_descriptor.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/core/forward_decls.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/core/trace_config.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/basic_types.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_muxer.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/buffer_exhausted_policy.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/core/data_source_config.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/core/tracing_service_state.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/data_source.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/data_source_internal.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/interceptor_trace_writer.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_backend_fake.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/trace_writer_base.h"
 // gen_amalgamated expanded: #include "perfetto/tracing/tracing.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/tracing_backend.h"
+// gen_amalgamated expanded: #include "src/tracing/core/null_trace_writer.h"
+// gen_amalgamated expanded: #include "src/tracing/internal/tracing_muxer_fake.h"

-// gen_amalgamated expanded: #include "protos/perfetto/common/interceptor_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/interceptor_config.gen.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <io.h>  // For dup()
+#else
+#include <unistd.h>  // For dup()
+#endif

 namespace perfetto {
+namespace internal {

-class ConsumerEndpoint;
-class DataSourceBase;
-class ProducerEndpoint;
-class TraceWriterBase;
-class TracingBackend;
-class TracingSession;
-struct TracingInitArgs;
+namespace {

-namespace base {
-class TaskRunner;
-}
+using RegisteredDataSource = TracingMuxerImpl::RegisteredDataSource;

-namespace shlib {
-void ResetForTesting();
+// A task runner which prevents calls to DataSource::Trace() while an operation
+// is in progress. Used to guard against unexpected re-entrancy where the
+// user-provided task runner implementation tries to enter a trace point under
+// the hood.
+class NonReentrantTaskRunner : public base::TaskRunner {
+ public:
+  NonReentrantTaskRunner(TracingMuxer* muxer,
+                         std::unique_ptr<base::TaskRunner> task_runner)
+      : muxer_(muxer), task_runner_(std::move(task_runner)) {}
+
+  // base::TaskRunner implementation.
+  void PostTask(std::function<void()> task) override {
+    CallWithGuard([&] { task_runner_->PostTask(std::move(task)); });
+  }
+
+  void PostDelayedTask(std::function<void()> task, uint32_t delay_ms) override {
+    CallWithGuard(
+        [&] { task_runner_->PostDelayedTask(std::move(task), delay_ms); });
+  }
+
+  void AddFileDescriptorWatch(base::PlatformHandle fd,
+                              std::function<void()> callback) override {
+    CallWithGuard(
+        [&] { task_runner_->AddFileDescriptorWatch(fd, std::move(callback)); });
+  }
+
+  void RemoveFileDescriptorWatch(base::PlatformHandle fd) override {
+    CallWithGuard([&] { task_runner_->RemoveFileDescriptorWatch(fd); });
+  }
+
+  bool RunsTasksOnCurrentThread() const override {
+    bool result;
+    CallWithGuard([&] { result = task_runner_->RunsTasksOnCurrentThread(); });
+    return result;
+  }
+
+ private:
+  template <typename T>
+  void CallWithGuard(T lambda) const {
+    auto* root_tls = muxer_->GetOrCreateTracingTLS();
+    if (PERFETTO_UNLIKELY(root_tls->is_in_trace_point)) {
+      lambda();
+      return;
+    }
+    ScopedReentrancyAnnotator scoped_annotator(*root_tls);
+    lambda();
+  }
+
+  TracingMuxer* const muxer_;
+  std::unique_ptr<base::TaskRunner> task_runner_;
+};
+
+class StopArgsImpl : public DataSourceBase::StopArgs {
+ public:
+  std::function<void()> HandleStopAsynchronously() const override {
+    auto closure = std::move(async_stop_closure);
+    async_stop_closure = std::function<void()>();
+    return closure;
+  }
+
+  mutable std::function<void()> async_stop_closure;
+};
+
+class FlushArgsImpl : public DataSourceBase::FlushArgs {
+ public:
+  std::function<void()> HandleFlushAsynchronously() const override {
+    auto closure = std::move(async_flush_closure);
+    async_flush_closure = std::function<void()>();
+    return closure;
+  }
+
+  mutable std::function<void()> async_flush_closure;
+};
+
+// Holds an earlier TracingMuxerImpl instance after ResetForTesting() is called.
+static TracingMuxerImpl* g_prev_instance{};
+
+template <typename RegisteredBackend>
+struct CompareBackendByType {
+  static int BackendTypePriority(BackendType type) {
+    switch (type) {
+      case kSystemBackend:
+        return 0;
+      case kInProcessBackend:
+        return 1;
+      case kCustomBackend:
+        return 2;
+      // The UnspecifiedBackend has the highest priority so that
+      // TracingBackendFake is the last one on the backend lists.
+      case kUnspecifiedBackend:
+        break;
+    }
+    return 3;
+  }
+  bool operator()(BackendType type, const RegisteredBackend& b) {
+    return BackendTypePriority(type) < BackendTypePriority(b.type);
+  }
+};
+
+BufferExhaustedPolicy ComputeBufferExhaustedPolicy(
+    const DataSourceConfig& cfg,
+    const DataSourceParams& params) {
+  if (!params.buffer_exhausted_policy_configurable) {
+    return params.default_buffer_exhausted_policy;
+  }
+
+  switch (cfg.buffer_exhausted_policy()) {
+    case DataSourceConfig::BUFFER_EXHAUSTED_UNSPECIFIED:
+      return params.default_buffer_exhausted_policy;
+    case DataSourceConfig::BUFFER_EXHAUSTED_DROP:
+      return BufferExhaustedPolicy::kDrop;
+    case DataSourceConfig::BUFFER_EXHAUSTED_STALL_THEN_ABORT:
+      return BufferExhaustedPolicy::kStall;
+    case DataSourceConfig::BUFFER_EXHAUSTED_STALL_THEN_DROP:
+      return BufferExhaustedPolicy::kStallThenDrop;
+  }
+
+  return params.default_buffer_exhausted_policy;
 }

-namespace test {
-class TracingMuxerImplInternalsForTest;
+}  // namespace
+
+// ----- Begin of TracingMuxerImpl::ProducerImpl
+TracingMuxerImpl::ProducerImpl::ProducerImpl(
+    TracingMuxerImpl* muxer,
+    TracingBackendId backend_id,
+    uint32_t shmem_batch_commits_duration_ms,
+    bool shmem_direct_patching_enabled)
+    : muxer_(muxer),
+      backend_id_(backend_id),
+      shmem_batch_commits_duration_ms_(shmem_batch_commits_duration_ms),
+      shmem_direct_patching_enabled_(shmem_direct_patching_enabled) {}
+
+TracingMuxerImpl::ProducerImpl::~ProducerImpl() {
+  muxer_ = nullptr;
 }

-namespace internal {
+void TracingMuxerImpl::ProducerImpl::Initialize(
+    std::unique_ptr<ProducerEndpoint> endpoint) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  PERFETTO_DCHECK(!connected_);
+  connection_id_.fetch_add(1, std::memory_order_relaxed);
+  is_producer_provided_smb_ = endpoint->shared_memory();
+  last_startup_target_buffer_reservation_ = 0;

-struct DataSourceStaticState;
+  // Adopt the endpoint into a shared pointer so that we can safely share it
+  // across threads that create trace writers. The custom deleter function
+  // ensures that the endpoint is always destroyed on the muxer's thread. (Note
+  // that |task_runner| is assumed to outlive tracing sessions on all threads.)
+  auto* task_runner = muxer_->task_runner_.get();
+  auto deleter = [task_runner](ProducerEndpoint* e) {
+    if (task_runner->RunsTasksOnCurrentThread()) {
+      delete e;
+      return;
+    }
+    task_runner->PostTask([e] { delete e; });
+  };
+  std::shared_ptr<ProducerEndpoint> service(endpoint.release(), deleter);
+  // This atomic store is needed because another thread might be concurrently
+  // creating a trace writer using the previous (disconnected) |service_|. See
+  // CreateTraceWriter().
+  std::atomic_store(&service_, std::move(service));
+  // Don't try to use the service here since it may not have connected yet. See
+  // OnConnect().
+}

-// This class acts as a bridge between the public API and the TracingBackend(s).
-// It exposes a simplified view of the world to the API methods handling all the
-// bookkeeping to map data source instances and trace writers to the various
-// backends. It deals with N data sources, M backends (1 backend == 1 tracing
-// service == 1 producer connection) and T concurrent tracing sessions.
-//
-// Handing data source registration and start/stop flows [producer side]:
-// ----------------------------------------------------------------------
-// 1. The API client subclasses perfetto::DataSource and calls
-//    DataSource::Register<MyDataSource>(). In turn this calls into the
-//    TracingMuxer.
-// 2. The tracing muxer iterates through all the backends (1 backend == 1
-//    service == 1 producer connection) and registers the data source on each
-//    backend.
-// 3. When any (services behind a) backend starts tracing and requests to start
-//    that specific data source, the TracingMuxerImpl constructs a new instance
-//    of MyDataSource and calls the OnStart() method.
-//
-// Controlling trace and retrieving trace data [consumer side]:
-// ------------------------------------------------------------
-// 1. The API client calls Tracing::NewTrace(), returns a RAII TracingSession
-//    object.
-// 2. NewTrace() calls into internal::TracingMuxer(Impl). TracingMuxer
-//    subclasses the TracingSession object (TracingSessionImpl) and returns it.
-// 3. The tracing muxer identifies the backend (according to the args passed to
-//    NewTrace), creates a new Consumer and connects to it.
-// 4. When the API client calls Start()/Stop()/ReadTrace() methods, the
-//    TracingMuxer forwards them to the consumer associated to the
-//    TracingSession. Likewise for callbacks coming from the consumer-side of
-//    the service.
-class TracingMuxerImpl : public TracingMuxer {
- public:
-  // This is different than TracingSessionID because it's global across all
-  // backends. TracingSessionID is global only within the scope of one service.
-  using TracingSessionGlobalID = uint64_t;
+void TracingMuxerImpl::ProducerImpl::OnConnect() {
+  PERFETTO_DLOG("Producer connected");
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  PERFETTO_DCHECK(!connected_);
+  if (is_producer_provided_smb_ && !service_->IsShmemProvidedByProducer()) {
+    PERFETTO_ELOG(
+        "The service likely doesn't support producer-provided SMBs. Preventing "
+        "future attempts to use producer-provided SMB again with this "
+        "backend.");
+    producer_provided_smb_failed_ = true;
+    // Will call OnDisconnect() and cause a reconnect without producer-provided
+    // SMB.
+    service_->Disconnect();
+    return;
+  }
+  connected_ = true;
+  muxer_->UpdateDataSourcesOnAllBackends();
+  SendOnConnectTriggers();
+}

-  struct RegisteredDataSource {
-    DataSourceDescriptor descriptor;
-    DataSourceFactory factory{};
-    DataSourceParams params;
-    bool no_flush = false;
-    DataSourceStaticState* static_state = nullptr;
-  };
+void TracingMuxerImpl::ProducerImpl::OnDisconnect() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  // If we're being destroyed, bail out.
+  if (!muxer_)
+    return;
+  connected_ = false;
+  // Active data sources for this producer will be stopped by
+  // DestroyStoppedTraceWritersForCurrentThread() since the reconnected producer
+  // will have a different connection id (even before it has finished
+  // connecting).
+  registered_data_sources_.reset();
+  DisposeConnection();

-  static void InitializeInstance(const TracingInitArgs&);
-  static void ResetForTesting();
-  static void Shutdown();
+  // Try reconnecting the producer.
+  muxer_->OnProducerDisconnected(this);
+}

-  // TracingMuxer implementation.
-  bool RegisterDataSource(const DataSourceDescriptor&,
-                          DataSourceFactory,
-                          DataSourceParams,
-                          bool no_flush,
-                          DataSourceStaticState*) override;
-  void UpdateDataSourceDescriptor(const DataSourceDescriptor&,
-                                  const DataSourceStaticState*) override;
-  std::unique_ptr<TraceWriterBase> CreateTraceWriter(
-      DataSourceStaticState*,
-      uint32_t data_source_instance_index,
-      DataSourceState*,
-      BufferExhaustedPolicy buffer_exhausted_policy) override;
-  void DestroyStoppedTraceWritersForCurrentThread() override;
-  void RegisterInterceptor(const InterceptorDescriptor&,
-                           InterceptorFactory,
-                           InterceptorBase::TLSFactory,
-                           InterceptorBase::TracePacketCallback) override;
+void TracingMuxerImpl::ProducerImpl::DisposeConnection() {
+  // Keep the old service around as a dead connection in case it has active
+  // trace writers. If any tracing sessions were created, we can't clear
+  // |service_| here because other threads may be concurrently creating new
+  // trace writers. Any reconnection attempt will atomically swap the new
+  // service in place of the old one.
+  if (did_setup_tracing_ || did_setup_startup_tracing_) {
+    dead_services_.push_back(service_);
+  } else {
+    service_.reset();
+  }
+}

-  void ActivateTriggers(const std::vector<std::string>&, uint32_t) override;
+void TracingMuxerImpl::ProducerImpl::OnTracingSetup() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  did_setup_tracing_ = true;
+  service_->MaybeSharedMemoryArbiter()->SetBatchCommitsDuration(
+      shmem_batch_commits_duration_ms_);
+  if (shmem_direct_patching_enabled_) {
+    service_->MaybeSharedMemoryArbiter()->EnableDirectSMBPatching();
+  }
+}

-  std::unique_ptr<TracingSession> CreateTracingSession(
-      BackendType,
-      TracingConsumerBackend* (*system_backend_factory)());
-  std::unique_ptr<StartupTracingSession> CreateStartupTracingSession(
-      const TraceConfig& config,
-      Tracing::SetupStartupTracingOpts);
-  std::unique_ptr<StartupTracingSession> CreateStartupTracingSessionBlocking(
-      const TraceConfig& config,
-      Tracing::SetupStartupTracingOpts);
+void TracingMuxerImpl::ProducerImpl::OnStartupTracingSetup() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  did_setup_startup_tracing_ = true;
+}

-  // Producer-side bookkeeping methods.
-  void UpdateDataSourcesOnAllBackends();
-  void SetupDataSource(TracingBackendId,
-                       uint32_t backend_connection_id,
-                       DataSourceInstanceID,
-                       const DataSourceConfig&);
-  void StartDataSource(TracingBackendId, DataSourceInstanceID);
-  void StopDataSource_AsyncBegin(TracingBackendId, DataSourceInstanceID);
-  void ClearDataSourceIncrementalState(TracingBackendId, DataSourceInstanceID);
-  void SyncProducersForTesting();
+void TracingMuxerImpl::ProducerImpl::SetupDataSource(
+    DataSourceInstanceID id,
+    const DataSourceConfig& cfg) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (!muxer_)
+    return;
+  muxer_->SetupDataSource(
+      backend_id_, connection_id_.load(std::memory_order_relaxed), id, cfg);
+}

-  // Consumer-side bookkeeping methods.
-  void SetupTracingSession(TracingSessionGlobalID,
-                           const std::shared_ptr<TraceConfig>&,
-                           base::ScopedFile trace_fd = base::ScopedFile());
-  void StartTracingSession(TracingSessionGlobalID);
-  void CloneTracingSession(TracingSessionGlobalID,
-                           TracingSession::CloneTraceArgs,
-                           TracingSession::CloneTraceCallback);
-  void ChangeTracingSessionConfig(TracingSessionGlobalID, const TraceConfig&);
-  void StopTracingSession(TracingSessionGlobalID);
-  void DestroyTracingSession(TracingSessionGlobalID);
-  void FlushTracingSession(TracingSessionGlobalID,
-                           uint32_t,
-                           std::function<void(bool)>);
-  void ReadTracingSessionData(
-      TracingSessionGlobalID,
-      std::function<void(TracingSession::ReadTraceCallbackArgs)>);
-  void GetTraceStats(TracingSessionGlobalID,
-                     TracingSession::GetTraceStatsCallback);
-  void QueryServiceState(TracingSessionGlobalID,
-                         TracingSession::QueryServiceStateCallback);
+void TracingMuxerImpl::ProducerImpl::StartDataSource(DataSourceInstanceID id,
+                                                     const DataSourceConfig&) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (!muxer_)
+    return;
+  muxer_->StartDataSource(backend_id_, id);
+  service_->NotifyDataSourceStarted(id);
+}

-  // Sets the batching period to |batch_commits_duration_ms| on the backends
-  // with type |backend_type|.
-  void SetBatchCommitsDurationForTesting(uint32_t batch_commits_duration_ms,
-                                         BackendType backend_type);
+void TracingMuxerImpl::ProducerImpl::StopDataSource(DataSourceInstanceID id) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (!muxer_)
+    return;
+  muxer_->StopDataSource_AsyncBegin(backend_id_, id);
+}

-  // Enables direct SMB patching on the backends with type |backend_type| (see
-  // SharedMemoryArbiter::EnableDirectSMBPatching). Returns true if the
-  // operation succeeded for all backends with type |backend_type|, false
-  // otherwise.
-  bool EnableDirectSMBPatchingForTesting(BackendType backend_type);
+void TracingMuxerImpl::ProducerImpl::Flush(
+    FlushRequestID flush_id,
+    const DataSourceInstanceID* instances,
+    size_t instance_count,
+    FlushFlags flush_flags) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  bool all_handled = true;
+  if (muxer_) {
+    for (size_t i = 0; i < instance_count; i++) {
+      DataSourceInstanceID ds_id = instances[i];
+      bool handled = muxer_->FlushDataSource_AsyncBegin(backend_id_, ds_id,
+                                                        flush_id, flush_flags);
+      if (!handled) {
+        pending_flushes_[flush_id].insert(ds_id);
+        all_handled = false;
+      }
+    }
+  }

-  void SetMaxProducerReconnectionsForTesting(uint32_t count);
+  if (all_handled) {
+    service_->NotifyFlushComplete(flush_id);
+  }
+}

- private:
-  friend class test::TracingMuxerImplInternalsForTest;
-  friend void shlib::ResetForTesting();
+void TracingMuxerImpl::ProducerImpl::ClearIncrementalState(
+    const DataSourceInstanceID* instances,
+    size_t instance_count) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (!muxer_)
+    return;
+  for (size_t inst_idx = 0; inst_idx < instance_count; inst_idx++) {
+    muxer_->ClearDataSourceIncrementalState(backend_id_, instances[inst_idx]);
+  }
+}

-  // For each TracingBackend we create and register one ProducerImpl instance.
-  // This talks to the producer-side of the service, gets start/stop requests
-  // from it and routes them to the registered data sources.
-  // One ProducerImpl == one backend == one tracing service.
-  // This class is needed to disambiguate callbacks coming from different
-  // services. TracingMuxerImpl can't directly implement the Producer interface
-  // because the Producer virtual methods don't allow to identify the service.
-  class ProducerImpl : public Producer {
-   public:
-    ProducerImpl(TracingMuxerImpl*,
-                 TracingBackendId,
-                 uint32_t shmem_batch_commits_duration_ms,
-                 bool shmem_direct_patching_enabled);
-    ~ProducerImpl() override;
+bool TracingMuxerImpl::ProducerImpl::SweepDeadServices() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  auto is_unused = [](const std::shared_ptr<ProducerEndpoint>& endpoint) {
+    auto* arbiter = endpoint->MaybeSharedMemoryArbiter();
+    return !arbiter || arbiter->TryShutdown();
+  };
+  for (auto it = dead_services_.begin(); it != dead_services_.end();) {
+    auto next_it = it;
+    next_it++;
+    if (is_unused(*it)) {
+      dead_services_.erase(it);
+    }
+    it = next_it;
+  }
+  return dead_services_.empty();
+}

-    void Initialize(std::unique_ptr<ProducerEndpoint> endpoint);
-    void RegisterDataSource(const DataSourceDescriptor&,
-                            DataSourceFactory,
-                            DataSourceStaticState*);
-    void DisposeConnection();
+void TracingMuxerImpl::ProducerImpl::SendOnConnectTriggers() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  base::TimeMillis now = base::GetWallTimeMs();
+  std::vector<std::string> triggers;
+  while (!on_connect_triggers_.empty()) {
+    // Skip if we passed TTL.
+    if (on_connect_triggers_.front().second > now) {
+      triggers.push_back(std::move(on_connect_triggers_.front().first));
+    }
+    on_connect_triggers_.pop_front();
+  }
+  if (!triggers.empty()) {
+    service_->ActivateTriggers(triggers);
+  }
+}

-    // perfetto::Producer implementation.
-    void OnConnect() override;
-    void OnDisconnect() override;
-    void OnTracingSetup() override;
-    void OnStartupTracingSetup() override;
-    void SetupDataSource(DataSourceInstanceID,
-                         const DataSourceConfig&) override;
-    void StartDataSource(DataSourceInstanceID,
-                         const DataSourceConfig&) override;
-    void StopDataSource(DataSourceInstanceID) override;
-    void Flush(FlushRequestID,
-               const DataSourceInstanceID*,
-               size_t,
-               FlushFlags) override;
-    void ClearIncrementalState(const DataSourceInstanceID*, size_t) override;
+void TracingMuxerImpl::ProducerImpl::NotifyFlushForDataSourceDone(
+    DataSourceInstanceID ds_id,
+    FlushRequestID flush_id) {
+  if (!connected_) {
+    return;
+  }

-    bool SweepDeadServices();
-    void SendOnConnectTriggers();
-    void NotifyFlushForDataSourceDone(DataSourceInstanceID, FlushRequestID);
+  {
+    auto it = pending_flushes_.find(flush_id);
+    if (it == pending_flushes_.end()) {
+      return;
+    }
+    std::set<DataSourceInstanceID>& ds_ids = it->second;
+    ds_ids.erase(ds_id);
+  }

-    PERFETTO_THREAD_CHECKER(thread_checker_)
-    TracingMuxerImpl* muxer_;
-    TracingBackendId const backend_id_;
-    bool connected_ = false;
-    bool did_setup_tracing_ = false;
-    bool did_setup_startup_tracing_ = false;
-    std::atomic<uint32_t> connection_id_{0};
-    uint16_t last_startup_target_buffer_reservation_ = 0;
-    bool is_producer_provided_smb_ = false;
-    bool producer_provided_smb_failed_ = false;
+  std::optional<DataSourceInstanceID> biggest_flush_id;
+  for (auto it = pending_flushes_.begin(); it != pending_flushes_.end();) {
+    if (it->second.empty()) {
+      biggest_flush_id = it->first;
+      it = pending_flushes_.erase(it);
+    } else {
+      break;
+    }
+  }

-    const uint32_t shmem_batch_commits_duration_ms_ = 0;
-    const bool shmem_direct_patching_enabled_ = false;
+  if (biggest_flush_id) {
+    service_->NotifyFlushComplete(*biggest_flush_id);
+  }
+}

-    // Set of data sources that have been actually registered on this producer.
-    // This can be a subset of the global |data_sources_|, because data sources
-    // can register before the producer is fully connected.
-    std::bitset<kMaxDataSources> registered_data_sources_{};
+// ----- End of TracingMuxerImpl::ProducerImpl methods.

-    // A collection of disconnected service endpoints. Since trace writers on
-    // arbitrary threads might continue writing data to disconnected services,
-    // we keep the old services around and periodically try to clean up ones
-    // that no longer have any writers (see SweepDeadServices).
-    std::list<std::shared_ptr<ProducerEndpoint>> dead_services_;
+// ----- Begin of TracingMuxerImpl::ConsumerImpl
+TracingMuxerImpl::ConsumerImpl::ConsumerImpl(TracingMuxerImpl* muxer,
+                                             BackendType backend_type,
+                                             TracingSessionGlobalID session_id)
+    : muxer_(muxer), backend_type_(backend_type), session_id_(session_id) {}

-    // Triggers that should be sent when the service connects (trigger_name,
-    // expiration).
-    std::list<std::pair<std::string, base::TimeMillis>> on_connect_triggers_;
+TracingMuxerImpl::ConsumerImpl::~ConsumerImpl() {
+  muxer_ = nullptr;
+}

-    std::map<FlushRequestID, std::set<DataSourceInstanceID>> pending_flushes_;
+void TracingMuxerImpl::ConsumerImpl::Initialize(
+    std::unique_ptr<ConsumerEndpoint> endpoint) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  service_ = std::move(endpoint);
+  // Don't try to use the service here since it may not have connected yet. See
+  // OnConnect().
+}

-    // The currently active service endpoint is maintained as an atomic shared
-    // pointer so it won't get deleted from underneath threads that are creating
-    // trace writers. At any given time one endpoint can be shared (and thus
-    // kept alive) by the |service_| pointer, an entry in |dead_services_| and
-    // as a pointer on the stack in CreateTraceWriter() (on an arbitrary
-    // thread). The endpoint is never shared outside ProducerImpl itself.
-    //
-    // WARNING: Any *write* access to this variable or any *read* access from a
-    // non-muxer thread must be done through std::atomic_{load,store} to avoid
-    // data races.
-    std::shared_ptr<ProducerEndpoint> service_;  // Keep last.
-  };
+void TracingMuxerImpl::ConsumerImpl::OnConnect() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  PERFETTO_DCHECK(!connected_);
+  connected_ = true;

-  // For each TracingSession created by the API client (Tracing::NewTrace() we
-  // create and register one ConsumerImpl instance.
-  // This talks to the consumer-side of the service, gets end-of-trace and
-  // on-trace-data callbacks and routes them to the API client callbacks.
-  // This class is needed to disambiguate callbacks coming from different
-  // tracing sessions.
-  class ConsumerImpl : public Consumer {
-   public:
-    ConsumerImpl(TracingMuxerImpl*, BackendType, TracingSessionGlobalID);
-    ~ConsumerImpl() override;
+  // Observe data source instance events so we get notified when tracing starts.
+  service_->ObserveEvents(ObservableEvents::TYPE_DATA_SOURCES_INSTANCES |
+                          ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED);

-    void Initialize(std::unique_ptr<ConsumerEndpoint> endpoint);
+  // If the API client configured and started tracing before we connected,
+  // tell the backend about it now.
+  if (trace_config_)
+    muxer_->SetupTracingSession(session_id_, trace_config_);
+  if (start_pending_)
+    muxer_->StartTracingSession(session_id_);
+  if (get_trace_stats_pending_) {
+    auto callback = std::move(get_trace_stats_callback_);
+    get_trace_stats_callback_ = nullptr;
+    muxer_->GetTraceStats(session_id_, std::move(callback));
+  }
+  if (query_service_state_callback_) {
+    auto callback = std::move(query_service_state_callback_);
+    query_service_state_callback_ = nullptr;
+    muxer_->QueryServiceState(session_id_, std::move(callback));
+  }
+  if (session_to_clone_) {
+    service_->CloneSession(std::move(*session_to_clone_));
+    session_to_clone_ = std::nullopt;
+  }

-    // perfetto::Consumer implementation.
-    void OnConnect() override;
-    void OnDisconnect() override;
-    void OnTracingDisabled(const std::string& error) override;
-    void OnTraceData(std::vector<TracePacket>, bool has_more) override;
-    void OnDetach(bool success) override;
-    void OnAttach(bool success, const TraceConfig&) override;
-    void OnTraceStats(bool success, const TraceStats&) override;
-    void OnObservableEvents(const ObservableEvents&) override;
-    void OnSessionCloned(const OnSessionClonedArgs&) override;
+  if (stop_pending_)
+    muxer_->StopTracingSession(session_id_);
+}

-    void NotifyStartComplete();
-    void NotifyError(const TracingError&);
-    void NotifyStopComplete();
+void TracingMuxerImpl::ConsumerImpl::OnDisconnect() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  // If we're being destroyed, bail out.
+  if (!muxer_)
+    return;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  if (!connected_ && backend_type_ == kSystemBackend) {
+    PERFETTO_ELOG(
+        "Unable to connect to the system tracing service as a consumer. On "
+        "Android, use the \"perfetto\" command line tool instead to start "
+        "system-wide tracing sessions");
+  }
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)

-    // Will eventually inform the |muxer_| when it is safe to remove |this|.
-    void Disconnect();
+  // Notify the client about disconnection.
+  NotifyError(TracingError{TracingError::kDisconnected, "Peer disconnected"});

-    TracingMuxerImpl* muxer_;
-    BackendType const backend_type_;
-    TracingSessionGlobalID const session_id_;
-    bool connected_ = false;
+  // Make sure the client doesn't hang in a blocking start/stop because of the
+  // disconnection.
+  NotifyStartComplete();
+  NotifyStopComplete();

-    // This is to handle the case where the Setup call from the API client
-    // arrives before the consumer has connected. In this case we keep around
-    // the config and check if we have it after connection.
-    bool start_pending_ = false;
+  // It shouldn't be necessary to call StopTracingSession. If we get this call
+  // it means that the service did shutdown before us, so there is no point
+  // trying it to ask it to stop the session. We should just remember to cleanup
+  // the consumer vector.
+  connected_ = false;

-    // Similarly if the session is stopped before the consumer was connected, we
-    // need to wait until the session has started before stopping it.
-    bool stop_pending_ = false;
+  // Notify the muxer that it is safe to destroy |this|. This is needed because
+  // the ConsumerEndpoint stored in |service_| requires that |this| be safe to
+  // access until OnDisconnect() is called.
+  muxer_->OnConsumerDisconnected(this);
+}

-    // Similarly we need to buffer a call to get trace statistics if the
-    // consumer wasn't connected yet.
-    bool get_trace_stats_pending_ = false;
+void TracingMuxerImpl::ConsumerImpl::Disconnect() {
+  // This is weird and deserves a comment.
+  //
+  // When we called the ConnectConsumer method on the service it returns
+  // us a ConsumerEndpoint which we stored in |service_|, however this
+  // ConsumerEndpoint holds a pointer to the ConsumerImpl pointed to by
+  // |this|. Part of the API contract to TracingService::ConnectConsumer is that
+  // the ConsumerImpl pointer has to be valid until the
+  // ConsumerImpl::OnDisconnect method is called. Therefore we reset the
+  // ConsumerEndpoint |service_|. Eventually this will call
+  // ConsumerImpl::OnDisconnect and we will inform the muxer it is safe to
+  // call the destructor of |this|.
+  service_.reset();
+}

-    // Similarly we need to buffer a session cloning args if the session is
-    // cloning another sesison before the consumer was connected.
-    std::optional<ConsumerEndpoint::CloneSessionArgs> session_to_clone_;
+void TracingMuxerImpl::ConsumerImpl::OnTracingDisabled(
+    const std::string& error) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  PERFETTO_DCHECK(!stopped_);
+  stopped_ = true;

-    // Whether this session was already stopped. This will happen in response to
-    // Stop{,Blocking}, but also if the service stops the session for us
-    // automatically (e.g., when there are no data sources).
-    bool stopped_ = false;
+  if (!error.empty())
+    NotifyError(TracingError{TracingError::kTracingFailed, error});

-    // shared_ptr because it's posted across threads. This is to avoid copying
-    // it more than once.
-    std::shared_ptr<TraceConfig> trace_config_;
-    base::ScopedFile trace_fd_;
+  // If we're still waiting for the start event, fire it now. This may happen if
+  // there are no active data sources in the session.
+  NotifyStartComplete();
+  NotifyStopComplete();
+}

-    // If the API client passes a callback to start, we should invoke this when
-    // NotifyStartComplete() is invoked.
-    std::function<void()> start_complete_callback_;
+void TracingMuxerImpl::ConsumerImpl::NotifyStartComplete() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (start_complete_callback_) {
+    muxer_->task_runner_->PostTask(std::move(start_complete_callback_));
+    start_complete_callback_ = nullptr;
+  }
+  if (blocking_start_complete_callback_) {
+    muxer_->task_runner_->PostTask(
+        std::move(blocking_start_complete_callback_));
+    blocking_start_complete_callback_ = nullptr;
+  }
+}

-    // An internal callback used to implement StartBlocking().
-    std::function<void()> blocking_start_complete_callback_;
+void TracingMuxerImpl::ConsumerImpl::NotifyError(const TracingError& error) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (error_callback_) {
+    muxer_->task_runner_->PostTask(
+        std::bind(std::move(error_callback_), error));
+  }
+}

-    // If the API client passes a callback to get notification about the
-    // errors, we should invoke this when NotifyError() is invoked.
-    std::function<void(TracingError)> error_callback_;
+void TracingMuxerImpl::ConsumerImpl::NotifyStopComplete() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (stop_complete_callback_) {
+    muxer_->task_runner_->PostTask(std::move(stop_complete_callback_));
+    stop_complete_callback_ = nullptr;
+  }
+  if (blocking_stop_complete_callback_) {
+    muxer_->task_runner_->PostTask(std::move(blocking_stop_complete_callback_));
+    blocking_stop_complete_callback_ = nullptr;
+  }
+}

-    // If the API client passes a callback to stop, we should invoke this when
-    // OnTracingDisabled() is invoked.
-    std::function<void()> stop_complete_callback_;
+void TracingMuxerImpl::ConsumerImpl::OnTraceData(
+    std::vector<TracePacket> packets,
+    bool has_more) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (!read_trace_callback_)
+    return;

-    // An internal callback used to implement StopBlocking().
-    std::function<void()> blocking_stop_complete_callback_;
+  size_t capacity = 0;
+  for (const auto& packet : packets) {
+    // 16 is an over-estimation of the proto preamble size
+    capacity += packet.size() + 16;
+  }

-    // Callback for a pending call to CloneTrace().
-    TracingSession::CloneTraceCallback clone_trace_callback_;
+  // The shared_ptr is to avoid making a copy of the buffer when PostTask-ing.
+  std::shared_ptr<std::vector<char>> buf(new std::vector<char>());
+  buf->reserve(capacity);
+  for (auto& packet : packets) {
+    char* start;
+    size_t size;
+    std::tie(start, size) = packet.GetProtoPreamble();
+    buf->insert(buf->end(), start, start + size);
+    for (auto& slice : packet.slices()) {
+      const auto* slice_data = reinterpret_cast<const char*>(slice.start);
+      buf->insert(buf->end(), slice_data, slice_data + slice.size);
+    }
+  }

-    // Callback passed to ReadTrace().
-    std::function<void(TracingSession::ReadTraceCallbackArgs)>
-        read_trace_callback_;
+  auto callback = read_trace_callback_;
+  muxer_->task_runner_->PostTask([callback, buf, has_more] {
+    TracingSession::ReadTraceCallbackArgs callback_arg{};
+    callback_arg.data = buf->empty() ? nullptr : &(*buf)[0];
+    callback_arg.size = buf->size();
+    callback_arg.has_more = has_more;
+    callback(callback_arg);
+  });

-    // Callback passed to GetTraceStats().
-    TracingSession::GetTraceStatsCallback get_trace_stats_callback_;
+  if (!has_more)
+    read_trace_callback_ = nullptr;
+}

-    // Callback for a pending call to QueryServiceState().
-    TracingSession::QueryServiceStateCallback query_service_state_callback_;
+void TracingMuxerImpl::ConsumerImpl::OnObservableEvents(
+    const ObservableEvents& events) {
+  if (events.instance_state_changes_size()) {
+    for (const auto& state_change : events.instance_state_changes()) {
+      DataSourceHandle handle{state_change.producer_name(),
+                              state_change.data_source_name()};
+      data_source_states_[handle] =
+          state_change.state() ==
+          ObservableEvents::DATA_SOURCE_INSTANCE_STATE_STARTED;
+    }
+  }

-    // The states of all data sources in this tracing session. |true| means the
-    // data source has started tracing.
-    using DataSourceHandle = std::pair<std::string, std::string>;
-    std::map<DataSourceHandle, bool> data_source_states_;
+  if (events.instance_state_changes_size() ||
+      events.all_data_sources_started()) {
+    // Data sources are first reported as being stopped before starting, so once
+    // all the data sources we know about have started we can declare tracing
+    // begun. In the case where there are no matching data sources for the
+    // session, the service will report the all_data_sources_started() event
+    // without adding any instances (only since Android S / Perfetto v10.0).
+    if (start_complete_callback_ || blocking_start_complete_callback_) {
+      bool all_data_sources_started = std::all_of(
+          data_source_states_.cbegin(), data_source_states_.cend(),
+          [](std::pair<DataSourceHandle, bool> state) { return state.second; });
+      if (all_data_sources_started)
+        NotifyStartComplete();
+    }
+  }
+}

-    std::unique_ptr<ConsumerEndpoint> service_;  // Keep before last.
-    PERFETTO_THREAD_CHECKER(thread_checker_)     // Keep last.
-  };
+void TracingMuxerImpl::ConsumerImpl::OnSessionCloned(
+    const OnSessionClonedArgs& args) {
+  if (!clone_trace_callback_)
+    return;
+  TracingSession::CloneTraceCallbackArgs callback_arg{};
+  callback_arg.success = args.success;
+  callback_arg.error = std::move(args.error);
+  callback_arg.uuid_msb = args.uuid.msb();
+  callback_arg.uuid_lsb = args.uuid.lsb();
+  muxer_->task_runner_->PostTask(
+      std::bind(std::move(clone_trace_callback_), std::move(callback_arg)));
+  clone_trace_callback_ = nullptr;
+}

-  // This object is returned to API clients when they call
-  // Tracing::CreateTracingSession().
-  class TracingSessionImpl : public TracingSession {
-   public:
-    TracingSessionImpl(TracingMuxerImpl*, TracingSessionGlobalID, BackendType);
-    ~TracingSessionImpl() override;
-    void Setup(const TraceConfig&, int fd) override;
-    void Start() override;
-    void StartBlocking() override;
-    void CloneTrace(CloneTraceArgs args, CloneTraceCallback) override;
-    void SetOnStartCallback(std::function<void()>) override;
-    void SetOnErrorCallback(std::function<void(TracingError)>) override;
-    void Stop() override;
-    void StopBlocking() override;
-    void Flush(std::function<void(bool)>, uint32_t timeout_ms) override;
-    void ReadTrace(ReadTraceCallback) override;
-    void SetOnStopCallback(std::function<void()>) override;
-    void GetTraceStats(GetTraceStatsCallback) override;
-    void QueryServiceState(QueryServiceStateCallback) override;
-    void ChangeTraceConfig(const TraceConfig&) override;
+void TracingMuxerImpl::ConsumerImpl::OnTraceStats(
+    bool success,
+    const TraceStats& trace_stats) {
+  if (!get_trace_stats_callback_)
+    return;
+  TracingSession::GetTraceStatsCallbackArgs callback_arg{};
+  callback_arg.success = success;
+  callback_arg.trace_stats_data = trace_stats.SerializeAsArray();
+  muxer_->task_runner_->PostTask(
+      std::bind(std::move(get_trace_stats_callback_), std::move(callback_arg)));
+  get_trace_stats_callback_ = nullptr;
+}

-   private:
-    TracingMuxerImpl* const muxer_;
-    TracingSessionGlobalID const session_id_;
-    BackendType const backend_type_;
-  };
+// The callbacks below are not used.
+void TracingMuxerImpl::ConsumerImpl::OnDetach(bool) {}
+void TracingMuxerImpl::ConsumerImpl::OnAttach(bool, const TraceConfig&) {}
+// ----- End of TracingMuxerImpl::ConsumerImpl

-  // This object is returned to API clients when they call
-  // Tracing::SetupStartupTracing().
-  class StartupTracingSessionImpl : public StartupTracingSession {
-   public:
-    StartupTracingSessionImpl(TracingMuxerImpl*,
-                              TracingSessionGlobalID,
-                              BackendType);
-    ~StartupTracingSessionImpl() override;
-    void Abort() override;
-    void AbortBlocking() override;
+// ----- Begin of TracingMuxerImpl::TracingSessionImpl

-   private:
-    TracingMuxerImpl* const muxer_;
-    TracingSessionGlobalID const session_id_;
-    BackendType backend_type_;
-  };
+// TracingSessionImpl is the RAII object returned to API clients when they
+// invoke Tracing::CreateTracingSession. They use it for starting/stopping
+// tracing.

-  struct RegisteredInterceptor {
-    protos::gen::InterceptorDescriptor descriptor;
-    InterceptorFactory factory{};
-    InterceptorBase::TLSFactory tls_factory{};
-    InterceptorBase::TracePacketCallback packet_callback{};
-  };
+TracingMuxerImpl::TracingSessionImpl::TracingSessionImpl(
+    TracingMuxerImpl* muxer,
+    TracingSessionGlobalID session_id,
+    BackendType backend_type)
+    : muxer_(muxer), session_id_(session_id), backend_type_(backend_type) {}

-  struct RegisteredStartupSession {
-    TracingSessionID session_id = 0;
-    int num_unbound_data_sources = 0;
+// Can be destroyed from any thread.
+TracingMuxerImpl::TracingSessionImpl::~TracingSessionImpl() {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  muxer->task_runner_->PostTask(
+      [muxer, session_id] { muxer->DestroyTracingSession(session_id); });
+}

-    bool is_aborting = false;
-    int num_aborting_data_sources = 0;
+// Can be called from any thread.
+void TracingMuxerImpl::TracingSessionImpl::Setup(const TraceConfig& cfg,
+                                                 int fd) {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  std::shared_ptr<TraceConfig> trace_config(new TraceConfig(cfg));
+  if (fd >= 0) {
+    base::ignore_result(backend_type_);  // For -Wunused in the amalgamation.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    if (backend_type_ != kInProcessBackend) {
+      PERFETTO_FATAL(
+          "Passing a file descriptor to TracingSession::Setup() is only "
+          "supported with the kInProcessBackend on Windows. Use "
+          "TracingSession::ReadTrace() instead");
+    }
+#endif
+    trace_config->set_write_into_file(true);
+    fd = dup(fd);
+  }
+  muxer->task_runner_->PostTask([muxer, session_id, trace_config, fd] {
+    muxer->SetupTracingSession(session_id, trace_config, base::ScopedFile(fd));
+  });
+}

-    std::function<void()> on_aborted;
-    std::function<void()> on_adopted;
-  };
+// Can be called from any thread.
+void TracingMuxerImpl::TracingSessionImpl::Start() {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  muxer->task_runner_->PostTask(
+      [muxer, session_id] { muxer->StartTracingSession(session_id); });
+}

-  struct RegisteredProducerBackend {
-    // Backends are supposed to have static lifetime.
-    TracingProducerBackend* backend = nullptr;
-    TracingBackendId id = 0;
-    BackendType type{};
+void TracingMuxerImpl::TracingSessionImpl::CloneTrace(CloneTraceArgs args,
+                                                      CloneTraceCallback cb) {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  muxer->task_runner_->PostTask([muxer, session_id, args, cb] {
+    muxer->CloneTracingSession(session_id, args, std::move(cb));
+  });
+}

-    TracingBackend::ConnectProducerArgs producer_conn_args;
-    std::unique_ptr<ProducerImpl> producer;
+// Can be called from any thread.
+void TracingMuxerImpl::TracingSessionImpl::ChangeTraceConfig(
+    const TraceConfig& cfg) {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  muxer->task_runner_->PostTask([muxer, session_id, cfg] {
+    muxer->ChangeTracingSessionConfig(session_id, cfg);
+  });
+}

-    std::vector<RegisteredStartupSession> startup_sessions;
-  };
+// Can be called from any thread except the service thread.
+void TracingMuxerImpl::TracingSessionImpl::StartBlocking() {
+  PERFETTO_DCHECK(!muxer_->task_runner_->RunsTasksOnCurrentThread());
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  base::WaitableEvent tracing_started;
+  muxer->task_runner_->PostTask([muxer, session_id, &tracing_started] {
+    auto* consumer = muxer->FindConsumer(session_id);
+    if (!consumer) {
+      // TODO(skyostil): Signal an error to the user.
+      tracing_started.Notify();
+      return;
+    }
+    PERFETTO_DCHECK(!consumer->blocking_start_complete_callback_);
+    consumer->blocking_start_complete_callback_ = [&] {
+      tracing_started.Notify();
+    };
+    muxer->StartTracingSession(session_id);
+  });
+  tracing_started.Wait();
+}

-  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;
-  };
+// Can be called from any thread.
+void TracingMuxerImpl::TracingSessionImpl::Flush(
+    std::function<void(bool)> user_callback,
+    uint32_t timeout_ms) {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  muxer->task_runner_->PostTask([muxer, session_id, timeout_ms, user_callback] {
+    auto* consumer = muxer->FindConsumer(session_id);
+    if (!consumer) {
+      std::move(user_callback)(false);
+      return;
+    }
+    muxer->FlushTracingSession(session_id, timeout_ms,
+                               std::move(user_callback));
+  });
+}

-  void UpdateDataSourceOnAllBackends(RegisteredDataSource& rds,
-                                     bool is_changed);
-  explicit TracingMuxerImpl(const TracingInitArgs&);
-  void Initialize(const TracingInitArgs& args);
-  void AddBackends(const TracingInitArgs& args);
-  void AddConsumerBackend(TracingConsumerBackend* backend, BackendType type);
-  void AddProducerBackend(TracingProducerBackend* backend,
-                          BackendType type,
-                          const TracingInitArgs& args);
-  ConsumerImpl* FindConsumer(TracingSessionGlobalID session_id);
-  std::pair<ConsumerImpl*, RegisteredConsumerBackend*> FindConsumerAndBackend(
-      TracingSessionGlobalID session_id);
-  RegisteredProducerBackend* FindProducerBackendById(TracingBackendId id);
-  RegisteredProducerBackend* FindProducerBackendByType(BackendType type);
-  RegisteredConsumerBackend* FindConsumerBackendByType(BackendType type);
-  void InitializeConsumer(TracingSessionGlobalID session_id);
-  void OnConsumerDisconnected(ConsumerImpl* consumer);
-  void OnProducerDisconnected(ProducerImpl* producer);
-  // Test only method.
-  void SweepDeadBackends();
+// Can be called from any thread.
+void TracingMuxerImpl::TracingSessionImpl::Stop() {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  muxer->task_runner_->PostTask(
+      [muxer, session_id] { muxer->StopTracingSession(session_id); });
+}

-  struct FindDataSourceRes {
-    FindDataSourceRes() = default;
-    FindDataSourceRes(DataSourceStaticState* a,
-                      DataSourceState* b,
-                      uint32_t c,
-                      bool d)
-        : static_state(a),
-          internal_state(b),
-          instance_idx(c),
-          requires_callbacks_under_lock(d) {}
-    explicit operator bool() const { return !!internal_state; }
+// Can be called from any thread except the service thread.
+void TracingMuxerImpl::TracingSessionImpl::StopBlocking() {
+  PERFETTO_DCHECK(!muxer_->task_runner_->RunsTasksOnCurrentThread());
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  base::WaitableEvent tracing_stopped;
+  muxer->task_runner_->PostTask([muxer, session_id, &tracing_stopped] {
+    auto* consumer = muxer->FindConsumer(session_id);
+    if (!consumer) {
+      // TODO(skyostil): Signal an error to the user.
+      tracing_stopped.Notify();
+      return;
+    }
+    PERFETTO_DCHECK(!consumer->blocking_stop_complete_callback_);
+    consumer->blocking_stop_complete_callback_ = [&] {
+      tracing_stopped.Notify();
+    };
+    muxer->StopTracingSession(session_id);
+  });
+  tracing_stopped.Wait();
+}

-    DataSourceStaticState* static_state = nullptr;
-    DataSourceState* internal_state = nullptr;
-    uint32_t instance_idx = 0;
-    bool requires_callbacks_under_lock = false;
-  };
-  FindDataSourceRes FindDataSource(TracingBackendId, DataSourceInstanceID);
+// Can be called from any thread.
+void TracingMuxerImpl::TracingSessionImpl::ReadTrace(ReadTraceCallback cb) {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  muxer->task_runner_->PostTask([muxer, session_id, cb] {
+    muxer->ReadTracingSessionData(session_id, std::move(cb));
+  });
+}

-  FindDataSourceRes SetupDataSourceImpl(
-      const RegisteredDataSource&,
-      TracingBackendId,
-      uint32_t backend_connection_id,
-      DataSourceInstanceID,
-      const DataSourceConfig&,
-      TracingSessionGlobalID startup_session_id);
-  void StartDataSourceImpl(const FindDataSourceRes&);
-  void StopDataSource_AsyncBeginImpl(const FindDataSourceRes&);
-  void StopDataSource_AsyncEnd(TracingBackendId,
-                               uint32_t backend_connection_id,
-                               DataSourceInstanceID,
-                               const FindDataSourceRes&);
-  bool FlushDataSource_AsyncBegin(TracingBackendId,
-                                  DataSourceInstanceID,
-                                  FlushRequestID,
-                                  FlushFlags);
-  void FlushDataSource_AsyncEnd(TracingBackendId,
-                                uint32_t backend_connection_id,
-                                DataSourceInstanceID,
-                                const FindDataSourceRes&,
-                                FlushRequestID);
-  void AbortStartupTracingSession(TracingSessionGlobalID, BackendType);
-  // When ResetForTesting() is executed, `cb` will be called on the calling
-  // thread and on the muxer thread.
-  void AppendResetForTestingCallback(std::function<void()> cb);
+// Can be called from any thread.
+void TracingMuxerImpl::TracingSessionImpl::SetOnStartCallback(
+    std::function<void()> cb) {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  muxer->task_runner_->PostTask([muxer, session_id, cb] {
+    auto* consumer = muxer->FindConsumer(session_id);
+    if (!consumer)
+      return;
+    consumer->start_complete_callback_ = cb;
+  });
+}

-  // WARNING: If you add new state here, be sure to update ResetForTesting.
-  std::unique_ptr<base::TaskRunner> task_runner_;
-  std::vector<RegisteredDataSource> data_sources_;
-  // These lists can only have one backend per BackendType. The elements are
-  // sorted by BackendType priority (see BackendTypePriority). They always
-  // contain a fake low-priority kUnspecifiedBackend at the end.
-  std::list<RegisteredProducerBackend> producer_backends_;
-  std::list<RegisteredConsumerBackend> consumer_backends_;
-  std::vector<RegisteredInterceptor> interceptors_;
-  TracingPolicy* policy_ = nullptr;
+// Can be called from any thread
+void TracingMuxerImpl::TracingSessionImpl::SetOnErrorCallback(
+    std::function<void(TracingError)> cb) {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  muxer->task_runner_->PostTask([muxer, session_id, cb] {
+    auto* consumer = muxer->FindConsumer(session_id);
+    if (!consumer) {
+      // Notify the client about concurrent disconnection of the session.
+      if (cb)
+        cb(TracingError{TracingError::kDisconnected, "Peer disconnected"});
+      return;
+    }
+    consumer->error_callback_ = cb;
+  });
+}
+
+// Can be called from any thread.
+void TracingMuxerImpl::TracingSessionImpl::SetOnStopCallback(
+    std::function<void()> cb) {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  muxer->task_runner_->PostTask([muxer, session_id, cb] {
+    auto* consumer = muxer->FindConsumer(session_id);
+    if (!consumer)
+      return;
+    consumer->stop_complete_callback_ = cb;
+  });
+}

-  // Learn more at TracingInitArgs::supports_multiple_data_source_instances
-  bool supports_multiple_data_source_instances_ = true;
+// Can be called from any thread.
+void TracingMuxerImpl::TracingSessionImpl::GetTraceStats(
+    GetTraceStatsCallback cb) {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  muxer->task_runner_->PostTask([muxer, session_id, cb] {
+    muxer->GetTraceStats(session_id, std::move(cb));
+  });
+}

-  std::atomic<TracingSessionGlobalID> next_tracing_session_id_{};
-  std::atomic<uint32_t> next_data_source_index_{};
-  uint32_t muxer_id_for_testing_{};
+// Can be called from any thread.
+void TracingMuxerImpl::TracingSessionImpl::QueryServiceState(
+    QueryServiceStateCallback cb) {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  muxer->task_runner_->PostTask([muxer, session_id, cb] {
+    muxer->QueryServiceState(session_id, std::move(cb));
+  });
+}

-  // Maximum number of times we will try to reconnect producer backend.
-  // Should only be modified for testing purposes.
-  std::atomic<uint32_t> max_producer_reconnections_{100u};
+// ----- End of TracingMuxerImpl::TracingSessionImpl

-  // Test only member.
-  // After ResetForTesting() is called, holds tracing backends which needs to be
-  // kept alive until all inbound references have gone away. See
-  // SweepDeadBackends().
-  std::list<RegisteredProducerBackend> dead_backends_;
+// ----- Begin of TracingMuxerImpl::StartupTracingSessionImpl

-  // Test only member.
-  // Executes these cleanup functions on the calling thread and on the muxer
-  // thread when ResetForTesting() is called.
-  std::list<std::function<void()>> reset_callbacks_;
+TracingMuxerImpl::StartupTracingSessionImpl::StartupTracingSessionImpl(
+    TracingMuxerImpl* muxer,
+    TracingSessionGlobalID session_id,
+    BackendType backend_type)
+    : muxer_(muxer), session_id_(session_id), backend_type_(backend_type) {}

-  PERFETTO_THREAD_CHECKER(thread_checker_)
-};
+// Can be destroyed from any thread.
+TracingMuxerImpl::StartupTracingSessionImpl::~StartupTracingSessionImpl() =
+    default;

-}  // namespace internal
-}  // namespace perfetto
+void TracingMuxerImpl::StartupTracingSessionImpl::Abort() {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  auto backend_type = backend_type_;
+  muxer->task_runner_->PostTask([muxer, session_id, backend_type] {
+    muxer->AbortStartupTracingSession(session_id, backend_type);
+  });
+}

-#endif  // SRC_TRACING_INTERNAL_TRACING_MUXER_IMPL_H_
-// gen_amalgamated begin header: include/perfetto/ext/tracing/core/trace_stats.h
-/*
- * 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.
- */
+// Must not be called from the SDK's internal thread.
+void TracingMuxerImpl::StartupTracingSessionImpl::AbortBlocking() {
+  auto* muxer = muxer_;
+  auto session_id = session_id_;
+  auto backend_type = backend_type_;
+  PERFETTO_CHECK(!muxer->task_runner_->RunsTasksOnCurrentThread());
+  base::WaitableEvent event;
+  muxer->task_runner_->PostTask([muxer, session_id, backend_type, &event] {
+    muxer->AbortStartupTracingSession(session_id, backend_type);
+    event.Notify();
+  });
+  event.Wait();
+}

-#ifndef INCLUDE_PERFETTO_EXT_TRACING_CORE_TRACE_STATS_H_
-#define INCLUDE_PERFETTO_EXT_TRACING_CORE_TRACE_STATS_H_
+// ----- End of TracingMuxerImpl::StartupTracingSessionImpl

-// Creates the aliases in the ::perfetto namespace, doing things like:
-// using ::perfetto::Foo = ::perfetto::protos::gen::Foo.
-// See comments in forward_decls.h for the historical reasons of this
-// indirection layer.
-// gen_amalgamated expanded: #include "perfetto/tracing/core/forward_decls.h"
+// static
+TracingMuxer* TracingMuxer::instance_ = TracingMuxerFake::Get();

-// gen_amalgamated expanded: #include "protos/perfetto/common/trace_stats.gen.h"
+// This is called by perfetto::Tracing::Initialize().
+// Can be called on any thread. Typically, but not necessarily, that will be
+// the embedder's main thread.
+TracingMuxerImpl::TracingMuxerImpl(const TracingInitArgs& args)
+    : TracingMuxer(args.platform ? args.platform
+                                 : Platform::GetDefaultPlatform()) {
+  PERFETTO_DETACH_FROM_THREAD(thread_checker_);
+  instance_ = this;

-#endif  // INCLUDE_PERFETTO_EXT_TRACING_CORE_TRACE_STATS_H_
-// gen_amalgamated begin header: include/perfetto/tracing/core/tracing_service_state.h
-/*
- * 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.
- */
+  // Create the thread where muxer, producers and service will live.
+  Platform::CreateTaskRunnerArgs tr_args{/*name_for_debugging=*/"TracingMuxer"};
+  task_runner_.reset(new NonReentrantTaskRunner(
+      this, platform_->CreateTaskRunner(std::move(tr_args))));

-#ifndef INCLUDE_PERFETTO_TRACING_CORE_TRACING_SERVICE_STATE_H_
-#define INCLUDE_PERFETTO_TRACING_CORE_TRACING_SERVICE_STATE_H_
+  // Run the initializer on that thread.
+  task_runner_->PostTask([this, args] {
+    Initialize(args);
+    AddBackends(args);
+  });
+}

-// Creates the aliases in the ::perfetto namespace, doing things like:
-// using ::perfetto::Foo = ::perfetto::protos::gen::Foo.
-// See comments in forward_decls.h for the historical reasons of this
-// indirection layer.
-// gen_amalgamated expanded: #include "perfetto/tracing/core/forward_decls.h"
+void TracingMuxerImpl::Initialize(const TracingInitArgs& args) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);  // Rebind the thread checker.

-// gen_amalgamated expanded: #include "protos/perfetto/common/tracing_service_state.gen.h"
+  policy_ = args.tracing_policy;
+  supports_multiple_data_source_instances_ =
+      args.supports_multiple_data_source_instances;

-#endif  // INCLUDE_PERFETTO_TRACING_CORE_TRACING_SERVICE_STATE_H_
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+  // Fallback backend for producer creation for an unsupported backend type.
+  PERFETTO_CHECK(producer_backends_.empty());
+  AddProducerBackend(internal::TracingBackendFake::GetInstance(),
+                     BackendType::kUnspecifiedBackend, args);
+  // Fallback backend for consumer creation for an unsupported backend type.
+  // This backend simply fails any attempt to start a tracing session.
+  PERFETTO_CHECK(consumer_backends_.empty());
+  AddConsumerBackend(internal::TracingBackendFake::GetInstance(),
+                     BackendType::kUnspecifiedBackend);
+}

-// gen_amalgamated expanded: #include "src/tracing/internal/tracing_muxer_impl.h"
+void TracingMuxerImpl::AddConsumerBackend(TracingConsumerBackend* backend,
+                                          BackendType type) {
+  if (!backend) {
+    // We skip the log in release builds because the *_backend_fake.cc code
+    // has already an ELOG before returning a nullptr.
+    PERFETTO_DLOG("Consumer backend creation failed, type %d",
+                  static_cast<int>(type));
+    return;
+  }
+  // Keep the backends sorted by type.
+  auto it =
+      std::upper_bound(consumer_backends_.begin(), consumer_backends_.end(),
+                       type, CompareBackendByType<RegisteredConsumerBackend>());
+  it = consumer_backends_.emplace(it);

-#include <algorithm>
-#include <atomic>
-#include <mutex>
-#include <optional>
-#include <vector>
+  RegisteredConsumerBackend& rb = *it;
+  rb.backend = backend;
+  rb.type = type;
+}

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
-// gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/fnv_hash.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/waitable_event.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory_arbiter.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_packet.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_stats.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_writer.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/tracing_service.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/buffer_exhausted_policy.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/core/data_source_config.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/core/tracing_service_state.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/data_source.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/data_source_internal.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/interceptor_trace_writer.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_backend_fake.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/trace_writer_base.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/tracing.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/tracing_backend.h"
-// gen_amalgamated expanded: #include "src/tracing/core/null_trace_writer.h"
-// gen_amalgamated expanded: #include "src/tracing/internal/tracing_muxer_fake.h"
+void TracingMuxerImpl::AddProducerBackend(TracingProducerBackend* backend,
+                                          BackendType type,
+                                          const TracingInitArgs& args) {
+  if (!backend) {
+    // We skip the log in release builds because the *_backend_fake.cc code
+    // has already an ELOG before returning a nullptr.
+    PERFETTO_DLOG("Producer backend creation failed, type %d",
+                  static_cast<int>(type));
+    return;
+  }
+  TracingBackendId backend_id = producer_backends_.size();
+  // Keep the backends sorted by type.
+  auto it =
+      std::upper_bound(producer_backends_.begin(), producer_backends_.end(),
+                       type, CompareBackendByType<RegisteredProducerBackend>());
+  it = producer_backends_.emplace(it);

-// gen_amalgamated expanded: #include "protos/perfetto/config/interceptor_config.gen.h"
+  RegisteredProducerBackend& rb = *it;
+  rb.backend = backend;
+  rb.id = backend_id;
+  rb.type = type;
+  rb.producer.reset(new ProducerImpl(this, backend_id,
+                                     args.shmem_batch_commits_duration_ms,
+                                     args.shmem_direct_patching_enabled));
+  rb.producer_conn_args.producer = rb.producer.get();
+  rb.producer_conn_args.producer_name = platform_->GetCurrentProcessName();
+  rb.producer_conn_args.task_runner = task_runner_.get();
+  rb.producer_conn_args.shmem_size_hint_bytes = args.shmem_size_hint_kb * 1024;
+  rb.producer_conn_args.shmem_page_size_hint_bytes =
+      args.shmem_page_size_hint_kb * 1024;
+  rb.producer_conn_args.create_socket_async = args.create_socket_async;
+  rb.producer_conn_args.machine_id = args.machine_id;
+  rb.producer->Initialize(rb.backend->ConnectProducer(rb.producer_conn_args));
+}

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <io.h>  // For dup()
-#else
-#include <unistd.h>  // For dup()
-#endif
+TracingMuxerImpl::RegisteredProducerBackend*
+TracingMuxerImpl::FindProducerBackendById(TracingBackendId id) {
+  for (RegisteredProducerBackend& b : producer_backends_) {
+    if (b.id == id) {
+      return &b;
+    }
+  }
+  return nullptr;
+}

-namespace perfetto {
-namespace internal {
+TracingMuxerImpl::RegisteredProducerBackend*
+TracingMuxerImpl::FindProducerBackendByType(BackendType type) {
+  for (RegisteredProducerBackend& b : producer_backends_) {
+    if (b.type == type) {
+      return &b;
+    }
+  }
+  return nullptr;
+}

-namespace {
+TracingMuxerImpl::RegisteredConsumerBackend*
+TracingMuxerImpl::FindConsumerBackendByType(BackendType type) {
+  for (RegisteredConsumerBackend& b : consumer_backends_) {
+    if (b.type == type) {
+      return &b;
+    }
+  }
+  return nullptr;
+}

-using RegisteredDataSource = TracingMuxerImpl::RegisteredDataSource;
+void TracingMuxerImpl::AddBackends(const TracingInitArgs& args) {
+  if (args.backends & kSystemBackend) {
+    PERFETTO_CHECK(args.system_producer_backend_factory_);
+    if (FindProducerBackendByType(kSystemBackend) == nullptr) {
+      AddProducerBackend(args.system_producer_backend_factory_(),
+                         kSystemBackend, args);
+    }
+    if (args.enable_system_consumer) {
+      PERFETTO_CHECK(args.system_consumer_backend_factory_);
+      if (FindConsumerBackendByType(kSystemBackend) == nullptr) {
+        AddConsumerBackend(args.system_consumer_backend_factory_(),
+                           kSystemBackend);
+      }
+    }
+  }

-// A task runner which prevents calls to DataSource::Trace() while an operation
-// is in progress. Used to guard against unexpected re-entrancy where the
-// user-provided task runner implementation tries to enter a trace point under
-// the hood.
-class NonReentrantTaskRunner : public base::TaskRunner {
- public:
-  NonReentrantTaskRunner(TracingMuxer* muxer,
-                         std::unique_ptr<base::TaskRunner> task_runner)
-      : muxer_(muxer), task_runner_(std::move(task_runner)) {}
+  if (args.backends & kInProcessBackend) {
+    TracingBackend* b = nullptr;
+    if (FindProducerBackendByType(kInProcessBackend) == nullptr) {
+      if (!b) {
+        PERFETTO_CHECK(args.in_process_backend_factory_);
+        b = args.in_process_backend_factory_();
+      }
+      AddProducerBackend(b, kInProcessBackend, args);
+    }
+    if (FindConsumerBackendByType(kInProcessBackend) == nullptr) {
+      if (!b) {
+        PERFETTO_CHECK(args.in_process_backend_factory_);
+        b = args.in_process_backend_factory_();
+      }
+      AddConsumerBackend(b, kInProcessBackend);
+    }
+  }

-  // base::TaskRunner implementation.
-  void PostTask(std::function<void()> task) override {
-    CallWithGuard([&] { task_runner_->PostTask(std::move(task)); });
+  if (args.backends & kCustomBackend) {
+    PERFETTO_CHECK(args.custom_backend);
+    if (FindProducerBackendByType(kCustomBackend) == nullptr) {
+      AddProducerBackend(args.custom_backend, kCustomBackend, args);
+    }
+    if (FindConsumerBackendByType(kCustomBackend) == nullptr) {
+      AddConsumerBackend(args.custom_backend, kCustomBackend);
+    }
   }

-  void PostDelayedTask(std::function<void()> task, uint32_t delay_ms) override {
-    CallWithGuard(
-        [&] { task_runner_->PostDelayedTask(std::move(task), delay_ms); });
+  if (args.backends & ~(kSystemBackend | kInProcessBackend | kCustomBackend)) {
+    PERFETTO_FATAL("Unsupported tracing backend type");
   }
+}

-  void AddFileDescriptorWatch(base::PlatformHandle fd,
-                              std::function<void()> callback) override {
-    CallWithGuard(
-        [&] { task_runner_->AddFileDescriptorWatch(fd, std::move(callback)); });
+// Can be called from any thread (but not concurrently).
+bool TracingMuxerImpl::RegisterDataSource(
+    const DataSourceDescriptor& descriptor,
+    DataSourceFactory factory,
+    DataSourceParams params,
+    bool no_flush,
+    DataSourceStaticState* static_state) {
+  // Ignore repeated registrations.
+  if (static_state->index != kMaxDataSources) {
+    PERFETTO_ELOG(
+        "Data source \"%s\" registration ignored: this data source type is "
+        "already registered. See "
+        "https://perfetto.dev/docs/instrumentation/"
+        "tracing-sdk#reporting-many-similar-things",
+        descriptor.name().c_str());
+    return true;
   }

-  void RemoveFileDescriptorWatch(base::PlatformHandle fd) override {
-    CallWithGuard([&] { task_runner_->RemoveFileDescriptorWatch(fd); });
+  uint32_t new_index = next_data_source_index_++;
+  if (new_index >= kMaxDataSources) {
+    PERFETTO_DLOG(
+        "RegisterDataSource failed: too many data sources already registered");
+    return false;
   }

-  bool RunsTasksOnCurrentThread() const override {
-    bool result;
-    CallWithGuard([&] { result = task_runner_->RunsTasksOnCurrentThread(); });
-    return result;
-  }
+  // Initialize the static state.
+  static_assert(sizeof(static_state->instances[0]) >= sizeof(DataSourceState),
+                "instances[] size mismatch");
+  for (size_t i = 0; i < static_state->instances.size(); i++)
+    new (&static_state->instances[i]) DataSourceState{};
+
+  static_state->index = new_index;
+
+  // Generate a semi-unique id for this data source.
+  uint64_t digest = base::FnvHasher::Combine(
+      reinterpret_cast<intptr_t>(static_state), base::GetWallTimeNs().count());
+  static_state->id = digest ? digest : 1;
+
+  task_runner_->PostTask(
+      [this, descriptor, factory, static_state, params, no_flush] {
+        data_sources_.emplace_back();
+        RegisteredDataSource& rds = data_sources_.back();
+        rds.descriptor = descriptor;
+        rds.factory = factory;
+        rds.params = params;
+        if (!supports_multiple_data_source_instances_) {
+          rds.params.supports_multiple_instances = false;
+        }
+        rds.static_state = static_state;
+        rds.no_flush = no_flush;
+
+        UpdateDataSourceOnAllBackends(rds, /*is_changed=*/false);
+      });
+  return true;
+}
+
+// Can be called from any thread (but not concurrently).
+void TracingMuxerImpl::UpdateDataSourceDescriptor(
+    const DataSourceDescriptor& descriptor,
+    const DataSourceStaticState* static_state) {
+  task_runner_->PostTask([this, descriptor, static_state] {
+    for (auto& rds : data_sources_) {
+      if (rds.static_state == static_state) {
+        PERFETTO_CHECK(rds.descriptor.name() == descriptor.name());
+        rds.descriptor = descriptor;
+        rds.descriptor.set_id(static_state->id);
+        UpdateDataSourceOnAllBackends(rds, /*is_changed=*/true);
+        return;
+      }
+    }
+  });
+}
+
+// Can be called from any thread (but not concurrently).
+void TracingMuxerImpl::RegisterInterceptor(
+    const InterceptorDescriptor& descriptor,
+    InterceptorFactory factory,
+    InterceptorBase::TLSFactory tls_factory,
+    InterceptorBase::TracePacketCallback packet_callback) {
+  task_runner_->PostTask([this, descriptor, factory, tls_factory,
+                          packet_callback] {
+    // Ignore repeated registrations.
+    for (const auto& interceptor : interceptors_) {
+      if (interceptor.descriptor.name() == descriptor.name()) {
+        PERFETTO_DCHECK(interceptor.tls_factory == tls_factory);
+        PERFETTO_DCHECK(interceptor.packet_callback == packet_callback);
+        return;
+      }
+    }
+    // Only allow certain interceptors for now.
+    if (descriptor.name() != "test_interceptor" &&
+        descriptor.name() != "console" && descriptor.name() != "etwexport") {
+      PERFETTO_ELOG(
+          "Interceptors are experimental. If you want to use them, please "
+          "get in touch with the project maintainers "
+          "(https://perfetto.dev/docs/contributing/"
+          "getting-started#community).");
+      return;
+    }
+    interceptors_.emplace_back();
+    RegisteredInterceptor& interceptor = interceptors_.back();
+    interceptor.descriptor = descriptor;
+    interceptor.factory = factory;
+    interceptor.tls_factory = tls_factory;
+    interceptor.packet_callback = packet_callback;
+  });
+}
+
+void TracingMuxerImpl::ActivateTriggers(
+    const std::vector<std::string>& triggers,
+    uint32_t ttl_ms) {
+  base::TimeMillis expire_time =
+      base::GetWallTimeMs() + base::TimeMillis(ttl_ms);
+  task_runner_->PostTask([this, triggers, expire_time] {
+    for (RegisteredProducerBackend& backend : producer_backends_) {
+      if (backend.producer->connected_) {
+        backend.producer->service_->ActivateTriggers(triggers);
+      } else {
+        for (const std::string& trigger : triggers) {
+          backend.producer->on_connect_triggers_.emplace_back(trigger,
+                                                              expire_time);
+        }
+      }
+    }
+  });
+}
+
+// Checks if there is any matching startup tracing data source instance for a
+// new SetupDataSource call. If so, moves the data source to this tracing
+// session (and its target buffer) and returns true, otherwise returns false.
+static bool MaybeAdoptStartupTracingInDataSource(
+    TracingBackendId backend_id,
+    uint32_t backend_connection_id,
+    DataSourceInstanceID instance_id,
+    const DataSourceConfig& cfg,
+    const std::vector<RegisteredDataSource>& data_sources) {
+  for (const auto& rds : data_sources) {
+    DataSourceStaticState* static_state = rds.static_state;
+    for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
+      auto* internal_state = static_state->TryGet(i);
+
+      if (internal_state &&
+          internal_state->startup_target_buffer_reservation.load(
+              std::memory_order_relaxed) &&
+          internal_state->data_source_instance_id == 0 &&
+          internal_state->backend_id == backend_id &&
+          internal_state->backend_connection_id == backend_connection_id &&
+          internal_state->config &&
+          internal_state->data_source->CanAdoptStartupSession(
+              *internal_state->config, cfg)) {
+        PERFETTO_DLOG("Setting up data source %" PRIu64
+                      " %s by adopting it from a startup tracing session",
+                      instance_id, cfg.name().c_str());
+
+        std::lock_guard<std::recursive_mutex> lock(internal_state->lock);
+        // Set the associations. The actual takeover happens in
+        // StartDataSource().
+        internal_state->data_source_instance_id = instance_id;
+        internal_state->buffer_id =
+            static_cast<internal::BufferId>(cfg.target_buffer());
+        internal_state->buffer_exhausted_policy =
+            ComputeBufferExhaustedPolicy(cfg, rds.params);
+        internal_state->config.reset(new DataSourceConfig(cfg));
+
+        // TODO(eseckler): Should the data source config provided by the service
+        // be allowed to specify additional interceptors / additional data
+        // source params?

- private:
-  template <typename T>
-  void CallWithGuard(T lambda) const {
-    auto* root_tls = muxer_->GetOrCreateTracingTLS();
-    if (PERFETTO_UNLIKELY(root_tls->is_in_trace_point)) {
-      lambda();
-      return;
+        return true;
+      }
     }
-    ScopedReentrancyAnnotator scoped_annotator(*root_tls);
-    lambda();
-  }
-
-  TracingMuxer* const muxer_;
-  std::unique_ptr<base::TaskRunner> task_runner_;
-};
-
-class StopArgsImpl : public DataSourceBase::StopArgs {
- public:
-  std::function<void()> HandleStopAsynchronously() const override {
-    auto closure = std::move(async_stop_closure);
-    async_stop_closure = std::function<void()>();
-    return closure;
   }
+  return false;
+}

-  mutable std::function<void()> async_stop_closure;
-};
+// Called by the service of one of the backends.
+void TracingMuxerImpl::SetupDataSource(TracingBackendId backend_id,
+                                       uint32_t backend_connection_id,
+                                       DataSourceInstanceID instance_id,
+                                       const DataSourceConfig& cfg) {
+  PERFETTO_DLOG("Setting up data source %" PRIu64 " %s", instance_id,
+                cfg.name().c_str());
+  PERFETTO_DCHECK_THREAD(thread_checker_);

-class FlushArgsImpl : public DataSourceBase::FlushArgs {
- public:
-  std::function<void()> HandleFlushAsynchronously() const override {
-    auto closure = std::move(async_flush_closure);
-    async_flush_closure = std::function<void()>();
-    return closure;
+  // First check if there is any matching startup tracing data source instance.
+  if (MaybeAdoptStartupTracingInDataSource(backend_id, backend_connection_id,
+                                           instance_id, cfg, data_sources_)) {
+    return;
   }

-  mutable std::function<void()> async_flush_closure;
-};
-
-// Holds an earlier TracingMuxerImpl instance after ResetForTesting() is called.
-static TracingMuxerImpl* g_prev_instance{};
+  for (const auto& rds : data_sources_) {
+    if (rds.descriptor.name() != cfg.name())
+      continue;
+    DataSourceStaticState& static_state = *rds.static_state;

-template <typename RegisteredBackend>
-struct CompareBackendByType {
-  static int BackendTypePriority(BackendType type) {
-    switch (type) {
-      case kSystemBackend:
-        return 0;
-      case kInProcessBackend:
-        return 1;
-      case kCustomBackend:
-        return 2;
-      // The UnspecifiedBackend has the highest priority so that
-      // TracingBackendFake is the last one on the backend lists.
-      case kUnspecifiedBackend:
+    // If this data source is already active for this exact config, don't start
+    // another instance. This happens when we have several data sources with the
+    // same name, in which case the service sends one SetupDataSource event for
+    // each one. Since we can't map which event maps to which data source, we
+    // ensure each event only starts one data source instance.
+    // TODO(skyostil): Register a unique id with each data source to the service
+    // to disambiguate.
+    bool active_for_config = false;
+    for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
+      if (!static_state.TryGet(i))
+        continue;
+      auto* internal_state =
+          reinterpret_cast<DataSourceState*>(&static_state.instances[i]);
+      if (internal_state->backend_id == backend_id &&
+          internal_state->backend_connection_id == backend_connection_id &&
+          internal_state->config && *internal_state->config == cfg) {
+        active_for_config = true;
         break;
+      }
     }
-    return 3;
-  }
-  bool operator()(BackendType type, const RegisteredBackend& b) {
-    return BackendTypePriority(type) < BackendTypePriority(b.type);
+    if (active_for_config) {
+      PERFETTO_DLOG(
+          "Data source %s is already active with this config, skipping",
+          cfg.name().c_str());
+      continue;
+    }
+
+    SetupDataSourceImpl(rds, backend_id, backend_connection_id, instance_id,
+                        cfg, /*startup_session_id=*/0);
+    return;
   }
-};
+}

-BufferExhaustedPolicy ComputeBufferExhaustedPolicy(
+TracingMuxerImpl::FindDataSourceRes TracingMuxerImpl::SetupDataSourceImpl(
+    const RegisteredDataSource& rds,
+    TracingBackendId backend_id,
+    uint32_t backend_connection_id,
+    DataSourceInstanceID instance_id,
     const DataSourceConfig& cfg,
-    const DataSourceParams& params) {
-  if (!params.buffer_exhausted_policy_configurable) {
-    return params.default_buffer_exhausted_policy;
-  }
+    TracingSessionGlobalID startup_session_id) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  DataSourceStaticState& static_state = *rds.static_state;

-  switch (cfg.buffer_exhausted_policy()) {
-    case DataSourceConfig::BUFFER_EXHAUSTED_UNSPECIFIED:
-      return params.default_buffer_exhausted_policy;
-    case DataSourceConfig::BUFFER_EXHAUSTED_DROP:
-      return BufferExhaustedPolicy::kDrop;
-    case DataSourceConfig::BUFFER_EXHAUSTED_STALL_THEN_ABORT:
-      return BufferExhaustedPolicy::kStall;
-    case DataSourceConfig::BUFFER_EXHAUSTED_STALL_THEN_DROP:
-      return BufferExhaustedPolicy::kStallThenDrop;
+  // If any bit is set in `static_state.valid_instances` then at least one
+  // other instance of data source is running.
+  if (!rds.params.supports_multiple_instances &&
+      static_state.valid_instances.load(std::memory_order_acquire) != 0) {
+    PERFETTO_ELOG(
+        "Failed to setup data source because some another instance of this "
+        "data source is already active");
+    return FindDataSourceRes();
   }

-  return params.default_buffer_exhausted_policy;
-}
+  for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
+    // Find a free slot.
+    if (static_state.TryGet(i))
+      continue;

-}  // namespace
+    auto* internal_state =
+        reinterpret_cast<DataSourceState*>(&static_state.instances[i]);
+    std::unique_lock<std::recursive_mutex> lock(internal_state->lock);
+    static_assert(
+        std::is_same<decltype(internal_state->data_source_instance_id),
+                     DataSourceInstanceID>::value,
+        "data_source_instance_id type mismatch");
+    internal_state->muxer_id_for_testing = muxer_id_for_testing_;
+    RegisteredProducerBackend& backend = *FindProducerBackendById(backend_id);

-// ----- Begin of TracingMuxerImpl::ProducerImpl
-TracingMuxerImpl::ProducerImpl::ProducerImpl(
-    TracingMuxerImpl* muxer,
-    TracingBackendId backend_id,
-    uint32_t shmem_batch_commits_duration_ms,
-    bool shmem_direct_patching_enabled)
-    : muxer_(muxer),
-      backend_id_(backend_id),
-      shmem_batch_commits_duration_ms_(shmem_batch_commits_duration_ms),
-      shmem_direct_patching_enabled_(shmem_direct_patching_enabled) {}
+    if (startup_session_id) {
+      uint16_t& last_reservation =
+          backend.producer->last_startup_target_buffer_reservation_;
+      if (last_reservation == std::numeric_limits<uint16_t>::max()) {
+        PERFETTO_ELOG(
+            "Startup buffer reservations exhausted, dropping data source");
+        return FindDataSourceRes();
+      }
+      internal_state->startup_target_buffer_reservation.store(
+          ++last_reservation, std::memory_order_relaxed);
+    } else {
+      internal_state->startup_target_buffer_reservation.store(
+          0, std::memory_order_relaxed);
+    }

-TracingMuxerImpl::ProducerImpl::~ProducerImpl() {
-  muxer_ = nullptr;
-}
+    internal_state->backend_id = backend_id;
+    internal_state->backend_connection_id = backend_connection_id;
+    internal_state->data_source_instance_id = instance_id;
+    internal_state->buffer_id =
+        static_cast<internal::BufferId>(cfg.target_buffer());
+    internal_state->buffer_exhausted_policy =
+        ComputeBufferExhaustedPolicy(cfg, rds.params);
+    internal_state->config.reset(new DataSourceConfig(cfg));
+    internal_state->startup_session_id = startup_session_id;
+    internal_state->data_source = rds.factory();
+    internal_state->interceptor = nullptr;
+    internal_state->interceptor_id = 0;
+    internal_state->will_notify_on_stop = rds.descriptor.will_notify_on_stop();

-void TracingMuxerImpl::ProducerImpl::Initialize(
-    std::unique_ptr<ProducerEndpoint> endpoint) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  PERFETTO_DCHECK(!connected_);
-  connection_id_.fetch_add(1, std::memory_order_relaxed);
-  is_producer_provided_smb_ = endpoint->shared_memory();
-  last_startup_target_buffer_reservation_ = 0;
+    if (cfg.has_interceptor_config()) {
+      for (size_t j = 0; j < interceptors_.size(); j++) {
+        if (cfg.interceptor_config().name() ==
+            interceptors_[j].descriptor.name()) {
+          PERFETTO_DLOG("Intercepting data source %" PRIu64
+                        " \"%s\" into \"%s\"",
+                        instance_id, cfg.name().c_str(),
+                        cfg.interceptor_config().name().c_str());
+          internal_state->interceptor_id = static_cast<uint32_t>(j + 1);
+          internal_state->interceptor = interceptors_[j].factory();
+          internal_state->interceptor->OnSetup({cfg});
+          break;
+        }
+      }
+      if (!internal_state->interceptor_id) {
+        PERFETTO_ELOG("Unknown interceptor configured for data source: %s",
+                      cfg.interceptor_config().name().c_str());
+      }
+    }

-  // Adopt the endpoint into a shared pointer so that we can safely share it
-  // across threads that create trace writers. The custom deleter function
-  // ensures that the endpoint is always destroyed on the muxer's thread. (Note
-  // that |task_runner| is assumed to outlive tracing sessions on all threads.)
-  auto* task_runner = muxer_->task_runner_.get();
-  auto deleter = [task_runner](ProducerEndpoint* e) {
-    if (task_runner->RunsTasksOnCurrentThread()) {
-      delete e;
-      return;
+    // Select v1 or v2 once, so that all writers of this instance agree.
+    // A startup instance keeps v1, also after adoption.
+    // An intercepted instance takes its writer from the interceptor.
+    internal_state->use_tracing_v2 =
+        !startup_session_id && !internal_state->interceptor_id &&
+        rds.params.supports_tracing_v2 && cfg.supports_tracing_v2();
+    const uint32_t probability =
+        cfg.experimental_tracing_v2().use_v2_probability_percent();
+    if (internal_state->use_tracing_v2 && probability < 100) {
+      internal_state->use_tracing_v2 =
+          static_cast<uint64_t>(base::Uuidv4().lsb()) % 100 < probability;
     }
-    task_runner->PostTask([e] { delete e; });
-  };
-  std::shared_ptr<ProducerEndpoint> service(endpoint.release(), deleter);
-  // This atomic store is needed because another thread might be concurrently
-  // creating a trace writer using the previous (disconnected) |service_|. See
-  // CreateTraceWriter().
-  std::atomic_store(&service_, std::move(service));
-  // Don't try to use the service here since it may not have connected yet. See
-  // OnConnect().
-}
+    // Complete initialization before writer threads can observe this instance.
+    if (internal_state->use_tracing_v2)
+      backend.producer->service_->InitializeV2RingBuffer(cfg);

-void TracingMuxerImpl::ProducerImpl::OnConnect() {
-  PERFETTO_DLOG("Producer connected");
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  PERFETTO_DCHECK(!connected_);
-  if (is_producer_provided_smb_ && !service_->IsShmemProvidedByProducer()) {
-    PERFETTO_ELOG(
-        "The service likely doesn't support producer-provided SMBs. Preventing "
-        "future attempts to use producer-provided SMB again with this "
-        "backend.");
-    producer_provided_smb_failed_ = true;
-    // Will call OnDisconnect() and cause a reconnect without producer-provided
-    // SMB.
-    service_->Disconnect();
-    return;
-  }
-  connected_ = true;
-  muxer_->UpdateDataSourcesOnAllBackends();
-  SendOnConnectTriggers();
-}
+    // This must be made at the end. See matching acquire-load in
+    // DataSource::Trace().
+    static_state.valid_instances.fetch_or(1 << i, std::memory_order_release);

-void TracingMuxerImpl::ProducerImpl::OnDisconnect() {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  // If we're being destroyed, bail out.
-  if (!muxer_)
-    return;
-  connected_ = false;
-  // Active data sources for this producer will be stopped by
-  // DestroyStoppedTraceWritersForCurrentThread() since the reconnected producer
-  // will have a different connection id (even before it has finished
-  // connecting).
-  registered_data_sources_.reset();
-  DisposeConnection();
+    DataSourceBase::SetupArgs setup_args;
+    setup_args.config = &cfg;
+    setup_args.backend_type = backend.type;
+    setup_args.internal_instance_index = i;

-  // Try reconnecting the producer.
-  muxer_->OnProducerDisconnected(this);
-}
+    if (!rds.params.requires_callbacks_under_lock)
+      lock.unlock();
+    internal_state->data_source->OnSetup(setup_args);

-void TracingMuxerImpl::ProducerImpl::DisposeConnection() {
-  // Keep the old service around as a dead connection in case it has active
-  // trace writers. If any tracing sessions were created, we can't clear
-  // |service_| here because other threads may be concurrently creating new
-  // trace writers. Any reconnection attempt will atomically swap the new
-  // service in place of the old one.
-  if (did_setup_tracing_ || did_setup_startup_tracing_) {
-    dead_services_.push_back(service_);
-  } else {
-    service_.reset();
+    return FindDataSourceRes(&static_state, internal_state, i,
+                             rds.params.requires_callbacks_under_lock);
   }
+  PERFETTO_ELOG(
+      "Maximum number of data source instances exhausted. "
+      "Dropping data source %" PRIu64,
+      instance_id);
+  return FindDataSourceRes();
 }

-void TracingMuxerImpl::ProducerImpl::OnTracingSetup() {
+// Called by the service of one of the backends.
+void TracingMuxerImpl::StartDataSource(TracingBackendId backend_id,
+                                       DataSourceInstanceID instance_id) {
+  PERFETTO_DLOG("Starting data source %" PRIu64, instance_id);
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  did_setup_tracing_ = true;
-  service_->MaybeSharedMemoryArbiter()->SetBatchCommitsDuration(
-      shmem_batch_commits_duration_ms_);
-  if (shmem_direct_patching_enabled_) {
-    service_->MaybeSharedMemoryArbiter()->EnableDirectSMBPatching();
+
+  auto ds = FindDataSource(backend_id, instance_id);
+  if (!ds) {
+    PERFETTO_ELOG("Could not find data source to start");
+    return;
   }
-}

-void TracingMuxerImpl::ProducerImpl::OnStartupTracingSetup() {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  did_setup_startup_tracing_ = true;
-}
+  // Check if the data source was already started for startup tracing.
+  uint16_t startup_reservation =
+      ds.internal_state->startup_target_buffer_reservation.load(
+          std::memory_order_relaxed);
+  if (startup_reservation) {
+    RegisteredProducerBackend& backend = *FindProducerBackendById(backend_id);
+    TracingSessionGlobalID session_id = ds.internal_state->startup_session_id;
+    auto session_it = std::find_if(
+        backend.startup_sessions.begin(), backend.startup_sessions.end(),
+        [session_id](const RegisteredStartupSession& session) {
+          return session.session_id == session_id;
+        });
+    PERFETTO_DCHECK(session_it != backend.startup_sessions.end());

-void TracingMuxerImpl::ProducerImpl::SetupDataSource(
-    DataSourceInstanceID id,
-    const DataSourceConfig& cfg) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  if (!muxer_)
-    return;
-  muxer_->SetupDataSource(
-      backend_id_, connection_id_.load(std::memory_order_relaxed), id, cfg);
-}
+    if (session_it->is_aborting) {
+      PERFETTO_DLOG("Data source %" PRIu64
+                    " was already aborted for startup tracing, not starting it",
+                    instance_id);
+      return;
+    }

-void TracingMuxerImpl::ProducerImpl::StartDataSource(DataSourceInstanceID id,
-                                                     const DataSourceConfig&) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  if (!muxer_)
-    return;
-  muxer_->StartDataSource(backend_id_, id);
-  service_->NotifyDataSourceStarted(id);
-}
+    PERFETTO_DLOG(
+        "Data source %" PRIu64
+        " was already started for startup tracing, binding its target buffer",
+        instance_id);

-void TracingMuxerImpl::ProducerImpl::StopDataSource(DataSourceInstanceID id) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  if (!muxer_)
+    backend.producer->service_->MaybeSharedMemoryArbiter()
+        ->BindStartupTargetBuffer(startup_reservation,
+                                  ds.internal_state->buffer_id);
+
+    // The reservation ID can be used even after binding it, so there's no need
+    // for any barriers here - we just need atomicity.
+    ds.internal_state->startup_target_buffer_reservation.store(
+        0, std::memory_order_relaxed);
+
+    // TODO(eseckler): Should we reset incremental state at this point, or
+    // notify the data source some other way?
+
+    // The session should not have been fully bound yet (or aborted).
+    PERFETTO_DCHECK(session_it->num_unbound_data_sources > 0);
+
+    session_it->num_unbound_data_sources--;
+    if (session_it->num_unbound_data_sources == 0) {
+      if (session_it->on_adopted)
+        task_runner_->PostTask(session_it->on_adopted);
+      backend.startup_sessions.erase(session_it);
+    }
     return;
-  muxer_->StopDataSource_AsyncBegin(backend_id_, id);
+  }
+
+  StartDataSourceImpl(ds);
 }

-void TracingMuxerImpl::ProducerImpl::Flush(
-    FlushRequestID flush_id,
-    const DataSourceInstanceID* instances,
-    size_t instance_count,
-    FlushFlags flush_flags) {
+void TracingMuxerImpl::StartDataSourceImpl(const FindDataSourceRes& ds) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  bool all_handled = true;
-  if (muxer_) {
-    for (size_t i = 0; i < instance_count; i++) {
-      DataSourceInstanceID ds_id = instances[i];
-      bool handled = muxer_->FlushDataSource_AsyncBegin(backend_id_, ds_id,
-                                                        flush_id, flush_flags);
-      if (!handled) {
-        pending_flushes_[flush_id].insert(ds_id);
-        all_handled = false;
-      }
-    }
-  }

-  if (all_handled) {
-    service_->NotifyFlushComplete(flush_id);
-  }
+  DataSourceBase::StartArgs start_args{};
+  start_args.internal_instance_index = ds.instance_idx;
+
+  std::unique_lock<std::recursive_mutex> lock(ds.internal_state->lock);
+  if (ds.internal_state->interceptor)
+    ds.internal_state->interceptor->OnStart({});
+  ds.internal_state->trace_lambda_enabled.store(true,
+                                                std::memory_order_relaxed);
+  PERFETTO_DCHECK(ds.internal_state->data_source != nullptr);
+
+  if (!ds.requires_callbacks_under_lock)
+    lock.unlock();
+  ds.internal_state->data_source->OnStart(start_args);
 }

-void TracingMuxerImpl::ProducerImpl::ClearIncrementalState(
-    const DataSourceInstanceID* instances,
-    size_t instance_count) {
+// Called by the service of one of the backends.
+void TracingMuxerImpl::StopDataSource_AsyncBegin(
+    TracingBackendId backend_id,
+    DataSourceInstanceID instance_id) {
+  PERFETTO_DLOG("Stopping data source %" PRIu64, instance_id);
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  if (!muxer_)
+
+  auto ds = FindDataSource(backend_id, instance_id);
+  if (!ds) {
+    PERFETTO_ELOG("Could not find data source to stop");
     return;
-  for (size_t inst_idx = 0; inst_idx < instance_count; inst_idx++) {
-    muxer_->ClearDataSourceIncrementalState(backend_id_, instances[inst_idx]);
   }
+
+  StopDataSource_AsyncBeginImpl(ds);
 }

-bool TracingMuxerImpl::ProducerImpl::SweepDeadServices() {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  auto is_unused = [](const std::shared_ptr<ProducerEndpoint>& endpoint) {
-    auto* arbiter = endpoint->MaybeSharedMemoryArbiter();
-    return !arbiter || arbiter->TryShutdown();
+void TracingMuxerImpl::StopDataSource_AsyncBeginImpl(
+    const FindDataSourceRes& ds) {
+  TracingBackendId backend_id = ds.internal_state->backend_id;
+  uint32_t backend_connection_id = ds.internal_state->backend_connection_id;
+  DataSourceInstanceID instance_id = ds.internal_state->data_source_instance_id;
+
+  StopArgsImpl stop_args{};
+  stop_args.internal_instance_index = ds.instance_idx;
+  stop_args.async_stop_closure = [this, backend_id, backend_connection_id,
+                                  instance_id, ds] {
+    // TracingMuxerImpl is long lived, capturing |this| is okay.
+    // The notification closure can be moved out of the StopArgs by the
+    // embedder to handle stop asynchronously. The embedder might then
+    // call the closure on a different thread than the current one, hence
+    // this nested PostTask().
+    task_runner_->PostTask(
+        [this, backend_id, backend_connection_id, instance_id, ds] {
+          StopDataSource_AsyncEnd(backend_id, backend_connection_id,
+                                  instance_id, ds);
+        });
   };
-  for (auto it = dead_services_.begin(); it != dead_services_.end();) {
-    auto next_it = it;
-    next_it++;
-    if (is_unused(*it)) {
-      dead_services_.erase(it);
-    }
-    it = next_it;
-  }
-  return dead_services_.empty();
-}

-void TracingMuxerImpl::ProducerImpl::SendOnConnectTriggers() {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  base::TimeMillis now = base::GetWallTimeMs();
-  std::vector<std::string> triggers;
-  while (!on_connect_triggers_.empty()) {
-    // Skip if we passed TTL.
-    if (on_connect_triggers_.front().second > now) {
-      triggers.push_back(std::move(on_connect_triggers_.front().first));
-    }
-    on_connect_triggers_.pop_front();
-  }
-  if (!triggers.empty()) {
-    service_->ActivateTriggers(triggers);
+  {
+    std::unique_lock<std::recursive_mutex> lock(ds.internal_state->lock);
+
+    // Don't call OnStop again if the datasource is already stopping.
+    if (ds.internal_state->async_stop_in_progress)
+      return;
+    ds.internal_state->async_stop_in_progress = true;
+
+    if (ds.internal_state->interceptor)
+      ds.internal_state->interceptor->OnStop({});
+
+    if (!ds.requires_callbacks_under_lock)
+      lock.unlock();
+    ds.internal_state->data_source->OnStop(stop_args);
   }
+
+  // If the embedder hasn't called StopArgs.HandleStopAsynchronously() run the
+  // async closure here. In theory we could avoid the PostTask and call
+  // straight into CompleteDataSourceAsyncStop(). We keep that to reduce
+  // divergencies between the deferred-stop vs non-deferred-stop code paths.
+  if (stop_args.async_stop_closure)
+    std::move(stop_args.async_stop_closure)();
 }

-void TracingMuxerImpl::ProducerImpl::NotifyFlushForDataSourceDone(
-    DataSourceInstanceID ds_id,
-    FlushRequestID flush_id) {
-  if (!connected_) {
+void TracingMuxerImpl::StopDataSource_AsyncEnd(TracingBackendId backend_id,
+                                               uint32_t backend_connection_id,
+                                               DataSourceInstanceID instance_id,
+                                               const FindDataSourceRes& ds) {
+  PERFETTO_DLOG("Ending async stop of data source %" PRIu64, instance_id);
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+
+  // Check that the data source instance is still active and was not modified
+  // while it was being stopped.
+  if (!ds.static_state->TryGet(ds.instance_idx) ||
+      ds.internal_state->backend_id != backend_id ||
+      ds.internal_state->backend_connection_id != backend_connection_id ||
+      ds.internal_state->data_source_instance_id != instance_id) {
+    PERFETTO_ELOG(
+        "Async stop of data source %" PRIu64
+        " failed. This might be due to calling the async_stop_closure twice.",
+        instance_id);
     return;
   }

+  const uint32_t mask = ~(1U << ds.instance_idx);
+  ds.static_state->valid_instances.fetch_and(mask, std::memory_order_acq_rel);
+
+  bool will_notify_on_stop;
+  // Take the mutex to prevent that the data source is in the middle of
+  // a Trace() execution where it called GetDataSourceLocked() while we
+  // destroy it.
+  uint16_t startup_buffer_reservation;
+  TracingSessionGlobalID startup_session_id;
   {
-    auto it = pending_flushes_.find(flush_id);
-    if (it == pending_flushes_.end()) {
-      return;
-    }
-    std::set<DataSourceInstanceID>& ds_ids = it->second;
-    ds_ids.erase(ds_id);
+    std::lock_guard<std::recursive_mutex> guard(ds.internal_state->lock);
+    ds.internal_state->trace_lambda_enabled.store(false,
+                                                  std::memory_order_relaxed);
+    ds.internal_state->data_source.reset();
+    ds.internal_state->interceptor.reset();
+    ds.internal_state->config.reset();
+    ds.internal_state->async_stop_in_progress = false;
+    will_notify_on_stop = ds.internal_state->will_notify_on_stop;
+    startup_buffer_reservation =
+        ds.internal_state->startup_target_buffer_reservation.load(
+            std::memory_order_relaxed);
+    startup_session_id = ds.internal_state->startup_session_id;
   }

-  std::optional<DataSourceInstanceID> biggest_flush_id;
-  for (auto it = pending_flushes_.begin(); it != pending_flushes_.end();) {
-    if (it->second.empty()) {
-      biggest_flush_id = it->first;
-      it = pending_flushes_.erase(it);
-    } else {
-      break;
-    }
-  }
+  // The other fields of internal_state are deliberately *not* cleared.
+  // See races-related comments of DataSource::Trace().

-  if (biggest_flush_id) {
-    service_->NotifyFlushComplete(*biggest_flush_id);
-  }
-}
+  TracingMuxer::generation_++;

-// ----- End of TracingMuxerImpl::ProducerImpl methods.
+  // |producer_backends_| is append-only, Backend instances are always valid.
+  PERFETTO_CHECK(backend_id < producer_backends_.size());
+  RegisteredProducerBackend& backend = *FindProducerBackendById(backend_id);
+  ProducerImpl* producer = backend.producer.get();
+  if (!producer)
+    return;

-// ----- Begin of TracingMuxerImpl::ConsumerImpl
-TracingMuxerImpl::ConsumerImpl::ConsumerImpl(TracingMuxerImpl* muxer,
-                                             BackendType backend_type,
-                                             TracingSessionGlobalID session_id)
-    : muxer_(muxer), backend_type_(backend_type), session_id_(session_id) {}
+  // If the data source instance still has a startup buffer reservation, it was
+  // only active for startup tracing and never started by the service. Discard
+  // the startup buffer reservation.
+  if (startup_buffer_reservation) {
+    PERFETTO_DCHECK(startup_session_id);

-TracingMuxerImpl::ConsumerImpl::~ConsumerImpl() {
-  muxer_ = nullptr;
-}
+    if (producer->service_ && producer->service_->MaybeSharedMemoryArbiter()) {
+      producer->service_->MaybeSharedMemoryArbiter()
+          ->AbortStartupTracingForReservation(startup_buffer_reservation);
+    }

-void TracingMuxerImpl::ConsumerImpl::Initialize(
-    std::unique_ptr<ConsumerEndpoint> endpoint) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  service_ = std::move(endpoint);
-  // Don't try to use the service here since it may not have connected yet. See
-  // OnConnect().
-}
+    auto session_it = std::find_if(
+        backend.startup_sessions.begin(), backend.startup_sessions.end(),
+        [startup_session_id](const RegisteredStartupSession& session) {
+          return session.session_id == startup_session_id;
+        });

-void TracingMuxerImpl::ConsumerImpl::OnConnect() {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  PERFETTO_DCHECK(!connected_);
-  connected_ = true;
+    // Session should not be removed until abortion of all data source instances
+    // is complete.
+    PERFETTO_DCHECK(session_it != backend.startup_sessions.end());

-  // Observe data source instance events so we get notified when tracing starts.
-  service_->ObserveEvents(ObservableEvents::TYPE_DATA_SOURCES_INSTANCES |
-                          ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED);
+    session_it->num_aborting_data_sources--;
+    if (session_it->num_aborting_data_sources == 0) {
+      if (session_it->on_aborted)
+        task_runner_->PostTask(session_it->on_aborted);

-  // If the API client configured and started tracing before we connected,
-  // tell the backend about it now.
-  if (trace_config_)
-    muxer_->SetupTracingSession(session_id_, trace_config_);
-  if (start_pending_)
-    muxer_->StartTracingSession(session_id_);
-  if (get_trace_stats_pending_) {
-    auto callback = std::move(get_trace_stats_callback_);
-    get_trace_stats_callback_ = nullptr;
-    muxer_->GetTraceStats(session_id_, std::move(callback));
-  }
-  if (query_service_state_callback_) {
-    auto callback = std::move(query_service_state_callback_);
-    query_service_state_callback_ = nullptr;
-    muxer_->QueryServiceState(session_id_, std::move(callback));
-  }
-  if (session_to_clone_) {
-    service_->CloneSession(std::move(*session_to_clone_));
-    session_to_clone_ = std::nullopt;
+      backend.startup_sessions.erase(session_it);
+    }
   }

-  if (stop_pending_)
-    muxer_->StopTracingSession(session_id_);
+  if (producer->connected_ &&
+      backend.producer->connection_id_.load(std::memory_order_relaxed) ==
+          backend_connection_id) {
+    // Flush any commits that might have been batched by SharedMemoryArbiter.
+    producer->service_->MaybeSharedMemoryArbiter()
+        ->FlushPendingCommitDataRequests();
+    if (instance_id && will_notify_on_stop)
+      producer->service_->NotifyDataSourceStopped(instance_id);
+  }
+  producer->SweepDeadServices();
+  base::MaybeReleaseAllocatorMemToOS();
 }

-void TracingMuxerImpl::ConsumerImpl::OnDisconnect() {
+void TracingMuxerImpl::ClearDataSourceIncrementalState(
+    TracingBackendId backend_id,
+    DataSourceInstanceID instance_id) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  // If we're being destroyed, bail out.
-  if (!muxer_)
+  PERFETTO_DLOG("Clearing incremental state for data source %" PRIu64,
+                instance_id);
+  auto ds = FindDataSource(backend_id, instance_id);
+  if (!ds) {
+    PERFETTO_ELOG("Could not find data source to clear incremental state for");
     return;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  if (!connected_ && backend_type_ == kSystemBackend) {
-    PERFETTO_ELOG(
-        "Unable to connect to the system tracing service as a consumer. On "
-        "Android, use the \"perfetto\" command line tool instead to start "
-        "system-wide tracing sessions");
   }
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-
-  // Notify the client about disconnection.
-  NotifyError(TracingError{TracingError::kDisconnected, "Peer disconnected"});
-
-  // Make sure the client doesn't hang in a blocking start/stop because of the
-  // disconnection.
-  NotifyStartComplete();
-  NotifyStopComplete();

-  // It shouldn't be necessary to call StopTracingSession. If we get this call
-  // it means that the service did shutdown before us, so there is no point
-  // trying it to ask it to stop the session. We should just remember to cleanup
-  // the consumer vector.
-  connected_ = false;
+  DataSourceBase::ClearIncrementalStateArgs clear_incremental_state_args;
+  clear_incremental_state_args.internal_instance_index = ds.instance_idx;
+  {
+    std::unique_lock<std::recursive_mutex> lock;
+    if (ds.requires_callbacks_under_lock)
+      lock = std::unique_lock<std::recursive_mutex>(ds.internal_state->lock);
+    ds.internal_state->data_source->WillClearIncrementalState(
+        clear_incremental_state_args);
+  }

-  // Notify the muxer that it is safe to destroy |this|. This is needed because
-  // the ConsumerEndpoint stored in |service_| requires that |this| be safe to
-  // access until OnDisconnect() is called.
-  muxer_->OnConsumerDisconnected(this);
+  // Make DataSource::TraceContext::GetIncrementalState() eventually notice that
+  // the incremental state should be cleared.
+  ds.static_state->GetUnsafe(ds.instance_idx)
+      ->incremental_state_generation.fetch_add(1, std::memory_order_relaxed);
 }

-void TracingMuxerImpl::ConsumerImpl::Disconnect() {
-  // This is weird and deserves a comment.
-  //
-  // When we called the ConnectConsumer method on the service it returns
-  // us a ConsumerEndpoint which we stored in |service_|, however this
-  // ConsumerEndpoint holds a pointer to the ConsumerImpl pointed to by
-  // |this|. Part of the API contract to TracingService::ConnectConsumer is that
-  // the ConsumerImpl pointer has to be valid until the
-  // ConsumerImpl::OnDisconnect method is called. Therefore we reset the
-  // ConsumerEndpoint |service_|. Eventually this will call
-  // ConsumerImpl::OnDisconnect and we will inform the muxer it is safe to
-  // call the destructor of |this|.
-  service_.reset();
-}
+bool TracingMuxerImpl::FlushDataSource_AsyncBegin(
+    TracingBackendId backend_id,
+    DataSourceInstanceID instance_id,
+    FlushRequestID flush_id,
+    FlushFlags flush_flags) {
+  PERFETTO_DLOG("Flushing data source %" PRIu64, instance_id);
+  auto ds = FindDataSource(backend_id, instance_id);
+  if (!ds) {
+    PERFETTO_ELOG("Could not find data source to flush");
+    return true;
+  }

-void TracingMuxerImpl::ConsumerImpl::OnTracingDisabled(
-    const std::string& error) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  PERFETTO_DCHECK(!stopped_);
-  stopped_ = true;
+  uint32_t backend_connection_id = ds.internal_state->backend_connection_id;

-  if (!error.empty())
-    NotifyError(TracingError{TracingError::kTracingFailed, error});
+  FlushArgsImpl flush_args;
+  flush_args.flush_flags = flush_flags;
+  flush_args.internal_instance_index = ds.instance_idx;
+  flush_args.async_flush_closure = [this, backend_id, backend_connection_id,
+                                    instance_id, ds, flush_id] {
+    // TracingMuxerImpl is long lived, capturing |this| is okay.
+    // The notification closure can be moved out of the StopArgs by the
+    // embedder to handle stop asynchronously. The embedder might then
+    // call the closure on a different thread than the current one, hence
+    // this nested PostTask().
+    task_runner_->PostTask(
+        [this, backend_id, backend_connection_id, instance_id, ds, flush_id] {
+          FlushDataSource_AsyncEnd(backend_id, backend_connection_id,
+                                   instance_id, ds, flush_id);
+        });
+  };
+  {
+    std::unique_lock<std::recursive_mutex> lock;
+    if (ds.requires_callbacks_under_lock)
+      lock = std::unique_lock<std::recursive_mutex>(ds.internal_state->lock);
+    ds.internal_state->data_source->OnFlush(flush_args);
+  }

-  // If we're still waiting for the start event, fire it now. This may happen if
-  // there are no active data sources in the session.
-  NotifyStartComplete();
-  NotifyStopComplete();
+  // |async_flush_closure| is moved out of |flush_args| if the producer
+  // requested to handle the flush asynchronously.
+  bool handled = static_cast<bool>(flush_args.async_flush_closure);
+  return handled;
 }

-void TracingMuxerImpl::ConsumerImpl::NotifyStartComplete() {
+void TracingMuxerImpl::FlushDataSource_AsyncEnd(
+    TracingBackendId backend_id,
+    uint32_t backend_connection_id,
+    DataSourceInstanceID instance_id,
+    const FindDataSourceRes& ds,
+    FlushRequestID flush_id) {
+  PERFETTO_DLOG("Ending async flush of data source %" PRIu64, instance_id);
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  if (start_complete_callback_) {
-    muxer_->task_runner_->PostTask(std::move(start_complete_callback_));
-    start_complete_callback_ = nullptr;
-  }
-  if (blocking_start_complete_callback_) {
-    muxer_->task_runner_->PostTask(
-        std::move(blocking_start_complete_callback_));
-    blocking_start_complete_callback_ = nullptr;
-  }
-}

-void TracingMuxerImpl::ConsumerImpl::NotifyError(const TracingError& error) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  if (error_callback_) {
-    muxer_->task_runner_->PostTask(
-        std::bind(std::move(error_callback_), error));
+  // Check that the data source instance is still active and was not modified
+  // while it was being flushed.
+  if (!ds.static_state->TryGet(ds.instance_idx) ||
+      ds.internal_state->backend_id != backend_id ||
+      ds.internal_state->backend_connection_id != backend_connection_id ||
+      ds.internal_state->data_source_instance_id != instance_id) {
+    PERFETTO_ELOG("Async flush of data source %" PRIu64
+                  " failed. This might be due to the data source being stopped "
+                  "in the meantime",
+                  instance_id);
+    return;
   }
-}

-void TracingMuxerImpl::ConsumerImpl::NotifyStopComplete() {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  if (stop_complete_callback_) {
-    muxer_->task_runner_->PostTask(std::move(stop_complete_callback_));
-    stop_complete_callback_ = nullptr;
-  }
-  if (blocking_stop_complete_callback_) {
-    muxer_->task_runner_->PostTask(std::move(blocking_stop_complete_callback_));
-    blocking_stop_complete_callback_ = nullptr;
-  }
-}
+  // |producer_backends_| is append-only, Backend instances are always valid.
+  PERFETTO_CHECK(backend_id < producer_backends_.size());
+  RegisteredProducerBackend& backend = *FindProducerBackendById(backend_id);

-void TracingMuxerImpl::ConsumerImpl::OnTraceData(
-    std::vector<TracePacket> packets,
-    bool has_more) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  if (!read_trace_callback_)
+  ProducerImpl* producer = backend.producer.get();
+  if (!producer)
     return;

-  size_t capacity = 0;
-  for (const auto& packet : packets) {
-    // 16 is an over-estimation of the proto preamble size
-    capacity += packet.size() + 16;
-  }
-
-  // The shared_ptr is to avoid making a copy of the buffer when PostTask-ing.
-  std::shared_ptr<std::vector<char>> buf(new std::vector<char>());
-  buf->reserve(capacity);
-  for (auto& packet : packets) {
-    char* start;
-    size_t size;
-    std::tie(start, size) = packet.GetProtoPreamble();
-    buf->insert(buf->end(), start, start + size);
-    for (auto& slice : packet.slices()) {
-      const auto* slice_data = reinterpret_cast<const char*>(slice.start);
-      buf->insert(buf->end(), slice_data, slice_data + slice.size);
-    }
+  // If the tracing service disconnects and reconnects while a data source is
+  // handling a flush request, there's no point is sending the flush reply to
+  // the newly reconnected producer.
+  if (producer->connected_ &&
+      backend.producer->connection_id_.load(std::memory_order_relaxed) ==
+          backend_connection_id) {
+    producer->NotifyFlushForDataSourceDone(instance_id, flush_id);
   }
+}

-  auto callback = read_trace_callback_;
-  muxer_->task_runner_->PostTask([callback, buf, has_more] {
-    TracingSession::ReadTraceCallbackArgs callback_arg{};
-    callback_arg.data = buf->empty() ? nullptr : &(*buf)[0];
-    callback_arg.size = buf->size();
-    callback_arg.has_more = has_more;
-    callback(callback_arg);
-  });
+void TracingMuxerImpl::SyncProducersForTesting() {
+  std::mutex mutex;
+  std::condition_variable cv;

-  if (!has_more)
-    read_trace_callback_ = nullptr;
-}
+  // IPC-based producers don't report connection errors explicitly for each
+  // command, but instead with an asynchronous callback
+  // (ProducerImpl::OnDisconnected). This means that the sync command below
+  // may have completed but failed to reach the service because of a
+  // disconnection, but we can't tell until the disconnection message comes
+  // through. To guard against this, we run two whole rounds of sync round-trips
+  // before returning; the first one will detect any disconnected producers and
+  // the second one will ensure any reconnections have completed and all data
+  // sources are registered in the service again.
+  for (size_t i = 0; i < 2; i++) {
+    size_t countdown = std::numeric_limits<size_t>::max();
+    task_runner_->PostTask([this, &mutex, &cv, &countdown] {
+      {
+        std::unique_lock<std::mutex> countdown_lock(mutex);
+        countdown = producer_backends_.size();
+      }
+      for (auto& backend : producer_backends_) {
+        auto* producer = backend.producer.get();
+        producer->service_->Sync([&mutex, &cv, &countdown] {
+          std::unique_lock<std::mutex> countdown_lock(mutex);
+          countdown--;
+          cv.notify_one();
+        });
+      }
+    });

-void TracingMuxerImpl::ConsumerImpl::OnObservableEvents(
-    const ObservableEvents& events) {
-  if (events.instance_state_changes_size()) {
-    for (const auto& state_change : events.instance_state_changes()) {
-      DataSourceHandle handle{state_change.producer_name(),
-                              state_change.data_source_name()};
-      data_source_states_[handle] =
-          state_change.state() ==
-          ObservableEvents::DATA_SOURCE_INSTANCE_STATE_STARTED;
+    {
+      std::unique_lock<std::mutex> countdown_lock(mutex);
+      cv.wait(countdown_lock, [&countdown] { return !countdown; });
     }
   }

-  if (events.instance_state_changes_size() ||
-      events.all_data_sources_started()) {
-    // Data sources are first reported as being stopped before starting, so once
-    // all the data sources we know about have started we can declare tracing
-    // begun. In the case where there are no matching data sources for the
-    // session, the service will report the all_data_sources_started() event
-    // without adding any instances (only since Android S / Perfetto v10.0).
-    if (start_complete_callback_ || blocking_start_complete_callback_) {
-      bool all_data_sources_started = std::all_of(
-          data_source_states_.cbegin(), data_source_states_.cend(),
-          [](std::pair<DataSourceHandle, bool> state) { return state.second; });
-      if (all_data_sources_started)
-        NotifyStartComplete();
-    }
-  }
-}
+  // Check that all producers are indeed connected.
+  bool done = false;
+  bool all_producers_connected = true;
+  task_runner_->PostTask([this, &mutex, &cv, &done, &all_producers_connected] {
+    for (auto& backend : producer_backends_)
+      all_producers_connected &= backend.producer->connected_;
+    std::unique_lock<std::mutex> lock(mutex);
+    done = true;
+    cv.notify_one();
+  });

-void TracingMuxerImpl::ConsumerImpl::OnSessionCloned(
-    const OnSessionClonedArgs& args) {
-  if (!clone_trace_callback_)
-    return;
-  TracingSession::CloneTraceCallbackArgs callback_arg{};
-  callback_arg.success = args.success;
-  callback_arg.error = std::move(args.error);
-  callback_arg.uuid_msb = args.uuid.msb();
-  callback_arg.uuid_lsb = args.uuid.lsb();
-  muxer_->task_runner_->PostTask(
-      std::bind(std::move(clone_trace_callback_), std::move(callback_arg)));
-  clone_trace_callback_ = nullptr;
+  {
+    std::unique_lock<std::mutex> lock(mutex);
+    cv.wait(lock, [&done] { return done; });
+  }
+  PERFETTO_DCHECK(all_producers_connected);
 }

-void TracingMuxerImpl::ConsumerImpl::OnTraceStats(
-    bool success,
-    const TraceStats& trace_stats) {
-  if (!get_trace_stats_callback_)
-    return;
-  TracingSession::GetTraceStatsCallbackArgs callback_arg{};
-  callback_arg.success = success;
-  callback_arg.trace_stats_data = trace_stats.SerializeAsArray();
-  muxer_->task_runner_->PostTask(
-      std::bind(std::move(get_trace_stats_callback_), std::move(callback_arg)));
-  get_trace_stats_callback_ = nullptr;
+void TracingMuxerImpl::DestroyAllTraceWritersForCurrentThread() {
+  // Forcefully reset all trace writers on the current thread, regardless of
+  // whether their data source instances are stopped.
+  auto* root_tls = GetOrCreateTracingTLS();
+  auto destroy_all_instances = [](DataSourceThreadLocalState& tls) {
+    for (auto& ds_tls : tls.per_instance) {
+      ds_tls.Reset();
+    }
+  };
+  for (auto& tls : root_tls->data_sources_tls)
+    destroy_all_instances(tls);
+  destroy_all_instances(root_tls->track_event_tls);
+  // Unlike DestroyStoppedTraceWritersForCurrentThread(), we deliberately don't
+  // sync |root_tls->generation|: this only runs at shutdown, after which any
+  // trace point on this thread aborts in TracingMuxerFake anyway.
 }

-// The callbacks below are not used.
-void TracingMuxerImpl::ConsumerImpl::OnDetach(bool) {}
-void TracingMuxerImpl::ConsumerImpl::OnAttach(bool, const TraceConfig&) {}
-// ----- End of TracingMuxerImpl::ConsumerImpl
-
-// ----- Begin of TracingMuxerImpl::TracingSessionImpl
+void TracingMuxerImpl::DestroyStoppedTraceWritersForCurrentThread() {
+  // Iterate across all possible data source types.
+  auto cur_generation = generation_.load(std::memory_order_acquire);
+  auto* root_tls = GetOrCreateTracingTLS();

-// TracingSessionImpl is the RAII object returned to API clients when they
-// invoke Tracing::CreateTracingSession. They use it for starting/stopping
-// tracing.
+  auto destroy_stopped_instances = [](DataSourceThreadLocalState& tls) {
+    // |tls| has a vector of per-data-source-instance thread-local state.
+    DataSourceStaticState* static_state = tls.static_state;
+    if (!static_state)
+      return;  // Slot not used.

-TracingMuxerImpl::TracingSessionImpl::TracingSessionImpl(
-    TracingMuxerImpl* muxer,
-    TracingSessionGlobalID session_id,
-    BackendType backend_type)
-    : muxer_(muxer), session_id_(session_id), backend_type_(backend_type) {}
+    // Iterate across all possible instances for this data source.
+    for (uint32_t inst = 0; inst < kMaxDataSourceInstances; inst++) {
+      DataSourceInstanceThreadLocalState& ds_tls = tls.per_instance[inst];
+      if (!ds_tls.trace_writer)
+        continue;

-// Can be destroyed from any thread.
-TracingMuxerImpl::TracingSessionImpl::~TracingSessionImpl() {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  muxer->task_runner_->PostTask(
-      [muxer, session_id] { muxer->DestroyTracingSession(session_id); });
-}
+      DataSourceState* ds_state = static_state->TryGet(inst);
+      if (ds_state &&
+          ds_state->muxer_id_for_testing == ds_tls.muxer_id_for_testing &&
+          ds_state->backend_id == ds_tls.backend_id &&
+          ds_state->backend_connection_id == ds_tls.backend_connection_id &&
+          ds_state->startup_target_buffer_reservation.load(
+              std::memory_order_relaxed) ==
+              ds_tls.startup_target_buffer_reservation &&
+          ds_state->buffer_id == ds_tls.buffer_id &&
+          ds_state->data_source_instance_id == ds_tls.data_source_instance_id) {
+        continue;
+      }

-// Can be called from any thread.
-void TracingMuxerImpl::TracingSessionImpl::Setup(const TraceConfig& cfg,
-                                                 int fd) {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  std::shared_ptr<TraceConfig> trace_config(new TraceConfig(cfg));
-  if (fd >= 0) {
-    base::ignore_result(backend_type_);  // For -Wunused in the amalgamation.
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    if (backend_type_ != kInProcessBackend) {
-      PERFETTO_FATAL(
-          "Passing a file descriptor to TracingSession::Setup() is only "
-          "supported with the kInProcessBackend on Windows. Use "
-          "TracingSession::ReadTrace() instead");
+      // The DataSource instance has been destroyed or recycled.
+      ds_tls.Reset();  // Will also destroy the |ds_tls.trace_writer|.
     }
-#endif
-    trace_config->set_write_into_file(true);
-    fd = dup(fd);
+  };
+
+  for (size_t ds_idx = 0; ds_idx < kMaxDataSources; ds_idx++) {
+    // |tls| has a vector of per-data-source-instance thread-local state.
+    DataSourceThreadLocalState& tls = root_tls->data_sources_tls[ds_idx];
+    destroy_stopped_instances(tls);
   }
-  muxer->task_runner_->PostTask([muxer, session_id, trace_config, fd] {
-    muxer->SetupTracingSession(session_id, trace_config, base::ScopedFile(fd));
-  });
+  destroy_stopped_instances(root_tls->track_event_tls);
+  root_tls->generation = cur_generation;
 }

-// Can be called from any thread.
-void TracingMuxerImpl::TracingSessionImpl::Start() {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  muxer->task_runner_->PostTask(
-      [muxer, session_id] { muxer->StartTracingSession(session_id); });
+// Called both when a new data source is registered or when a new backend
+// connects. In both cases we want to be sure we reflected the data source
+// registrations on the backends.
+void TracingMuxerImpl::UpdateDataSourcesOnAllBackends() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  for (RegisteredDataSource& rds : data_sources_) {
+    UpdateDataSourceOnAllBackends(rds, /*is_changed=*/false);
+  }
 }

-void TracingMuxerImpl::TracingSessionImpl::CloneTrace(CloneTraceArgs args,
-                                                      CloneTraceCallback cb) {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  muxer->task_runner_->PostTask([muxer, session_id, args, cb] {
-    muxer->CloneTracingSession(session_id, args, std::move(cb));
-  });
-}
+void TracingMuxerImpl::UpdateDataSourceOnAllBackends(RegisteredDataSource& rds,
+                                                     bool is_changed) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  for (RegisteredProducerBackend& backend : producer_backends_) {
+    // We cannot call RegisterDataSource on the backend before it connects.
+    if (!backend.producer->connected_)
+      continue;

-// Can be called from any thread.
-void TracingMuxerImpl::TracingSessionImpl::ChangeTraceConfig(
-    const TraceConfig& cfg) {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  muxer->task_runner_->PostTask([muxer, session_id, cfg] {
-    muxer->ChangeTracingSessionConfig(session_id, cfg);
-  });
-}
+    PERFETTO_DCHECK(rds.static_state->index < kMaxDataSources);
+    bool is_registered = backend.producer->registered_data_sources_.test(
+        rds.static_state->index);
+    if (is_registered && !is_changed)
+      continue;

-// Can be called from any thread except the service thread.
-void TracingMuxerImpl::TracingSessionImpl::StartBlocking() {
-  PERFETTO_DCHECK(!muxer_->task_runner_->RunsTasksOnCurrentThread());
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  base::WaitableEvent tracing_started;
-  muxer->task_runner_->PostTask([muxer, session_id, &tracing_started] {
-    auto* consumer = muxer->FindConsumer(session_id);
-    if (!consumer) {
-      // TODO(skyostil): Signal an error to the user.
-      tracing_started.Notify();
-      return;
+    if (!rds.descriptor.no_flush()) {
+      rds.descriptor.set_no_flush(rds.no_flush);
     }
-    PERFETTO_DCHECK(!consumer->blocking_start_complete_callback_);
-    consumer->blocking_start_complete_callback_ = [&] {
-      tracing_started.Notify();
-    };
-    muxer->StartTracingSession(session_id);
-  });
-  tracing_started.Wait();
-}
-
-// Can be called from any thread.
-void TracingMuxerImpl::TracingSessionImpl::Flush(
-    std::function<void(bool)> user_callback,
-    uint32_t timeout_ms) {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  muxer->task_runner_->PostTask([muxer, session_id, timeout_ms, user_callback] {
-    auto* consumer = muxer->FindConsumer(session_id);
-    if (!consumer) {
-      std::move(user_callback)(false);
-      return;
+    rds.descriptor.set_will_notify_on_start(true);
+    if (!rds.descriptor.has_will_notify_on_stop()) {
+      rds.descriptor.set_will_notify_on_stop(true);
     }
-    muxer->FlushTracingSession(session_id, timeout_ms,
-                               std::move(user_callback));
-  });
-}
-
-// Can be called from any thread.
-void TracingMuxerImpl::TracingSessionImpl::Stop() {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  muxer->task_runner_->PostTask(
-      [muxer, session_id] { muxer->StopTracingSession(session_id); });
-}

-// Can be called from any thread except the service thread.
-void TracingMuxerImpl::TracingSessionImpl::StopBlocking() {
-  PERFETTO_DCHECK(!muxer_->task_runner_->RunsTasksOnCurrentThread());
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  base::WaitableEvent tracing_stopped;
-  muxer->task_runner_->PostTask([muxer, session_id, &tracing_stopped] {
-    auto* consumer = muxer->FindConsumer(session_id);
-    if (!consumer) {
-      // TODO(skyostil): Signal an error to the user.
-      tracing_stopped.Notify();
-      return;
+    rds.descriptor.set_handles_incremental_state_clear(true);
+    rds.descriptor.set_id(rds.static_state->id);
+    if (is_registered) {
+      backend.producer->service_->UpdateDataSource(rds.descriptor);
+    } else {
+      backend.producer->service_->RegisterDataSource(rds.descriptor);
     }
-    PERFETTO_DCHECK(!consumer->blocking_stop_complete_callback_);
-    consumer->blocking_stop_complete_callback_ = [&] {
-      tracing_stopped.Notify();
-    };
-    muxer->StopTracingSession(session_id);
-  });
-  tracing_stopped.Wait();
+    backend.producer->registered_data_sources_.set(rds.static_state->index);
+  }
 }

-// Can be called from any thread.
-void TracingMuxerImpl::TracingSessionImpl::ReadTrace(ReadTraceCallback cb) {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  muxer->task_runner_->PostTask([muxer, session_id, cb] {
-    muxer->ReadTracingSessionData(session_id, std::move(cb));
-  });
-}
+void TracingMuxerImpl::SetupTracingSession(
+    TracingSessionGlobalID session_id,
+    const std::shared_ptr<TraceConfig>& trace_config,
+    base::ScopedFile trace_fd) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  PERFETTO_CHECK(!trace_fd || trace_config->write_into_file());

-// Can be called from any thread.
-void TracingMuxerImpl::TracingSessionImpl::SetOnStartCallback(
-    std::function<void()> cb) {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  muxer->task_runner_->PostTask([muxer, session_id, cb] {
-    auto* consumer = muxer->FindConsumer(session_id);
-    if (!consumer)
-      return;
-    consumer->start_complete_callback_ = cb;
-  });
-}
+  auto* consumer = FindConsumer(session_id);
+  if (!consumer)
+    return;

-// Can be called from any thread
-void TracingMuxerImpl::TracingSessionImpl::SetOnErrorCallback(
-    std::function<void(TracingError)> cb) {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  muxer->task_runner_->PostTask([muxer, session_id, cb] {
-    auto* consumer = muxer->FindConsumer(session_id);
-    if (!consumer) {
-      // Notify the client about concurrent disconnection of the session.
-      if (cb)
-        cb(TracingError{TracingError::kDisconnected, "Peer disconnected"});
-      return;
-    }
-    consumer->error_callback_ = cb;
-  });
-}
+  consumer->trace_config_ = trace_config;
+  if (trace_fd)
+    consumer->trace_fd_ = std::move(trace_fd);

-// Can be called from any thread.
-void TracingMuxerImpl::TracingSessionImpl::SetOnStopCallback(
-    std::function<void()> cb) {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  muxer->task_runner_->PostTask([muxer, session_id, cb] {
-    auto* consumer = muxer->FindConsumer(session_id);
-    if (!consumer)
-      return;
-    consumer->stop_complete_callback_ = cb;
-  });
-}
+  if (!consumer->connected_)
+    return;

-// Can be called from any thread.
-void TracingMuxerImpl::TracingSessionImpl::GetTraceStats(
-    GetTraceStatsCallback cb) {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  muxer->task_runner_->PostTask([muxer, session_id, cb] {
-    muxer->GetTraceStats(session_id, std::move(cb));
-  });
+  // Only used in the deferred start mode.
+  if (trace_config->deferred_start()) {
+    consumer->service_->EnableTracing(*trace_config,
+                                      std::move(consumer->trace_fd_));
+  }
 }

-// Can be called from any thread.
-void TracingMuxerImpl::TracingSessionImpl::QueryServiceState(
-    QueryServiceStateCallback cb) {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  muxer->task_runner_->PostTask([muxer, session_id, cb] {
-    muxer->QueryServiceState(session_id, std::move(cb));
-  });
-}
+void TracingMuxerImpl::StartTracingSession(TracingSessionGlobalID session_id) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);

-// ----- End of TracingMuxerImpl::TracingSessionImpl
+  auto* consumer = FindConsumer(session_id);

-// ----- Begin of TracingMuxerImpl::StartupTracingSessionImpl
+  if (!consumer)
+    return;

-TracingMuxerImpl::StartupTracingSessionImpl::StartupTracingSessionImpl(
-    TracingMuxerImpl* muxer,
-    TracingSessionGlobalID session_id,
-    BackendType backend_type)
-    : muxer_(muxer), session_id_(session_id), backend_type_(backend_type) {}
+  if (!consumer->trace_config_) {
+    PERFETTO_ELOG("Must call Setup(config) first");
+    return;
+  }

-// Can be destroyed from any thread.
-TracingMuxerImpl::StartupTracingSessionImpl::~StartupTracingSessionImpl() =
-    default;
+  if (!consumer->connected_) {
+    consumer->start_pending_ = true;
+    return;
+  }

-void TracingMuxerImpl::StartupTracingSessionImpl::Abort() {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  auto backend_type = backend_type_;
-  muxer->task_runner_->PostTask([muxer, session_id, backend_type] {
-    muxer->AbortStartupTracingSession(session_id, backend_type);
-  });
+  consumer->start_pending_ = false;
+  if (consumer->trace_config_->deferred_start()) {
+    consumer->service_->StartTracing();
+  } else {
+    consumer->service_->EnableTracing(*consumer->trace_config_,
+                                      std::move(consumer->trace_fd_));
+  }
+
+  // TODO implement support for the deferred-start + fast-triggering case.
 }

-// Must not be called from the SDK's internal thread.
-void TracingMuxerImpl::StartupTracingSessionImpl::AbortBlocking() {
-  auto* muxer = muxer_;
-  auto session_id = session_id_;
-  auto backend_type = backend_type_;
-  PERFETTO_CHECK(!muxer->task_runner_->RunsTasksOnCurrentThread());
-  base::WaitableEvent event;
-  muxer->task_runner_->PostTask([muxer, session_id, backend_type, &event] {
-    muxer->AbortStartupTracingSession(session_id, backend_type);
-    event.Notify();
-  });
-  event.Wait();
+void TracingMuxerImpl::CloneTracingSession(
+    TracingSessionGlobalID session_id,
+    TracingSession::CloneTraceArgs args,
+    TracingSession::CloneTraceCallback callback) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  auto* consumer = FindConsumer(session_id);
+  if (!consumer) {
+    TracingSession::CloneTraceCallbackArgs callback_arg{};
+    callback_arg.success = false;
+    callback_arg.error = "Tracing session not found";
+    callback(callback_arg);
+    return;
+  }
+  // Multiple concurrent cloning isn't supported.
+  PERFETTO_DCHECK(!consumer->clone_trace_callback_);
+  consumer->clone_trace_callback_ = std::move(callback);
+  ConsumerEndpoint::CloneSessionArgs consumer_args;
+  consumer_args.unique_session_name = args.unique_session_name;
+  if (!consumer->connected_) {
+    consumer->session_to_clone_ = std::move(consumer_args);
+    return;
+  }
+  consumer->session_to_clone_ = std::nullopt;
+  consumer->service_->CloneSession(std::move(consumer_args));
 }

-// ----- End of TracingMuxerImpl::StartupTracingSessionImpl
+void TracingMuxerImpl::ChangeTracingSessionConfig(
+    TracingSessionGlobalID session_id,
+    const TraceConfig& trace_config) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);

-// static
-TracingMuxer* TracingMuxer::instance_ = TracingMuxerFake::Get();
+  auto* consumer = FindConsumer(session_id);

-// This is called by perfetto::Tracing::Initialize().
-// Can be called on any thread. Typically, but not necessarily, that will be
-// the embedder's main thread.
-TracingMuxerImpl::TracingMuxerImpl(const TracingInitArgs& args)
-    : TracingMuxer(args.platform ? args.platform
-                                 : Platform::GetDefaultPlatform()) {
-  PERFETTO_DETACH_FROM_THREAD(thread_checker_);
-  instance_ = this;
+  if (!consumer)
+    return;

-  // Create the thread where muxer, producers and service will live.
-  Platform::CreateTaskRunnerArgs tr_args{/*name_for_debugging=*/"TracingMuxer"};
-  task_runner_.reset(new NonReentrantTaskRunner(
-      this, platform_->CreateTaskRunner(std::move(tr_args))));
+  if (!consumer->trace_config_) {
+    // Changing the config is only supported for started sessions.
+    PERFETTO_ELOG("Must call Setup(config) and Start() first");
+    return;
+  }

-  // Run the initializer on that thread.
-  task_runner_->PostTask([this, args] {
-    Initialize(args);
-    AddBackends(args);
-  });
+  consumer->trace_config_ = std::make_shared<TraceConfig>(trace_config);
+  if (consumer->connected_)
+    consumer->service_->ChangeTraceConfig(trace_config);
 }

-void TracingMuxerImpl::Initialize(const TracingInitArgs& args) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);  // Rebind the thread checker.
-
-  policy_ = args.tracing_policy;
-  supports_multiple_data_source_instances_ =
-      args.supports_multiple_data_source_instances;
+void TracingMuxerImpl::FlushTracingSession(TracingSessionGlobalID session_id,
+                                           uint32_t timeout_ms,
+                                           std::function<void(bool)> callback) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  auto* consumer = FindConsumer(session_id);
+  if (!consumer || consumer->start_pending_ || consumer->stop_pending_ ||
+      !consumer->trace_config_) {
+    PERFETTO_ELOG("Flush() can be called only after Start() and before Stop()");
+    std::move(callback)(false);
+    return;
+  }

-  // Fallback backend for producer creation for an unsupported backend type.
-  PERFETTO_CHECK(producer_backends_.empty());
-  AddProducerBackend(internal::TracingBackendFake::GetInstance(),
-                     BackendType::kUnspecifiedBackend, args);
-  // Fallback backend for consumer creation for an unsupported backend type.
-  // This backend simply fails any attempt to start a tracing session.
-  PERFETTO_CHECK(consumer_backends_.empty());
-  AddConsumerBackend(internal::TracingBackendFake::GetInstance(),
-                     BackendType::kUnspecifiedBackend);
+  // For now we don't want to expose the flush reason to the consumer-side SDK
+  // users to avoid misuses until there is a strong need.
+  consumer->service_->Flush(timeout_ms, std::move(callback),
+                            FlushFlags(FlushFlags::Initiator::kConsumerSdk,
+                                       FlushFlags::Reason::kExplicit));
 }

-void TracingMuxerImpl::AddConsumerBackend(TracingConsumerBackend* backend,
-                                          BackendType type) {
-  if (!backend) {
-    // We skip the log in release builds because the *_backend_fake.cc code
-    // has already an ELOG before returning a nullptr.
-    PERFETTO_DLOG("Consumer backend creation failed, type %d",
-                  static_cast<int>(type));
+void TracingMuxerImpl::StopTracingSession(TracingSessionGlobalID session_id) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  auto* consumer = FindConsumer(session_id);
+  if (!consumer)
+    return;
+
+  if (consumer->start_pending_) {
+    // If the session hasn't started yet, wait until it does before stopping.
+    consumer->stop_pending_ = true;
     return;
   }
-  // Keep the backends sorted by type.
-  auto it =
-      std::upper_bound(consumer_backends_.begin(), consumer_backends_.end(),
-                       type, CompareBackendByType<RegisteredConsumerBackend>());
-  it = consumer_backends_.emplace(it);

-  RegisteredConsumerBackend& rb = *it;
-  rb.backend = backend;
-  rb.type = type;
+  consumer->stop_pending_ = false;
+  if (consumer->stopped_) {
+    // If the session was already stopped (e.g., it failed to start), don't try
+    // stopping again.
+    consumer->NotifyStopComplete();
+  } else if (!consumer->trace_config_) {
+    PERFETTO_ELOG("Must call Setup(config) and Start() first");
+    return;
+  } else {
+    consumer->service_->DisableTracing();
+  }
+
+  consumer->trace_config_.reset();
 }

-void TracingMuxerImpl::AddProducerBackend(TracingProducerBackend* backend,
-                                          BackendType type,
-                                          const TracingInitArgs& args) {
-  if (!backend) {
-    // We skip the log in release builds because the *_backend_fake.cc code
-    // has already an ELOG before returning a nullptr.
-    PERFETTO_DLOG("Producer backend creation failed, type %d",
-                  static_cast<int>(type));
+void TracingMuxerImpl::DestroyTracingSession(
+    TracingSessionGlobalID session_id) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  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
+    // OnConsumerDisconnect which will invalidate all the iterators to
+    // |backend.consumers|.
+    ConsumerImpl* consumer = nullptr;
+    for (auto& con : backend.consumers) {
+      if (con->session_id_ == session_id) {
+        consumer = con.get();
+        break;
+      }
+    }
+    if (consumer) {
+      // We broke out of the loop above on the assumption that each backend will
+      // only have a single consumer per session. This DCHECK ensures that
+      // this is the case.
+      PERFETTO_DCHECK(
+          std::count_if(backend.consumers.begin(), backend.consumers.end(),
+                        [session_id](const std::unique_ptr<ConsumerImpl>& con) {
+                          return con->session_id_ == session_id;
+                        }) == 1u);
+      consumer->Disconnect();
+    }
+  }
+}
+
+void TracingMuxerImpl::ReadTracingSessionData(
+    TracingSessionGlobalID session_id,
+    std::function<void(TracingSession::ReadTraceCallbackArgs)> callback) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  auto* consumer = FindConsumer(session_id);
+  if (!consumer) {
+    // TODO(skyostil): Signal an error to the user.
+    TracingSession::ReadTraceCallbackArgs callback_arg{};
+    callback(callback_arg);
     return;
   }
-  TracingBackendId backend_id = producer_backends_.size();
-  // Keep the backends sorted by type.
-  auto it =
-      std::upper_bound(producer_backends_.begin(), producer_backends_.end(),
-                       type, CompareBackendByType<RegisteredProducerBackend>());
-  it = producer_backends_.emplace(it);
+  PERFETTO_DCHECK(!consumer->read_trace_callback_);
+  consumer->read_trace_callback_ = std::move(callback);
+  consumer->service_->ReadBuffers();
+}

-  RegisteredProducerBackend& rb = *it;
-  rb.backend = backend;
-  rb.id = backend_id;
-  rb.type = type;
-  rb.producer.reset(new ProducerImpl(this, backend_id,
-                                     args.shmem_batch_commits_duration_ms,
-                                     args.shmem_direct_patching_enabled));
-  rb.producer_conn_args.producer = rb.producer.get();
-  rb.producer_conn_args.producer_name = platform_->GetCurrentProcessName();
-  rb.producer_conn_args.task_runner = task_runner_.get();
-  rb.producer_conn_args.shmem_size_hint_bytes = args.shmem_size_hint_kb * 1024;
-  rb.producer_conn_args.shmem_page_size_hint_bytes =
-      args.shmem_page_size_hint_kb * 1024;
-  rb.producer_conn_args.create_socket_async = args.create_socket_async;
-  rb.producer_conn_args.machine_id = args.machine_id;
-  rb.producer->Initialize(rb.backend->ConnectProducer(rb.producer_conn_args));
+void TracingMuxerImpl::GetTraceStats(
+    TracingSessionGlobalID session_id,
+    TracingSession::GetTraceStatsCallback callback) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  auto* consumer = FindConsumer(session_id);
+  if (!consumer) {
+    TracingSession::GetTraceStatsCallbackArgs callback_arg{};
+    callback_arg.success = false;
+    callback(std::move(callback_arg));
+    return;
+  }
+  PERFETTO_DCHECK(!consumer->get_trace_stats_callback_);
+  consumer->get_trace_stats_callback_ = std::move(callback);
+  if (!consumer->connected_) {
+    consumer->get_trace_stats_pending_ = true;
+    return;
+  }
+  consumer->get_trace_stats_pending_ = false;
+  consumer->service_->GetTraceStats();
+}
+
+void TracingMuxerImpl::QueryServiceState(
+    TracingSessionGlobalID session_id,
+    TracingSession::QueryServiceStateCallback callback) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  auto* consumer = FindConsumer(session_id);
+  if (!consumer) {
+    TracingSession::QueryServiceStateCallbackArgs callback_arg{};
+    callback_arg.success = false;
+    callback(std::move(callback_arg));
+    return;
+  }
+  PERFETTO_DCHECK(!consumer->query_service_state_callback_);
+  if (!consumer->connected_) {
+    consumer->query_service_state_callback_ = std::move(callback);
+    return;
+  }
+  auto callback_wrapper = [callback](bool success,
+                                     protos::gen::TracingServiceState state) {
+    TracingSession::QueryServiceStateCallbackArgs callback_arg{};
+    callback_arg.success = success;
+    callback_arg.service_state_data = state.SerializeAsArray();
+    callback(std::move(callback_arg));
+  };
+  consumer->service_->QueryServiceState({}, std::move(callback_wrapper));
 }

-TracingMuxerImpl::RegisteredProducerBackend*
-TracingMuxerImpl::FindProducerBackendById(TracingBackendId id) {
-  for (RegisteredProducerBackend& b : producer_backends_) {
-    if (b.id == id) {
-      return &b;
+void TracingMuxerImpl::SetBatchCommitsDurationForTesting(
+    uint32_t batch_commits_duration_ms,
+    BackendType backend_type) {
+  for (RegisteredProducerBackend& backend : producer_backends_) {
+    if (backend.producer && backend.producer->connected_ &&
+        backend.type == backend_type) {
+      backend.producer->service_->MaybeSharedMemoryArbiter()
+          ->SetBatchCommitsDuration(batch_commits_duration_ms);
     }
   }
-  return nullptr;
 }

-TracingMuxerImpl::RegisteredProducerBackend*
-TracingMuxerImpl::FindProducerBackendByType(BackendType type) {
-  for (RegisteredProducerBackend& b : producer_backends_) {
-    if (b.type == type) {
-      return &b;
+bool TracingMuxerImpl::EnableDirectSMBPatchingForTesting(
+    BackendType backend_type) {
+  for (RegisteredProducerBackend& backend : producer_backends_) {
+    if (backend.producer && backend.producer->connected_ &&
+        backend.type == backend_type &&
+        !backend.producer->service_->MaybeSharedMemoryArbiter()
+             ->EnableDirectSMBPatching()) {
+      return false;
     }
   }
-  return nullptr;
+  return true;
 }

-TracingMuxerImpl::RegisteredConsumerBackend*
-TracingMuxerImpl::FindConsumerBackendByType(BackendType type) {
-  for (RegisteredConsumerBackend& b : consumer_backends_) {
-    if (b.type == type) {
-      return &b;
-    }
-  }
-  return nullptr;
+TracingMuxerImpl::ConsumerImpl* TracingMuxerImpl::FindConsumer(
+    TracingSessionGlobalID session_id) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  return FindConsumerAndBackend(session_id).first;
 }

-void TracingMuxerImpl::AddBackends(const TracingInitArgs& args) {
-  if (args.backends & kSystemBackend) {
-    PERFETTO_CHECK(args.system_producer_backend_factory_);
-    if (FindProducerBackendByType(kSystemBackend) == nullptr) {
-      AddProducerBackend(args.system_producer_backend_factory_(),
-                         kSystemBackend, args);
-    }
-    if (args.enable_system_consumer) {
-      PERFETTO_CHECK(args.system_consumer_backend_factory_);
-      if (FindConsumerBackendByType(kSystemBackend) == nullptr) {
-        AddConsumerBackend(args.system_consumer_backend_factory_(),
-                           kSystemBackend);
+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(), &backend};
       }
     }
   }
+  return {nullptr, nullptr};
+}

-  if (args.backends & kInProcessBackend) {
-    TracingBackend* b = nullptr;
-    if (FindProducerBackendByType(kInProcessBackend) == nullptr) {
-      if (!b) {
-        PERFETTO_CHECK(args.in_process_backend_factory_);
-        b = args.in_process_backend_factory_();
-      }
-      AddProducerBackend(b, kInProcessBackend, args);
-    }
-    if (FindConsumerBackendByType(kInProcessBackend) == nullptr) {
-      if (!b) {
-        PERFETTO_CHECK(args.in_process_backend_factory_);
-        b = args.in_process_backend_factory_();
-      }
-      AddConsumerBackend(b, kInProcessBackend);
-    }
-  }
+void TracingMuxerImpl::InitializeConsumer(TracingSessionGlobalID session_id) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);

-  if (args.backends & kCustomBackend) {
-    PERFETTO_CHECK(args.custom_backend);
-    if (FindProducerBackendByType(kCustomBackend) == nullptr) {
-      AddProducerBackend(args.custom_backend, kCustomBackend, args);
-    }
-    if (FindConsumerBackendByType(kCustomBackend) == nullptr) {
-      AddConsumerBackend(args.custom_backend, kCustomBackend);
-    }
-  }
+  auto res = FindConsumerAndBackend(session_id);
+  if (!res.first || !res.second)
+    return;
+  TracingMuxerImpl::ConsumerImpl* consumer = res.first;
+  RegisteredConsumerBackend& backend = *res.second;

-  if (args.backends & ~(kSystemBackend | kInProcessBackend | kCustomBackend)) {
-    PERFETTO_FATAL("Unsupported tracing backend type");
-  }
+  TracingBackend::ConnectConsumerArgs conn_args;
+  conn_args.consumer = consumer;
+  conn_args.task_runner = task_runner_.get();
+  consumer->Initialize(backend.backend->ConnectConsumer(conn_args));
 }

-// Can be called from any thread (but not concurrently).
-bool TracingMuxerImpl::RegisterDataSource(
-    const DataSourceDescriptor& descriptor,
-    DataSourceFactory factory,
-    DataSourceParams params,
-    bool no_flush,
-    DataSourceStaticState* static_state) {
-  // Ignore repeated registrations.
-  if (static_state->index != kMaxDataSources) {
-    PERFETTO_ELOG(
-        "Data source \"%s\" registration ignored: this data source type is "
-        "already registered. See "
-        "https://perfetto.dev/docs/instrumentation/"
-        "tracing-sdk#reporting-many-similar-things",
-        descriptor.name().c_str());
-    return true;
-  }
-
-  uint32_t new_index = next_data_source_index_++;
-  if (new_index >= kMaxDataSources) {
-    PERFETTO_DLOG(
-        "RegisterDataSource failed: too many data sources already registered");
-    return false;
+void TracingMuxerImpl::OnConsumerDisconnected(ConsumerImpl* consumer) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  for (RegisteredConsumerBackend& backend : consumer_backends_) {
+    auto pred = [consumer](const std::unique_ptr<ConsumerImpl>& con) {
+      return con.get() == consumer;
+    };
+    backend.consumers.erase(std::remove_if(backend.consumers.begin(),
+                                           backend.consumers.end(), pred),
+                            backend.consumers.end());
   }
+}

-  // Initialize the static state.
-  static_assert(sizeof(static_state->instances[0]) >= sizeof(DataSourceState),
-                "instances[] size mismatch");
-  for (size_t i = 0; i < static_state->instances.size(); i++)
-    new (&static_state->instances[i]) DataSourceState{};
-
-  static_state->index = new_index;
+void TracingMuxerImpl::SetMaxProducerReconnectionsForTesting(uint32_t count) {
+  max_producer_reconnections_.store(count);
+}

-  // Generate a semi-unique id for this data source.
-  uint64_t digest = base::FnvHasher::Combine(
-      reinterpret_cast<intptr_t>(static_state), base::GetWallTimeNs().count());
-  static_state->id = digest ? digest : 1;
+void TracingMuxerImpl::OnProducerDisconnected(ProducerImpl* producer) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  for (RegisteredProducerBackend& backend : producer_backends_) {
+    if (backend.producer.get() != producer)
+      continue;

-  task_runner_->PostTask(
-      [this, descriptor, factory, static_state, params, no_flush] {
-        data_sources_.emplace_back();
-        RegisteredDataSource& rds = data_sources_.back();
-        rds.descriptor = descriptor;
-        rds.factory = factory;
-        rds.params = params;
-        if (!supports_multiple_data_source_instances_) {
-          rds.params.supports_multiple_instances = false;
+    // The tracing service is disconnected. It does not make sense to keep
+    // tracing (we wouldn't be able to commit). On reconnection, the tracing
+    // service will restart the data sources.
+    for (const auto& rds : data_sources_) {
+      DataSourceStaticState* static_state = rds.static_state;
+      for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
+        auto* internal_state = static_state->TryGet(i);
+        if (internal_state && internal_state->backend_id == backend.id &&
+            internal_state->backend_connection_id ==
+                backend.producer->connection_id_.load(
+                    std::memory_order_relaxed)) {
+          StopDataSource_AsyncBeginImpl(
+              FindDataSourceRes(static_state, internal_state, i,
+                                rds.params.requires_callbacks_under_lock));
         }
-        rds.static_state = static_state;
-        rds.no_flush = no_flush;
-
-        UpdateDataSourceOnAllBackends(rds, /*is_changed=*/false);
-      });
-  return true;
-}
-
-// Can be called from any thread (but not concurrently).
-void TracingMuxerImpl::UpdateDataSourceDescriptor(
-    const DataSourceDescriptor& descriptor,
-    const DataSourceStaticState* static_state) {
-  task_runner_->PostTask([this, descriptor, static_state] {
-    for (auto& rds : data_sources_) {
-      if (rds.static_state == static_state) {
-        PERFETTO_CHECK(rds.descriptor.name() == descriptor.name());
-        rds.descriptor = descriptor;
-        rds.descriptor.set_id(static_state->id);
-        UpdateDataSourceOnAllBackends(rds, /*is_changed=*/true);
-        return;
       }
     }
-  });
-}

-// Can be called from any thread (but not concurrently).
-void TracingMuxerImpl::RegisterInterceptor(
-    const InterceptorDescriptor& descriptor,
-    InterceptorFactory factory,
-    InterceptorBase::TLSFactory tls_factory,
-    InterceptorBase::TracePacketCallback packet_callback) {
-  task_runner_->PostTask([this, descriptor, factory, tls_factory,
-                          packet_callback] {
-    // Ignore repeated registrations.
-    for (const auto& interceptor : interceptors_) {
-      if (interceptor.descriptor.name() == descriptor.name()) {
-        PERFETTO_DCHECK(interceptor.tls_factory == tls_factory);
-        PERFETTO_DCHECK(interceptor.packet_callback == packet_callback);
-        return;
-      }
-    }
-    // Only allow certain interceptors for now.
-    if (descriptor.name() != "test_interceptor" &&
-        descriptor.name() != "console" && descriptor.name() != "etwexport") {
-      PERFETTO_ELOG(
-          "Interceptors are experimental. If you want to use them, please "
-          "get in touch with the project maintainers "
-          "(https://perfetto.dev/docs/contributing/"
-          "getting-started#community).");
-      return;
+    // Try reconnecting the disconnected producer. If the connection succeeds,
+    // all the data sources will be automatically re-registered.
+    if (producer->connection_id_.load(std::memory_order_relaxed) >
+        max_producer_reconnections_.load()) {
+      // Avoid reconnecting a failing producer too many times. Instead we just
+      // leak the producer instead of trying to avoid further complicating
+      // cross-thread trace writer creation.
+      PERFETTO_ELOG("Producer disconnected too many times; not reconnecting");
+      continue;
     }
-    interceptors_.emplace_back();
-    RegisteredInterceptor& interceptor = interceptors_.back();
-    interceptor.descriptor = descriptor;
-    interceptor.factory = factory;
-    interceptor.tls_factory = tls_factory;
-    interceptor.packet_callback = packet_callback;
-  });
+
+    backend.producer->Initialize(
+        backend.backend->ConnectProducer(backend.producer_conn_args));
+    // Don't use producer-provided SMBs for the next connection unless startup
+    // tracing requires it again.
+    backend.producer_conn_args.use_producer_provided_smb = false;
+  }
 }

-void TracingMuxerImpl::ActivateTriggers(
-    const std::vector<std::string>& triggers,
-    uint32_t ttl_ms) {
-  base::TimeMillis expire_time =
-      base::GetWallTimeMs() + base::TimeMillis(ttl_ms);
-  task_runner_->PostTask([this, triggers, expire_time] {
-    for (RegisteredProducerBackend& backend : producer_backends_) {
-      if (backend.producer->connected_) {
-        backend.producer->service_->ActivateTriggers(triggers);
-      } else {
-        for (const std::string& trigger : triggers) {
-          backend.producer->on_connect_triggers_.emplace_back(trigger,
-                                                              expire_time);
-        }
-      }
-    }
-  });
+void TracingMuxerImpl::SweepDeadBackends() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  for (auto it = dead_backends_.begin(); it != dead_backends_.end();) {
+    auto next_it = it;
+    next_it++;
+    if (it->producer->SweepDeadServices())
+      dead_backends_.erase(it);
+    it = next_it;
+  }
 }

-// Checks if there is any matching startup tracing data source instance for a
-// new SetupDataSource call. If so, moves the data source to this tracing
-// session (and its target buffer) and returns true, otherwise returns false.
-static bool MaybeAdoptStartupTracingInDataSource(
+TracingMuxerImpl::FindDataSourceRes TracingMuxerImpl::FindDataSource(
     TracingBackendId backend_id,
-    uint32_t backend_connection_id,
-    DataSourceInstanceID instance_id,
-    const DataSourceConfig& cfg,
-    const std::vector<RegisteredDataSource>& data_sources) {
-  for (const auto& rds : data_sources) {
+    DataSourceInstanceID instance_id) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  // 0 is the sentinel id for unadopted startup data sources; never match it.
+  if (instance_id == 0)
+    return FindDataSourceRes();
+  RegisteredProducerBackend& backend = *FindProducerBackendById(backend_id);
+  for (const auto& rds : data_sources_) {
     DataSourceStaticState* static_state = rds.static_state;
     for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
       auto* internal_state = static_state->TryGet(i);
-
-      if (internal_state &&
-          internal_state->startup_target_buffer_reservation.load(
-              std::memory_order_relaxed) &&
-          internal_state->data_source_instance_id == 0 &&
-          internal_state->backend_id == backend_id &&
-          internal_state->backend_connection_id == backend_connection_id &&
-          internal_state->config &&
-          internal_state->data_source->CanAdoptStartupSession(
-              *internal_state->config, cfg)) {
-        PERFETTO_DLOG("Setting up data source %" PRIu64
-                      " %s by adopting it from a startup tracing session",
-                      instance_id, cfg.name().c_str());
-
-        std::lock_guard<std::recursive_mutex> lock(internal_state->lock);
-        // Set the associations. The actual takeover happens in
-        // StartDataSource().
-        internal_state->data_source_instance_id = instance_id;
-        internal_state->buffer_id =
-            static_cast<internal::BufferId>(cfg.target_buffer());
-        internal_state->buffer_exhausted_policy =
-            ComputeBufferExhaustedPolicy(cfg, rds.params);
-        internal_state->config.reset(new DataSourceConfig(cfg));
-
-        // TODO(eseckler): Should the data source config provided by the service
-        // be allowed to specify additional interceptors / additional data
-        // source params?
-
-        return true;
+      if (internal_state && internal_state->backend_id == backend_id &&
+          internal_state->backend_connection_id ==
+              backend.producer->connection_id_.load(
+                  std::memory_order_relaxed) &&
+          internal_state->data_source_instance_id == instance_id) {
+        return FindDataSourceRes(static_state, internal_state, i,
+                                 rds.params.requires_callbacks_under_lock);
       }
     }
   }
-  return false;
+  return FindDataSourceRes();
 }

-// Called by the service of one of the backends.
-void TracingMuxerImpl::SetupDataSource(TracingBackendId backend_id,
-                                       uint32_t backend_connection_id,
-                                       DataSourceInstanceID instance_id,
-                                       const DataSourceConfig& cfg) {
-  PERFETTO_DLOG("Setting up data source %" PRIu64 " %s", instance_id,
-                cfg.name().c_str());
-  PERFETTO_DCHECK_THREAD(thread_checker_);
+// Can be called from any thread.
+std::unique_ptr<TraceWriterBase> TracingMuxerImpl::CreateTraceWriter(
+    DataSourceStaticState* static_state,
+    uint32_t data_source_instance_index,
+    DataSourceState* data_source,
+    BufferExhaustedPolicy buffer_exhausted_policy) {
+  if (PERFETTO_UNLIKELY(data_source->interceptor_id)) {
+    // If the session is being intercepted, return a heap-backed trace writer
+    // instead. This is safe because all the data given to the interceptor is
+    // either thread-local (|instance_index|), statically allocated
+    // (|static_state|) or constant after initialization (|interceptor|). Access
+    // to the interceptor instance itself through |data_source| is protected by
+    // a statically allocated lock (similarly to the data source instance).
+    auto& interceptor = interceptors_[data_source->interceptor_id - 1];
+    return std::unique_ptr<TraceWriterBase>(new InterceptorTraceWriter(
+        interceptor.tls_factory(static_state, data_source_instance_index),
+        interceptor.packet_callback, static_state, data_source_instance_index));
+  }
+  ProducerImpl* producer =
+      FindProducerBackendById(data_source->backend_id)->producer.get();
+  // Atomically load the current service endpoint. We keep the pointer as a
+  // shared pointer on the stack to guard against it from being concurrently
+  // modified on the thread by ProducerImpl::Initialize() swapping in a
+  // reconnected service on the muxer task runner thread.
+  //
+  // The endpoint may also be concurrently modified by SweepDeadServices()
+  // clearing out old disconnected services. We guard against that by
+  // SharedMemoryArbiter keeping track of any outstanding trace writers. After
+  // shutdown has started, the trace writer created below will be a null one
+  // which will drop any written data. See SharedMemoryArbiter::TryShutdown().
+  //
+  // We use an atomic pointer instead of holding a lock because
+  // CreateTraceWriter posts tasks under the hood.
+  std::shared_ptr<ProducerEndpoint> service =
+      std::atomic_load(&producer->service_);

-  // First check if there is any matching startup tracing data source instance.
-  if (MaybeAdoptStartupTracingInDataSource(backend_id, backend_connection_id,
-                                           instance_id, cfg, data_sources_)) {
-    return;
+  // The service may have been disconnected and reconnected concurrently after
+  // the data source was enabled, in which case we may not have an arbiter, or
+  // would be creating a TraceWriter for the wrong (a newer) connection / SMB.
+  // Instead, early-out now. A relaxed load is fine here because the atomic_load
+  // above ensures that the |service| isn't newer.
+  if (producer->connection_id_.load(std::memory_order_relaxed) !=
+      data_source->backend_connection_id) {
+    return std::unique_ptr<TraceWriter>(new NullTraceWriter());
   }

-  for (const auto& rds : data_sources_) {
-    if (rds.descriptor.name() != cfg.name())
-      continue;
-    DataSourceStaticState& static_state = *rds.static_state;
+  // We just need a relaxed atomic read here: We can use the reservation ID even
+  // after the buffer was bound, we just need to be sure to read it atomically.
+  uint16_t startup_buffer_reservation =
+      data_source->startup_target_buffer_reservation.load(
+          std::memory_order_relaxed);
+  if (startup_buffer_reservation) {
+    return service->MaybeSharedMemoryArbiter()->CreateStartupTraceWriter(
+        startup_buffer_reservation);
+  }

-    // If this data source is already active for this exact config, don't start
-    // another instance. This happens when we have several data sources with the
-    // same name, in which case the service sends one SetupDataSource event for
-    // each one. Since we can't map which event maps to which data source, we
-    // ensure each event only starts one data source instance.
-    // TODO(skyostil): Register a unique id with each data source to the service
-    // to disambiguate.
-    bool active_for_config = false;
-    for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
-      if (!static_state.TryGet(i))
+  return data_source->use_tracing_v2
+             ? service->CreateTraceWriterV2(data_source->buffer_id,
+                                            buffer_exhausted_policy)
+             : service->CreateTraceWriter(data_source->buffer_id,
+                                          buffer_exhausted_policy);
+}
+
+// This is called via the public API Tracing::NewTrace().
+// Can be called from any thread.
+std::unique_ptr<TracingSession> TracingMuxerImpl::CreateTracingSession(
+    BackendType requested_backend_type,
+    TracingConsumerBackend* (*system_backend_factory)()) {
+  TracingSessionGlobalID session_id = ++next_tracing_session_id_;
+
+  // |backend_type| can only specify one backend, not an OR-ed mask.
+  PERFETTO_CHECK((requested_backend_type & (requested_backend_type - 1)) == 0);
+
+  // Capturing |this| is fine because the TracingMuxer is a leaky singleton.
+  task_runner_->PostTask([this, requested_backend_type, session_id,
+                          system_backend_factory] {
+    if (requested_backend_type == kSystemBackend && system_backend_factory &&
+        !FindConsumerBackendByType(kSystemBackend)) {
+      AddConsumerBackend(system_backend_factory(), kSystemBackend);
+    }
+    for (RegisteredConsumerBackend& backend : consumer_backends_) {
+      if (requested_backend_type && backend.type &&
+          backend.type != requested_backend_type) {
         continue;
-      auto* internal_state =
-          reinterpret_cast<DataSourceState*>(&static_state.instances[i]);
-      if (internal_state->backend_id == backend_id &&
-          internal_state->backend_connection_id == backend_connection_id &&
-          internal_state->config && *internal_state->config == cfg) {
-        active_for_config = true;
-        break;
       }
+
+      // 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, session_id));
+
+      // 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=%u, consumer will disconnect",
+            requested_backend_type);
+        InitializeConsumer(session_id);
+        return;
+      }
+
+      // Check if the embedder wants to be asked for permission before
+      // connecting the consumer.
+      if (!policy_) {
+        InitializeConsumer(session_id);
+        return;
+      }
+
+      BackendType type = backend.type;
+      TracingPolicy::ShouldAllowConsumerSessionArgs args;
+      args.backend_type = backend.type;
+      args.result_callback = [this, type, session_id](bool allow) {
+        task_runner_->PostTask([this, type, session_id, allow] {
+          if (allow) {
+            InitializeConsumer(session_id);
+            return;
+          }
+
+          PERFETTO_ELOG(
+              "Consumer session for backend type type=%u forbidden, "
+              "consumer will disconnect",
+              type);
+
+          auto* consumer = FindConsumer(session_id);
+          if (!consumer)
+            return;
+
+          consumer->OnDisconnect();
+        });
+      };
+      policy_->ShouldAllowConsumerSession(args);
+      return;
     }
-    if (active_for_config) {
-      PERFETTO_DLOG(
-          "Data source %s is already active with this config, skipping",
-          cfg.name().c_str());
-      continue;
-    }
+    PERFETTO_DFATAL("Not reached");
+  });

-    SetupDataSourceImpl(rds, backend_id, backend_connection_id, instance_id,
-                        cfg, /*startup_session_id=*/0);
-    return;
-  }
+  return std::unique_ptr<TracingSession>(
+      new TracingSessionImpl(this, session_id, requested_backend_type));
 }

-TracingMuxerImpl::FindDataSourceRes TracingMuxerImpl::SetupDataSourceImpl(
-    const RegisteredDataSource& rds,
-    TracingBackendId backend_id,
-    uint32_t backend_connection_id,
-    DataSourceInstanceID instance_id,
-    const DataSourceConfig& cfg,
-    TracingSessionGlobalID startup_session_id) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  DataSourceStaticState& static_state = *rds.static_state;
+// static
+// This is called via the public API Tracing::SetupStartupTracing().
+// Can be called from any thread.
+std::unique_ptr<StartupTracingSession>
+TracingMuxerImpl::CreateStartupTracingSession(
+    const TraceConfig& config,
+    Tracing::SetupStartupTracingOpts opts) {
+  BackendType backend_type = opts.backend;
+  // |backend_type| can only specify one backend, not an OR-ed mask.
+  PERFETTO_CHECK((backend_type & (backend_type - 1)) == 0);
+  // The in-process backend doesn't support startup tracing.
+  PERFETTO_CHECK(backend_type != BackendType::kInProcessBackend);

-  // If any bit is set in `static_state.valid_instances` then at least one
-  // other instance of data source is running.
-  if (!rds.params.supports_multiple_instances &&
-      static_state.valid_instances.load(std::memory_order_acquire) != 0) {
-    PERFETTO_ELOG(
-        "Failed to setup data source because some another instance of this "
-        "data source is already active");
-    return FindDataSourceRes();
-  }
+  TracingSessionGlobalID session_id = ++next_tracing_session_id_;

-  for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
-    // Find a free slot.
-    if (static_state.TryGet(i))
-      continue;
+  // Capturing |this| is fine because the TracingMuxer is a leaky singleton.
+  task_runner_->PostTask([this, config, opts, backend_type, session_id] {
+    for (RegisteredProducerBackend& backend : producer_backends_) {
+      if (backend_type && backend.type && backend.type != backend_type) {
+        continue;
+      }

-    auto* internal_state =
-        reinterpret_cast<DataSourceState*>(&static_state.instances[i]);
-    std::unique_lock<std::recursive_mutex> lock(internal_state->lock);
-    static_assert(
-        std::is_same<decltype(internal_state->data_source_instance_id),
-                     DataSourceInstanceID>::value,
-        "data_source_instance_id type mismatch");
-    internal_state->muxer_id_for_testing = muxer_id_for_testing_;
-    RegisteredProducerBackend& backend = *FindProducerBackendById(backend_id);
+      TracingBackendId backend_id = backend.id;

-    if (startup_session_id) {
-      uint16_t& last_reservation =
-          backend.producer->last_startup_target_buffer_reservation_;
-      if (last_reservation == std::numeric_limits<uint16_t>::max()) {
+      // The last registered backend in |producer_backends_| is the unsupported
+      // backend without a valid type.
+      if (!backend.type) {
         PERFETTO_ELOG(
-            "Startup buffer reservations exhausted, dropping data source");
-        return FindDataSourceRes();
+            "No tracing backend initialized for type=%u, startup tracing "
+            "failed",
+            backend_type);
+        if (opts.on_setup)
+          opts.on_setup(Tracing::OnStartupTracingSetupCallbackArgs{
+              0 /* num_data_sources_started */});
+        return;
       }
-      internal_state->startup_target_buffer_reservation.store(
-          ++last_reservation, std::memory_order_relaxed);
-    } else {
-      internal_state->startup_target_buffer_reservation.store(
-          0, std::memory_order_relaxed);
-    }

-    internal_state->backend_id = backend_id;
-    internal_state->backend_connection_id = backend_connection_id;
-    internal_state->data_source_instance_id = instance_id;
-    internal_state->buffer_id =
-        static_cast<internal::BufferId>(cfg.target_buffer());
-    internal_state->buffer_exhausted_policy =
-        ComputeBufferExhaustedPolicy(cfg, rds.params);
-    internal_state->config.reset(new DataSourceConfig(cfg));
-    internal_state->startup_session_id = startup_session_id;
-    internal_state->data_source = rds.factory();
-    internal_state->interceptor = nullptr;
-    internal_state->interceptor_id = 0;
-    internal_state->will_notify_on_stop = rds.descriptor.will_notify_on_stop();
+      if (!backend.producer->service_ ||
+          !backend.producer->service_->shared_memory()) {
+        // If we unsuccessfully attempted to use a producer-provided SMB in the
+        // past, don't try again.
+        if (backend.producer->producer_provided_smb_failed_) {
+          PERFETTO_ELOG(
+              "Backend %zu doesn't seem to support producer-provided "
+              "SMBs, startup tracing failed",
+              backend_id);
+          if (opts.on_setup)
+            opts.on_setup(Tracing::OnStartupTracingSetupCallbackArgs{
+                0 /* num_data_sources_started */});
+          return;
+        }

-    if (cfg.has_interceptor_config()) {
-      for (size_t j = 0; j < interceptors_.size(); j++) {
-        if (cfg.interceptor_config().name() ==
-            interceptors_[j].descriptor.name()) {
-          PERFETTO_DLOG("Intercepting data source %" PRIu64
-                        " \"%s\" into \"%s\"",
-                        instance_id, cfg.name().c_str(),
-                        cfg.interceptor_config().name().c_str());
-          internal_state->interceptor_id = static_cast<uint32_t>(j + 1);
-          internal_state->interceptor = interceptors_[j].factory();
-          internal_state->interceptor->OnSetup({cfg});
-          break;
+        PERFETTO_DLOG("Reconnecting backend %zu for startup tracing",
+                      backend_id);
+        backend.producer_conn_args.use_producer_provided_smb = true;
+        backend.producer->service_->Disconnect();  // Causes a reconnect.
+        PERFETTO_DCHECK(backend.producer->service_ &&
+                        backend.producer->service_->MaybeSharedMemoryArbiter());
+      }
+
+      RegisteredStartupSession session;
+      session.session_id = session_id;
+      session.on_aborted = opts.on_aborted;
+      session.on_adopted = opts.on_adopted;
+
+      for (const TraceConfig::DataSource& ds_cfg : config.data_sources()) {
+        // Find all matching data sources and start one instance of each.
+        for (const auto& rds : data_sources_) {
+          if (rds.descriptor.name() != ds_cfg.config().name())
+            continue;
+
+          PERFETTO_DLOG(
+              "Setting up data source %s for startup tracing with target "
+              "buffer reservation %" PRIi32,
+              rds.descriptor.name().c_str(),
+              backend.producer->last_startup_target_buffer_reservation_ + 1);
+          auto ds = SetupDataSourceImpl(
+              rds, backend_id,
+              backend.producer->connection_id_.load(std::memory_order_relaxed),
+              /*instance_id=*/0, ds_cfg.config(),
+              /*startup_session_id=*/session_id);
+          if (ds) {
+            StartDataSourceImpl(ds);
+            session.num_unbound_data_sources++;
+          }
         }
       }
-      if (!internal_state->interceptor_id) {
-        PERFETTO_ELOG("Unknown interceptor configured for data source: %s",
-                      cfg.interceptor_config().name().c_str());
+
+      int num_ds = session.num_unbound_data_sources;
+      auto on_setup = opts.on_setup;
+      if (on_setup) {
+        backend.producer->OnStartupTracingSetup();
+        task_runner_->PostTask([on_setup, num_ds] {
+          on_setup(Tracing::OnStartupTracingSetupCallbackArgs{num_ds});
+        });
       }
-    }

-    // This must be made at the end. See matching acquire-load in
-    // DataSource::Trace().
-    static_state.valid_instances.fetch_or(1 << i, std::memory_order_release);
+      if (num_ds > 0) {
+        backend.startup_sessions.push_back(std::move(session));

-    DataSourceBase::SetupArgs setup_args;
-    setup_args.config = &cfg;
-    setup_args.backend_type = backend.type;
-    setup_args.internal_instance_index = i;
+        if (opts.timeout_ms > 0) {
+          task_runner_->PostDelayedTask(
+              [this, session_id, backend_type] {
+                AbortStartupTracingSession(session_id, backend_type);
+              },
+              opts.timeout_ms);
+        }
+      }
+      return;
+    }
+    PERFETTO_DFATAL("Invalid startup tracing session backend");
+  });

-    if (!rds.params.requires_callbacks_under_lock)
-      lock.unlock();
-    internal_state->data_source->OnSetup(setup_args);
+  return std::unique_ptr<StartupTracingSession>(
+      new StartupTracingSessionImpl(this, session_id, backend_type));
+}

-    return FindDataSourceRes(&static_state, internal_state, i,
-                             rds.params.requires_callbacks_under_lock);
-  }
-  PERFETTO_ELOG(
-      "Maximum number of data source instances exhausted. "
-      "Dropping data source %" PRIu64,
-      instance_id);
-  return FindDataSourceRes();
+// Must not be called from the SDK's internal thread.
+std::unique_ptr<StartupTracingSession>
+TracingMuxerImpl::CreateStartupTracingSessionBlocking(
+    const TraceConfig& config,
+    Tracing::SetupStartupTracingOpts opts) {
+  auto previous_on_setup = std::move(opts.on_setup);
+  PERFETTO_CHECK(!task_runner_->RunsTasksOnCurrentThread());
+  base::WaitableEvent event;
+  // It is safe to capture by reference because once on_setup is called only
+  // once before this method returns.
+  opts.on_setup = [&](Tracing::OnStartupTracingSetupCallbackArgs args) {
+    if (previous_on_setup) {
+      previous_on_setup(std::move(args));
+    }
+    event.Notify();
+  };
+  auto session = CreateStartupTracingSession(config, std::move(opts));
+  event.Wait();
+  return session;
 }

-// Called by the service of one of the backends.
-void TracingMuxerImpl::StartDataSource(TracingBackendId backend_id,
-                                       DataSourceInstanceID instance_id) {
-  PERFETTO_DLOG("Starting data source %" PRIu64, instance_id);
+void TracingMuxerImpl::AbortStartupTracingSession(
+    TracingSessionGlobalID session_id,
+    BackendType backend_type) {
   PERFETTO_DCHECK_THREAD(thread_checker_);

-  auto ds = FindDataSource(backend_id, instance_id);
-  if (!ds) {
-    PERFETTO_ELOG("Could not find data source to start");
-    return;
-  }
+  for (RegisteredProducerBackend& backend : producer_backends_) {
+    if (backend_type != backend.type)
+      continue;

-  // Check if the data source was already started for startup tracing.
-  uint16_t startup_reservation =
-      ds.internal_state->startup_target_buffer_reservation.load(
-          std::memory_order_relaxed);
-  if (startup_reservation) {
-    RegisteredProducerBackend& backend = *FindProducerBackendById(backend_id);
-    TracingSessionGlobalID session_id = ds.internal_state->startup_session_id;
     auto session_it = std::find_if(
         backend.startup_sessions.begin(), backend.startup_sessions.end(),
         [session_id](const RegisteredStartupSession& session) {
           return session.session_id == session_id;
         });
-    PERFETTO_DCHECK(session_it != backend.startup_sessions.end());

-    if (session_it->is_aborting) {
-      PERFETTO_DLOG("Data source %" PRIu64
-                    " was already aborted for startup tracing, not starting it",
-                    instance_id);
+    // The startup session may have already been aborted or fully adopted.
+    if (session_it == backend.startup_sessions.end())
+      return;
+    if (session_it->is_aborting)
       return;
-    }

-    PERFETTO_DLOG(
-        "Data source %" PRIu64
-        " was already started for startup tracing, binding its target buffer",
-        instance_id);
+    session_it->is_aborting = true;

-    backend.producer->service_->MaybeSharedMemoryArbiter()
-        ->BindStartupTargetBuffer(startup_reservation,
-                                  ds.internal_state->buffer_id);
+    // Iterate all data sources and abort them if they weren't adopted yet.
+    for (const auto& rds : data_sources_) {
+      DataSourceStaticState* static_state = rds.static_state;
+      for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
+        auto* internal_state = static_state->TryGet(i);
+        if (internal_state &&
+            internal_state->startup_target_buffer_reservation.load(
+                std::memory_order_relaxed) &&
+            internal_state->data_source_instance_id == 0 &&
+            internal_state->startup_session_id == session_id) {
+          PERFETTO_DLOG(
+              "Aborting startup tracing for data source %s (target buffer "
+              "reservation %" PRIu16 ")",
+              rds.descriptor.name().c_str(),
+              internal_state->startup_target_buffer_reservation.load(
+                  std::memory_order_relaxed));

-    // The reservation ID can be used even after binding it, so there's no need
-    // for any barriers here - we just need atomicity.
-    ds.internal_state->startup_target_buffer_reservation.store(
-        0, std::memory_order_relaxed);
+          // Abort the instance asynchronously by stopping it. From this point
+          // onwards, the service will not be able to adopt it via
+          // StartDataSource().
+          session_it->num_aborting_data_sources++;
+          StopDataSource_AsyncBeginImpl(
+              FindDataSourceRes(static_state, internal_state, i,
+                                rds.params.requires_callbacks_under_lock));
+        }
+      }
+    }

-    // TODO(eseckler): Should we reset incremental state at this point, or
-    // notify the data source some other way?
+    // If we did everything right, we should have aborted all still-unbound data
+    // source instances.
+    PERFETTO_DCHECK(session_it->num_unbound_data_sources ==
+                    session_it->num_aborting_data_sources);

-    // The session should not have been fully bound yet (or aborted).
-    PERFETTO_DCHECK(session_it->num_unbound_data_sources > 0);
+    if (session_it->num_aborting_data_sources == 0) {
+      if (session_it->on_aborted)
+        task_runner_->PostTask(session_it->on_aborted);

-    session_it->num_unbound_data_sources--;
-    if (session_it->num_unbound_data_sources == 0) {
-      if (session_it->on_adopted)
-        task_runner_->PostTask(session_it->on_adopted);
       backend.startup_sessions.erase(session_it);
     }
     return;
   }
+  // We might reach here in tests because when we start a trace, we post the
+  // Task(AbortStartupTrace, delay=timeout). When we do
+  // perfetto::ResetForTesting, we sweep dead backends, and we are not able to
+  // kill those delayed tasks because TaskRunner doesn't have support for
+  // deleting scheduled future tasks and TaskRunner doesn't have any API for us
+  // to wait for the completion of all the scheduled tasks (apart from
+  // deleting the TaskRunner) and we want to avoid doing that because we need
+  // a long running TaskRunner in muxer.
+  PERFETTO_DLOG("Invalid startup tracing session backend");
+}

-  StartDataSourceImpl(ds);
+void TracingMuxerImpl::InitializeInstance(const TracingInitArgs& args) {
+  if (instance_ != TracingMuxerFake::Get()) {
+    // The tracing muxer was already initialized. We might need to initialize
+    // additional backends that were not configured earlier.
+    auto* muxer = static_cast<TracingMuxerImpl*>(instance_);
+    muxer->task_runner_->PostTask([muxer, args] { muxer->AddBackends(args); });
+    return;
+  }
+  // If we previously had a TracingMuxerImpl instance which was reset,
+  // reinitialize and reuse it instead of trying to create a new one. See
+  // ResetForTesting().
+  if (g_prev_instance) {
+    auto* muxer = g_prev_instance;
+    g_prev_instance = nullptr;
+    instance_ = muxer;
+    muxer->task_runner_->PostTask([muxer, args] {
+      muxer->Initialize(args);
+      muxer->AddBackends(args);
+    });
+  } else {
+    new TracingMuxerImpl(args);
+  }
 }

-void TracingMuxerImpl::StartDataSourceImpl(const FindDataSourceRes& ds) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
+// static
+void TracingMuxerImpl::ResetForTesting() {
+  // Ideally we'd tear down the entire TracingMuxerImpl, but the lifetimes of
+  // various objects make that a non-starter. In particular:
+  //
+  // 1) Any thread that has entered a trace event has a TraceWriter, which holds
+  //    a reference back to ProducerImpl::service_.
+  //
+  // 2) ProducerImpl::service_ has a reference back to the ProducerImpl.
+  //
+  // 3) ProducerImpl holds reference to TracingMuxerImpl::task_runner_, which in
+  //    turn depends on TracingMuxerImpl itself.
+  //
+  // Because of this, it's not safe to deallocate TracingMuxerImpl until all
+  // threads have dropped their TraceWriters. Since we can't really ask the
+  // caller to guarantee this, we'll instead reset enough of the muxer's state
+  // so that it can be reinitialized later and ensure all necessary objects from
+  // the old state remain alive until all references have gone away.
+  auto* muxer = reinterpret_cast<TracingMuxerImpl*>(instance_);

-  DataSourceBase::StartArgs start_args{};
-  start_args.internal_instance_index = ds.instance_idx;
+  base::WaitableEvent reset_done;
+  auto do_reset = [muxer, &reset_done] {
+    muxer->DestroyStoppedTraceWritersForCurrentThread();
+    // Unregister all data sources so they don't interfere with any future
+    // tracing sessions.
+    for (RegisteredDataSource& rds : muxer->data_sources_) {
+      for (RegisteredProducerBackend& backend : muxer->producer_backends_) {
+        if (!backend.producer->service_ || !backend.producer->connected_)
+          continue;
+        backend.producer->service_->UnregisterDataSource(rds.descriptor.name());
+      }
+    }
+    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->consumer_backends_.clear();
+    muxer->producer_backends_.clear();
+    muxer->interceptors_.clear();

-  std::unique_lock<std::recursive_mutex> lock(ds.internal_state->lock);
-  if (ds.internal_state->interceptor)
-    ds.internal_state->interceptor->OnStart({});
-  ds.internal_state->trace_lambda_enabled.store(true,
-                                                std::memory_order_relaxed);
-  PERFETTO_DCHECK(ds.internal_state->data_source != nullptr);
+    for (auto& ds : muxer->data_sources_) {
+      ds.static_state->ResetForTesting();
+    }

-  if (!ds.requires_callbacks_under_lock)
-    lock.unlock();
-  ds.internal_state->data_source->OnStart(start_args);
-}
+    muxer->data_sources_.clear();
+    muxer->next_data_source_index_ = 0;

-// Called by the service of one of the backends.
-void TracingMuxerImpl::StopDataSource_AsyncBegin(
-    TracingBackendId backend_id,
-    DataSourceInstanceID instance_id) {
-  PERFETTO_DLOG("Stopping data source %" PRIu64, instance_id);
-  PERFETTO_DCHECK_THREAD(thread_checker_);
+    // Free all backends without active trace writers or other inbound
+    // references. Note that even if all the backends get swept, the muxer still
+    // needs to stay around since |task_runner_| is assumed to be long-lived.
+    muxer->SweepDeadBackends();

-  auto ds = FindDataSource(backend_id, instance_id);
-  if (!ds) {
-    PERFETTO_ELOG("Could not find data source to stop");
-    return;
+    // Make sure we eventually discard any per-thread trace writers from the
+    // previous instance.
+    muxer->muxer_id_for_testing_++;
+
+    g_prev_instance = muxer;
+    instance_ = TracingMuxerFake::Get();
+
+    // Call the user provided cleanups on the muxer thread.
+    for (auto& cb : muxer->reset_callbacks_) {
+      cb();
+    }
+
+    reset_done.Notify();
+  };
+
+  // Some tests run the muxer and the test on the same thread. In these cases,
+  // we can reset synchronously.
+  if (muxer->task_runner_->RunsTasksOnCurrentThread()) {
+    do_reset();
+  } else {
+    muxer->DestroyStoppedTraceWritersForCurrentThread();
+    muxer->task_runner_->PostTask(std::move(do_reset));
+    reset_done.Wait();
+    // Call the user provided cleanups also on this thread.
+    for (auto& cb : muxer->reset_callbacks_) {
+      cb();
+    }
   }
+  muxer->reset_callbacks_.clear();
+}

-  StopDataSource_AsyncBeginImpl(ds);
+// static
+void TracingMuxerImpl::Shutdown() {
+  auto* muxer = reinterpret_cast<TracingMuxerImpl*>(instance_);
+
+  // Shutting down on the muxer thread would lead to a deadlock.
+  PERFETTO_CHECK(!muxer->task_runner_->RunsTasksOnCurrentThread());
+  // Destroy this thread's trace writers, including those of still-running data
+  // sources: the SMB dies with `delete muxer` below, but this thread's TLS only
+  // at platform->Shutdown() further down. Flushing then is a UAF (b/534222391).
+  muxer->DestroyAllTraceWritersForCurrentThread();
+
+  std::unique_ptr<base::TaskRunner> owned_task_runner(
+      muxer->task_runner_.get());
+  Platform* platform = muxer->platform_;
+  base::WaitableEvent shutdown_done;
+  owned_task_runner->PostTask([muxer, &shutdown_done] {
+    // 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->consumer_backends_) {
+      for (auto& consumer : backend.consumers) {
+        PERFETTO_CHECK(!consumer->service_);
+      }
+    }
+    // Destroy all writers before delete muxer frees the SMB; any writer left
+    // alive would UAF when the muxer thread exits (b/534222391).
+    muxer->DestroyAllTraceWritersForCurrentThread();
+    // The task runner must be deleted outside the muxer thread. This is done by
+    // `owned_task_runner` above.
+    muxer->task_runner_.release();
+    delete muxer;
+    instance_ = TracingMuxerFake::Get();
+    shutdown_done.Notify();
+  });
+  shutdown_done.Wait();
+
+  // Join the muxer thread before the platform (and its TLS key) is destroyed.
+  owned_task_runner.reset();
+
+  // On the calling thread, so the platform can free this thread's TLS object.
+  platform->Shutdown();
 }

-void TracingMuxerImpl::StopDataSource_AsyncBeginImpl(
-    const FindDataSourceRes& ds) {
-  TracingBackendId backend_id = ds.internal_state->backend_id;
-  uint32_t backend_connection_id = ds.internal_state->backend_connection_id;
-  DataSourceInstanceID instance_id = ds.internal_state->data_source_instance_id;
+void TracingMuxerImpl::AppendResetForTestingCallback(std::function<void()> cb) {
+  reset_callbacks_.push_back(std::move(cb));
+}

-  StopArgsImpl stop_args{};
-  stop_args.internal_instance_index = ds.instance_idx;
-  stop_args.async_stop_closure = [this, backend_id, backend_connection_id,
-                                  instance_id, ds] {
-    // TracingMuxerImpl is long lived, capturing |this| is okay.
-    // The notification closure can be moved out of the StopArgs by the
-    // embedder to handle stop asynchronously. The embedder might then
-    // call the closure on a different thread than the current one, hence
-    // this nested PostTask().
-    task_runner_->PostTask(
-        [this, backend_id, backend_connection_id, instance_id, ds] {
-          StopDataSource_AsyncEnd(backend_id, backend_connection_id,
-                                  instance_id, ds);
-        });
-  };
+TracingMuxer::~TracingMuxer() = default;
+
+static_assert(std::is_same<internal::BufferId, BufferID>::value,
+              "public's BufferId and tracing/core's BufferID diverged");
+
+}  // namespace internal
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/internal/track_event_internal.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/no_destructor.h
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_NO_DESTRUCTOR_H_
+#define INCLUDE_PERFETTO_EXT_BASE_NO_DESTRUCTOR_H_
+
+#include <new>
+#include <utility>
+
+namespace perfetto {
+namespace base {

-  {
-    std::unique_lock<std::recursive_mutex> lock(ds.internal_state->lock);
+// Wrapper that can hold an object of type T, without invoking the contained
+// object's destructor when being destroyed. Useful for creating statics while
+// avoiding static destructors.
+//
+// Stores the object inline, and therefore doesn't incur memory allocation and
+// pointer indirection overheads.
+//
+// Example of use:
+//
+//   const std::string& GetStr() {
+//     static base::NoDestructor<std::string> s("hello");
+//     return s.ref();
+//   }
+//
+template <typename T>
+class NoDestructor {
+ public:
+  // Forward arguments to T's constructor. Note that this doesn't cover
+  // construction from initializer lists.
+  template <typename... Args>
+  explicit NoDestructor(Args&&... args) {
+    new (storage_) T(std::forward<Args>(args)...);
+  }

-    // Don't call OnStop again if the datasource is already stopping.
-    if (ds.internal_state->async_stop_in_progress)
-      return;
-    ds.internal_state->async_stop_in_progress = true;
+  NoDestructor(const NoDestructor&) = delete;
+  NoDestructor& operator=(const NoDestructor&) = delete;
+  NoDestructor(NoDestructor&&) = delete;
+  NoDestructor& operator=(NoDestructor&&) = delete;

-    if (ds.internal_state->interceptor)
-      ds.internal_state->interceptor->OnStop({});
+  ~NoDestructor() = default;

-    if (!ds.requires_callbacks_under_lock)
-      lock.unlock();
-    ds.internal_state->data_source->OnStop(stop_args);
+  /* To avoid type-punned pointer strict aliasing warnings on GCC6 and below
+   * these need to be split over two lines. If they are collapsed onto one line.
+   *   return reinterpret_cast<const T*>(storage_);
+   * The error fires.
+   */
+  const T& ref() const {
+    auto* const cast = reinterpret_cast<const T*>(storage_);
+    return *cast;
+  }
+  T& ref() {
+    auto* const cast = reinterpret_cast<T*>(storage_);
+    return *cast;
   }

-  // If the embedder hasn't called StopArgs.HandleStopAsynchronously() run the
-  // async closure here. In theory we could avoid the PostTask and call
-  // straight into CompleteDataSourceAsyncStop(). We keep that to reduce
-  // divergencies between the deferred-stop vs non-deferred-stop code paths.
-  if (stop_args.async_stop_closure)
-    std::move(stop_args.async_stop_closure)();
-}
+ private:
+  alignas(T) char storage_[sizeof(T)];
+};

-void TracingMuxerImpl::StopDataSource_AsyncEnd(TracingBackendId backend_id,
-                                               uint32_t backend_connection_id,
-                                               DataSourceInstanceID instance_id,
-                                               const FindDataSourceRes& ds) {
-  PERFETTO_DLOG("Ending async stop of data source %" PRIu64, instance_id);
-  PERFETTO_DCHECK_THREAD(thread_checker_);
+}  // namespace base
+}  // namespace perfetto

-  // Check that the data source instance is still active and was not modified
-  // while it was being stopped.
-  if (!ds.static_state->TryGet(ds.instance_idx) ||
-      ds.internal_state->backend_id != backend_id ||
-      ds.internal_state->backend_connection_id != backend_connection_id ||
-      ds.internal_state->data_source_instance_id != instance_id) {
-    PERFETTO_ELOG(
-        "Async stop of data source %" PRIu64
-        " failed. This might be due to calling the async_stop_closure twice.",
-        instance_id);
-    return;
-  }
+#endif  // INCLUDE_PERFETTO_EXT_BASE_NO_DESTRUCTOR_H_
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */

-  const uint32_t mask = ~(1U << ds.instance_idx);
-  ds.static_state->valid_instances.fetch_and(mask, std::memory_order_acq_rel);
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_internal.h"

-  bool will_notify_on_stop;
-  // Take the mutex to prevent that the data source is in the middle of
-  // a Trace() execution where it called GetDataSourceLocked() while we
-  // destroy it.
-  uint16_t startup_buffer_reservation;
-  TracingSessionGlobalID startup_session_id;
-  {
-    std::lock_guard<std::recursive_mutex> guard(ds.internal_state->lock);
-    ds.internal_state->trace_lambda_enabled.store(false,
-                                                  std::memory_order_relaxed);
-    ds.internal_state->data_source.reset();
-    ds.internal_state->interceptor.reset();
-    ds.internal_state->config.reset();
-    ds.internal_state->async_stop_in_progress = false;
-    will_notify_on_stop = ds.internal_state->will_notify_on_stop;
-    startup_buffer_reservation =
-        ds.internal_state->startup_target_buffer_reservation.load(
-            std::memory_order_relaxed);
-    startup_session_id = ds.internal_state->startup_session_id;
-  }
+// gen_amalgamated expanded: #include "perfetto/base/proc_utils.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/no_destructor.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/core/data_source_config.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/data_source.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_interned_fields.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/track_event.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/track_event_category_registry.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/track_event_interned_data_index.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/data_source_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/track_event_descriptor.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <os/signpost.h>
+#endif

-  // The other fields of internal_state are deliberately *not* cleared.
-  // See races-related comments of DataSource::Trace().
+using perfetto::protos::pbzero::ClockSnapshot;

-  TracingMuxer::generation_++;
+namespace perfetto {

-  // |producer_backends_| is append-only, Backend instances are always valid.
-  PERFETTO_CHECK(backend_id < producer_backends_.size());
-  RegisteredProducerBackend& backend = *FindProducerBackendById(backend_id);
-  ProducerImpl* producer = backend.producer.get();
-  if (!producer)
-    return;
+TrackEventSessionObserver::~TrackEventSessionObserver() = default;
+void TrackEventSessionObserver::OnSetup(const DataSourceBase::SetupArgs&) {}
+void TrackEventSessionObserver::OnStart(const DataSourceBase::StartArgs&) {}
+void TrackEventSessionObserver::OnStop(const DataSourceBase::StopArgs&) {}
+void TrackEventSessionObserver::WillClearIncrementalState(
+    const DataSourceBase::ClearIncrementalStateArgs&) {}

-  // If the data source instance still has a startup buffer reservation, it was
-  // only active for startup tracing and never started by the service. Discard
-  // the startup buffer reservation.
-  if (startup_buffer_reservation) {
-    PERFETTO_DCHECK(startup_session_id);
+TrackEventTlsStateUserData::~TrackEventTlsStateUserData() = default;

-    if (producer->service_ && producer->service_->MaybeSharedMemoryArbiter()) {
-      producer->service_->MaybeSharedMemoryArbiter()
-          ->AbortStartupTracingForReservation(startup_buffer_reservation);
-    }
+namespace internal {

-    auto session_it = std::find_if(
-        backend.startup_sessions.begin(), backend.startup_sessions.end(),
-        [startup_session_id](const RegisteredStartupSession& session) {
-          return session.session_id == startup_session_id;
-        });
+BaseTrackEventInternedDataIndex::~BaseTrackEventInternedDataIndex() = default;

-    // Session should not be removed until abortion of all data source instances
-    // is complete.
-    PERFETTO_DCHECK(session_it != backend.startup_sessions.end());
+namespace {

-    session_it->num_aborting_data_sources--;
-    if (session_it->num_aborting_data_sources == 0) {
-      if (session_it->on_aborted)
-        task_runner_->PostTask(session_it->on_aborted);
+static constexpr const char kSlowTag[] = "slow";
+static constexpr const char kDebugTag[] = "debug";
+static constexpr const char kFilteredEventName[] = "FILTERED";

-      backend.startup_sessions.erase(session_it);
-    }
+constexpr auto kClockIdIncremental =
+    TrackEventIncrementalState::kClockIdIncremental;
+
+constexpr auto kClockIdAbsolute = TrackEventIncrementalState::kClockIdAbsolute;
+
+class TrackEventSessionObserverRegistry {
+ public:
+  static TrackEventSessionObserverRegistry* GetInstance() {
+    static TrackEventSessionObserverRegistry* instance =
+        new TrackEventSessionObserverRegistry();  // leaked
+    return instance;
   }

-  if (producer->connected_ &&
-      backend.producer->connection_id_.load(std::memory_order_relaxed) ==
-          backend_connection_id) {
-    // Flush any commits that might have been batched by SharedMemoryArbiter.
-    producer->service_->MaybeSharedMemoryArbiter()
-        ->FlushPendingCommitDataRequests();
-    if (instance_id && will_notify_on_stop)
-      producer->service_->NotifyDataSourceStopped(instance_id);
+  void AddObserverForRegistry(const TrackEventCategoryRegistry& registry,
+                              TrackEventSessionObserver* observer) {
+    std::unique_lock<std::recursive_mutex> lock(mutex_);
+    observers_.emplace_back(&registry, observer);
   }
-  producer->SweepDeadServices();
-  base::MaybeReleaseAllocatorMemToOS();
-}

-void TracingMuxerImpl::ClearDataSourceIncrementalState(
-    TracingBackendId backend_id,
-    DataSourceInstanceID instance_id) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  PERFETTO_DLOG("Clearing incremental state for data source %" PRIu64,
-                instance_id);
-  auto ds = FindDataSource(backend_id, instance_id);
-  if (!ds) {
-    PERFETTO_ELOG("Could not find data source to clear incremental state for");
-    return;
+  void RemoveObserverForRegistry(const TrackEventCategoryRegistry& registry,
+                                 TrackEventSessionObserver* observer) {
+    std::unique_lock<std::recursive_mutex> lock(mutex_);
+    observers_.erase(std::remove(observers_.begin(), observers_.end(),
+                                 RegisteredObserver(&registry, observer)),
+                     observers_.end());
   }

-  DataSourceBase::ClearIncrementalStateArgs clear_incremental_state_args;
-  clear_incremental_state_args.internal_instance_index = ds.instance_idx;
-  {
-    std::unique_lock<std::recursive_mutex> lock;
-    if (ds.requires_callbacks_under_lock)
-      lock = std::unique_lock<std::recursive_mutex>(ds.internal_state->lock);
-    ds.internal_state->data_source->WillClearIncrementalState(
-        clear_incremental_state_args);
+  void ForEachObserverForRegistries(
+      const std::vector<const TrackEventCategoryRegistry*>& registries,
+      std::function<void(TrackEventSessionObserver*)> callback) {
+    std::unique_lock<std::recursive_mutex> lock(mutex_);
+    for (auto& registered_observer : observers_) {
+      for (const auto* registry : registries) {
+        if (registry == registered_observer.registry) {
+          callback(registered_observer.observer);
+        }
+      }
+    }
   }

-  // Make DataSource::TraceContext::GetIncrementalState() eventually notice that
-  // the incremental state should be cleared.
-  ds.static_state->GetUnsafe(ds.instance_idx)
-      ->incremental_state_generation.fetch_add(1, std::memory_order_relaxed);
-}
+ private:
+  struct RegisteredObserver {
+    RegisteredObserver(const TrackEventCategoryRegistry* r,
+                       TrackEventSessionObserver* o)
+        : registry(r), observer(o) {}
+    bool operator==(const RegisteredObserver& other) {
+      return registry == other.registry && observer == other.observer;
+    }
+    const TrackEventCategoryRegistry* registry;
+    TrackEventSessionObserver* observer;
+  };

-bool TracingMuxerImpl::FlushDataSource_AsyncBegin(
-    TracingBackendId backend_id,
-    DataSourceInstanceID instance_id,
-    FlushRequestID flush_id,
-    FlushFlags flush_flags) {
-  PERFETTO_DLOG("Flushing data source %" PRIu64, instance_id);
-  auto ds = FindDataSource(backend_id, instance_id);
-  if (!ds) {
-    PERFETTO_ELOG("Could not find data source to flush");
-    return true;
-  }
+  std::recursive_mutex mutex_;
+  std::vector<RegisteredObserver> observers_;
+};

-  uint32_t backend_connection_id = ds.internal_state->backend_connection_id;
+enum class MatchType { kExact, kPattern, kWildcard };

-  FlushArgsImpl flush_args;
-  flush_args.flush_flags = flush_flags;
-  flush_args.internal_instance_index = ds.instance_idx;
-  flush_args.async_flush_closure = [this, backend_id, backend_connection_id,
-                                    instance_id, ds, flush_id] {
-    // TracingMuxerImpl is long lived, capturing |this| is okay.
-    // The notification closure can be moved out of the StopArgs by the
-    // embedder to handle stop asynchronously. The embedder might then
-    // call the closure on a different thread than the current one, hence
-    // this nested PostTask().
-    task_runner_->PostTask(
-        [this, backend_id, backend_connection_id, instance_id, ds, flush_id] {
-          FlushDataSource_AsyncEnd(backend_id, backend_connection_id,
-                                   instance_id, ds, flush_id);
-        });
-  };
-  {
-    std::unique_lock<std::recursive_mutex> lock;
-    if (ds.requires_callbacks_under_lock)
-      lock = std::unique_lock<std::recursive_mutex>(ds.internal_state->lock);
-    ds.internal_state->data_source->OnFlush(flush_args);
+bool NameMatchesPattern(const std::string& pattern,
+                        const std::string& name,
+                        MatchType match_type) {
+  // To avoid pulling in all of std::regex, for now we only support a single "*"
+  // wildcard at the end of the pattern.
+  size_t i = pattern.find('*');
+  if (i != std::string::npos) {
+    PERFETTO_DCHECK(i == pattern.size() - 1);
+    if (i == 0) {
+      return match_type == MatchType::kWildcard;
+    }
+    if (match_type != MatchType::kPattern)
+      return false;
+    return name.substr(0, i) == pattern.substr(0, i);
   }
+  return name == pattern;
+}

-  // |async_flush_closure| is moved out of |flush_args| if the producer
-  // requested to handle the flush asynchronously.
-  bool handled = static_cast<bool>(flush_args.async_flush_closure);
-  return handled;
+bool NameMatchesPatternList(const std::vector<std::string>& patterns,
+                            const std::string& name,
+                            MatchType match_type) {
+  for (const auto& pattern : patterns) {
+    if (NameMatchesPattern(pattern, name, match_type))
+      return true;
+  }
+  return false;
 }

-void TracingMuxerImpl::FlushDataSource_AsyncEnd(
-    TracingBackendId backend_id,
-    uint32_t backend_connection_id,
-    DataSourceInstanceID instance_id,
-    const FindDataSourceRes& ds,
-    FlushRequestID flush_id) {
-  PERFETTO_DLOG("Ending async flush of data source %" PRIu64, instance_id);
-  PERFETTO_DCHECK_THREAD(thread_checker_);
+}  // namespace

-  // Check that the data source instance is still active and was not modified
-  // while it was being flushed.
-  if (!ds.static_state->TryGet(ds.instance_idx) ||
-      ds.internal_state->backend_id != backend_id ||
-      ds.internal_state->backend_connection_id != backend_connection_id ||
-      ds.internal_state->data_source_instance_id != instance_id) {
-    PERFETTO_ELOG("Async flush of data source %" PRIu64
-                  " failed. This might be due to the data source being stopped "
-                  "in the meantime",
-                  instance_id);
-    return;
-  }
+TrackEventInternal& TrackEventInternal::GetInstance() {
+  static base::NoDestructor<TrackEventInternal> state;
+  return state.ref();
+}

-  // |producer_backends_| is append-only, Backend instances are always valid.
-  PERFETTO_CHECK(backend_id < producer_backends_.size());
-  RegisteredProducerBackend& backend = *FindProducerBackendById(backend_id);
+// static
+const Track TrackEventInternal::kDefaultTrack{};

-  ProducerImpl* producer = backend.producer.get();
-  if (!producer)
-    return;
+// static
+std::atomic<int> TrackEventInternal::session_count_{};

-  // If the tracing service disconnects and reconnects while a data source is
-  // handling a flush request, there's no point is sending the flush reply to
-  // the newly reconnected producer.
-  if (producer->connected_ &&
-      backend.producer->connection_id_.load(std::memory_order_relaxed) ==
-          backend_connection_id) {
-    producer->NotifyFlushForDataSourceDone(instance_id, flush_id);
-  }
+std::vector<const TrackEventCategoryRegistry*>
+TrackEventInternal::GetRegistries() {
+  std::unique_lock<std::mutex> lock(mu_);
+  return registries_;
 }

-void TracingMuxerImpl::SyncProducersForTesting() {
-  std::mutex mutex;
-  std::condition_variable cv;
+std::vector<const TrackEventCategoryRegistry*> TrackEventInternal::AddRegistry(
+    const TrackEventCategoryRegistry* registry) {
+  std::unique_lock<std::mutex> lock(mu_);
+  registries_.push_back(registry);
+  return registries_;
+}

-  // IPC-based producers don't report connection errors explicitly for each
-  // command, but instead with an asynchronous callback
-  // (ProducerImpl::OnDisconnected). This means that the sync command below
-  // may have completed but failed to reach the service because of a
-  // disconnection, but we can't tell until the disconnection message comes
-  // through. To guard against this, we run two whole rounds of sync round-trips
-  // before returning; the first one will detect any disconnected producers and
-  // the second one will ensure any reconnections have completed and all data
-  // sources are registered in the service again.
-  for (size_t i = 0; i < 2; i++) {
-    size_t countdown = std::numeric_limits<size_t>::max();
-    task_runner_->PostTask([this, &mutex, &cv, &countdown] {
-      {
-        std::unique_lock<std::mutex> countdown_lock(mutex);
-        countdown = producer_backends_.size();
-      }
-      for (auto& backend : producer_backends_) {
-        auto* producer = backend.producer.get();
-        producer->service_->Sync([&mutex, &cv, &countdown] {
-          std::unique_lock<std::mutex> countdown_lock(mutex);
-          countdown--;
-          cv.notify_one();
-        });
-      }
-    });
+void TrackEventInternal::ResetRegistriesForTesting() {
+  std::unique_lock<std::mutex> lock(mu_);
+  registries_.clear();
+}

-    {
-      std::unique_lock<std::mutex> countdown_lock(mutex);
-      cv.wait(countdown_lock, [&countdown] { return !countdown; });
+// static
+bool TrackEventInternal::Initialize(
+    const std::vector<const TrackEventCategoryRegistry*> registries,
+    bool (*register_data_source)(const DataSourceDescriptor&)) {
+  DataSourceDescriptor dsd;
+  dsd.set_name("track_event");
+
+  protozero::HeapBuffered<protos::pbzero::TrackEventDescriptor> ted;
+  for (const auto* registry : registries) {
+    for (size_t i = 0; i < registry->category_count(); i++) {
+      auto category = registry->GetCategory(i);
+      // Don't register group categories.
+      if (category->IsGroup())
+        continue;
+      auto cat = ted->add_available_categories();
+      cat->set_name(category->name);
+      if (category->description)
+        cat->set_description(category->description);
+      for (const auto& tag : category->tags) {
+        if (tag) {
+          cat->add_tags(tag);
+        }
+      }
     }
   }
+  dsd.set_track_event_descriptor_raw(ted.SerializeAsString());

-  // Check that all producers are indeed connected.
-  bool done = false;
-  bool all_producers_connected = true;
-  task_runner_->PostTask([this, &mutex, &cv, &done, &all_producers_connected] {
-    for (auto& backend : producer_backends_)
-      all_producers_connected &= backend.producer->connected_;
-    std::unique_lock<std::mutex> lock(mutex);
-    done = true;
-    cv.notify_one();
-  });
+  return register_data_source(dsd);
+}

-  {
-    std::unique_lock<std::mutex> lock(mutex);
-    cv.wait(lock, [&done] { return done; });
-  }
-  PERFETTO_DCHECK(all_producers_connected);
+// static
+bool TrackEventInternal::AddSessionObserver(
+    const TrackEventCategoryRegistry& registry,
+    TrackEventSessionObserver* observer) {
+  TrackEventSessionObserverRegistry::GetInstance()->AddObserverForRegistry(
+      registry, observer);
+  return true;
 }

-void TracingMuxerImpl::DestroyStoppedTraceWritersForCurrentThread() {
-  // Iterate across all possible data source types.
-  auto cur_generation = generation_.load(std::memory_order_acquire);
-  auto* root_tls = GetOrCreateTracingTLS();
+// static
+void TrackEventInternal::RemoveSessionObserver(
+    const TrackEventCategoryRegistry& registry,
+    TrackEventSessionObserver* observer) {
+  TrackEventSessionObserverRegistry::GetInstance()->RemoveObserverForRegistry(
+      registry, observer);
+}

-  auto destroy_stopped_instances = [](DataSourceThreadLocalState& tls) {
-    // |tls| has a vector of per-data-source-instance thread-local state.
-    DataSourceStaticState* static_state = tls.static_state;
-    if (!static_state)
-      return;  // Slot not used.
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) && \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+static constexpr protos::pbzero::BuiltinClock kDefaultTraceClock =
+    protos::pbzero::BUILTIN_CLOCK_BOOTTIME;
+#else
+static constexpr protos::pbzero::BuiltinClock kDefaultTraceClock =
+    protos::pbzero::BUILTIN_CLOCK_MONOTONIC;
+#endif

-    // Iterate across all possible instances for this data source.
-    for (uint32_t inst = 0; inst < kMaxDataSourceInstances; inst++) {
-      DataSourceInstanceThreadLocalState& ds_tls = tls.per_instance[inst];
-      if (!ds_tls.trace_writer)
-        continue;
+// static
+protos::pbzero::BuiltinClock TrackEventInternal::clock_ = kDefaultTraceClock;

-      DataSourceState* ds_state = static_state->TryGet(inst);
-      if (ds_state &&
-          ds_state->muxer_id_for_testing == ds_tls.muxer_id_for_testing &&
-          ds_state->backend_id == ds_tls.backend_id &&
-          ds_state->backend_connection_id == ds_tls.backend_connection_id &&
-          ds_state->startup_target_buffer_reservation.load(
-              std::memory_order_relaxed) ==
-              ds_tls.startup_target_buffer_reservation &&
-          ds_state->buffer_id == ds_tls.buffer_id &&
-          ds_state->data_source_instance_id == ds_tls.data_source_instance_id) {
-        continue;
-      }
+// static
+bool TrackEventInternal::disallow_merging_with_system_tracks_ = false;
+
+// static
+BufferExhaustedPolicy TrackEventInternal::buffer_exhausted_policy_ =
+    BufferExhaustedPolicy::kDrop;

-      // The DataSource instance has been destroyed or recycled.
-      ds_tls.Reset();  // Will also destroy the |ds_tls.trace_writer|.
+// static
+void TrackEventInternal::EnableRegistry(
+    const TrackEventCategoryRegistry* registry,
+    const protos::gen::TrackEventConfig& config,
+    uint32_t internal_instance_index) {
+  for (size_t i = 0; i < registry->category_count(); i++) {
+    if (IsCategoryEnabled(*registry, config, *registry->GetCategory(i))) {
+      PERFETTO_DLOG("EnableRegistry %" PRIu32 " %zu", internal_instance_index,
+                    i);
+      registry->EnableCategoryForInstance(i, internal_instance_index);
     }
-  };
+  }
+}

-  for (size_t ds_idx = 0; ds_idx < kMaxDataSources; ds_idx++) {
-    // |tls| has a vector of per-data-source-instance thread-local state.
-    DataSourceThreadLocalState& tls = root_tls->data_sources_tls[ds_idx];
-    destroy_stopped_instances(tls);
+void TrackEventInternal::EnableTracing(
+    const protos::gen::TrackEventConfig& config,
+    const DataSourceBase::SetupArgs& args) {
+  std::vector<const TrackEventCategoryRegistry*> registries;
+  {
+    std::unique_lock<std::mutex> lock(mu_);
+    registries = registries_;
+    for (const auto& registry : registries) {
+      for (size_t i = 0; i < registry->category_count(); i++) {
+        if (IsCategoryEnabled(*registry, config, *registry->GetCategory(i)))
+          registry->EnableCategoryForInstance(i, args.internal_instance_index);
+      }
+    }
   }
-  destroy_stopped_instances(root_tls->track_event_tls);
-  root_tls->generation = cur_generation;
+  TrackEventSessionObserverRegistry::GetInstance()
+      ->ForEachObserverForRegistries(
+          registries, [&](TrackEventSessionObserver* o) { o->OnSetup(args); });
 }

-// Called both when a new data source is registered or when a new backend
-// connects. In both cases we want to be sure we reflected the data source
-// registrations on the backends.
-void TracingMuxerImpl::UpdateDataSourcesOnAllBackends() {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  for (RegisteredDataSource& rds : data_sources_) {
-    UpdateDataSourceOnAllBackends(rds, /*is_changed=*/false);
+void TrackEventInternal::DisableTracing(uint32_t internal_instance_index) {
+  std::unique_lock<std::mutex> lock(mu_);
+  for (const auto& registry : registries_) {
+    for (size_t i = 0; i < registry->category_count(); i++)
+      registry->DisableCategoryForInstance(i, internal_instance_index);
   }
 }

-void TracingMuxerImpl::UpdateDataSourceOnAllBackends(RegisteredDataSource& rds,
-                                                     bool is_changed) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  for (RegisteredProducerBackend& backend : producer_backends_) {
-    // We cannot call RegisterDataSource on the backend before it connects.
-    if (!backend.producer->connected_)
-      continue;
+// static
+void TrackEventInternal::OnStart(const DataSourceBase::StartArgs& args) {
+  session_count_.fetch_add(1);
+  TrackEventSessionObserverRegistry::GetInstance()
+      ->ForEachObserverForRegistries(
+          GetInstance().GetRegistries(),
+          [&](TrackEventSessionObserver* o) { o->OnStart(args); });
+}

-    PERFETTO_DCHECK(rds.static_state->index < kMaxDataSources);
-    bool is_registered = backend.producer->registered_data_sources_.test(
-        rds.static_state->index);
-    if (is_registered && !is_changed)
-      continue;
+// static
+void TrackEventInternal::OnStop(const DataSourceBase::StopArgs& args) {
+  TrackEventSessionObserverRegistry::GetInstance()
+      ->ForEachObserverForRegistries(
+          GetInstance().GetRegistries(),
+          [&](TrackEventSessionObserver* o) { o->OnStop(args); });
+}

-    if (!rds.descriptor.no_flush()) {
-      rds.descriptor.set_no_flush(rds.no_flush);
-    }
-    rds.descriptor.set_will_notify_on_start(true);
-    if (!rds.descriptor.has_will_notify_on_stop()) {
-      rds.descriptor.set_will_notify_on_stop(true);
-    }
+// static
+void TrackEventInternal::WillClearIncrementalState(
+    const DataSourceBase::ClearIncrementalStateArgs& args) {
+  TrackEventSessionObserverRegistry::GetInstance()
+      ->ForEachObserverForRegistries(GetInstance().GetRegistries(),
+                                     [&](TrackEventSessionObserver* o) {
+                                       o->WillClearIncrementalState(args);
+                                     });
+}

-    rds.descriptor.set_handles_incremental_state_clear(true);
-    rds.descriptor.set_id(rds.static_state->id);
-    if (is_registered) {
-      backend.producer->service_->UpdateDataSource(rds.descriptor);
-    } else {
-      backend.producer->service_->RegisterDataSource(rds.descriptor);
-    }
-    backend.producer->registered_data_sources_.set(rds.static_state->index);
+// static
+bool TrackEventInternal::IsCategoryEnabled(
+    const TrackEventCategoryRegistry& registry,
+    const protos::gen::TrackEventConfig& config,
+    const Category& category) {
+  // If this is a group category, check if any of its constituent categories are
+  // enabled. If so, then this one is enabled too.
+  if (category.IsGroup()) {
+    bool result = false;
+    category.ForEachGroupMember([&](const char* member_name, size_t name_size) {
+      for (size_t i = 0; i < registry.category_count(); i++) {
+        const auto ref_category = registry.GetCategory(i);
+        // Groups can't refer to other groups.
+        if (ref_category->IsGroup())
+          continue;
+        // Require an exact match.
+        if (ref_category->name_size() != name_size ||
+            strncmp(ref_category->name, member_name, name_size)) {
+          continue;
+        }
+        if (IsCategoryEnabled(registry, config, *ref_category)) {
+          result = true;
+          // Break ForEachGroupMember() loop.
+          return false;
+        }
+        break;
+      }
+      // No match? Must be a dynamic category.
+      DynamicCategory dyn_category(std::string(member_name, name_size));
+      Category ref_category{Category::FromDynamicCategory(dyn_category)};
+      if (IsCategoryEnabled(registry, config, ref_category)) {
+        result = true;
+        // Break ForEachGroupMember() loop.
+        return false;
+      }
+      // No match found => keep iterating.
+      return true;
+    });
+    return result;
   }
-}

-void TracingMuxerImpl::SetupTracingSession(
-    TracingSessionGlobalID session_id,
-    const std::shared_ptr<TraceConfig>& trace_config,
-    base::ScopedFile trace_fd) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  PERFETTO_CHECK(!trace_fd || trace_config->write_into_file());
+  auto has_matching_tag = [&](std::function<bool(const char*)> matcher) {
+    for (const auto& tag : category.tags) {
+      if (!tag)
+        break;
+      if (matcher(tag))
+        return true;
+    }
+    return false;
+  };

-  auto* consumer = FindConsumer(session_id);
-  if (!consumer)
-    return;
+  // First try exact matches, then pattern matches. Last, try the global
+  // wildcard.
+  const std::array<MatchType, 3> match_types = {
+      {MatchType::kExact, MatchType::kPattern, MatchType::kWildcard}};
+  for (auto match_type : match_types) {
+    // 1. Enabled categories.
+    if (NameMatchesPatternList(config.enabled_categories(), category.name,
+                               match_type)) {
+      return true;
+    }

-  consumer->trace_config_ = trace_config;
-  if (trace_fd)
-    consumer->trace_fd_ = std::move(trace_fd);
+    // 2. Disabled categories.
+    if (NameMatchesPatternList(config.disabled_categories(), category.name,
+                               match_type)) {
+      return false;
+    }

-  if (!consumer->connected_)
-    return;
+    // 3. Disabled tags.
+    if (has_matching_tag([&](const char* tag) {
+          if (config.disabled_tags_size()) {
+            return NameMatchesPatternList(config.disabled_tags(), tag,
+                                          match_type);
+          } else if (config.enabled_tags_size() == 0) {
+            // The "slow" and "debug" tags are disabled by default.
+            return NameMatchesPattern(kSlowTag, tag, match_type) ||
+                   NameMatchesPattern(kDebugTag, tag, match_type);
+          }
+          return false;
+        })) {
+      return false;
+    }

-  // Only used in the deferred start mode.
-  if (trace_config->deferred_start()) {
-    consumer->service_->EnableTracing(*trace_config,
-                                      std::move(consumer->trace_fd_));
+    // 4. Enabled tags.
+    if (has_matching_tag([&](const char* tag) {
+          return NameMatchesPatternList(config.enabled_tags(), tag, match_type);
+        })) {
+      return true;
+    }
   }
+
+  // If nothing matched, enable the category by default.
+  return true;
 }

-void TracingMuxerImpl::StartTracingSession(TracingSessionGlobalID session_id) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
+// static
+uint64_t TrackEventInternal::GetTimeNs() {
+  if (GetClockId() == protos::pbzero::BUILTIN_CLOCK_BOOTTIME)
+    return static_cast<uint64_t>(perfetto::base::GetBootTimeNs().count());
+  else if (GetClockId() == protos::pbzero::BUILTIN_CLOCK_MONOTONIC)
+    return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
+  PERFETTO_DCHECK(GetClockId() == protos::pbzero::BUILTIN_CLOCK_MONOTONIC_RAW);
+  return static_cast<uint64_t>(perfetto::base::GetWallTimeRawNs().count());
+}

-  auto* consumer = FindConsumer(session_id);
+// static
+TraceTimestamp TrackEventInternal::GetTraceTime() {
+  return {kClockIdIncremental, GetTimeNs()};
+}

-  if (!consumer)
-    return;
+// static
+int TrackEventInternal::GetSessionCount() {
+  return session_count_.load();
+}

-  if (!consumer->trace_config_) {
-    PERFETTO_ELOG("Must call Setup(config) first");
-    return;
+// static
+void TrackEventInternal::ResetIncrementalState(
+    TraceWriterBase* trace_writer,
+    TrackEventIncrementalState* incr_state,
+    const TrackEventTlsState& tls_state,
+    const TraceTimestamp& timestamp) {
+  auto sequence_timestamp = timestamp;
+  if (timestamp.clock_id != kClockIdIncremental) {
+    sequence_timestamp = TrackEventInternal::GetTraceTime();
   }

-  if (!consumer->connected_) {
-    consumer->start_pending_ = true;
-    return;
-  }
+  incr_state->last_timestamp_ns = sequence_timestamp.value;
+  auto default_track = ThreadTrack::Current();
+  auto ts_unit_multiplier = tls_state.timestamp_unit_multiplier;
+  auto thread_time_counter_track =
+      CounterTrack("thread_time", default_track)
+          .set_is_incremental(true)
+          .set_unit_multiplier(static_cast<int64_t>(ts_unit_multiplier))
+          .set_type(protos::gen::CounterDescriptor::COUNTER_THREAD_TIME_NS);
+  {
+    // Mark any incremental state before this point invalid. Also set up
+    // defaults so that we don't need to repeat constant data for each packet.
+    auto packet = NewTracePacket(
+        trace_writer, incr_state, tls_state, timestamp,
+        protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
+    auto defaults = packet->set_trace_packet_defaults();
+    defaults->set_timestamp_clock_id(tls_state.default_clock);
+    // Establish the default track for this event sequence.
+    auto track_defaults = defaults->set_track_event_defaults();
+    track_defaults->set_track_uuid(default_track.uuid);
+    if (tls_state.enable_thread_time_sampling) {
+      track_defaults->add_extra_counter_track_uuids(
+          thread_time_counter_track.uuid);
+    }

-  consumer->start_pending_ = false;
-  if (consumer->trace_config_->deferred_start()) {
-    consumer->service_->StartTracing();
-  } else {
-    consumer->service_->EnableTracing(*consumer->trace_config_,
-                                      std::move(consumer->trace_fd_));
-  }
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+    // Emit a MacOS point-of-interest signpost to synchronize Mac profiler time
+    // with boot time.
+    // TODO(leszeks): Consider allowing synchronization against other clocks
+    // than boot time.
+    static os_log_t log_handle = os_log_create(
+        "dev.perfetto.clock_sync", OS_LOG_CATEGORY_POINTS_OF_INTEREST);
+    os_signpost_event_emit(
+        log_handle, OS_SIGNPOST_ID_EXCLUSIVE, "boottime", "%" PRId64,
+        static_cast<uint64_t>(perfetto::base::GetBootTimeNs().count()));
+#endif

-  // TODO implement support for the deferred-start + fast-triggering case.
-}
+    if (tls_state.default_clock != static_cast<uint32_t>(GetClockId())) {
+      ClockSnapshot* clocks = packet->set_clock_snapshot();
+      // Trace clock.
+      ClockSnapshot::Clock* trace_clock = clocks->add_clocks();
+      trace_clock->set_clock_id(static_cast<uint32_t>(GetClockId()));
+      trace_clock->set_timestamp(sequence_timestamp.value);

-void TracingMuxerImpl::CloneTracingSession(
-    TracingSessionGlobalID session_id,
-    TracingSession::CloneTraceArgs args,
-    TracingSession::CloneTraceCallback callback) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  auto* consumer = FindConsumer(session_id);
-  if (!consumer) {
-    TracingSession::CloneTraceCallbackArgs callback_arg{};
-    callback_arg.success = false;
-    callback_arg.error = "Tracing session not found";
-    callback(callback_arg);
-    return;
-  }
-  // Multiple concurrent cloning isn't supported.
-  PERFETTO_DCHECK(!consumer->clone_trace_callback_);
-  consumer->clone_trace_callback_ = std::move(callback);
-  ConsumerEndpoint::CloneSessionArgs consumer_args;
-  consumer_args.unique_session_name = args.unique_session_name;
-  if (!consumer->connected_) {
-    consumer->session_to_clone_ = std::move(consumer_args);
-    return;
+      if (PERFETTO_LIKELY(tls_state.default_clock == kClockIdIncremental)) {
+        // Delta-encoded incremental clock in nanoseconds by default but
+        // configurable by |tls_state.timestamp_unit_multiplier|.
+        ClockSnapshot::Clock* clock_incremental = clocks->add_clocks();
+        clock_incremental->set_clock_id(kClockIdIncremental);
+        clock_incremental->set_timestamp(sequence_timestamp.value /
+                                         ts_unit_multiplier);
+        clock_incremental->set_is_incremental(true);
+        clock_incremental->set_unit_multiplier_ns(ts_unit_multiplier);
+      }
+      if (ts_unit_multiplier > 1) {
+        // absolute clock with custom timestamp_unit_multiplier.
+        ClockSnapshot::Clock* absolute_clock = clocks->add_clocks();
+        absolute_clock->set_clock_id(kClockIdAbsolute);
+        absolute_clock->set_timestamp(sequence_timestamp.value /
+                                      ts_unit_multiplier);
+        absolute_clock->set_is_incremental(false);
+        absolute_clock->set_unit_multiplier_ns(ts_unit_multiplier);
+      }
+    }
   }
-  consumer->session_to_clone_ = std::nullopt;
-  consumer->service_->CloneSession(std::move(consumer_args));
-}
-
-void TracingMuxerImpl::ChangeTracingSessionConfig(
-    TracingSessionGlobalID session_id,
-    const TraceConfig& trace_config) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);

-  auto* consumer = FindConsumer(session_id);
+  // Every thread should write a descriptor for its default track, because most
+  // trace points won't explicitly reference it. We also write the process
+  // descriptor from every thread that writes trace events to ensure it gets
+  // emitted at least once.
+  incr_state->seen_tracks.insert(default_track.uuid);
+  WriteTrackDescriptor(default_track, trace_writer, incr_state, tls_state,
+                       sequence_timestamp);

-  if (!consumer)
-    return;
+  incr_state->seen_tracks.insert(ProcessTrack::Current().uuid);
+  WriteTrackDescriptor(ProcessTrack::Current(), trace_writer, incr_state,
+                       tls_state, sequence_timestamp);

-  if (!consumer->trace_config_) {
-    // Changing the config is only supported for started sessions.
-    PERFETTO_ELOG("Must call Setup(config) and Start() first");
-    return;
+  if (tls_state.enable_thread_time_sampling) {
+    WriteTrackDescriptor(thread_time_counter_track, trace_writer, incr_state,
+                         tls_state, sequence_timestamp);
   }
-
-  consumer->trace_config_ = std::make_shared<TraceConfig>(trace_config);
-  if (consumer->connected_)
-    consumer->service_->ChangeTraceConfig(trace_config);
 }

-void TracingMuxerImpl::FlushTracingSession(TracingSessionGlobalID session_id,
-                                           uint32_t timeout_ms,
-                                           std::function<void(bool)> callback) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  auto* consumer = FindConsumer(session_id);
-  if (!consumer || consumer->start_pending_ || consumer->stop_pending_ ||
-      !consumer->trace_config_) {
-    PERFETTO_ELOG("Flush() can be called only after Start() and before Stop()");
-    std::move(callback)(false);
-    return;
+// static
+protozero::MessageHandle<protos::pbzero::TracePacket>
+TrackEventInternal::NewTracePacket(TraceWriterBase* trace_writer,
+                                   TrackEventIncrementalState* incr_state,
+                                   const TrackEventTlsState& tls_state,
+                                   TraceTimestamp timestamp,
+                                   uint32_t seq_flags) {
+  if (PERFETTO_UNLIKELY(tls_state.default_clock != kClockIdIncremental &&
+                        timestamp.clock_id == kClockIdIncremental)) {
+    timestamp.clock_id = tls_state.default_clock;
   }
-
-  // For now we don't want to expose the flush reason to the consumer-side SDK
-  // users to avoid misuses until there is a strong need.
-  consumer->service_->Flush(timeout_ms, std::move(callback),
-                            FlushFlags(FlushFlags::Initiator::kConsumerSdk,
-                                       FlushFlags::Reason::kExplicit));
+  auto packet = trace_writer->NewTracePacket();
+  auto ts_unit_multiplier = tls_state.timestamp_unit_multiplier;
+  if (PERFETTO_LIKELY(timestamp.clock_id == kClockIdIncremental)) {
+    if (PERFETTO_LIKELY(incr_state->last_timestamp_ns <= timestamp.value)) {
+      // No need to set the clock id here, since kClockIdIncremental is the
+      // clock id assumed by default.
+      auto time_diff_ns = timestamp.value - incr_state->last_timestamp_ns;
+      auto time_diff_units = time_diff_ns / ts_unit_multiplier;
+      packet->set_timestamp(time_diff_units);
+      incr_state->last_timestamp_ns += time_diff_units * ts_unit_multiplier;
+    } else {
+      packet->set_timestamp(timestamp.value / ts_unit_multiplier);
+      packet->set_timestamp_clock_id(ts_unit_multiplier == 1
+                                         ? static_cast<uint32_t>(GetClockId())
+                                         : kClockIdAbsolute);
+    }
+  } else if (PERFETTO_LIKELY(timestamp.clock_id == tls_state.default_clock)) {
+    packet->set_timestamp(timestamp.value / ts_unit_multiplier);
+  } else {
+    packet->set_timestamp(timestamp.value);
+    packet->set_timestamp_clock_id(timestamp.clock_id);
+  }
+  packet->set_sequence_flags(seq_flags);
+  return packet;
 }

-void TracingMuxerImpl::StopTracingSession(TracingSessionGlobalID session_id) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  auto* consumer = FindConsumer(session_id);
-  if (!consumer)
-    return;
-
-  if (consumer->start_pending_) {
-    // If the session hasn't started yet, wait until it does before stopping.
-    consumer->stop_pending_ = true;
-    return;
+// static
+void TrackEventInternal::WriteEventName(StaticString event_name,
+                                        perfetto::EventContext& event_ctx,
+                                        const TrackEventTlsState&) {
+  if (PERFETTO_LIKELY(event_name.value != nullptr)) {
+    size_t name_iid = InternedEventName::Get(&event_ctx, event_name.value);
+    event_ctx.event()->set_name_iid(name_iid);
   }
+}

-  consumer->stop_pending_ = false;
-  if (consumer->stopped_) {
-    // If the session was already stopped (e.g., it failed to start), don't try
-    // stopping again.
-    consumer->NotifyStopComplete();
-  } else if (!consumer->trace_config_) {
-    PERFETTO_ELOG("Must call Setup(config) and Start() first");
-    return;
+// static
+void TrackEventInternal::WriteEventName(perfetto::DynamicString event_name,
+                                        perfetto::EventContext& event_ctx,
+                                        const TrackEventTlsState& tls_state) {
+  if (PERFETTO_UNLIKELY(tls_state.filter_dynamic_event_names)) {
+    event_ctx.event()->set_name(kFilteredEventName,
+                                sizeof(kFilteredEventName) - 1);
   } else {
-    consumer->service_->DisableTracing();
+    event_ctx.event()->set_name(event_name.value, event_name.length);
   }
-
-  consumer->trace_config_.reset();
 }

-void TracingMuxerImpl::DestroyTracingSession(
-    TracingSessionGlobalID session_id) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  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
-    // OnConsumerDisconnect which will invalidate all the iterators to
-    // |backend.consumers|.
-    ConsumerImpl* consumer = nullptr;
-    for (auto& con : backend.consumers) {
-      if (con->session_id_ == session_id) {
-        consumer = con.get();
-        break;
-      }
-    }
-    if (consumer) {
-      // We broke out of the loop above on the assumption that each backend will
-      // only have a single consumer per session. This DCHECK ensures that
-      // this is the case.
-      PERFETTO_DCHECK(
-          std::count_if(backend.consumers.begin(), backend.consumers.end(),
-                        [session_id](const std::unique_ptr<ConsumerImpl>& con) {
-                          return con->session_id_ == session_id;
-                        }) == 1u);
-      consumer->Disconnect();
+// static
+EventContext TrackEventInternal::WriteEvent(
+    TraceWriterBase* trace_writer,
+    TrackEventIncrementalState* incr_state,
+    TrackEventTlsState& tls_state,
+    const Category* category,
+    perfetto::protos::pbzero::TrackEvent::Type type,
+    const TraceTimestamp& timestamp,
+    bool on_current_thread_track) {
+  PERFETTO_DCHECK(!incr_state->was_cleared);
+  auto packet = NewTracePacket(trace_writer, incr_state, tls_state, timestamp);
+  EventContext ctx(std::move(packet), incr_state, &tls_state);
+
+  auto track_event = ctx.event();
+  if (type != protos::pbzero::TrackEvent::TYPE_UNSPECIFIED)
+    track_event->set_type(type);
+
+  if (tls_state.enable_thread_time_sampling && on_current_thread_track) {
+    if (tls_state.thread_time_subsampling_ns == 0 ||
+        incr_state->last_thread_time_timestamp_ns == 0 ||
+        timestamp.value >= incr_state->last_thread_time_timestamp_ns +
+                               tls_state.thread_time_subsampling_ns) {
+      auto thread_time_ns = base::GetThreadCPUTimeNs().count();
+      auto thread_time_delta_ns =
+          thread_time_ns - incr_state->last_thread_time_ns;
+      incr_state->last_thread_time_ns = thread_time_ns;
+      incr_state->last_thread_time_timestamp_ns = timestamp.value;
+      track_event->add_extra_counter_values(
+          thread_time_delta_ns /
+          static_cast<int64_t>(tls_state.timestamp_unit_multiplier));
+    } else {
+      // When subsampling, the skip emitting values.
+      track_event->add_extra_counter_values(0);
     }
   }
-}

-void TracingMuxerImpl::ReadTracingSessionData(
-    TracingSessionGlobalID session_id,
-    std::function<void(TracingSession::ReadTraceCallbackArgs)> callback) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  auto* consumer = FindConsumer(session_id);
-  if (!consumer) {
-    // TODO(skyostil): Signal an error to the user.
-    TracingSession::ReadTraceCallbackArgs callback_arg{};
-    callback(callback_arg);
-    return;
+  // We assume that |category| points to the string with static lifetime.
+  // This means we can use their addresses as interning keys.
+  // TODO(skyostil): Intern categories at compile time.
+  if (category && type != protos::pbzero::TrackEvent::TYPE_SLICE_END &&
+      type != protos::pbzero::TrackEvent::TYPE_COUNTER) {
+    category->ForEachGroupMember(
+        [&](const char* member_name, size_t name_size) {
+          size_t category_iid =
+              InternedEventCategory::Get(&ctx, member_name, name_size);
+          track_event->add_category_iids(category_iid);
+          return true;
+        });
   }
-  PERFETTO_DCHECK(!consumer->read_trace_callback_);
-  consumer->read_trace_callback_ = std::move(callback);
-  consumer->service_->ReadBuffers();
+  return ctx;
 }

-void TracingMuxerImpl::GetTraceStats(
-    TracingSessionGlobalID session_id,
-    TracingSession::GetTraceStatsCallback callback) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  auto* consumer = FindConsumer(session_id);
-  if (!consumer) {
-    TracingSession::GetTraceStatsCallbackArgs callback_arg{};
-    callback_arg.success = false;
-    callback(std::move(callback_arg));
-    return;
-  }
-  PERFETTO_DCHECK(!consumer->get_trace_stats_callback_);
-  consumer->get_trace_stats_callback_ = std::move(callback);
-  if (!consumer->connected_) {
-    consumer->get_trace_stats_pending_ = true;
-    return;
-  }
-  consumer->get_trace_stats_pending_ = false;
-  consumer->service_->GetTraceStats();
+// static
+protos::pbzero::DebugAnnotation* TrackEventInternal::AddDebugAnnotation(
+    perfetto::EventContext* event_ctx,
+    const char* name) {
+  auto annotation = event_ctx->event()->add_debug_annotations();
+  annotation->set_name_iid(InternedDebugAnnotationName::Get(event_ctx, name));
+  return annotation;
 }

-void TracingMuxerImpl::QueryServiceState(
-    TracingSessionGlobalID session_id,
-    TracingSession::QueryServiceStateCallback callback) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  auto* consumer = FindConsumer(session_id);
-  if (!consumer) {
-    TracingSession::QueryServiceStateCallbackArgs callback_arg{};
-    callback_arg.success = false;
-    callback(std::move(callback_arg));
-    return;
-  }
-  PERFETTO_DCHECK(!consumer->query_service_state_callback_);
-  if (!consumer->connected_) {
-    consumer->query_service_state_callback_ = std::move(callback);
-    return;
-  }
-  auto callback_wrapper = [callback](bool success,
-                                     protos::gen::TracingServiceState state) {
-    TracingSession::QueryServiceStateCallbackArgs callback_arg{};
-    callback_arg.success = success;
-    callback_arg.service_state_data = state.SerializeAsArray();
-    callback(std::move(callback_arg));
-  };
-  consumer->service_->QueryServiceState({}, std::move(callback_wrapper));
+// static
+protos::pbzero::DebugAnnotation* TrackEventInternal::AddDebugAnnotation(
+    perfetto::EventContext* event_ctx,
+    perfetto::DynamicString name) {
+  auto annotation = event_ctx->event()->add_debug_annotations();
+  annotation->set_name(name.value);
+  return annotation;
 }

-void TracingMuxerImpl::SetBatchCommitsDurationForTesting(
-    uint32_t batch_commits_duration_ms,
-    BackendType backend_type) {
-  for (RegisteredProducerBackend& backend : producer_backends_) {
-    if (backend.producer && backend.producer->connected_ &&
-        backend.type == backend_type) {
-      backend.producer->service_->MaybeSharedMemoryArbiter()
-          ->SetBatchCommitsDuration(batch_commits_duration_ms);
-    }
-  }
+void TrackEventDataSource::OnStart(const DataSourceBase::StartArgs& args) {
+  TrackEventInternal::OnStart(args);
 }

-bool TracingMuxerImpl::EnableDirectSMBPatchingForTesting(
-    BackendType backend_type) {
-  for (RegisteredProducerBackend& backend : producer_backends_) {
-    if (backend.producer && backend.producer->connected_ &&
-        backend.type == backend_type &&
-        !backend.producer->service_->MaybeSharedMemoryArbiter()
-             ->EnableDirectSMBPatching()) {
-      return false;
-    }
-  }
-  return true;
-}
+}  // namespace internal

-TracingMuxerImpl::ConsumerImpl* TracingMuxerImpl::FindConsumer(
-    TracingSessionGlobalID session_id) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  return FindConsumerAndBackend(session_id).first;
-}
+PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS(
+    PERFETTO_EXPORT_COMPONENT,
+    perfetto::internal::TrackEventDataSource,
+    perfetto::internal::TrackEventDataSourceTraits);

-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(), &backend};
-      }
-    }
-  }
-  return {nullptr, nullptr};
-}
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/internal/track_event_interned_fields.cc
+/*
+ * Copyright (C) 2021 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.
+ */

-void TracingMuxerImpl::InitializeConsumer(TracingSessionGlobalID session_id) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_interned_fields.h"

-  auto res = FindConsumerAndBackend(session_id);
-  if (!res.first || !res.second)
-    return;
-  TracingMuxerImpl::ConsumerImpl* consumer = res.first;
-  RegisteredConsumerBackend& backend = *res.second;
+namespace perfetto {
+namespace internal {

-  TracingBackend::ConnectConsumerArgs conn_args;
-  conn_args.consumer = consumer;
-  conn_args.task_runner = task_runner_.get();
-  consumer->Initialize(backend.backend->ConnectConsumer(conn_args));
-}
+InternedEventCategory::~InternedEventCategory() = default;

-void TracingMuxerImpl::OnConsumerDisconnected(ConsumerImpl* consumer) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  for (RegisteredConsumerBackend& backend : consumer_backends_) {
-    auto pred = [consumer](const std::unique_ptr<ConsumerImpl>& con) {
-      return con.get() == consumer;
-    };
-    backend.consumers.erase(std::remove_if(backend.consumers.begin(),
-                                           backend.consumers.end(), pred),
-                            backend.consumers.end());
-  }
+// static
+void InternedEventCategory::Add(protos::pbzero::InternedData* interned_data,
+                                size_t iid,
+                                const char* value,
+                                size_t length) {
+  auto category = interned_data->add_event_categories();
+  category->set_iid(iid);
+  category->set_name(value, length);
 }

-void TracingMuxerImpl::SetMaxProducerReconnectionsForTesting(uint32_t count) {
-  max_producer_reconnections_.store(count);
+InternedEventName::~InternedEventName() = default;
+
+// static
+void InternedEventName::Add(protos::pbzero::InternedData* interned_data,
+                            size_t iid,
+                            const char* value) {
+  auto name = interned_data->add_event_names();
+  name->set_iid(iid);
+  name->set_name(value);
 }

-void TracingMuxerImpl::OnProducerDisconnected(ProducerImpl* producer) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  for (RegisteredProducerBackend& backend : producer_backends_) {
-    if (backend.producer.get() != producer)
-      continue;
+InternedDebugAnnotationName::~InternedDebugAnnotationName() = default;

-    // The tracing service is disconnected. It does not make sense to keep
-    // tracing (we wouldn't be able to commit). On reconnection, the tracing
-    // service will restart the data sources.
-    for (const auto& rds : data_sources_) {
-      DataSourceStaticState* static_state = rds.static_state;
-      for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
-        auto* internal_state = static_state->TryGet(i);
-        if (internal_state && internal_state->backend_id == backend.id &&
-            internal_state->backend_connection_id ==
-                backend.producer->connection_id_.load(
-                    std::memory_order_relaxed)) {
-          StopDataSource_AsyncBeginImpl(
-              FindDataSourceRes(static_state, internal_state, i,
-                                rds.params.requires_callbacks_under_lock));
-        }
-      }
-    }
+// static
+void InternedDebugAnnotationName::Add(
+    protos::pbzero::InternedData* interned_data,
+    size_t iid,
+    const char* value) {
+  auto name = interned_data->add_debug_annotation_names();
+  name->set_iid(iid);
+  name->set_name(value);
+}

-    // Try reconnecting the disconnected producer. If the connection succeeds,
-    // all the data sources will be automatically re-registered.
-    if (producer->connection_id_.load(std::memory_order_relaxed) >
-        max_producer_reconnections_.load()) {
-      // Avoid reconnecting a failing producer too many times. Instead we just
-      // leak the producer instead of trying to avoid further complicating
-      // cross-thread trace writer creation.
-      PERFETTO_ELOG("Producer disconnected too many times; not reconnecting");
-      continue;
-    }
+InternedDebugAnnotationValueTypeName::~InternedDebugAnnotationValueTypeName() =
+    default;

-    backend.producer->Initialize(
-        backend.backend->ConnectProducer(backend.producer_conn_args));
-    // Don't use producer-provided SMBs for the next connection unless startup
-    // tracing requires it again.
-    backend.producer_conn_args.use_producer_provided_smb = false;
-  }
+// static
+void InternedDebugAnnotationValueTypeName::Add(
+    protos::pbzero::InternedData* interned_data,
+    size_t iid,
+    const char* value) {
+  auto name = interned_data->add_debug_annotation_value_type_names();
+  name->set_iid(iid);
+  name->set_name(value);
 }

-void TracingMuxerImpl::SweepDeadBackends() {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  for (auto it = dead_backends_.begin(); it != dead_backends_.end();) {
-    auto next_it = it;
-    next_it++;
-    if (it->producer->SweepDeadServices())
-      dead_backends_.erase(it);
-    it = next_it;
-  }
+}  // namespace internal
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/platform.cc
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/tracing/platform.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_tls.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/trace_writer_base.h"
+
+namespace perfetto {
+
+PlatformThreadLocalObject::~PlatformThreadLocalObject() = default;
+Platform::~Platform() = default;
+
+void Platform::Shutdown() {}
+
+base::PlatformThreadId Platform::GetCurrentThreadId() {
+  return base::GetThreadId();
 }

-TracingMuxerImpl::FindDataSourceRes TracingMuxerImpl::FindDataSource(
-    TracingBackendId backend_id,
-    DataSourceInstanceID instance_id) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  // 0 is the sentinel id for unadopted startup data sources; never match it.
-  if (instance_id == 0)
-    return FindDataSourceRes();
-  RegisteredProducerBackend& backend = *FindProducerBackendById(backend_id);
-  for (const auto& rds : data_sources_) {
-    DataSourceStaticState* static_state = rds.static_state;
-    for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
-      auto* internal_state = static_state->TryGet(i);
-      if (internal_state && internal_state->backend_id == backend_id &&
-          internal_state->backend_connection_id ==
-              backend.producer->connection_id_.load(
-                  std::memory_order_relaxed) &&
-          internal_state->data_source_instance_id == instance_id) {
-        return FindDataSourceRes(static_state, internal_state, i,
-                                 rds.params.requires_callbacks_under_lock);
-      }
-    }
-  }
-  return FindDataSourceRes();
+// static
+std::unique_ptr<PlatformThreadLocalObject>
+PlatformThreadLocalObject::CreateInstance() {
+  return std::unique_ptr<PlatformThreadLocalObject>(new internal::TracingTLS());
 }

-// Can be called from any thread.
-std::unique_ptr<TraceWriterBase> TracingMuxerImpl::CreateTraceWriter(
-    DataSourceStaticState* static_state,
-    uint32_t data_source_instance_index,
-    DataSourceState* data_source,
-    BufferExhaustedPolicy buffer_exhausted_policy) {
-  if (PERFETTO_UNLIKELY(data_source->interceptor_id)) {
-    // If the session is being intercepted, return a heap-backed trace writer
-    // instead. This is safe because all the data given to the interceptor is
-    // either thread-local (|instance_index|), statically allocated
-    // (|static_state|) or constant after initialization (|interceptor|). Access
-    // to the interceptor instance itself through |data_source| is protected by
-    // a statically allocated lock (similarly to the data source instance).
-    auto& interceptor = interceptors_[data_source->interceptor_id - 1];
-    return std::unique_ptr<TraceWriterBase>(new InterceptorTraceWriter(
-        interceptor.tls_factory(static_state, data_source_instance_index),
-        interceptor.packet_callback, static_state, data_source_instance_index));
-  }
-  ProducerImpl* producer =
-      FindProducerBackendById(data_source->backend_id)->producer.get();
-  // Atomically load the current service endpoint. We keep the pointer as a
-  // shared pointer on the stack to guard against it from being concurrently
-  // modified on the thread by ProducerImpl::Initialize() swapping in a
-  // reconnected service on the muxer task runner thread.
-  //
-  // The endpoint may also be concurrently modified by SweepDeadServices()
-  // clearing out old disconnected services. We guard against that by
-  // SharedMemoryArbiter keeping track of any outstanding trace writers. After
-  // shutdown has started, the trace writer created below will be a null one
-  // which will drop any written data. See SharedMemoryArbiter::TryShutdown().
-  //
-  // We use an atomic pointer instead of holding a lock because
-  // CreateTraceWriter posts tasks under the hood.
-  std::shared_ptr<ProducerEndpoint> service =
-      std::atomic_load(&producer->service_);
+// static
+base::PlatformProcessId Platform::process_id_ = 0;

-  // The service may have been disconnected and reconnected concurrently after
-  // the data source was enabled, in which case we may not have an arbiter, or
-  // would be creating a TraceWriter for the wrong (a newer) connection / SMB.
-  // Instead, early-out now. A relaxed load is fine here because the atomic_load
-  // above ensures that the |service| isn't newer.
-  if (producer->connection_id_.load(std::memory_order_relaxed) !=
-      data_source->backend_connection_id) {
-    return std::unique_ptr<TraceWriter>(new NullTraceWriter());
-  }
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/platform_posix.cc
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */

-  // We just need a relaxed atomic read here: We can use the reservation ID even
-  // after the buffer was bound, we just need to be sure to read it atomically.
-  uint16_t startup_buffer_reservation =
-      data_source->startup_target_buffer_reservation.load(
-          std::memory_order_relaxed);
-  if (startup_buffer_reservation) {
-    return service->MaybeSharedMemoryArbiter()->CreateStartupTraceWriter(
-        startup_buffer_reservation);
-  }
-  return service->CreateTraceWriter(data_source->buffer_id,
-                                    buffer_exhausted_policy);
-}
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-// This is called via the public API Tracing::NewTrace().
-// Can be called from any thread.
-std::unique_ptr<TracingSession> TracingMuxerImpl::CreateTracingSession(
-    BackendType requested_backend_type,
-    TracingConsumerBackend* (*system_backend_factory)()) {
-  TracingSessionGlobalID session_id = ++next_tracing_session_id_;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)

-  // |backend_type| can only specify one backend, not an OR-ed mask.
-  PERFETTO_CHECK((requested_backend_type & (requested_backend_type - 1)) == 0);
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_tls.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/platform.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/trace_writer_base.h"

-  // Capturing |this| is fine because the TracingMuxer is a leaky singleton.
-  task_runner_->PostTask([this, requested_backend_type, session_id,
-                          system_backend_factory] {
-    if (requested_backend_type == kSystemBackend && system_backend_factory &&
-        !FindConsumerBackendByType(kSystemBackend)) {
-      AddConsumerBackend(system_backend_factory(), kSystemBackend);
-    }
-    for (RegisteredConsumerBackend& backend : consumer_backends_) {
-      if (requested_backend_type && backend.type &&
-          backend.type != requested_backend_type) {
-        continue;
-      }
+#include <pthread.h>
+#include <stdlib.h>

-      // 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, session_id));
+namespace perfetto {

-      // 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=%u, consumer will disconnect",
-            requested_backend_type);
-        InitializeConsumer(session_id);
-        return;
-      }
+namespace {

-      // Check if the embedder wants to be asked for permission before
-      // connecting the consumer.
-      if (!policy_) {
-        InitializeConsumer(session_id);
-        return;
-      }
+class PlatformPosix : public Platform {
+ public:
+  PlatformPosix();
+  ~PlatformPosix() override;

-      BackendType type = backend.type;
-      TracingPolicy::ShouldAllowConsumerSessionArgs args;
-      args.backend_type = backend.type;
-      args.result_callback = [this, type, session_id](bool allow) {
-        task_runner_->PostTask([this, type, session_id, allow] {
-          if (allow) {
-            InitializeConsumer(session_id);
-            return;
-          }
+  ThreadLocalObject* GetOrCreateThreadLocalObject() override;

-          PERFETTO_ELOG(
-              "Consumer session for backend type type=%u forbidden, "
-              "consumer will disconnect",
-              type);
+  std::unique_ptr<base::TaskRunner> CreateTaskRunner(
+      const CreateTaskRunnerArgs&) override;
+  std::string GetCurrentProcessName() override;
+  void Shutdown() override;

-          auto* consumer = FindConsumer(session_id);
-          if (!consumer)
-            return;
+ private:
+  pthread_key_t tls_key_{};
+};

-          consumer->OnDisconnect();
-        });
-      };
-      policy_->ShouldAllowConsumerSession(args);
-      return;
-    }
-    PERFETTO_DFATAL("Not reached");
-  });
+PlatformPosix* g_instance = nullptr;

-  return std::unique_ptr<TracingSession>(
-      new TracingSessionImpl(this, session_id, requested_backend_type));
+using ThreadLocalObject = Platform::ThreadLocalObject;
+
+PlatformPosix::PlatformPosix() {
+  PERFETTO_CHECK(!g_instance);
+  g_instance = this;
+  auto tls_dtor = [](void* obj) {
+    // The Posix TLS implementation resets the key before calling this dtor.
+    // Here we re-reset it to the object we are about to delete. This is to
+    // handle re-entrant usages of tracing in the PostTask done during the dtor
+    // (see comments in TracingTLS::~TracingTLS()). Chromium's platform
+    // implementation (which does NOT use this platform impl) has a similar
+    // workaround (https://crrev.com/c/2748300).
+    pthread_setspecific(g_instance->tls_key_, obj);
+    delete static_cast<ThreadLocalObject*>(obj);
+    pthread_setspecific(g_instance->tls_key_, nullptr);
+  };
+  PERFETTO_CHECK(pthread_key_create(&tls_key_, tls_dtor) == 0);
+}
+
+PlatformPosix::~PlatformPosix() {
+  // pthread_key_delete doesn't call destructors, so do it manually for the
+  // calling thread.
+  void* tls_ptr = pthread_getspecific(tls_key_);
+  delete static_cast<ThreadLocalObject*>(tls_ptr);
+
+  pthread_key_delete(tls_key_);
+  g_instance = nullptr;
 }

-// static
-// This is called via the public API Tracing::SetupStartupTracing().
-// Can be called from any thread.
-std::unique_ptr<StartupTracingSession>
-TracingMuxerImpl::CreateStartupTracingSession(
-    const TraceConfig& config,
-    Tracing::SetupStartupTracingOpts opts) {
-  BackendType backend_type = opts.backend;
-  // |backend_type| can only specify one backend, not an OR-ed mask.
-  PERFETTO_CHECK((backend_type & (backend_type - 1)) == 0);
-  // The in-process backend doesn't support startup tracing.
-  PERFETTO_CHECK(backend_type != BackendType::kInProcessBackend);
-
-  TracingSessionGlobalID session_id = ++next_tracing_session_id_;
+void PlatformPosix::Shutdown() {
+  PERFETTO_CHECK(g_instance == this);
+  delete this;
+  PERFETTO_CHECK(!g_instance);
+  // We're not clearing out the instance in GetDefaultPlatform() since it's not
+  // possible to re-initialize Perfetto after calling this function anyway.
+}

-  // Capturing |this| is fine because the TracingMuxer is a leaky singleton.
-  task_runner_->PostTask([this, config, opts, backend_type, session_id] {
-    for (RegisteredProducerBackend& backend : producer_backends_) {
-      if (backend_type && backend.type && backend.type != backend_type) {
-        continue;
-      }
+ThreadLocalObject* PlatformPosix::GetOrCreateThreadLocalObject() {
+  // In chromium this should be implemented using base::ThreadLocalStorage.
+  void* tls_ptr = pthread_getspecific(tls_key_);

-      TracingBackendId backend_id = backend.id;
+  // This is needed to handle re-entrant calls during TLS dtor.
+  // See comments in platform.cc and aosp/1712371 .
+  ThreadLocalObject* tls = static_cast<ThreadLocalObject*>(tls_ptr);
+  if (!tls) {
+    tls = ThreadLocalObject::CreateInstance().release();
+    pthread_setspecific(tls_key_, tls);
+  }
+  return tls;
+}

-      // 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=%u, startup tracing "
-            "failed",
-            backend_type);
-        if (opts.on_setup)
-          opts.on_setup(Tracing::OnStartupTracingSetupCallbackArgs{
-              0 /* num_data_sources_started */});
-        return;
-      }
+std::unique_ptr<base::TaskRunner> PlatformPosix::CreateTaskRunner(
+    const CreateTaskRunnerArgs& args) {
+  return std::unique_ptr<base::TaskRunner>(new base::ThreadTaskRunner(
+      base::ThreadTaskRunner::CreateAndStart(args.name_for_debugging)));
+}

-      if (!backend.producer->service_ ||
-          !backend.producer->service_->shared_memory()) {
-        // If we unsuccessfully attempted to use a producer-provided SMB in the
-        // past, don't try again.
-        if (backend.producer->producer_provided_smb_failed_) {
-          PERFETTO_ELOG(
-              "Backend %zu doesn't seem to support producer-provided "
-              "SMBs, startup tracing failed",
-              backend_id);
-          if (opts.on_setup)
-            opts.on_setup(Tracing::OnStartupTracingSetupCallbackArgs{
-                0 /* num_data_sources_started */});
-          return;
-        }
+std::string PlatformPosix::GetCurrentProcessName() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  std::string cmdline;
+  base::ReadFile("/proc/self/cmdline", &cmdline);
+  return cmdline.substr(0, cmdline.find('\0'));
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD)
+  return std::string(getprogname());
+#else
+  return "unknown_producer";
+#endif
+}

-        PERFETTO_DLOG("Reconnecting backend %zu for startup tracing",
-                      backend_id);
-        backend.producer_conn_args.use_producer_provided_smb = true;
-        backend.producer->service_->Disconnect();  // Causes a reconnect.
-        PERFETTO_DCHECK(backend.producer->service_ &&
-                        backend.producer->service_->MaybeSharedMemoryArbiter());
-      }
+}  // namespace

-      RegisteredStartupSession session;
-      session.session_id = session_id;
-      session.on_aborted = opts.on_aborted;
-      session.on_adopted = opts.on_adopted;
+// static
+Platform* Platform::GetDefaultPlatform() {
+  static PlatformPosix* instance = new PlatformPosix();
+  return instance;
+}

-      for (const TraceConfig::DataSource& ds_cfg : config.data_sources()) {
-        // Find all matching data sources and start one instance of each.
-        for (const auto& rds : data_sources_) {
-          if (rds.descriptor.name() != ds_cfg.config().name())
-            continue;
+}  // namespace perfetto
+#endif  // OS_LINUX || OS_ANDROID || OS_APPLE || OS_FUCHSIA
+// gen_amalgamated begin source: src/tracing/platform_windows.cc
+/*
+ * Copyright (C) 2021 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.
+ */

-          PERFETTO_DLOG(
-              "Setting up data source %s for startup tracing with target "
-              "buffer reservation %" PRIi32,
-              rds.descriptor.name().c_str(),
-              backend.producer->last_startup_target_buffer_reservation_ + 1u);
-          auto ds = SetupDataSourceImpl(
-              rds, backend_id,
-              backend.producer->connection_id_.load(std::memory_order_relaxed),
-              /*instance_id=*/0, ds_cfg.config(),
-              /*startup_session_id=*/session_id);
-          if (ds) {
-            StartDataSourceImpl(ds);
-            session.num_unbound_data_sources++;
-          }
-        }
-      }
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-      int num_ds = session.num_unbound_data_sources;
-      auto on_setup = opts.on_setup;
-      if (on_setup) {
-        backend.producer->OnStartupTracingSetup();
-        task_runner_->PostTask([on_setup, num_ds] {
-          on_setup(Tracing::OnStartupTracingSetupCallbackArgs{num_ds});
-        });
-      }
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)

-      if (num_ds > 0) {
-        backend.startup_sessions.push_back(std::move(session));
+#include <windows.h>

-        if (opts.timeout_ms > 0) {
-          task_runner_->PostDelayedTask(
-              [this, session_id, backend_type] {
-                AbortStartupTracingSession(session_id, backend_type);
-              },
-              opts.timeout_ms);
-        }
-      }
-      return;
-    }
-    PERFETTO_DFATAL("Invalid startup tracing session backend");
-  });
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_tls.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/platform.h"

-  return std::unique_ptr<StartupTracingSession>(
-      new StartupTracingSessionImpl(this, session_id, backend_type));
-}
+// Thread Termination Callbacks.
+// Windows doesn't support a per-thread destructor with its
+// TLS primitives. So, we build it manually by inserting a
+// function to be called on each thread's exit.
+// This magic is from chromium's base/threading/thread_local_storage_win.cc
+// which in turn is from http://www.codeproject.com/threads/tls.asp.

-// Must not be called from the SDK's internal thread.
-std::unique_ptr<StartupTracingSession>
-TracingMuxerImpl::CreateStartupTracingSessionBlocking(
-    const TraceConfig& config,
-    Tracing::SetupStartupTracingOpts opts) {
-  auto previous_on_setup = std::move(opts.on_setup);
-  PERFETTO_CHECK(!task_runner_->RunsTasksOnCurrentThread());
-  base::WaitableEvent event;
-  // It is safe to capture by reference because once on_setup is called only
-  // once before this method returns.
-  opts.on_setup = [&](Tracing::OnStartupTracingSetupCallbackArgs args) {
-    if (previous_on_setup) {
-      previous_on_setup(std::move(args));
-    }
-    event.Notify();
-  };
-  auto session = CreateStartupTracingSession(config, std::move(opts));
-  event.Wait();
-  return session;
-}
+#ifdef _WIN64
+#pragma comment(linker, "/INCLUDE:_tls_used")
+#pragma comment(linker, "/INCLUDE:perfetto_thread_callback_base")
+#else
+#pragma comment(linker, "/INCLUDE:__tls_used")
+#pragma comment(linker, "/INCLUDE:_perfetto_thread_callback_base")
+#endif

-void TracingMuxerImpl::AbortStartupTracingSession(
-    TracingSessionGlobalID session_id,
-    BackendType backend_type) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
+namespace perfetto {

-  for (RegisteredProducerBackend& backend : producer_backends_) {
-    if (backend_type != backend.type)
-      continue;
+namespace {

-    auto session_it = std::find_if(
-        backend.startup_sessions.begin(), backend.startup_sessions.end(),
-        [session_id](const RegisteredStartupSession& session) {
-          return session.session_id == session_id;
-        });
+class PlatformWindows : public Platform {
+ public:
+  static PlatformWindows* instance;
+  PlatformWindows();
+  ~PlatformWindows() override;

-    // The startup session may have already been aborted or fully adopted.
-    if (session_it == backend.startup_sessions.end())
-      return;
-    if (session_it->is_aborting)
-      return;
+  ThreadLocalObject* GetOrCreateThreadLocalObject() override;
+  std::unique_ptr<base::TaskRunner> CreateTaskRunner(
+      const CreateTaskRunnerArgs&) override;
+  std::string GetCurrentProcessName() override;
+  void OnThreadExit();

-    session_it->is_aborting = true;
+ private:
+  DWORD tls_key_{};
+};

-    // Iterate all data sources and abort them if they weren't adopted yet.
-    for (const auto& rds : data_sources_) {
-      DataSourceStaticState* static_state = rds.static_state;
-      for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
-        auto* internal_state = static_state->TryGet(i);
-        if (internal_state &&
-            internal_state->startup_target_buffer_reservation.load(
-                std::memory_order_relaxed) &&
-            internal_state->data_source_instance_id == 0 &&
-            internal_state->startup_session_id == session_id) {
-          PERFETTO_DLOG(
-              "Aborting startup tracing for data source %s (target buffer "
-              "reservation %" PRIu16 ")",
-              rds.descriptor.name().c_str(),
-              internal_state->startup_target_buffer_reservation.load(
-                  std::memory_order_relaxed));
+using ThreadLocalObject = Platform::ThreadLocalObject;

-          // Abort the instance asynchronously by stopping it. From this point
-          // onwards, the service will not be able to adopt it via
-          // StartDataSource().
-          session_it->num_aborting_data_sources++;
-          StopDataSource_AsyncBeginImpl(
-              FindDataSourceRes(static_state, internal_state, i,
-                                rds.params.requires_callbacks_under_lock));
-        }
-      }
-    }
+// static
+PlatformWindows* PlatformWindows::instance = nullptr;

-    // If we did everything right, we should have aborted all still-unbound data
-    // source instances.
-    PERFETTO_DCHECK(session_it->num_unbound_data_sources ==
-                    session_it->num_aborting_data_sources);
+PlatformWindows::PlatformWindows() {
+  instance = this;
+  tls_key_ = ::TlsAlloc();
+  PERFETTO_CHECK(tls_key_ != TLS_OUT_OF_INDEXES);
+}

-    if (session_it->num_aborting_data_sources == 0) {
-      if (session_it->on_aborted)
-        task_runner_->PostTask(session_it->on_aborted);
+PlatformWindows::~PlatformWindows() {
+  // TlsFree doesn't call destructors, so do it manually for the calling
+  // thread.
+  OnThreadExit();
+  ::TlsFree(tls_key_);
+  instance = nullptr;
+}

-      backend.startup_sessions.erase(session_it);
-    }
-    return;
+void PlatformWindows::OnThreadExit() {
+  auto tls = static_cast<ThreadLocalObject*>(::TlsGetValue(tls_key_));
+  if (tls) {
+    // At this point we rely on the TLS object to be still set to the TracingTLS
+    // we are deleting. See comments in TracingTLS::~TracingTLS().
+    delete tls;
   }
-  // We might reach here in tests because when we start a trace, we post the
-  // Task(AbortStartupTrace, delay=timeout). When we do
-  // perfetto::ResetForTesting, we sweep dead backends, and we are not able to
-  // kill those delayed tasks because TaskRunner doesn't have support for
-  // deleting scheduled future tasks and TaskRunner doesn't have any API for us
-  // to wait for the completion of all the scheduled tasks (apart from
-  // deleting the TaskRunner) and we want to avoid doing that because we need
-  // a long running TaskRunner in muxer.
-  PERFETTO_DLOG("Invalid startup tracing session backend");
 }

-void TracingMuxerImpl::InitializeInstance(const TracingInitArgs& args) {
-  if (instance_ != TracingMuxerFake::Get()) {
-    // The tracing muxer was already initialized. We might need to initialize
-    // additional backends that were not configured earlier.
-    auto* muxer = static_cast<TracingMuxerImpl*>(instance_);
-    muxer->task_runner_->PostTask([muxer, args] { muxer->AddBackends(args); });
-    return;
-  }
-  // If we previously had a TracingMuxerImpl instance which was reset,
-  // reinitialize and reuse it instead of trying to create a new one. See
-  // ResetForTesting().
-  if (g_prev_instance) {
-    auto* muxer = g_prev_instance;
-    g_prev_instance = nullptr;
-    instance_ = muxer;
-    muxer->task_runner_->PostTask([muxer, args] {
-      muxer->Initialize(args);
-      muxer->AddBackends(args);
-    });
-  } else {
-    new TracingMuxerImpl(args);
+ThreadLocalObject* PlatformWindows::GetOrCreateThreadLocalObject() {
+  void* tls_ptr = ::TlsGetValue(tls_key_);
+
+  auto* tls = static_cast<ThreadLocalObject*>(tls_ptr);
+  if (!tls) {
+    tls = ThreadLocalObject::CreateInstance().release();
+    ::TlsSetValue(tls_key_, tls);
   }
+  return tls;
 }

-// static
-void TracingMuxerImpl::ResetForTesting() {
-  // Ideally we'd tear down the entire TracingMuxerImpl, but the lifetimes of
-  // various objects make that a non-starter. In particular:
-  //
-  // 1) Any thread that has entered a trace event has a TraceWriter, which holds
-  //    a reference back to ProducerImpl::service_.
-  //
-  // 2) ProducerImpl::service_ has a reference back to the ProducerImpl.
-  //
-  // 3) ProducerImpl holds reference to TracingMuxerImpl::task_runner_, which in
-  //    turn depends on TracingMuxerImpl itself.
-  //
-  // Because of this, it's not safe to deallocate TracingMuxerImpl until all
-  // threads have dropped their TraceWriters. Since we can't really ask the
-  // caller to guarantee this, we'll instead reset enough of the muxer's state
-  // so that it can be reinitialized later and ensure all necessary objects from
-  // the old state remain alive until all references have gone away.
-  auto* muxer = reinterpret_cast<TracingMuxerImpl*>(instance_);
-
-  base::WaitableEvent reset_done;
-  auto do_reset = [muxer, &reset_done] {
-    muxer->DestroyStoppedTraceWritersForCurrentThread();
-    // Unregister all data sources so they don't interfere with any future
-    // tracing sessions.
-    for (RegisteredDataSource& rds : muxer->data_sources_) {
-      for (RegisteredProducerBackend& backend : muxer->producer_backends_) {
-        if (!backend.producer->service_ || !backend.producer->connected_)
-          continue;
-        backend.producer->service_->UnregisterDataSource(rds.descriptor.name());
-      }
-    }
-    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->consumer_backends_.clear();
-    muxer->producer_backends_.clear();
-    muxer->interceptors_.clear();
+std::unique_ptr<base::TaskRunner> PlatformWindows::CreateTaskRunner(
+    const CreateTaskRunnerArgs& args) {
+  return std::unique_ptr<base::TaskRunner>(new base::ThreadTaskRunner(
+      base::ThreadTaskRunner::CreateAndStart(args.name_for_debugging)));
+}

-    for (auto& ds : muxer->data_sources_) {
-      ds.static_state->ResetForTesting();
-    }
+std::string PlatformWindows::GetCurrentProcessName() {
+  char buf[MAX_PATH];
+  auto len = ::GetModuleFileNameA(nullptr /*current*/, buf, sizeof(buf));
+  std::string name(buf, static_cast<size_t>(len));
+  size_t sep = name.find_last_of('\\');
+  if (sep != std::string::npos)
+    name = name.substr(sep + 1);
+  return name;
+}

-    muxer->data_sources_.clear();
-    muxer->next_data_source_index_ = 0;
+}  // namespace

-    // Free all backends without active trace writers or other inbound
-    // references. Note that even if all the backends get swept, the muxer still
-    // needs to stay around since |task_runner_| is assumed to be long-lived.
-    muxer->SweepDeadBackends();
+// static
+Platform* Platform::GetDefaultPlatform() {
+  static PlatformWindows* thread_safe_init_instance = new PlatformWindows();
+  return thread_safe_init_instance;
+}

-    // Make sure we eventually discard any per-thread trace writers from the
-    // previous instance.
-    muxer->muxer_id_for_testing_++;
+}  // namespace perfetto

-    g_prev_instance = muxer;
-    instance_ = TracingMuxerFake::Get();
+// -----------------------
+// Thread-local destructor
+// -----------------------

-    // Call the user provided cleanups on the muxer thread.
-    for (auto& cb : muxer->reset_callbacks_) {
-      cb();
-    }
+// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are
+// called automatically by the OS loader code (not the CRT) when the module is
+// loaded and on thread creation. They are NOT called if the module has been
+// loaded by a LoadLibrary() call. It must have implicitly been loaded at
+// process startup.
+// See VC\crt\src\tlssup.c for reference.

-    reset_done.Notify();
-  };
+// extern "C" suppresses C++ name mangling so we know the symbol name for the
+// linker /INCLUDE:symbol pragma above.
+extern "C" {
+// The linker must not discard perfetto_thread_callback_base. (We force a
+// reference to this variable with a linker /INCLUDE:symbol pragma to ensure
+// that.) If this variable is discarded, the OnThreadExit function will never be
+// called.

-  // Some tests run the muxer and the test on the same thread. In these cases,
-  // we can reset synchronously.
-  if (muxer->task_runner_->RunsTasksOnCurrentThread()) {
-    do_reset();
-  } else {
-    muxer->DestroyStoppedTraceWritersForCurrentThread();
-    muxer->task_runner_->PostTask(std::move(do_reset));
-    reset_done.Wait();
-    // Call the user provided cleanups also on this thread.
-    for (auto& cb : muxer->reset_callbacks_) {
-      cb();
-    }
+void NTAPI PerfettoOnThreadExit(PVOID, DWORD, PVOID);
+void NTAPI PerfettoOnThreadExit(PVOID, DWORD reason, PVOID) {
+  if (reason == DLL_THREAD_DETACH || reason == DLL_PROCESS_DETACH) {
+    if (perfetto::PlatformWindows::instance)
+      perfetto::PlatformWindows::instance->OnThreadExit();
   }
-  muxer->reset_callbacks_.clear();
 }

-// static
-void TracingMuxerImpl::Shutdown() {
-  auto* muxer = reinterpret_cast<TracingMuxerImpl*>(instance_);
+#ifdef _WIN64

-  // Shutting down on the muxer thread would lead to a deadlock.
-  PERFETTO_CHECK(!muxer->task_runner_->RunsTasksOnCurrentThread());
-  muxer->DestroyStoppedTraceWritersForCurrentThread();
+// .CRT section is merged with .rdata on x64 so it must be constant data.
+#pragma const_seg(".CRT$XLP")

-  std::unique_ptr<base::TaskRunner> owned_task_runner(
-      muxer->task_runner_.get());
-  Platform* platform = muxer->platform_;
-  base::WaitableEvent shutdown_done;
-  owned_task_runner->PostTask([muxer, &shutdown_done] {
-    // 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->consumer_backends_) {
-      for (auto& consumer : backend.consumers) {
-        PERFETTO_CHECK(!consumer->service_);
-      }
-    }
-    // Make sure no trace writers are lingering around on the muxer thread. Note
-    // that we can't do this for any arbitrary thread in the process; it is the
-    // caller's responsibility to clean them up before shutting down Perfetto.
-    muxer->DestroyStoppedTraceWritersForCurrentThread();
-    // The task runner must be deleted outside the muxer thread. This is done by
-    // `owned_task_runner` above.
-    muxer->task_runner_.release();
-    delete muxer;
-    instance_ = TracingMuxerFake::Get();
-    shutdown_done.Notify();
-  });
-  shutdown_done.Wait();
+// When defining a const variable, it must have external linkage to be sure the
+// linker doesn't discard it.
+extern const PIMAGE_TLS_CALLBACK perfetto_thread_callback_base;
+const PIMAGE_TLS_CALLBACK perfetto_thread_callback_base = PerfettoOnThreadExit;

-  // Join the muxer thread before the platform (and its TLS key) is destroyed.
-  owned_task_runner.reset();
+// Reset the default section.
+#pragma const_seg()

-  // On the calling thread, so the platform can free this thread's TLS object.
-  platform->Shutdown();
-}
+#else  // _WIN64

-void TracingMuxerImpl::AppendResetForTestingCallback(std::function<void()> cb) {
-  reset_callbacks_.push_back(std::move(cb));
-}
+#pragma data_seg(".CRT$XLP")
+PIMAGE_TLS_CALLBACK perfetto_thread_callback_base = PerfettoOnThreadExit;
+// Reset the default section.
+#pragma data_seg()

-TracingMuxer::~TracingMuxer() = default;
+#endif  // _WIN64

-static_assert(std::is_same<internal::BufferId, BufferID>::value,
-              "public's BufferId and tracing/core's BufferID diverged");
+}  // extern "C"

-}  // namespace internal
-}  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/internal/track_event_internal.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/no_destructor.h
+#endif  // OS_WIN
+// gen_amalgamated begin source: src/tracing/traced_value.cc
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -48974,68 +53861,175 @@ static_assert(std::is_same<internal::BufferId, BufferID>::value,
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_NO_DESTRUCTOR_H_
-#define INCLUDE_PERFETTO_EXT_BASE_NO_DESTRUCTOR_H_
+// gen_amalgamated expanded: #include "perfetto/tracing/traced_value.h"

-#include <new>
-#include <utility>
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/debug_annotation.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_interned_fields.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"

 namespace perfetto {
-namespace base {

-// Wrapper that can hold an object of type T, without invoking the contained
-// object's destructor when being destroyed. Useful for creating statics while
-// avoiding static destructors.
-//
-// Stores the object inline, and therefore doesn't incur memory allocation and
-// pointer indirection overheads.
-//
-// Example of use:
-//
-//   const std::string& GetStr() {
-//     static base::NoDestructor<std::string> s("hello");
-//     return s.ref();
-//   }
-//
-template <typename T>
-class NoDestructor {
- public:
-  // Forward arguments to T's constructor. Note that this doesn't cover
-  // construction from initializer lists.
-  template <typename... Args>
-  explicit NoDestructor(Args&&... args) {
-    new (storage_) T(std::forward<Args>(args)...);
-  }
+namespace internal {

-  NoDestructor(const NoDestructor&) = delete;
-  NoDestructor& operator=(const NoDestructor&) = delete;
-  NoDestructor(NoDestructor&&) = delete;
-  NoDestructor& operator=(NoDestructor&&) = delete;
+TracedValue CreateTracedValueFromProto(
+    protos::pbzero::DebugAnnotation* annotation,
+    EventContext* event_context) {
+  return TracedValue::CreateFromProto(annotation, event_context);
+}

-  ~NoDestructor() = default;
+}  // namespace internal

-  /* To avoid type-punned pointer strict aliasing warnings on GCC6 and below
-   * these need to be split over two lines. If they are collapsed onto one line.
-   *   return reinterpret_cast<const T*>(storage_);
-   * The error fires.
-   */
-  const T& ref() const {
-    auto* const cast = reinterpret_cast<const T*>(storage_);
-    return *cast;
-  }
-  T& ref() {
-    auto* const cast = reinterpret_cast<T*>(storage_);
-    return *cast;
+// static
+TracedValue TracedValue::CreateFromProto(
+    protos::pbzero::DebugAnnotation* annotation,
+    EventContext* event_context) {
+  return TracedValue(annotation, event_context, nullptr);
+}
+
+TracedValue::TracedValue(TracedValue&&) = default;
+TracedValue::~TracedValue() = default;
+
+void TracedValue::WriteInt64(int64_t value) && {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  annotation_->set_int_value(value);
+}
+
+void TracedValue::WriteUInt64(uint64_t value) && {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  annotation_->set_uint_value(value);
+}
+
+void TracedValue::WriteDouble(double value) && {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  annotation_->set_double_value(value);
+}
+
+void TracedValue::WriteBoolean(bool value) && {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  annotation_->set_bool_value(value);
+}
+
+void TracedValue::WriteString(const char* value) && {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  annotation_->set_string_value(value);
+}
+
+void TracedValue::WriteString(const char* value, size_t len) && {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  annotation_->set_string_value(value, len);
+}
+
+void TracedValue::WriteString(const std::string& value) && {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  annotation_->set_string_value(value);
+}
+
+void TracedValue::WriteString(std::string_view value) && {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  annotation_->set_string_value(value.data(), value.size());
+}
+
+void TracedValue::WritePointer(const void* value) && {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  annotation_->set_pointer_value(reinterpret_cast<uint64_t>(value));
+}
+
+TracedDictionary TracedValue::WriteDictionary() && {
+  // Note: this passes |checked_scope_.is_active_| bit to the parent to be
+  // picked up later by the new TracedDictionary.
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  checked_scope_.Reset();
+
+  PERFETTO_DCHECK(!annotation_->is_finalized());
+  return TracedDictionary(annotation_,
+                          protos::pbzero::DebugAnnotation::kDictEntries,
+                          event_context_, checked_scope_.parent_scope());
+}
+
+TracedArray TracedValue::WriteArray() && {
+  // Note: this passes |checked_scope_.is_active_| bit to the parent to be
+  // picked up later by the new TracedDictionary.
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  checked_scope_.Reset();
+
+  PERFETTO_DCHECK(!annotation_->is_finalized());
+  return TracedArray(annotation_, event_context_,
+                     checked_scope_.parent_scope());
+}
+
+protozero::Message* TracedValue::WriteProtoInternal(const char* name) {
+  if (event_context_) {
+    annotation_->set_proto_type_name_iid(
+        internal::InternedDebugAnnotationValueTypeName::Get(event_context_,
+                                                            name));
+  } else {
+    annotation_->set_proto_type_name(name);
   }
+  return annotation_->template BeginNestedMessage<protozero::Message>(
+      protos::pbzero::DebugAnnotation::kProtoValueFieldNumber);
+}

- private:
-  alignas(T) char storage_[sizeof(T)];
-};
+TracedArray::TracedArray(TracedValue annotation)
+    : TracedArray(std::move(annotation).WriteArray()) {}

-}  // namespace base
-}  // namespace perfetto
+TracedValue TracedArray::AppendItem() {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  return TracedValue(annotation_->add_array_values(), event_context_,
+                     &checked_scope_);
+}

-#endif  // INCLUDE_PERFETTO_EXT_BASE_NO_DESTRUCTOR_H_
+TracedDictionary TracedArray::AppendDictionary() {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  return AppendItem().WriteDictionary();
+}
+
+TracedArray TracedArray::AppendArray() {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  return AppendItem().WriteArray();
+}
+
+TracedDictionary::TracedDictionary(TracedValue annotation)
+    : TracedDictionary(std::move(annotation).WriteDictionary()) {}
+
+TracedValue TracedDictionary::AddItem(StaticString key) {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  protos::pbzero::DebugAnnotation* item =
+      message_->BeginNestedMessage<protos::pbzero::DebugAnnotation>(field_id_);
+  item->set_name(key.value);
+  return TracedValue(item, event_context_, &checked_scope_);
+}
+
+TracedValue TracedDictionary::AddItem(DynamicString key) {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  protos::pbzero::DebugAnnotation* item =
+      message_->BeginNestedMessage<protos::pbzero::DebugAnnotation>(field_id_);
+  item->set_name(key.value);
+  return TracedValue(item, event_context_, &checked_scope_);
+}
+
+TracedDictionary TracedDictionary::AddDictionary(StaticString key) {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  return AddItem(key).WriteDictionary();
+}
+
+TracedDictionary TracedDictionary::AddDictionary(DynamicString key) {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  return AddItem(key).WriteDictionary();
+}
+
+TracedArray TracedDictionary::AddArray(StaticString key) {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  return AddItem(key).WriteArray();
+}
+
+TracedArray TracedDictionary::AddArray(DynamicString key) {
+  PERFETTO_DCHECK(checked_scope_.is_active());
+  return AddItem(key).WriteArray();
+}
+
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/tracing.cc
 /*
  * Copyright (C) 2019 The Android Open Source Project
  *
@@ -49052,655 +54046,581 @@ class NoDestructor {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_internal.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/tracing.h"
+
+#include <atomic>
+#include <condition_variable>
+#include <mutex>

-// gen_amalgamated expanded: #include "perfetto/base/proc_utils.h"
 // gen_amalgamated expanded: #include "perfetto/base/time.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/no_destructor.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/core/data_source_config.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/data_source.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_interned_fields.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/track_event.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/track_event_category_registry.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/track_event_interned_data_index.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/data_source_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/track_event_descriptor.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-#include <os/signpost.h>
-#endif
-
-using perfetto::protos::pbzero::ClockSnapshot;
+// gen_amalgamated expanded: #include "perfetto/ext/base/waitable_event.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_data_source.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_internal.h"
+// gen_amalgamated expanded: #include "src/tracing/internal/tracing_muxer_impl.h"

 namespace perfetto {
+namespace {
+bool g_was_initialized = false;

-TrackEventSessionObserver::~TrackEventSessionObserver() = default;
-void TrackEventSessionObserver::OnSetup(const DataSourceBase::SetupArgs&) {}
-void TrackEventSessionObserver::OnStart(const DataSourceBase::StartArgs&) {}
-void TrackEventSessionObserver::OnStop(const DataSourceBase::StopArgs&) {}
-void TrackEventSessionObserver::WillClearIncrementalState(
-    const DataSourceBase::ClearIncrementalStateArgs&) {}
+// Wrapped in a function to avoid global constructor
+std::mutex& InitializedMutex() {
+  static base::NoDestructor<std::mutex> initialized_mutex;
+  return initialized_mutex.ref();
+}
+}  // namespace

-TrackEventTlsStateUserData::~TrackEventTlsStateUserData() = default;
+// static
+void Tracing::InitializeInternal(const TracingInitArgs& args) {
+  base::InitializeTime();
+  std::unique_lock<std::mutex> lock(InitializedMutex());
+  // If it's the first time Initialize is called, set some global params.
+  if (!g_was_initialized) {
+    // Make sure the headers and implementation files agree on the build config.
+    PERFETTO_CHECK(args.dcheck_is_on_ == PERFETTO_DCHECK_IS_ON());
+    if (args.log_message_callback) {
+      base::SetLogMessageCallback(args.log_message_callback);
+    }

-namespace internal {
+    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);
+    }

-BaseTrackEventInternedDataIndex::~BaseTrackEventInternedDataIndex() = default;
+    if (args.disallow_merging_with_system_tracks) {
+      internal::TrackEventInternal::SetDisallowMergingWithSystemTracks(true);
+    }

-namespace {
+    internal::TrackEventInternal::SetBufferExhaustedPolicy(
+        args.track_event_buffer_exhausted_policy);
+  }

-static constexpr const char kSlowTag[] = "slow";
-static constexpr const char kDebugTag[] = "debug";
-static constexpr const char kFilteredEventName[] = "FILTERED";
+  internal::TracingMuxerImpl::InitializeInstance(args);
+  internal::TrackRegistry::InitializeInstance(args.process_uuid);
+  g_was_initialized = true;
+}

-constexpr auto kClockIdIncremental =
-    TrackEventIncrementalState::kClockIdIncremental;
+// static
+bool Tracing::IsInitialized() {
+  std::unique_lock<std::mutex> lock(InitializedMutex());
+  return g_was_initialized;
+}

-constexpr auto kClockIdAbsolute = TrackEventIncrementalState::kClockIdAbsolute;
+// static
+void Tracing::Shutdown() {
+  std::unique_lock<std::mutex> lock(InitializedMutex());
+  if (!g_was_initialized)
+    return;
+  internal::TracingMuxerImpl::Shutdown();
+  g_was_initialized = false;
+}

-class TrackEventSessionObserverRegistry {
- public:
-  static TrackEventSessionObserverRegistry* GetInstance() {
-    static TrackEventSessionObserverRegistry* instance =
-        new TrackEventSessionObserverRegistry();  // leaked
-    return instance;
-  }
+// static
+void Tracing::ResetForTesting() {
+  std::unique_lock<std::mutex> lock(InitializedMutex());
+  if (!g_was_initialized)
+    return;
+  base::SetLogMessageCallback(nullptr);
+  internal::TracingMuxerImpl::ResetForTesting();
+  internal::TrackRegistry::ResetForTesting();
+  internal::TrackEventDataSource::ResetForTesting();
+  g_was_initialized = false;
+}

-  void AddObserverForRegistry(const TrackEventCategoryRegistry& registry,
-                              TrackEventSessionObserver* observer) {
-    std::unique_lock<std::recursive_mutex> lock(mutex_);
-    observers_.emplace_back(&registry, observer);
-  }
+//  static
+std::unique_ptr<TracingSession> Tracing::NewTraceInternal(
+    BackendType backend,
+    TracingConsumerBackend* (*system_backend_factory)()) {
+  return static_cast<internal::TracingMuxerImpl*>(internal::TracingMuxer::Get())
+      ->CreateTracingSession(backend, system_backend_factory);
+}

-  void RemoveObserverForRegistry(const TrackEventCategoryRegistry& registry,
-                                 TrackEventSessionObserver* observer) {
-    std::unique_lock<std::recursive_mutex> lock(mutex_);
-    observers_.erase(std::remove(observers_.begin(), observers_.end(),
-                                 RegisteredObserver(&registry, observer)),
-                     observers_.end());
-  }
+//  static
+std::unique_ptr<StartupTracingSession> Tracing::SetupStartupTracing(
+    const TraceConfig& config,
+    Tracing::SetupStartupTracingOpts opts) {
+  return static_cast<internal::TracingMuxerImpl*>(internal::TracingMuxer::Get())
+      ->CreateStartupTracingSession(config, std::move(opts));
+}

-  void ForEachObserverForRegistries(
-      const std::vector<const TrackEventCategoryRegistry*>& registries,
-      std::function<void(TrackEventSessionObserver*)> callback) {
-    std::unique_lock<std::recursive_mutex> lock(mutex_);
-    for (auto& registered_observer : observers_) {
-      for (const auto* registry : registries) {
-        if (registry == registered_observer.registry) {
-          callback(registered_observer.observer);
-        }
-      }
-    }
-  }
+//  static
+std::unique_ptr<StartupTracingSession> Tracing::SetupStartupTracingBlocking(
+    const TraceConfig& config,
+    Tracing::SetupStartupTracingOpts opts) {
+  return static_cast<internal::TracingMuxerImpl*>(internal::TracingMuxer::Get())
+      ->CreateStartupTracingSessionBlocking(config, std::move(opts));
+}

- private:
-  struct RegisteredObserver {
-    RegisteredObserver(const TrackEventCategoryRegistry* r,
-                       TrackEventSessionObserver* o)
-        : registry(r), observer(o) {}
-    bool operator==(const RegisteredObserver& other) {
-      return registry == other.registry && observer == other.observer;
-    }
-    const TrackEventCategoryRegistry* registry;
-    TrackEventSessionObserver* observer;
-  };
+//  static
+void Tracing::ActivateTriggers(const std::vector<std::string>& triggers,
+                               uint32_t ttl_ms) {
+  internal::TracingMuxer::Get()->ActivateTriggers(triggers, ttl_ms);
+}

-  std::recursive_mutex mutex_;
-  std::vector<RegisteredObserver> observers_;
-};
+TracingSession::~TracingSession() = default;

-enum class MatchType { kExact, kPattern, kWildcard };
+void TracingSession::CloneTrace(CloneTraceArgs, CloneTraceCallback) {}

-bool NameMatchesPattern(const std::string& pattern,
-                        const std::string& name,
-                        MatchType match_type) {
-  // To avoid pulling in all of std::regex, for now we only support a single "*"
-  // wildcard at the end of the pattern.
-  size_t i = pattern.find('*');
-  if (i != std::string::npos) {
-    PERFETTO_DCHECK(i == pattern.size() - 1);
-    if (i == 0) {
-      return match_type == MatchType::kWildcard;
-    }
-    if (match_type != MatchType::kPattern)
-      return false;
-    return name.substr(0, i) == pattern.substr(0, i);
+// Can be called from any thread.
+bool TracingSession::FlushBlocking(uint32_t timeout_ms) {
+  std::atomic<bool> flush_result;
+  base::WaitableEvent flush_ack;
+
+  // The non blocking Flush() can be called on any thread. It does the PostTask
+  // internally.
+  Flush(
+      [&flush_ack, &flush_result](bool res) {
+        flush_result = res;
+        flush_ack.Notify();
+      },
+      timeout_ms);
+  flush_ack.Wait();
+  return flush_result;
+}
+
+std::vector<char> TracingSession::ReadTraceBlocking() {
+  std::vector<char> raw_trace;
+  std::mutex mutex;
+  std::condition_variable cv;
+
+  bool all_read = false;
+
+  ReadTrace([&mutex, &raw_trace, &all_read, &cv](ReadTraceCallbackArgs cb) {
+    raw_trace.insert(raw_trace.end(), cb.data, cb.data + cb.size);
+    std::unique_lock<std::mutex> lock(mutex);
+    all_read = !cb.has_more;
+    if (all_read)
+      cv.notify_one();
+  });
+
+  {
+    std::unique_lock<std::mutex> lock(mutex);
+    cv.wait(lock, [&all_read] { return all_read; });
   }
-  return name == pattern;
+  return raw_trace;
 }

-bool NameMatchesPatternList(const std::vector<std::string>& patterns,
-                            const std::string& name,
-                            MatchType match_type) {
-  for (const auto& pattern : patterns) {
-    if (NameMatchesPattern(pattern, name, match_type))
-      return true;
+TracingSession::GetTraceStatsCallbackArgs
+TracingSession::GetTraceStatsBlocking() {
+  std::mutex mutex;
+  std::condition_variable cv;
+  GetTraceStatsCallbackArgs result;
+  bool stats_read = false;
+
+  GetTraceStats(
+      [&mutex, &result, &stats_read, &cv](GetTraceStatsCallbackArgs args) {
+        result = std::move(args);
+        std::unique_lock<std::mutex> lock(mutex);
+        stats_read = true;
+        cv.notify_one();
+      });
+
+  {
+    std::unique_lock<std::mutex> lock(mutex);
+    cv.wait(lock, [&stats_read] { return stats_read; });
   }
-  return false;
+  return result;
 }

-}  // namespace
+TracingSession::QueryServiceStateCallbackArgs
+TracingSession::QueryServiceStateBlocking() {
+  std::mutex mutex;
+  std::condition_variable cv;
+  QueryServiceStateCallbackArgs result;
+  bool status_read = false;

-TrackEventInternal& TrackEventInternal::GetInstance() {
-  static base::NoDestructor<TrackEventInternal> state;
-  return state.ref();
+  QueryServiceState(
+      [&mutex, &result, &status_read, &cv](QueryServiceStateCallbackArgs args) {
+        result = std::move(args);
+        std::unique_lock<std::mutex> lock(mutex);
+        status_read = true;
+        cv.notify_one();
+      });
+
+  {
+    std::unique_lock<std::mutex> lock(mutex);
+    cv.wait(lock, [&status_read] { return status_read; });
+  }
+  return result;
 }

-// static
-const Track TrackEventInternal::kDefaultTrack{};
+StartupTracingSession::~StartupTracingSession() = default;
+
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/tracing_policy.cc
+/*
+ * Copyright (C) 2021 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.
+ */

-// static
-std::atomic<int> TrackEventInternal::session_count_{};
+// gen_amalgamated expanded: #include "perfetto/tracing/tracing_policy.h"

-std::vector<const TrackEventCategoryRegistry*>
-TrackEventInternal::GetRegistries() {
-  std::unique_lock<std::mutex> lock(mu_);
-  return registries_;
-}
+namespace perfetto {

-std::vector<const TrackEventCategoryRegistry*> TrackEventInternal::AddRegistry(
-    const TrackEventCategoryRegistry* registry) {
-  std::unique_lock<std::mutex> lock(mu_);
-  registries_.push_back(registry);
-  return registries_;
-}
+TracingPolicy::~TracingPolicy() = default;

-void TrackEventInternal::ResetRegistriesForTesting() {
-  std::unique_lock<std::mutex> lock(mu_);
-  registries_.clear();
-}
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/track.cc
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */

-// static
-bool TrackEventInternal::Initialize(
-    const std::vector<const TrackEventCategoryRegistry*> registries,
-    bool (*register_data_source)(const DataSourceDescriptor&)) {
-  DataSourceDescriptor dsd;
-  dsd.set_name("track_event");
+// gen_amalgamated expanded: #include "perfetto/tracing/track.h"

-  protozero::HeapBuffered<protos::pbzero::TrackEventDescriptor> ted;
-  for (const auto* registry : registries) {
-    for (size_t i = 0; i < registry->category_count(); i++) {
-      auto category = registry->GetCategory(i);
-      // Don't register group categories.
-      if (category->IsGroup())
-        continue;
-      auto cat = ted->add_available_categories();
-      cat->set_name(category->name);
-      if (category->description)
-        cat->set_description(category->description);
-      for (const auto& tag : category->tags) {
-        if (tag) {
-          cat->add_tags(tag);
-        }
-      }
-    }
-  }
-  dsd.set_track_event_descriptor_raw(ted.SerializeAsString());
+// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/fnv_hash.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_splitter.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/uuid.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_data_source.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_internal.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/counter_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/process_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/thread_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"

-  return register_data_source(dsd);
-}
+namespace perfetto {

 // static
-bool TrackEventInternal::AddSessionObserver(
-    const TrackEventCategoryRegistry& registry,
-    TrackEventSessionObserver* observer) {
-  TrackEventSessionObserverRegistry::GetInstance()->AddObserverForRegistry(
-      registry, observer);
-  return true;
-}
+uint64_t Track::process_uuid = 0;

-// static
-void TrackEventInternal::RemoveSessionObserver(
-    const TrackEventCategoryRegistry& registry,
-    TrackEventSessionObserver* observer) {
-  TrackEventSessionObserverRegistry::GetInstance()->RemoveObserverForRegistry(
-      registry, observer);
+protos::gen::TrackDescriptor Track::Serialize() const {
+  protos::gen::TrackDescriptor desc;
+  desc.set_uuid(uuid);
+  if (parent_uuid)
+    desc.set_parent_uuid(parent_uuid);
+  return desc;
 }

-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) && \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-static constexpr protos::pbzero::BuiltinClock kDefaultTraceClock =
-    protos::pbzero::BUILTIN_CLOCK_BOOTTIME;
-#else
-static constexpr protos::pbzero::BuiltinClock kDefaultTraceClock =
-    protos::pbzero::BUILTIN_CLOCK_MONOTONIC;
-#endif
-
-// static
-protos::pbzero::BuiltinClock TrackEventInternal::clock_ = kDefaultTraceClock;
-
-// static
-bool TrackEventInternal::disallow_merging_with_system_tracks_ = false;
+void Track::Serialize(protos::pbzero::TrackDescriptor* desc) const {
+  auto bytes = Serialize().SerializeAsString();
+  desc->AppendRawProtoBytes(bytes.data(), bytes.size());
+}

 // static
-BufferExhaustedPolicy TrackEventInternal::buffer_exhausted_policy_ =
-    BufferExhaustedPolicy::kDrop;
+Track Track::ThreadScoped(const void* ptr, Track parent) {
+  if (parent.uuid == 0)
+    return Track::FromPointer(ptr, ThreadTrack::Current());
+  return Track::FromPointer(ptr, parent);
+}

-// static
-void TrackEventInternal::EnableRegistry(
-    const TrackEventCategoryRegistry* registry,
-    const protos::gen::TrackEventConfig& config,
-    uint32_t internal_instance_index) {
-  for (size_t i = 0; i < registry->category_count(); i++) {
-    if (IsCategoryEnabled(*registry, config, *registry->GetCategory(i))) {
-      PERFETTO_DLOG("EnableRegistry %" PRIu32 " %zu", internal_instance_index,
-                    i);
-      registry->EnableCategoryForInstance(i, internal_instance_index);
+protos::gen::TrackDescriptor ProcessTrack::Serialize() const {
+  auto desc = Track::Serialize();
+  auto pd = desc.mutable_process();
+  pd->set_pid(static_cast<int32_t>(pid));
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  std::string cmdline;
+  if (base::ReadFile("/proc/self/cmdline", &cmdline)) {
+    // Since cmdline is a zero-terminated list of arguments, this ends up
+    // writing just the first element, i.e., the process name, into the process
+    // name field.
+    pd->set_process_name(cmdline.c_str());
+    base::StringSplitter splitter(std::move(cmdline), '\0');
+    while (splitter.Next()) {
+      pd->add_cmdline(
+          std::string(splitter.cur_token(), splitter.cur_token_size()));
     }
   }
+  // TODO(skyostil): Record command line on Windows, FreeBSD and Mac.
+#endif
+  return desc;
 }

-void TrackEventInternal::EnableTracing(
-    const protos::gen::TrackEventConfig& config,
-    const DataSourceBase::SetupArgs& args) {
-  std::vector<const TrackEventCategoryRegistry*> registries;
-  {
-    std::unique_lock<std::mutex> lock(mu_);
-    registries = registries_;
-    for (const auto& registry : registries) {
-      for (size_t i = 0; i < registry->category_count(); i++) {
-        if (IsCategoryEnabled(*registry, config, *registry->GetCategory(i)))
-          registry->EnableCategoryForInstance(i, args.internal_instance_index);
-      }
-    }
-  }
-  TrackEventSessionObserverRegistry::GetInstance()
-      ->ForEachObserverForRegistries(
-          registries, [&](TrackEventSessionObserver* o) { o->OnSetup(args); });
+void ProcessTrack::Serialize(protos::pbzero::TrackDescriptor* desc) const {
+  auto bytes = Serialize().SerializeAsString();
+  desc->AppendRawProtoBytes(bytes.data(), bytes.size());
 }

-void TrackEventInternal::DisableTracing(uint32_t internal_instance_index) {
-  std::unique_lock<std::mutex> lock(mu_);
-  for (const auto& registry : registries_) {
-    for (size_t i = 0; i < registry->category_count(); i++)
-      registry->DisableCategoryForInstance(i, internal_instance_index);
+protos::gen::TrackDescriptor ThreadTrack::Serialize() const {
+  auto desc = Track::Serialize();
+  auto td = desc.mutable_thread();
+  td->set_pid(static_cast<int32_t>(pid));
+  td->set_tid(static_cast<int32_t>(tid));
+  if (disallow_merging_with_system_tracks) {
+    desc.set_disallow_merging_with_system_tracks(true);
   }
+  std::string thread_name;
+  if (base::GetThreadName(thread_name))
+    td->set_thread_name(thread_name);
+  return desc;
 }

 // static
-void TrackEventInternal::OnStart(const DataSourceBase::StartArgs& args) {
-  session_count_.fetch_add(1);
-  TrackEventSessionObserverRegistry::GetInstance()
-      ->ForEachObserverForRegistries(
-          GetInstance().GetRegistries(),
-          [&](TrackEventSessionObserver* o) { o->OnStart(args); });
+ThreadTrack ThreadTrack::Current() {
+  return ThreadTrack(
+      internal::TracingMuxer::Get()->GetCurrentThreadId(),
+      internal::TrackEventInternal::GetDisallowMergingWithSystemTracks());
 }

 // static
-void TrackEventInternal::OnStop(const DataSourceBase::StopArgs& args) {
-  TrackEventSessionObserverRegistry::GetInstance()
-      ->ForEachObserverForRegistries(
-          GetInstance().GetRegistries(),
-          [&](TrackEventSessionObserver* o) { o->OnStop(args); });
+ThreadTrack ThreadTrack::ForThread(base::PlatformThreadId tid_) {
+  return ThreadTrack(
+      tid_, internal::TrackEventInternal::GetDisallowMergingWithSystemTracks());
 }

-// static
-void TrackEventInternal::WillClearIncrementalState(
-    const DataSourceBase::ClearIncrementalStateArgs& args) {
-  TrackEventSessionObserverRegistry::GetInstance()
-      ->ForEachObserverForRegistries(GetInstance().GetRegistries(),
-                                     [&](TrackEventSessionObserver* o) {
-                                       o->WillClearIncrementalState(args);
-                                     });
+void ThreadTrack::Serialize(protos::pbzero::TrackDescriptor* desc) const {
+  auto bytes = Serialize().SerializeAsString();
+  desc->AppendRawProtoBytes(bytes.data(), bytes.size());
 }

-// static
-bool TrackEventInternal::IsCategoryEnabled(
-    const TrackEventCategoryRegistry& registry,
-    const protos::gen::TrackEventConfig& config,
-    const Category& category) {
-  // If this is a group category, check if any of its constituent categories are
-  // enabled. If so, then this one is enabled too.
-  if (category.IsGroup()) {
-    bool result = false;
-    category.ForEachGroupMember([&](const char* member_name, size_t name_size) {
-      for (size_t i = 0; i < registry.category_count(); i++) {
-        const auto ref_category = registry.GetCategory(i);
-        // Groups can't refer to other groups.
-        if (ref_category->IsGroup())
-          continue;
-        // Require an exact match.
-        if (ref_category->name_size() != name_size ||
-            strncmp(ref_category->name, member_name, name_size)) {
-          continue;
-        }
-        if (IsCategoryEnabled(registry, config, *ref_category)) {
-          result = true;
-          // Break ForEachGroupMember() loop.
-          return false;
-        }
-        break;
-      }
-      // No match? Must be a dynamic category.
-      DynamicCategory dyn_category(std::string(member_name, name_size));
-      Category ref_category{Category::FromDynamicCategory(dyn_category)};
-      if (IsCategoryEnabled(registry, config, ref_category)) {
-        result = true;
-        // Break ForEachGroupMember() loop.
-        return false;
-      }
-      // No match found => keep iterating.
-      return true;
-    });
-    return result;
+protos::gen::TrackDescriptor NamedTrack::Serialize() const {
+  auto desc = NamedTrackBase::Serialize();
+  if (sibling_merge_behavior_ != perfetto::protos::gen::TrackDescriptor::
+                                     SIBLING_MERGE_BEHAVIOR_UNSPECIFIED) {
+    desc.set_sibling_merge_behavior(sibling_merge_behavior_);
   }
-
-  auto has_matching_tag = [&](std::function<bool(const char*)> matcher) {
-    for (const auto& tag : category.tags) {
-      if (!tag)
-        break;
-      if (matcher(tag))
-        return true;
-    }
-    return false;
-  };
-
-  // First try exact matches, then pattern matches. Last, try the global
-  // wildcard.
-  const std::array<MatchType, 3> match_types = {
-      {MatchType::kExact, MatchType::kPattern, MatchType::kWildcard}};
-  for (auto match_type : match_types) {
-    // 1. Enabled categories.
-    if (NameMatchesPatternList(config.enabled_categories(), category.name,
-                               match_type)) {
-      return true;
-    }
-
-    // 2. Disabled categories.
-    if (NameMatchesPatternList(config.disabled_categories(), category.name,
-                               match_type)) {
-      return false;
-    }
-
-    // 3. Disabled tags.
-    if (has_matching_tag([&](const char* tag) {
-          if (config.disabled_tags_size()) {
-            return NameMatchesPatternList(config.disabled_tags(), tag,
-                                          match_type);
-          } else if (config.enabled_tags_size() == 0) {
-            // The "slow" and "debug" tags are disabled by default.
-            return NameMatchesPattern(kSlowTag, tag, match_type) ||
-                   NameMatchesPattern(kDebugTag, tag, match_type);
-          }
-          return false;
-        })) {
-      return false;
-    }
-
-    // 4. Enabled tags.
-    if (has_matching_tag([&](const char* tag) {
-          return NameMatchesPatternList(config.enabled_tags(), tag, match_type);
-        })) {
-      return true;
-    }
+  if (sibling_merge_key_) {
+    desc.set_sibling_merge_key(sibling_merge_key_);
+  } else if (sibling_merge_key_int_.has_value()) {
+    desc.set_sibling_merge_key_int(*sibling_merge_key_int_);
   }
-
-  // If nothing matched, enable the category by default.
-  return true;
-}
-
-// static
-uint64_t TrackEventInternal::GetTimeNs() {
-  if (GetClockId() == protos::pbzero::BUILTIN_CLOCK_BOOTTIME)
-    return static_cast<uint64_t>(perfetto::base::GetBootTimeNs().count());
-  else if (GetClockId() == protos::pbzero::BUILTIN_CLOCK_MONOTONIC)
-    return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
-  PERFETTO_DCHECK(GetClockId() == protos::pbzero::BUILTIN_CLOCK_MONOTONIC_RAW);
-  return static_cast<uint64_t>(perfetto::base::GetWallTimeRawNs().count());
-}
-
-// static
-TraceTimestamp TrackEventInternal::GetTraceTime() {
-  return {kClockIdIncremental, GetTimeNs()};
+  return desc;
 }

-// static
-int TrackEventInternal::GetSessionCount() {
-  return session_count_.load();
+protos::gen::TrackDescriptor StateTrack::Serialize() const {
+  auto desc = NamedTrackBase::Serialize();
+  // Initialize the state submessage to mark this track as a StateTrack.
+  desc.mutable_state();
+  return desc;
 }

-// static
-void TrackEventInternal::ResetIncrementalState(
-    TraceWriterBase* trace_writer,
-    TrackEventIncrementalState* incr_state,
-    const TrackEventTlsState& tls_state,
-    const TraceTimestamp& timestamp) {
-  auto sequence_timestamp = timestamp;
-  if (timestamp.clock_id != kClockIdIncremental) {
-    sequence_timestamp = TrackEventInternal::GetTraceTime();
+protos::gen::TrackDescriptor CounterTrack::Serialize() const {
+  auto desc = Track::Serialize();
+  auto* counter = desc.mutable_counter();
+  if (static_name_) {
+    desc.set_static_name(static_name_.value);
+  } else {
+    desc.set_name(dynamic_name_.value);
   }

-  incr_state->last_timestamp_ns = sequence_timestamp.value;
-  auto default_track = ThreadTrack::Current();
-  auto ts_unit_multiplier = tls_state.timestamp_unit_multiplier;
-  auto thread_time_counter_track =
-      CounterTrack("thread_time", default_track)
-          .set_is_incremental(true)
-          .set_unit_multiplier(static_cast<int64_t>(ts_unit_multiplier))
-          .set_type(protos::gen::CounterDescriptor::COUNTER_THREAD_TIME_NS);
+  if (category_)
+    counter->add_categories(category_);
+  if (unit_ != perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED)
+    counter->set_unit(static_cast<protos::gen::CounterDescriptor_Unit>(unit_));
   {
-    // Mark any incremental state before this point invalid. Also set up
-    // defaults so that we don't need to repeat constant data for each packet.
-    auto packet = NewTracePacket(
-        trace_writer, incr_state, tls_state, timestamp,
-        protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
-    auto defaults = packet->set_trace_packet_defaults();
-    defaults->set_timestamp_clock_id(tls_state.default_clock);
-    // Establish the default track for this event sequence.
-    auto track_defaults = defaults->set_track_event_defaults();
-    track_defaults->set_track_uuid(default_track.uuid);
-    if (tls_state.enable_thread_time_sampling) {
-      track_defaults->add_extra_counter_track_uuids(
-          thread_time_counter_track.uuid);
-    }
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-    // Emit a MacOS point-of-interest signpost to synchronize Mac profiler time
-    // with boot time.
-    // TODO(leszeks): Consider allowing synchronization against other clocks
-    // than boot time.
-    static os_log_t log_handle = os_log_create(
-        "dev.perfetto.clock_sync", OS_LOG_CATEGORY_POINTS_OF_INTEREST);
-    os_signpost_event_emit(
-        log_handle, OS_SIGNPOST_ID_EXCLUSIVE, "boottime", "%" PRId64,
-        static_cast<uint64_t>(perfetto::base::GetBootTimeNs().count()));
-#endif
-
-    if (tls_state.default_clock != static_cast<uint32_t>(GetClockId())) {
-      ClockSnapshot* clocks = packet->set_clock_snapshot();
-      // Trace clock.
-      ClockSnapshot::Clock* trace_clock = clocks->add_clocks();
-      trace_clock->set_clock_id(static_cast<uint32_t>(GetClockId()));
-      trace_clock->set_timestamp(sequence_timestamp.value);
-
-      if (PERFETTO_LIKELY(tls_state.default_clock == kClockIdIncremental)) {
-        // Delta-encoded incremental clock in nanoseconds by default but
-        // configurable by |tls_state.timestamp_unit_multiplier|.
-        ClockSnapshot::Clock* clock_incremental = clocks->add_clocks();
-        clock_incremental->set_clock_id(kClockIdIncremental);
-        clock_incremental->set_timestamp(sequence_timestamp.value /
-                                         ts_unit_multiplier);
-        clock_incremental->set_is_incremental(true);
-        clock_incremental->set_unit_multiplier_ns(ts_unit_multiplier);
-      }
-      if (ts_unit_multiplier > 1) {
-        // absolute clock with custom timestamp_unit_multiplier.
-        ClockSnapshot::Clock* absolute_clock = clocks->add_clocks();
-        absolute_clock->set_clock_id(kClockIdAbsolute);
-        absolute_clock->set_timestamp(sequence_timestamp.value /
-                                      ts_unit_multiplier);
-        absolute_clock->set_is_incremental(false);
-        absolute_clock->set_unit_multiplier_ns(ts_unit_multiplier);
-      }
+    // if |type| is set, we don't want to emit |unit_name|. Trace processor
+    // infers the track name from the type in that case.
+    if (type_ !=
+        perfetto::protos::gen::CounterDescriptor::COUNTER_UNSPECIFIED) {
+      counter->set_type(type_);
+    } else if (unit_name_) {
+      counter->set_unit_name(unit_name_);
     }
   }
+  if (unit_multiplier_ != 1)
+    counter->set_unit_multiplier(unit_multiplier_);
+  if (is_incremental_)
+    counter->set_is_incremental(is_incremental_);
+  return desc;
+}

-  // Every thread should write a descriptor for its default track, because most
-  // trace points won't explicitly reference it. We also write the process
-  // descriptor from every thread that writes trace events to ensure it gets
-  // emitted at least once.
-  incr_state->seen_tracks.insert(default_track.uuid);
-  WriteTrackDescriptor(default_track, trace_writer, incr_state, tls_state,
-                       sequence_timestamp);
-
-  incr_state->seen_tracks.insert(ProcessTrack::Current().uuid);
-  WriteTrackDescriptor(ProcessTrack::Current(), trace_writer, incr_state,
-                       tls_state, sequence_timestamp);
-
-  if (tls_state.enable_thread_time_sampling) {
-    WriteTrackDescriptor(thread_time_counter_track, trace_writer, incr_state,
-                         tls_state, sequence_timestamp);
-  }
+void CounterTrack::Serialize(protos::pbzero::TrackDescriptor* desc) const {
+  auto bytes = Serialize().SerializeAsString();
+  desc->AppendRawProtoBytes(bytes.data(), bytes.size());
 }

-// static
-protozero::MessageHandle<protos::pbzero::TracePacket>
-TrackEventInternal::NewTracePacket(TraceWriterBase* trace_writer,
-                                   TrackEventIncrementalState* incr_state,
-                                   const TrackEventTlsState& tls_state,
-                                   TraceTimestamp timestamp,
-                                   uint32_t seq_flags) {
-  if (PERFETTO_UNLIKELY(tls_state.default_clock != kClockIdIncremental &&
-                        timestamp.clock_id == kClockIdIncremental)) {
-    timestamp.clock_id = tls_state.default_clock;
-  }
-  auto packet = trace_writer->NewTracePacket();
-  auto ts_unit_multiplier = tls_state.timestamp_unit_multiplier;
-  if (PERFETTO_LIKELY(timestamp.clock_id == kClockIdIncremental)) {
-    if (PERFETTO_LIKELY(incr_state->last_timestamp_ns <= timestamp.value)) {
-      // No need to set the clock id here, since kClockIdIncremental is the
-      // clock id assumed by default.
-      auto time_diff_ns = timestamp.value - incr_state->last_timestamp_ns;
-      auto time_diff_units = time_diff_ns / ts_unit_multiplier;
-      packet->set_timestamp(time_diff_units);
-      incr_state->last_timestamp_ns += time_diff_units * ts_unit_multiplier;
-    } else {
-      packet->set_timestamp(timestamp.value / ts_unit_multiplier);
-      packet->set_timestamp_clock_id(ts_unit_multiplier == 1
-                                         ? static_cast<uint32_t>(GetClockId())
-                                         : kClockIdAbsolute);
-    }
-  } else if (PERFETTO_LIKELY(timestamp.clock_id == tls_state.default_clock)) {
-    packet->set_timestamp(timestamp.value / ts_unit_multiplier);
-  } else {
-    packet->set_timestamp(timestamp.value);
-    packet->set_timestamp_clock_id(timestamp.clock_id);
+namespace internal {
+namespace {
+
+uint64_t GetProcessStartTime() {
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  std::string stat;
+  if (!base::ReadFile("/proc/self/stat", &stat))
+    return 0u;
+  // The stat file is a single line split into space-separated fields as "pid
+  // (comm) state ppid ...". However because the command name can contain any
+  // characters (including parentheses and spaces), we need to skip past it
+  // before parsing the rest of the fields. To do that, we look for the last
+  // instance of ") " (parentheses followed by space) and parse forward from
+  // that point.
+  size_t comm_end = stat.rfind(") ");
+  if (comm_end == std::string::npos)
+    return 0u;
+  stat = stat.substr(comm_end + strlen(") "));
+  base::StringSplitter splitter(stat, ' ');
+  for (size_t skip = 0; skip < 20; skip++) {
+    if (!splitter.Next())
+      return 0u;
   }
-  packet->set_sequence_flags(seq_flags);
-  return packet;
+  return base::CStringToUInt64(splitter.cur_token()).value_or(0u);
+#else
+  return 0;
+#endif  // !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 }

+}  // namespace
+
 // static
-void TrackEventInternal::WriteEventName(StaticString event_name,
-                                        perfetto::EventContext& event_ctx,
-                                        const TrackEventTlsState&) {
-  if (PERFETTO_LIKELY(event_name.value != nullptr)) {
-    size_t name_iid = InternedEventName::Get(&event_ctx, event_name.value);
-    event_ctx.event()->set_name_iid(name_iid);
-  }
-}
+TrackRegistry* TrackRegistry::instance_;
+
+TrackRegistry::TrackRegistry() = default;
+TrackRegistry::~TrackRegistry() = default;

 // static
-void TrackEventInternal::WriteEventName(perfetto::DynamicString event_name,
-                                        perfetto::EventContext& event_ctx,
-                                        const TrackEventTlsState& tls_state) {
-  if (PERFETTO_UNLIKELY(tls_state.filter_dynamic_event_names)) {
-    event_ctx.event()->set_name(kFilteredEventName,
-                                sizeof(kFilteredEventName) - 1);
+void TrackRegistry::InitializeInstance(std::optional<uint64_t> process_uuid) {
+  if (instance_)
+    return;
+  instance_ = new TrackRegistry();
+  if (process_uuid) {
+    Track::process_uuid = *process_uuid;
   } else {
-    event_ctx.event()->set_name(event_name.value, event_name.length);
+    Track::process_uuid = ComputeProcessUuid();
   }
 }

 // static
-EventContext TrackEventInternal::WriteEvent(
-    TraceWriterBase* trace_writer,
-    TrackEventIncrementalState* incr_state,
-    TrackEventTlsState& tls_state,
-    const Category* category,
-    perfetto::protos::pbzero::TrackEvent::Type type,
-    const TraceTimestamp& timestamp,
-    bool on_current_thread_track) {
-  PERFETTO_DCHECK(!incr_state->was_cleared);
-  auto packet = NewTracePacket(trace_writer, incr_state, tls_state, timestamp);
-  EventContext ctx(std::move(packet), incr_state, &tls_state);
+uint64_t TrackRegistry::ComputeProcessUuid() {
+  // Use the process start time + pid as the unique identifier for this process.
+  // This ensures that if there are two independent copies of the Perfetto SDK
+  // in the same process (e.g., one in the app and another in a system
+  // framework), events emitted by each will be consistently interleaved on
+  // common thread and process tracks.
+  uint64_t random;
+  if (uint64_t start_time = GetProcessStartTime()) {
+    random = start_time;
+  } else {
+    // Fall back to a randomly generated identifier.
+    static uint64_t random_once = static_cast<uint64_t>(base::Uuidv4().lsb());
+    random = random_once;
+  }
+  return base::FnvHasher::Combine(random, Platform::GetCurrentProcessId());
+}

-  auto track_event = ctx.event();
-  if (type != protos::pbzero::TrackEvent::TYPE_UNSPECIFIED)
-    track_event->set_type(type);
+void TrackRegistry::ResetForTesting() {
+  instance_->tracks_.clear();
+}

-  if (tls_state.enable_thread_time_sampling && on_current_thread_track) {
-    if (tls_state.thread_time_subsampling_ns == 0 ||
-        incr_state->last_thread_time_timestamp_ns == 0 ||
-        timestamp.value >= incr_state->last_thread_time_timestamp_ns +
-                               tls_state.thread_time_subsampling_ns) {
-      auto thread_time_ns = base::GetThreadCPUTimeNs().count();
-      auto thread_time_delta_ns =
-          thread_time_ns - incr_state->last_thread_time_ns;
-      incr_state->last_thread_time_ns = thread_time_ns;
-      incr_state->last_thread_time_timestamp_ns = timestamp.value;
-      track_event->add_extra_counter_values(
-          thread_time_delta_ns /
-          static_cast<int64_t>(tls_state.timestamp_unit_multiplier));
-    } else {
-      // When subsampling, the skip emitting values.
-      track_event->add_extra_counter_values(0);
-    }
-  }
+void TrackRegistry::UpdateTrack(Track track,
+                                const std::string& serialized_desc) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  tracks_[track.uuid] = {serialized_desc, track.parent_uuid};
+}

-  // We assume that |category| points to the string with static lifetime.
-  // This means we can use their addresses as interning keys.
-  // TODO(skyostil): Intern categories at compile time.
-  if (category && type != protos::pbzero::TrackEvent::TYPE_SLICE_END &&
-      type != protos::pbzero::TrackEvent::TYPE_COUNTER) {
-    category->ForEachGroupMember(
-        [&](const char* member_name, size_t name_size) {
-          size_t category_iid =
-              InternedEventCategory::Get(&ctx, member_name, name_size);
-          track_event->add_category_iids(category_iid);
-          return true;
-        });
-  }
-  return ctx;
+void TrackRegistry::EraseTrack(Track track) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  tracks_.erase(track.uuid);
 }

 // static
-protos::pbzero::DebugAnnotation* TrackEventInternal::AddDebugAnnotation(
-    perfetto::EventContext* event_ctx,
-    const char* name) {
-  auto annotation = event_ctx->event()->add_debug_annotations();
-  annotation->set_name_iid(InternedDebugAnnotationName::Get(event_ctx, name));
-  return annotation;
+void TrackRegistry::WriteTrackDescriptor(
+    const SerializedTrackDescriptor& desc,
+    protozero::MessageHandle<protos::pbzero::TracePacket> packet) {
+  packet->AppendString(
+      perfetto::protos::pbzero::TracePacket::kTrackDescriptorFieldNumber, desc);
 }

+}  // namespace internal
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/track_event_category_registry.cc
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/tracing/track_event_category_registry.h"
+
+namespace perfetto {
+namespace {
+
+static constexpr const char kLegacySlowPrefix[] = "disabled-by-default-";
+static constexpr const char kSlowTag[] = "slow";
+
+}  // namespace
+
 // static
-protos::pbzero::DebugAnnotation* TrackEventInternal::AddDebugAnnotation(
-    perfetto::EventContext* event_ctx,
-    perfetto::DynamicString name) {
-  auto annotation = event_ctx->event()->add_debug_annotations();
-  annotation->set_name(name.value);
-  return annotation;
+Category Category::FromDynamicCategory(const char* name) {
+  if (GetNthNameSize(1, name, name)) {
+    Category group(Group(name));
+    PERFETTO_DCHECK(group.name);
+    return group;
+  }
+  Category category(name);
+  PERFETTO_DCHECK(category.name);
+  // Disabled-by-default categories get a "slow" tag.
+  if (!strncmp(name, kLegacySlowPrefix, strlen(kLegacySlowPrefix))) {
+    return category.SetTags(kSlowTag);
+  }
+  return category;
 }

-void TrackEventDataSource::OnStart(const DataSourceBase::StartArgs& args) {
-  TrackEventInternal::OnStart(args);
+Category Category::FromDynamicCategory(
+    const DynamicCategory& dynamic_category) {
+  return FromDynamicCategory(dynamic_category.name.c_str());
 }

-}  // namespace internal
+namespace internal {

-PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS(
-    PERFETTO_EXPORT_COMPONENT,
-    perfetto::internal::TrackEventDataSource,
-    perfetto::internal::TrackEventDataSourceTraits);
+perfetto::DynamicCategory NullCategory(const perfetto::DynamicCategory&) {
+  return perfetto::DynamicCategory{};
+}
+
+void TrackEventCategoryRegistry::EnableCategoryForInstance(
+    size_t category_index,
+    uint32_t instance_index) const {
+  PERFETTO_DCHECK(instance_index < kMaxDataSourceInstances);
+  PERFETTO_DCHECK(category_index < category_count_);
+  // Matches the acquire_load in DataSource::Trace().
+  state_storage_[category_index].fetch_or(
+      static_cast<uint8_t>(1u << instance_index), std::memory_order_release);
+}
+
+void TrackEventCategoryRegistry::DisableCategoryForInstance(
+    size_t category_index,
+    uint32_t instance_index) const {
+  PERFETTO_DCHECK(instance_index < kMaxDataSourceInstances);
+  PERFETTO_DCHECK(category_index < category_count_);
+  // Matches the acquire_load in DataSource::Trace().
+  state_storage_[category_index].fetch_and(
+      static_cast<uint8_t>(~(1u << instance_index)), std::memory_order_release);
+}

+}  // namespace internal
 }  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/internal/track_event_interned_fields.cc
+// gen_amalgamated begin source: src/tracing/track_event_legacy.cc
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -49715,62 +54635,357 @@ PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS(
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_interned_fields.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/track_event_legacy.h"
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/fnv_hash.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/track.h"

 namespace perfetto {
+namespace legacy {
+
+template <>
+ThreadTrack ConvertThreadId(const PerfettoLegacyCurrentThreadId&) {
+  // Because of the short-circuit in PERFETTO_INTERNAL_LEGACY_EVENT, we should
+  // never get here.
+  PERFETTO_DCHECK(false);
+  return ThreadTrack::Current();
+}
+
+}  // namespace legacy
+
 namespace internal {

-InternedEventCategory::~InternedEventCategory() = default;
+void LegacyTraceId::Write(protos::pbzero::TrackEvent::LegacyEvent* event,
+                          uint32_t event_flags) const {
+  // Legacy flow events always use bind_id.
+  if (event_flags &
+      (legacy::kTraceEventFlagFlowOut | legacy::kTraceEventFlagFlowIn)) {
+    // Flow bind_ids don't have scopes, so we need to mangle in-process ones to
+    // avoid collisions.
+    if (id_flags_ & legacy::kTraceEventFlagHasLocalId) {
+      event->set_bind_id(raw_id_ ^ ProcessTrack::Current().uuid);
+    } else {
+      event->set_bind_id(raw_id_);
+    }
+    return;
+  }

-// static
-void InternedEventCategory::Add(protos::pbzero::InternedData* interned_data,
-                                size_t iid,
-                                const char* value,
-                                size_t length) {
-  auto category = interned_data->add_event_categories();
-  category->set_iid(iid);
-  category->set_name(value, length);
+  uint32_t scope_flags = id_flags_ & (legacy::kTraceEventFlagHasId |
+                                      legacy::kTraceEventFlagHasLocalId |
+                                      legacy::kTraceEventFlagHasGlobalId);
+  uint64_t id = raw_id_;
+  if (scope_ && scope_flags != legacy::kTraceEventFlagHasGlobalId) {
+    id = base::FnvHasher::Combine(id, scope_);
+  }
+
+  switch (scope_flags) {
+    case legacy::kTraceEventFlagHasId:
+      event->set_unscoped_id(id);
+      break;
+    case legacy::kTraceEventFlagHasLocalId:
+      event->set_local_id(id);
+      break;
+    case legacy::kTraceEventFlagHasGlobalId:
+      event->set_global_id(id);
+      break;
+  }
+  if (scope_)
+    event->set_id_scope(scope_);
 }

-InternedEventName::~InternedEventName() = default;
+}  // namespace internal
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/track_event_state_tracker.cc
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// gen_amalgamated expanded: #include "perfetto/tracing/track_event_state_tracker.h"
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/fnv_hash.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_internal.h"
+
+// gen_amalgamated expanded: #include "protos/perfetto/common/interceptor_descriptor.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/trace_packet.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
+
+namespace perfetto {
+
+using internal::TrackEventIncrementalState;
+
+TrackEventStateTracker::~TrackEventStateTracker() = default;
+TrackEventStateTracker::Delegate::~Delegate() = default;

 // static
-void InternedEventName::Add(protos::pbzero::InternedData* interned_data,
-                            size_t iid,
-                            const char* value) {
-  auto name = interned_data->add_event_names();
-  name->set_iid(iid);
-  name->set_name(value);
+void TrackEventStateTracker::ProcessTracePacket(
+    Delegate& delegate,
+    SequenceState& sequence_state,
+    const protos::pbzero::TracePacket_Decoder& packet) {
+  UpdateIncrementalState(delegate, sequence_state, packet);
+
+  if (!packet.has_track_event())
+    return;
+  perfetto::protos::pbzero::TrackEvent::Decoder track_event(
+      packet.track_event());
+
+  auto clock_id = packet.timestamp_clock_id();
+  if (!packet.has_timestamp_clock_id())
+    clock_id = sequence_state.default_clock_id;
+  uint64_t timestamp = packet.timestamp();
+  // TODO(mohitms): Incorporate unit multiplier as well.
+  if (clock_id == internal::TrackEventIncrementalState::kClockIdIncremental) {
+    timestamp += sequence_state.most_recent_absolute_time_ns;
+    sequence_state.most_recent_absolute_time_ns = timestamp;
+  }
+
+  Track* track = &sequence_state.track;
+  if (track_event.has_track_uuid()) {
+    auto* session_state = delegate.GetSessionState();
+    if (!session_state)
+      return;  // Tracing must have ended.
+    track = &session_state->tracks[track_event.track_uuid()];
+  }
+
+  // We only log the first category of each event.
+  protozero::ConstChars category{};
+  uint64_t category_iid = 0;
+  if (auto iid_it = track_event.category_iids()) {
+    category_iid = *iid_it;
+    category.data = sequence_state.event_categories[category_iid].data();
+    category.size = sequence_state.event_categories[category_iid].size();
+  } else if (auto cat_it = track_event.categories()) {
+    category.data = reinterpret_cast<const char*>(cat_it->data());
+    category.size = cat_it->size();
+  }
+
+  protozero::ConstChars name{};
+  uint64_t name_iid = track_event.name_iid();
+  uint64_t name_hash = 0;
+  uint64_t duration = 0;
+  if (name_iid) {
+    name.data = sequence_state.event_names[name_iid].data();
+    name.size = sequence_state.event_names[name_iid].size();
+  } else if (track_event.has_name()) {
+    name.data = track_event.name().data;
+    name.size = track_event.name().size;
+  }
+
+  if (name.data) {
+    base::FnvHasher hash;
+    hash.Update(name.data, name.size);
+    name_hash = hash.digest();
+  }
+
+  size_t depth = track->stack.size();
+  switch (track_event.type()) {
+    case protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN: {
+      StackFrame frame;
+      frame.timestamp = timestamp;
+      frame.name_hash = name_hash;
+      if (track_event.has_track_uuid()) {
+        frame.name = name.ToStdString();
+        frame.category = category.ToStdString();
+      } else {
+        frame.name_iid = name_iid;
+        frame.category_iid = category_iid;
+      }
+      track->stack.push_back(std::move(frame));
+      break;
+    }
+    case protos::pbzero::TrackEvent::TYPE_SLICE_END:
+      if (!track->stack.empty()) {
+        const auto& prev_frame = track->stack.back();
+        if (prev_frame.name_iid) {
+          name.data = sequence_state.event_names[prev_frame.name_iid].data();
+          name.size = sequence_state.event_names[prev_frame.name_iid].size();
+        } else {
+          name.data = prev_frame.name.data();
+          name.size = prev_frame.name.size();
+        }
+        name_hash = prev_frame.name_hash;
+        if (prev_frame.category_iid) {
+          category.data =
+              sequence_state.event_categories[prev_frame.category_iid].data();
+          category.size =
+              sequence_state.event_categories[prev_frame.category_iid].size();
+        } else {
+          category.data = prev_frame.category.data();
+          category.size = prev_frame.category.size();
+        }
+        duration = timestamp - prev_frame.timestamp;
+        depth--;
+      }
+      break;
+    case protos::pbzero::TrackEvent::TYPE_INSTANT:
+      break;
+    case protos::pbzero::TrackEvent::TYPE_COUNTER:
+    case protos::pbzero::TrackEvent::TYPE_UNSPECIFIED:
+      // TODO(skyostil): Support counters.
+      return;
+  }
+
+  ParsedTrackEvent parsed_event{track_event};
+  parsed_event.timestamp_ns = timestamp;
+  parsed_event.duration_ns = duration;
+  parsed_event.stack_depth = depth;
+  parsed_event.category = category;
+  parsed_event.name = name;
+  parsed_event.name_hash = name_hash;
+  delegate.OnTrackEvent(*track, parsed_event);
+
+  if (track_event.type() == protos::pbzero::TrackEvent::TYPE_SLICE_END &&
+      !track->stack.empty()) {
+    track->stack.pop_back();
+  }
 }

-InternedDebugAnnotationName::~InternedDebugAnnotationName() = default;
+// static
+void TrackEventStateTracker::UpdateIncrementalState(
+    Delegate& delegate,
+    SequenceState& sequence_state,
+    const protos::pbzero::TracePacket_Decoder& packet) {
+#if PERFETTO_DCHECK_IS_ON()
+  if (!sequence_state.sequence_id) {
+    sequence_state.sequence_id = packet.trusted_packet_sequence_id();
+  } else {
+    PERFETTO_DCHECK(sequence_state.sequence_id ==
+                    packet.trusted_packet_sequence_id());
+  }
+#endif
+
+  perfetto::protos::pbzero::ClockSnapshot::Decoder snapshot(
+      packet.clock_snapshot());
+  for (auto it = snapshot.clocks(); it; ++it) {
+    perfetto::protos::pbzero::ClockSnapshot::Clock::Decoder clock(*it);
+    // TODO(mohitms) : Handle the incremental clock other than default one.
+    if (clock.is_incremental() &&
+        clock.clock_id() ==
+            internal::TrackEventIncrementalState::kClockIdIncremental) {
+      sequence_state.most_recent_absolute_time_ns =
+          clock.timestamp() * clock.unit_multiplier_ns();
+      break;
+    }
+  }
+
+  if (packet.sequence_flags() &
+      perfetto::protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED) {
+    // Convert any existing event names and categories on the stack to
+    // non-interned strings so we can look up their names even after the
+    // incremental state is gone.
+    for (auto& frame : sequence_state.track.stack) {
+      if (frame.name_iid) {
+        frame.name = sequence_state.event_names[frame.name_iid];
+        frame.name_iid = 0u;
+      }
+      if (frame.category_iid) {
+        frame.category = sequence_state.event_categories[frame.category_iid];
+        frame.category_iid = 0u;
+      }
+    }
+    sequence_state.event_names.clear();
+    sequence_state.event_categories.clear();
+    sequence_state.debug_annotation_names.clear();
+    sequence_state.track.uuid = 0u;
+    sequence_state.track.index = 0u;
+  }
+  if (packet.has_interned_data()) {
+    perfetto::protos::pbzero::InternedData::Decoder interned_data(
+        packet.interned_data());
+    for (auto it = interned_data.event_names(); it; it++) {
+      perfetto::protos::pbzero::EventName::Decoder entry(*it);
+      sequence_state.event_names[entry.iid()] = entry.name().ToStdString();
+    }
+    for (auto it = interned_data.event_categories(); it; it++) {
+      perfetto::protos::pbzero::EventCategory::Decoder entry(*it);
+      sequence_state.event_categories[entry.iid()] = entry.name().ToStdString();
+    }
+    for (auto it = interned_data.debug_annotation_names(); it; it++) {
+      perfetto::protos::pbzero::DebugAnnotationName::Decoder entry(*it);
+      sequence_state.debug_annotation_names[entry.iid()] =
+          entry.name().ToStdString();
+    }
+  }
+  if (packet.has_trace_packet_defaults()) {
+    perfetto::protos::pbzero::TracePacketDefaults::Decoder defaults(
+        packet.trace_packet_defaults());
+    if (defaults.has_track_event_defaults()) {
+      perfetto::protos::pbzero::TrackEventDefaults::Decoder
+          track_event_defaults(defaults.track_event_defaults());
+      sequence_state.track.uuid = track_event_defaults.track_uuid();
+      if (defaults.has_timestamp_clock_id())
+        sequence_state.default_clock_id = defaults.timestamp_clock_id();
+    }
+  }
+  if (packet.has_track_descriptor()) {
+    perfetto::protos::pbzero::TrackDescriptor::Decoder track_descriptor(
+        packet.track_descriptor());
+    auto* session_state = delegate.GetSessionState();
+    auto& track = session_state->tracks[track_descriptor.uuid()];
+    if (!track.index)
+      track.index = static_cast<uint32_t>(session_state->tracks.size() + 1);
+    track.uuid = track_descriptor.uuid();
+
+    if (track_descriptor.has_name()) {
+      track.name = track_descriptor.name().ToStdString();
+    } else if (track_descriptor.has_static_name()) {
+      track.name = track_descriptor.static_name().ToStdString();
+    }
+    track.pid = 0;
+    track.tid = 0;
+    if (track_descriptor.has_process()) {
+      perfetto::protos::pbzero::ProcessDescriptor::Decoder process(
+          track_descriptor.process());
+      track.pid = process.pid();
+      if (track.name.empty())
+        track.name = process.process_name().ToStdString();
+    } else if (track_descriptor.has_thread()) {
+      perfetto::protos::pbzero::ThreadDescriptor::Decoder thread(
+          track_descriptor.thread());
+      track.pid = thread.pid();
+      track.tid = thread.tid();
+      if (track.name.empty())
+        track.name = thread.thread_name().ToStdString();
+    }
+    delegate.OnTrackUpdated(track);

-// static
-void InternedDebugAnnotationName::Add(
-    protos::pbzero::InternedData* interned_data,
-    size_t iid,
-    const char* value) {
-  auto name = interned_data->add_debug_annotation_names();
-  name->set_iid(iid);
-  name->set_name(value);
+    // Mirror properties to the default track of the sequence. Note that
+    // this does not catch updates to the default track written through other
+    // sequences.
+    if (track.uuid == sequence_state.track.uuid) {
+      sequence_state.track.index = track.index;
+      sequence_state.track.name = track.name;
+      sequence_state.track.pid = track.pid;
+      sequence_state.track.tid = track.tid;
+      sequence_state.track.user_data = track.user_data;
+    }
+  }
 }

-InternedDebugAnnotationValueTypeName::~InternedDebugAnnotationValueTypeName() =
-    default;
-
-// static
-void InternedDebugAnnotationValueTypeName::Add(
-    protos::pbzero::InternedData* interned_data,
-    size_t iid,
-    const char* value) {
-  auto name = interned_data->add_debug_annotation_value_type_names();
-  name->set_iid(iid);
-  name->set_name(value);
-}
+TrackEventStateTracker::ParsedTrackEvent::ParsedTrackEvent(
+    const perfetto::protos::pbzero::TrackEvent::Decoder& track_event_)
+    : track_event(track_event_) {}

-}  // namespace internal
 }  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/platform.cc
+// gen_amalgamated begin source: src/tracing/virtual_destructors.cc
 /*
  * Copyright (C) 2019 The Android Open Source Project
  *
@@ -49787,34 +55002,47 @@ void InternedDebugAnnotationValueTypeName::Add(
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/tracing/platform.h"
 // gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_tls.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/trace_writer_base.h"
-
-namespace perfetto {
+// gen_amalgamated expanded: #include "perfetto/tracing/tracing.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/tracing_backend.h"

-PlatformThreadLocalObject::~PlatformThreadLocalObject() = default;
-Platform::~Platform() = default;
+// This translation unit contains the definitions for the destructor of pure
+// virtual interfaces for the src/public:public target. The alternative would be
+// introducing a one-liner .cc file for each pure virtual interface, which is
+// overkill. This is for compliance with -Wweak-vtables.

-void Platform::Shutdown() {}
+namespace perfetto {
+namespace internal {

-base::PlatformThreadId Platform::GetCurrentThreadId() {
-  return base::GetThreadId();
+TracingTLS::~TracingTLS() {
+  // Avoid entering trace points while the thread is being torn down.
+  // This is the problem: when a thread exits, the at-thread-exit destroys the
+  // TracingTLS. As part of that the various TraceWriter for the active data
+  // sources are destroyd. A TraceWriter dtor will issue a PostTask on the IPC
+  // thread to issue a final flush and unregister its ID with the service.
+  // The PostTask, in chromium, might have a trace event that will try to
+  // re-enter the tracing system.
+  // We fix this by resetting the TLS key to the TracingTLS object that is
+  // being destroyed in the platform impl (platform_posix.cc,
+  // platform_windows.cc, chromium's platform.cc). We carefully rely on the fact
+  // that all the tracing path that will be invoked during thread exit will
+  // early out if |is_in_trace_point| == true and will not depend on the other
+  // TLS state that has been destroyed.
+  is_in_trace_point = true;
 }

-// static
-std::unique_ptr<PlatformThreadLocalObject>
-PlatformThreadLocalObject::CreateInstance() {
-  return std::unique_ptr<PlatformThreadLocalObject>(new internal::TracingTLS());
-}
+}  // namespace internal

-// static
-base::PlatformProcessId Platform::process_id_ = 0;
+TracingProducerBackend::~TracingProducerBackend() = default;
+TracingConsumerBackend::~TracingConsumerBackend() = default;
+TracingBackend::~TracingBackend() = default;

 }  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/platform_posix.cc
+// gen_amalgamated begin source: src/android_stats/statsd_logging_helper.cc
+// gen_amalgamated begin header: src/android_stats/statsd_logging_helper.h
+// gen_amalgamated begin header: src/android_stats/perfetto_atoms.h
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -49829,129 +55057,140 @@ base::PlatformProcessId Platform::process_id_ = 0;
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#ifndef SRC_ANDROID_STATS_PERFETTO_ATOMS_H_
+#define SRC_ANDROID_STATS_PERFETTO_ATOMS_H_

-// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/thread_task_runner.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_tls.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/platform.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/trace_writer_base.h"
+namespace perfetto {

-#include <pthread.h>
-#include <stdlib.h>
+// This must match the values of the PerfettoUploadEvent enum in:
+// frameworks/proto_logging/stats/atoms.proto
+enum class PerfettoStatsdAtom {
+  kUndefined = 0,

-namespace perfetto {
+  // Checkpoints inside perfetto_cmd before tracing is finished.
+  kTraceBegin = 1,
+  kBackgroundTraceBegin = 2,
+  kCmdCloneTraceBegin = 55,
+  kCmdCloneTriggerTraceBegin = 56,
+  kOnConnect = 3,
+  kCmdOnSessionClone = 58,
+  kCmdOnTriggerSessionClone = 59,

-namespace {
+  // Guardrails inside perfetto_cmd before tracing is finished.
+  kOnTimeout = 16,

-class PlatformPosix : public Platform {
- public:
-  PlatformPosix();
-  ~PlatformPosix() override;
+  // Checkpoints inside traced.
+  kTracedEnableTracing = 37,
+  kTracedStartTracing = 38,
+  kTracedDisableTracing = 39,
+  kTracedNotifyTracingDisabled = 40,

-  ThreadLocalObject* GetOrCreateThreadLocalObject() override;
+  // Trigger checkpoints inside traced.
+  // These atoms are special because, along with the UUID,
+  // they log the trigger name.
+  kTracedTriggerStartTracing = 41,
+  kTracedTriggerStopTracing = 42,
+  kTracedTriggerCloneSnapshot = 53,

-  std::unique_ptr<base::TaskRunner> CreateTaskRunner(
-      const CreateTaskRunnerArgs&) override;
-  std::string GetCurrentProcessName() override;
-  void Shutdown() override;
+  // Guardrails inside traced.
+  kTracedEnableTracingExistingTraceSession = 18,
+  kTracedEnableTracingTooLongTrace = 19,
+  kTracedEnableTracingInvalidTriggerTimeout = 20,
+  kTracedEnableTracingDurationWithTrigger = 21,
+  kTracedEnableTracingStopTracingWriteIntoFile = 22,
+  kTracedEnableTracingDuplicateTriggerName = 23,
+  kTracedEnableTracingInvalidDeferredStart = 24,
+  kTracedEnableTracingInvalidBufferSize = 25,
+  kTracedEnableTracingBufferSizeTooLarge = 26,
+  kTracedEnableTracingTooManyBuffers = 27,
+  kTracedEnableTracingDuplicateSessionName = 28,
+  kTracedEnableTracingSessionNameTooRecent = 29,
+  kTracedEnableTracingTooManySessionsForUid = 30,
+  kTracedEnableTracingTooManyConcurrentSessions = 31,
+  kTracedEnableTracingInvalidFdOutputFile = 32,
+  kTracedEnableTracingFailedToCreateFile = 33,
+  kTracedEnableTracingOom = 34,
+  kTracedEnableTracingUnknown = 35,
+  kTracedStartTracingInvalidSessionState = 36,
+  kTracedEnableTracingInvalidFilter = 47,
+  kTracedEnableTracingOobTargetBuffer = 48,
+  kTracedEnableTracingInvalidTriggerMode = 52,
+  kTracedEnableTracingInvalidBrFilename = 54,
+  kTracedEnableTracingFailedSessionSemaphoreCheck = 57,
+  kTracedEnablePriorityBoostInvalidConfig = 61,
+  kTracedEnablePriorityBoostOtherError = 62,
+  kTracedEnableTracingExclusiveSessionRejected = 63,
+  kTracedEnableTracingExclusiveSessionNotAllowed = 64,
+  kTracedEnableTracingDuplicateBufferName = 65,

- private:
-  pthread_key_t tls_key_{};
-};
+  // Checkpoints inside perfetto_cmd after tracing has finished.
+  kOnTracingDisabled = 4,
+  kFinalizeTraceAndExit = 11,
+  kCmdFwReportBegin = 49,
+  kRebootTraceRecovered = 66,
+  kRebootTraceUploadTimeout = 67,
+  kRebootTraceUploadLeftover = 68,
+  kRebootTraceParseFailed = 69,
+  // Will be removed once incidentd is no longer used.
+  kUploadIncidentBegin = 8,
+  kNotUploadingEmptyTrace = 17,

-PlatformPosix* g_instance = nullptr;
+  // Guardrails inside perfetto_cmd after tracing has finished.
+  kCmdFwReportEmptyTrace = 50,
+  // Will be removed once incidentd is no longer used.
+  kUploadIncidentFailure = 10,

-using ThreadLocalObject = Platform::ThreadLocalObject;
+  // "Successful" terminal states inside perfetto_cmd.
+  kCmdFwReportHandoff = 51,

-PlatformPosix::PlatformPosix() {
-  PERFETTO_CHECK(!g_instance);
-  g_instance = this;
-  auto tls_dtor = [](void* obj) {
-    // The Posix TLS implementation resets the key before calling this dtor.
-    // Here we re-reset it to the object we are about to delete. This is to
-    // handle re-entrant usages of tracing in the PostTask done during the dtor
-    // (see comments in TracingTLS::~TracingTLS()). Chromium's platform
-    // implementation (which does NOT use this platform impl) has a similar
-    // workaround (https://crrev.com/c/2748300).
-    pthread_setspecific(g_instance->tls_key_, obj);
-    delete static_cast<ThreadLocalObject*>(obj);
-    pthread_setspecific(g_instance->tls_key_, nullptr);
-  };
-  PERFETTO_CHECK(pthread_key_create(&tls_key_, tls_dtor) == 0);
-}
+  // Deprecated as "success" is misleading; it simply means we were
+  // able to communicate with incidentd. Will be removed once
+  // incidentd is no longer used.
+  kUploadIncidentSuccess = 9,

-PlatformPosix::~PlatformPosix() {
-  // pthread_key_delete doesn't call destructors, so do it manually for the
-  // calling thread.
-  void* tls_ptr = pthread_getspecific(tls_key_);
-  delete static_cast<ThreadLocalObject*>(tls_ptr);
+  // Contained trigger begin/success/failure. Replaced by
+  // |PerfettoTriggerAtom| to allow aggregation using a count metric
+  // and reduce spam.
+  // reserved 12, 13, 14;

-  pthread_key_delete(tls_key_);
-  g_instance = nullptr;
-}
+  // Contained that a guardrail in perfetto_cmd was hit. Replaced with
+  // kCmd* guardrails.
+  // reserved 15;

-void PlatformPosix::Shutdown() {
-  PERFETTO_CHECK(g_instance == this);
-  delete this;
-  PERFETTO_CHECK(!g_instance);
-  // We're not clearing out the instance in GetDefaultPlatform() since it's not
-  // possible to re-initialize Perfetto after calling this function anyway.
-}
+  // Contained status of Dropbox uploads. Removed as Perfetto no
+  // longer supports uploading traces using Dropbox.
+  // reserved 5, 6, 7;

-ThreadLocalObject* PlatformPosix::GetOrCreateThreadLocalObject() {
-  // In chromium this should be implemented using base::ThreadLocalStorage.
-  void* tls_ptr = pthread_getspecific(tls_key_);
+  // Contained status of guardrail state initialization and upload limit in
+  // perfetto_cmd. Removed as perfetto no longer manages stateful guardrails
+  // reserved 44, 45, 46;

-  // This is needed to handle re-entrant calls during TLS dtor.
-  // See comments in platform.cc and aosp/1712371 .
-  ThreadLocalObject* tls = static_cast<ThreadLocalObject*>(tls_ptr);
-  if (!tls) {
-    tls = ThreadLocalObject::CreateInstance().release();
-    pthread_setspecific(tls_key_, tls);
-  }
-  return tls;
-}
+  // Contained the guardrail for user build tracing. Removed as this guardrail
+  // causes more problem than it solves these days.
+  // reserved 43;
+};

-std::unique_ptr<base::TaskRunner> PlatformPosix::CreateTaskRunner(
-    const CreateTaskRunnerArgs& args) {
-  return std::unique_ptr<base::TaskRunner>(new base::ThreadTaskRunner(
-      base::ThreadTaskRunner::CreateAndStart(args.name_for_debugging)));
-}
+// This must match the values of the PerfettoTrigger::TriggerType enum in:
+// frameworks/proto_logging/stats/atoms.proto
+enum PerfettoTriggerAtom {
+  kUndefined = 0,

-std::string PlatformPosix::GetCurrentProcessName() {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  std::string cmdline;
-  base::ReadFile("/proc/self/cmdline", &cmdline);
-  return cmdline.substr(0, cmdline.find('\0'));
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD)
-  return std::string(getprogname());
-#else
-  return "unknown_producer";
-#endif
-}
+  kTracedLimitProbability = 5,
+  kTracedLimitMaxPer24h = 6,

-}  // namespace
+  kTracedTrigger = 9,

-// static
-Platform* Platform::GetDefaultPlatform() {
-  static PlatformPosix* instance = new PlatformPosix();
-  return instance;
-}
+  // Contained events of logging triggers through perfetto_cmd, probes and
+  // trigger_perfetto.
+  // Removed in W (Oct 2024) and replaced by |kTracedTrigger|.
+  // reserved 1, 2, 3, 4, 7, 8
+};

 }  // namespace perfetto
-#endif  // OS_LINUX || OS_ANDROID || OS_APPLE || OS_FUCHSIA
-// gen_amalgamated begin source: src/tracing/platform_windows.cc
+
+#endif  // SRC_ANDROID_STATS_PERFETTO_ATOMS_H_
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -49966,171 +55205,110 @@ Platform* Platform::GetDefaultPlatform() {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-
-#include <windows.h>
-
-// gen_amalgamated expanded: #include "perfetto/ext/base/thread_task_runner.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_tls.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/platform.h"
+#ifndef SRC_ANDROID_STATS_STATSD_LOGGING_HELPER_H_
+#define SRC_ANDROID_STATS_STATSD_LOGGING_HELPER_H_

-// Thread Termination Callbacks.
-// Windows doesn't support a per-thread destructor with its
-// TLS primitives. So, we build it manually by inserting a
-// function to be called on each thread's exit.
-// This magic is from chromium's base/threading/thread_local_storage_win.cc
-// which in turn is from http://www.codeproject.com/threads/tls.asp.
+#include <stdint.h>
+#include <optional>
+#include <string>
+#include <vector>

-#ifdef _WIN64
-#pragma comment(linker, "/INCLUDE:_tls_used")
-#pragma comment(linker, "/INCLUDE:perfetto_thread_callback_base")
-#else
-#pragma comment(linker, "/INCLUDE:__tls_used")
-#pragma comment(linker, "/INCLUDE:_perfetto_thread_callback_base")
-#endif
+// gen_amalgamated expanded: #include "src/android_stats/perfetto_atoms.h"

 namespace perfetto {
+namespace android_stats {

-namespace {
-
-class PlatformWindows : public Platform {
- public:
-  static PlatformWindows* instance;
-  PlatformWindows();
-  ~PlatformWindows() override;
-
-  ThreadLocalObject* GetOrCreateThreadLocalObject() override;
-  std::unique_ptr<base::TaskRunner> CreateTaskRunner(
-      const CreateTaskRunnerArgs&) override;
-  std::string GetCurrentProcessName() override;
-  void OnThreadExit();
-
- private:
-  DWORD tls_key_{};
-};
-
-using ThreadLocalObject = Platform::ThreadLocalObject;
-
-// static
-PlatformWindows* PlatformWindows::instance = nullptr;
-
-PlatformWindows::PlatformWindows() {
-  instance = this;
-  tls_key_ = ::TlsAlloc();
-  PERFETTO_CHECK(tls_key_ != TLS_OUT_OF_INDEXES);
-}
-
-PlatformWindows::~PlatformWindows() {
-  // TlsFree doesn't call destructors, so do it manually for the calling
-  // thread.
-  OnThreadExit();
-  ::TlsFree(tls_key_);
-  instance = nullptr;
-}
-
-void PlatformWindows::OnThreadExit() {
-  auto tls = static_cast<ThreadLocalObject*>(::TlsGetValue(tls_key_));
-  if (tls) {
-    // At this point we rely on the TLS object to be still set to the TracingTLS
-    // we are deleting. See comments in TracingTLS::~TracingTLS().
-    delete tls;
-  }
-}
-
-ThreadLocalObject* PlatformWindows::GetOrCreateThreadLocalObject() {
-  void* tls_ptr = ::TlsGetValue(tls_key_);
-
-  auto* tls = static_cast<ThreadLocalObject*>(tls_ptr);
-  if (!tls) {
-    tls = ThreadLocalObject::CreateInstance().release();
-    ::TlsSetValue(tls_key_, tls);
-  }
-  return tls;
-}
-
-std::unique_ptr<base::TaskRunner> PlatformWindows::CreateTaskRunner(
-    const CreateTaskRunnerArgs& args) {
-  return std::unique_ptr<base::TaskRunner>(new base::ThreadTaskRunner(
-      base::ThreadTaskRunner::CreateAndStart(args.name_for_debugging)));
-}
-
-std::string PlatformWindows::GetCurrentProcessName() {
-  char buf[MAX_PATH];
-  auto len = ::GetModuleFileNameA(nullptr /*current*/, buf, sizeof(buf));
-  std::string name(buf, static_cast<size_t>(len));
-  size_t sep = name.find_last_of('\\');
-  if (sep != std::string::npos)
-    name = name.substr(sep + 1);
-  return name;
-}
+// Functions in this file are only active on built in the Android
+// tree. On other platforms (including Android standalone and Chromium
+// on Android) these functions are a noop.

-}  // namespace
+// Logs the upload event to statsd if built in the Android tree.
+void MaybeLogUploadEvent(PerfettoStatsdAtom atom,
+                         int64_t uuid_lsb,
+                         int64_t uuid_msb,
+                         const std::string& trigger_name = "");

-// static
-Platform* Platform::GetDefaultPlatform() {
-  static PlatformWindows* thread_safe_init_instance = new PlatformWindows();
-  return thread_safe_init_instance;
-}
+// Logs the trigger events to statsd if built in the Android tree.
+void MaybeLogTriggerEvent(PerfettoTriggerAtom atom,
+                          int64_t uuid_lsb,
+                          const std::string& trigger);

+}  // namespace android_stats
 }  // namespace perfetto

-// -----------------------
-// Thread-local destructor
-// -----------------------
-
-// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are
-// called automatically by the OS loader code (not the CRT) when the module is
-// loaded and on thread creation. They are NOT called if the module has been
-// loaded by a LoadLibrary() call. It must have implicitly been loaded at
-// process startup.
-// See VC\crt\src\tlssup.c for reference.
-
-// extern "C" suppresses C++ name mangling so we know the symbol name for the
-// linker /INCLUDE:symbol pragma above.
-extern "C" {
-// The linker must not discard perfetto_thread_callback_base. (We force a
-// reference to this variable with a linker /INCLUDE:symbol pragma to ensure
-// that.) If this variable is discarded, the OnThreadExit function will never be
-// called.
+#endif  // SRC_ANDROID_STATS_STATSD_LOGGING_HELPER_H_
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */

-void NTAPI PerfettoOnThreadExit(PVOID, DWORD, PVOID);
-void NTAPI PerfettoOnThreadExit(PVOID, DWORD reason, PVOID) {
-  if (reason == DLL_THREAD_DETACH || reason == DLL_PROCESS_DETACH) {
-    if (perfetto::PlatformWindows::instance)
-      perfetto::PlatformWindows::instance->OnThreadExit();
-  }
-}
+// gen_amalgamated expanded: #include "src/android_stats/statsd_logging_helper.h"

-#ifdef _WIN64
+#include <cstdint>
+#include <string>
+#include <vector>

-// .CRT section is merged with .rdata on x64 so it must be constant data.
-#pragma const_seg(".CRT$XLP")
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "src/android_stats/perfetto_atoms.h"

-// When defining a const variable, it must have external linkage to be sure the
-// linker doesn't discard it.
-extern const PIMAGE_TLS_CALLBACK perfetto_thread_callback_base;
-const PIMAGE_TLS_CALLBACK perfetto_thread_callback_base = PerfettoOnThreadExit;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
+    PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+// gen_amalgamated expanded: #include "src/android_internal/lazy_library_loader.h"  // nogncheck
+// gen_amalgamated expanded: #include "src/android_internal/statsd_logging.h"       // nogncheck
+#endif

-// Reset the default section.
-#pragma const_seg()
+namespace perfetto::android_stats {

-#else  // _WIN64
+// Make sure we don't accidentally log on non-Android tree build. Note that even
+// removing this ifdef still doesn't make uploads work on OS_ANDROID.
+// PERFETTO_LAZY_LOAD will return a nullptr on non-Android and non-in-tree
+// builds as libperfetto_android_internal will not be available.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
+    PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)

-#pragma data_seg(".CRT$XLP")
-PIMAGE_TLS_CALLBACK perfetto_thread_callback_base = PerfettoOnThreadExit;
-// Reset the default section.
-#pragma data_seg()
+void MaybeLogUploadEvent(PerfettoStatsdAtom atom,
+                         int64_t uuid_lsb,
+                         int64_t uuid_msb,
+                         const std::string& trigger_name) {
+  PERFETTO_LAZY_LOAD(android_internal::StatsdLogUploadEvent, log_event_fn);
+  if (log_event_fn) {
+    log_event_fn(atom, uuid_lsb, uuid_msb, trigger_name.c_str());
+  }
+}

-#endif  // _WIN64
+void MaybeLogTriggerEvent(PerfettoTriggerAtom atom,
+                          int64_t uuid_lsb,
+                          const std::string& trigger_name) {
+  PERFETTO_LAZY_LOAD(android_internal::StatsdLogTriggerEvent, log_event_fn);
+  if (log_event_fn) {
+    log_event_fn(atom, uuid_lsb, trigger_name.c_str());
+  }
+}

-}  // extern "C"
+#else
+void MaybeLogUploadEvent(PerfettoStatsdAtom,
+                         int64_t,
+                         int64_t,
+                         const std::string&) {}
+void MaybeLogTriggerEvent(PerfettoTriggerAtom, int64_t, const std::string&) {}
+#endif

-#endif  // OS_WIN
-// gen_amalgamated begin source: src/tracing/traced_value.cc
+}  // namespace perfetto::android_stats
+// gen_amalgamated begin source: src/base/regex/regex.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/regex.h
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -50145,177 +55323,180 @@ PIMAGE_TLS_CALLBACK perfetto_thread_callback_base = PerfettoOnThreadExit;
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/tracing/traced_value.h"
-
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/debug_annotation.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_interned_fields.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
-
-namespace perfetto {
-
-namespace internal {
-
-TracedValue CreateTracedValueFromProto(
-    protos::pbzero::DebugAnnotation* annotation,
-    EventContext* event_context) {
-  return TracedValue::CreateFromProto(annotation, event_context);
-}
-
-}  // namespace internal
-
-// static
-TracedValue TracedValue::CreateFromProto(
-    protos::pbzero::DebugAnnotation* annotation,
-    EventContext* event_context) {
-  return TracedValue(annotation, event_context, nullptr);
-}
-
-TracedValue::TracedValue(TracedValue&&) = default;
-TracedValue::~TracedValue() = default;
-
-void TracedValue::WriteInt64(int64_t value) && {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  annotation_->set_int_value(value);
-}
+#ifndef INCLUDE_PERFETTO_EXT_BASE_REGEX_H_
+#define INCLUDE_PERFETTO_EXT_BASE_REGEX_H_

-void TracedValue::WriteUInt64(uint64_t value) && {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  annotation_->set_uint_value(value);
-}
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <vector>

-void TracedValue::WriteDouble(double value) && {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  annotation_->set_double_value(value);
-}
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"

-void TracedValue::WriteBoolean(bool value) && {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  annotation_->set_bool_value(value);
-}
+namespace perfetto {
+namespace base {

-void TracedValue::WriteString(const char* value) && {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  annotation_->set_string_value(value);
-}
+// Pimpl for the regex engine. Defined in src/base/regex/regex.cc.
+class RegexImpl;

-void TracedValue::WriteString(const char* value, size_t len) && {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  annotation_->set_string_value(value, len);
-}
+// A regular expression abstraction that hides the underlying engine.
+//
+// Construct via the two static factories:
+//   - Create()        – fallible; returns StatusOr<Regex>.
+//   - CreateOrCheck() – infallible; crashes on invalid pattern.
+//                       Use for compile-time constant patterns only.
+//
+// Regex is movable but NOT copyable. Use CopyableRegex when copy semantics
+// are needed (e.g. for storage in copyable containers).
+//
+// Patterns use ECMAScript syntax, supporting Perl-style shorthands (\d, \w,
+// \s).
+//
+// Operations:
+//   GlobalReplace()          – global substitution; returns a new string.
+//   FullMatch()              – anchored, no groups.
+//   FullMatchWithGroups()    – anchored, with capture groups.
+//   PartialMatch()           – unanchored, no groups.
+//   PartialMatchWithGroups() – unanchored, with capture groups.
+//   PartialMatchAll()        – iterator over all non-overlapping matches.
+class Regex {
+ public:
+  // Controls whether matching is case-sensitive or case-insensitive.
+  enum class CaseSensitivity : uint8_t {
+    kSensitive,
+    kInsensitive,
+  };

-void TracedValue::WriteString(const std::string& value) && {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  annotation_->set_string_value(value);
-}
+  Regex(Regex&&) noexcept;
+  Regex& operator=(Regex&&) noexcept;
+  ~Regex();

-void TracedValue::WriteString(std::string_view value) && {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  annotation_->set_string_value(value.data(), value.size());
-}
+  // Regex is not copyable. Use CopyableRegex for copy semantics.
+  Regex(const Regex&) = delete;
+  Regex& operator=(const Regex&) = delete;

-void TracedValue::WritePointer(const void* value) && {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  annotation_->set_pointer_value(reinterpret_cast<uint64_t>(value));
-}
+  // Compiles |pattern| into a Regex.
+  // Returns an error if the pattern syntax is invalid.
+  static StatusOr<Regex> Create(
+      std::string_view pattern,
+      CaseSensitivity cs = CaseSensitivity::kSensitive);

-TracedDictionary TracedValue::WriteDictionary() && {
-  // Note: this passes |checked_scope_.is_active_| bit to the parent to be
-  // picked up later by the new TracedDictionary.
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  checked_scope_.Reset();
+  // Like Create(), but crashes (PERFETTO_CHECK) on an invalid pattern.
+  // Use only for patterns that are known at compile time.
+  static Regex CreateOrCheck(std::string_view pattern,
+                             CaseSensitivity cs = CaseSensitivity::kSensitive);

-  PERFETTO_DCHECK(!annotation_->is_finalized());
-  return TracedDictionary(annotation_,
-                          protos::pbzero::DebugAnnotation::kDictEntries,
-                          event_context_, checked_scope_.parent_scope());
-}
+  // Replaces every occurrence of the regex in |s| with |replacement| and
+  // returns the resulting string.  |s| is unchanged.
+  std::string GlobalReplace(std::string_view s,
+                            std::string_view replacement) const;

-TracedArray TracedValue::WriteArray() && {
-  // Note: this passes |checked_scope_.is_active_| bit to the parent to be
-  // picked up later by the new TracedDictionary.
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  checked_scope_.Reset();
+  // Returns true if the regex matches the entirety of |s|.
+  bool FullMatch(std::string_view s) const;

-  PERFETTO_DCHECK(!annotation_->is_finalized());
-  return TracedArray(annotation_, event_context_,
-                     checked_scope_.parent_scope());
-}
+  // Like FullMatch(), but also fills |out| with captured groups on success.
+  //   out[0]    – the full match.
+  //   out[1..N] – the parenthesised sub-groups.
+  // Returns false (and clears |out|) when there is no match.
+  //
+  // The string_views in |out| point into |s|; they are invalidated if |s| is
+  // modified or destroyed.
+  bool FullMatchWithGroups(std::string_view s,
+                           std::vector<std::string_view>& out) const;

-protozero::Message* TracedValue::WriteProtoInternal(const char* name) {
-  if (event_context_) {
-    annotation_->set_proto_type_name_iid(
-        internal::InternedDebugAnnotationValueTypeName::Get(event_context_,
-                                                            name));
-  } else {
-    annotation_->set_proto_type_name(name);
-  }
-  return annotation_->template BeginNestedMessage<protozero::Message>(
-      protos::pbzero::DebugAnnotation::kProtoValueFieldNumber);
-}
+  // Returns true if the regex matches any substring of |s|.
+  bool PartialMatch(std::string_view s) const;

-TracedArray::TracedArray(TracedValue annotation)
-    : TracedArray(std::move(annotation).WriteArray()) {}
+  // Like PartialMatch(), but also fills |out| with captured groups on success.
+  //   out[0]    – the full match.
+  //   out[1..N] – the parenthesised sub-groups.
+  // Returns false (and clears |out|) when there is no match.
+  //
+  // The string_views in |out| point into |s|; they are invalidated if |s| is
+  // modified or destroyed.
+  bool PartialMatchWithGroups(std::string_view s,
+                              std::vector<std::string_view>& out) const;

-TracedValue TracedArray::AppendItem() {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  return TracedValue(annotation_->add_array_values(), event_context_,
-                     &checked_scope_);
-}
+  // Iterator over all non-overlapping matches in a string.
+  class PartialMatchIterator {
+   public:
+    // Returns the next match, or std::nullopt when no more matches remain.
+    std::optional<std::string_view> Next();

-TracedDictionary TracedArray::AppendDictionary() {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  return AppendItem().WriteDictionary();
-}
+    // Returns the next match with capture groups. Groups are filled into
+    // |groups|. Returns std::nullopt when no more matches remain.
+    std::optional<std::string_view> NextWithGroups(
+        std::vector<std::string_view>& groups);

-TracedArray TracedArray::AppendArray() {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  return AppendItem().WriteArray();
-}
+   private:
+    friend class Regex;
+    PartialMatchIterator(const Regex* re, std::string_view input);
+    const Regex* re_;
+    std::string_view input_;
+    size_t offset_ = 0;
+    std::vector<std::string_view> groups_;  // Cached for Next().
+  };

-TracedDictionary::TracedDictionary(TracedValue annotation)
-    : TracedDictionary(std::move(annotation).WriteDictionary()) {}
+  // Returns an iterator that yields all non-overlapping matches in |s|.
+  PartialMatchIterator PartialMatchAll(std::string_view s) const;

-TracedValue TracedDictionary::AddItem(StaticString key) {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  protos::pbzero::DebugAnnotation* item =
-      message_->BeginNestedMessage<protos::pbzero::DebugAnnotation>(field_id_);
-  item->set_name(key.value);
-  return TracedValue(item, event_context_, &checked_scope_);
-}
+  // Accessors for pattern and case sensitivity (used by CopyableRegex).
+  const std::string& pattern() const { return pattern_; }
+  CaseSensitivity case_sensitivity() const { return cs_; }

-TracedValue TracedDictionary::AddItem(DynamicString key) {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  protos::pbzero::DebugAnnotation* item =
-      message_->BeginNestedMessage<protos::pbzero::DebugAnnotation>(field_id_);
-  item->set_name(key.value);
-  return TracedValue(item, event_context_, &checked_scope_);
-}
+ private:
+  Regex() = default;
+  std::unique_ptr<RegexImpl> impl_;
+  std::string pattern_;
+  CaseSensitivity cs_ = CaseSensitivity::kSensitive;
+};

-TracedDictionary TracedDictionary::AddDictionary(StaticString key) {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  return AddItem(key).WriteDictionary();
-}
+// A copyable wrapper around Regex. Copying re-compiles the pattern from the
+// stored string, so prefer moving when the source is no longer needed.
+//
+// Use this class when Regex needs to be stored in a copyable container
+// (e.g. std::optional in a copyable struct).
+class CopyableRegex {
+ public:
+  explicit CopyableRegex(Regex regex);

-TracedDictionary TracedDictionary::AddDictionary(DynamicString key) {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  return AddItem(key).WriteDictionary();
-}
+  CopyableRegex(const CopyableRegex& other);
+  CopyableRegex& operator=(const CopyableRegex& other);
+  CopyableRegex(CopyableRegex&&) noexcept = default;
+  CopyableRegex& operator=(CopyableRegex&&) noexcept = default;
+  ~CopyableRegex() = default;

-TracedArray TracedDictionary::AddArray(StaticString key) {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  return AddItem(key).WriteArray();
-}
+  std::string GlobalReplace(std::string_view s,
+                            std::string_view replacement) const {
+    return regex_.GlobalReplace(s, replacement);
+  }
+  bool FullMatch(std::string_view s) const { return regex_.FullMatch(s); }
+  bool FullMatchWithGroups(std::string_view s,
+                           std::vector<std::string_view>& out) const {
+    return regex_.FullMatchWithGroups(s, out);
+  }
+  bool PartialMatch(std::string_view s) const { return regex_.PartialMatch(s); }
+  bool PartialMatchWithGroups(std::string_view s,
+                              std::vector<std::string_view>& out) const {
+    return regex_.PartialMatchWithGroups(s, out);
+  }
+  Regex::PartialMatchIterator PartialMatchAll(std::string_view s) const {
+    return regex_.PartialMatchAll(s);
+  }

-TracedArray TracedDictionary::AddArray(DynamicString key) {
-  PERFETTO_DCHECK(checked_scope_.is_active());
-  return AddItem(key).WriteArray();
-}
+ private:
+  Regex regex_;
+};

+}  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/tracing.cc
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_REGEX_H_
+// gen_amalgamated begin header: src/base/regex/regex_pcre2.h
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -50330,214 +55511,199 @@ TracedArray TracedDictionary::AddArray(DynamicString key) {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/tracing/tracing.h"
-
-#include <atomic>
-#include <condition_variable>
-#include <mutex>
-
-// gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/no_destructor.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/waitable_event.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_data_source.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_internal.h"
-// gen_amalgamated expanded: #include "src/tracing/internal/tracing_muxer_impl.h"
-
-namespace perfetto {
-namespace {
-bool g_was_initialized = false;
-
-// Wrapped in a function to avoid global constructor
-std::mutex& InitializedMutex() {
-  static base::NoDestructor<std::mutex> initialized_mutex;
-  return initialized_mutex.ref();
-}
-}  // namespace
-
-// static
-void Tracing::InitializeInternal(const TracingInitArgs& args) {
-  base::InitializeTime();
-  std::unique_lock<std::mutex> lock(InitializedMutex());
-  // If it's the first time Initialize is called, set some global params.
-  if (!g_was_initialized) {
-    // Make sure the headers and implementation files agree on the build config.
-    PERFETTO_CHECK(args.dcheck_is_on_ == PERFETTO_DCHECK_IS_ON());
-    if (args.log_message_callback) {
-      base::SetLogMessageCallback(args.log_message_callback);
-    }
-
-    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);
-    }
-
-    if (args.disallow_merging_with_system_tracks) {
-      internal::TrackEventInternal::SetDisallowMergingWithSystemTracks(true);
-    }
-
-    internal::TrackEventInternal::SetBufferExhaustedPolicy(
-        args.track_event_buffer_exhausted_policy);
-  }
-
-  internal::TracingMuxerImpl::InitializeInstance(args);
-  internal::TrackRegistry::InitializeInstance(args.process_uuid);
-  g_was_initialized = true;
-}
-
-// static
-bool Tracing::IsInitialized() {
-  std::unique_lock<std::mutex> lock(InitializedMutex());
-  return g_was_initialized;
-}
+#ifndef SRC_BASE_REGEX_REGEX_PCRE2_H_
+#define SRC_BASE_REGEX_REGEX_PCRE2_H_

-// static
-void Tracing::Shutdown() {
-  std::unique_lock<std::mutex> lock(InitializedMutex());
-  if (!g_was_initialized)
-    return;
-  internal::TracingMuxerImpl::Shutdown();
-  g_was_initialized = false;
-}
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-// static
-void Tracing::ResetForTesting() {
-  std::unique_lock<std::mutex> lock(InitializedMutex());
-  if (!g_was_initialized)
-    return;
-  base::SetLogMessageCallback(nullptr);
-  internal::TracingMuxerImpl::ResetForTesting();
-  internal::TrackRegistry::ResetForTesting();
-  internal::TrackEventDataSource::ResetForTesting();
-  g_was_initialized = false;
-}
+// See note in regex_re2.h. Also honor PERFETTO_REGEX_FORCE_STD: targets that
+// define it (e.g. libperfetto_client_experimental) opt out of the optional
+// backends and must not pull in <pcre2.h>, even when PERFETTO_PCRE2 is set.
+#if PERFETTO_BUILDFLAG(PERFETTO_PCRE2) && !defined(PERFETTO_REGEX_FORCE_STD)

-//  static
-std::unique_ptr<TracingSession> Tracing::NewTraceInternal(
-    BackendType backend,
-    TracingConsumerBackend* (*system_backend_factory)()) {
-  return static_cast<internal::TracingMuxerImpl*>(internal::TracingMuxer::Get())
-      ->CreateTracingSession(backend, system_backend_factory);
-}
+#include <limits>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <vector>

-//  static
-std::unique_ptr<StartupTracingSession> Tracing::SetupStartupTracing(
-    const TraceConfig& config,
-    Tracing::SetupStartupTracingOpts opts) {
-  return static_cast<internal::TracingMuxerImpl*>(internal::TracingMuxer::Get())
-      ->CreateStartupTracingSession(config, std::move(opts));
-}
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"

-//  static
-std::unique_ptr<StartupTracingSession> Tracing::SetupStartupTracingBlocking(
-    const TraceConfig& config,
-    Tracing::SetupStartupTracingOpts opts) {
-  return static_cast<internal::TracingMuxerImpl*>(internal::TracingMuxer::Get())
-      ->CreateStartupTracingSessionBlocking(config, std::move(opts));
-}
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>

-//  static
-void Tracing::ActivateTriggers(const std::vector<std::string>& triggers,
-                               uint32_t ttl_ms) {
-  internal::TracingMuxer::Get()->ActivateTriggers(triggers, ttl_ms);
-}
+namespace perfetto {
+namespace base {

-TracingSession::~TracingSession() = default;
+struct Pcre2CodeDeleter {
+  void operator()(pcre2_code* p) const { pcre2_code_free(p); }
+};

-void TracingSession::CloneTrace(CloneTraceArgs, CloneTraceCallback) {}
+struct Pcre2MatchDataDeleter {
+  void operator()(pcre2_match_data* p) const { pcre2_match_data_free(p); }
+};

-// Can be called from any thread.
-bool TracingSession::FlushBlocking(uint32_t timeout_ms) {
-  std::atomic<bool> flush_result;
-  base::WaitableEvent flush_ack;
+using ScopedPcre2Code = std::unique_ptr<pcre2_code, Pcre2CodeDeleter>;
+using ScopedPcre2MatchData =
+    std::unique_ptr<pcre2_match_data, Pcre2MatchDataDeleter>;

-  // The non blocking Flush() can be called on any thread. It does the PostTask
-  // internally.
-  Flush(
-      [&flush_ack, &flush_result](bool res) {
-        flush_result = res;
-        flush_ack.Notify();
-      },
-      timeout_ms);
-  flush_ack.Wait();
-  return flush_result;
-}
+// PCRE2 backend for Regex.
+// Header-only so the compiler can inline through unique_ptr<RegexImpl>.
+class RegexPcre2 {
+ public:
+  RegexPcre2(RegexPcre2&&) noexcept = default;
+  RegexPcre2& operator=(RegexPcre2&&) noexcept = default;
+  ~RegexPcre2() = default;

-std::vector<char> TracingSession::ReadTraceBlocking() {
-  std::vector<char> raw_trace;
-  std::mutex mutex;
-  std::condition_variable cv;
+  RegexPcre2(const RegexPcre2&) = delete;
+  RegexPcre2& operator=(const RegexPcre2&) = delete;

-  bool all_read = false;
+  static StatusOr<RegexPcre2> Create(std::string_view pattern,
+                                     bool case_insensitive) {
+    RegexPcre2 result;
+    int error_code;
+    size_t error_offset;
+    uint32_t pcre2_flags = 0;
+    if (case_insensitive) {
+      pcre2_flags |= PCRE2_CASELESS;
+    }
+    result.code_.reset(pcre2_compile(
+        reinterpret_cast<PCRE2_SPTR>(pattern.data()), pattern.size(),
+        pcre2_flags, &error_code, &error_offset, nullptr));
+    if (!result.code_) {
+      PCRE2_UCHAR buffer[256];
+      pcre2_get_error_message(error_code, buffer, sizeof(buffer));
+      return ErrStatus("PCRE2 compile error at offset %zu: %s", error_offset,
+                       reinterpret_cast<char*>(buffer));
+    }
+    // JIT-compile for faster matching. Non-fatal if JIT is unavailable.
+    pcre2_jit_compile(result.code_.get(), PCRE2_JIT_COMPLETE);
+    result.match_data_.reset(
+        pcre2_match_data_create_from_pattern(result.code_.get(), nullptr));
+    return std::move(result);
+  }

-  ReadTrace([&mutex, &raw_trace, &all_read, &cv](ReadTraceCallbackArgs cb) {
-    raw_trace.insert(raw_trace.end(), cb.data, cb.data + cb.size);
-    std::unique_lock<std::mutex> lock(mutex);
-    all_read = !cb.has_more;
-    if (all_read)
-      cv.notify_one();
-  });
+  std::string GlobalReplace(std::string_view s,
+                            std::string_view replacement) const {
+    std::string out;
+    size_t out_len = s.size() + replacement.size() * 2 + 64;
+    auto do_substitute = [&](size_t* len) {
+      out.resize(*len);
+      return pcre2_substitute(
+          code_.get(), reinterpret_cast<PCRE2_SPTR>(s.data()), s.size(),
+          /*startoffset=*/0,
+          PCRE2_SUBSTITUTE_GLOBAL | PCRE2_SUBSTITUTE_OVERFLOW_LENGTH,
+          /*match_data=*/nullptr, /*mcontext=*/nullptr,
+          reinterpret_cast<PCRE2_SPTR>(replacement.data()), replacement.size(),
+          reinterpret_cast<PCRE2_UCHAR*>(out.data()), len);
+    };
+    int rc = do_substitute(&out_len);
+    if (rc == PCRE2_ERROR_NOMEMORY) {
+      // PCRE2_SUBSTITUTE_OVERFLOW_LENGTH sets out_len to the required size.
+      rc = do_substitute(&out_len);
+    }
+    if (rc >= 0) {
+      out.resize(out_len);
+      return out;
+    }
+    return std::string(s);
+  }

-  {
-    std::unique_lock<std::mutex> lock(mutex);
-    cv.wait(lock, [&all_read] { return all_read; });
+  bool FullMatch(std::string_view s) const {
+    int rc = pcre2_match(code_.get(), reinterpret_cast<PCRE2_SPTR>(s.data()),
+                         s.size(),
+                         /*startoffset=*/0, PCRE2_ANCHORED | PCRE2_ENDANCHORED,
+                         match_data_.get(), /*mcontext=*/nullptr);
+    return rc >= 0;
   }
-  return raw_trace;
-}

-TracingSession::GetTraceStatsCallbackArgs
-TracingSession::GetTraceStatsBlocking() {
-  std::mutex mutex;
-  std::condition_variable cv;
-  GetTraceStatsCallbackArgs result;
-  bool stats_read = false;
+  bool FullMatchWithGroups(std::string_view s,
+                           std::vector<std::string_view>& out) const {
+    out.clear();
+    int rc = pcre2_match(code_.get(), reinterpret_cast<PCRE2_SPTR>(s.data()),
+                         s.size(),
+                         /*startoffset=*/0, PCRE2_ANCHORED | PCRE2_ENDANCHORED,
+                         match_data_.get(), /*mcontext=*/nullptr);
+    if (rc <= 0)
+      return false;
+    FillGroups(s, out);
+    return true;
+  }

-  GetTraceStats(
-      [&mutex, &result, &stats_read, &cv](GetTraceStatsCallbackArgs args) {
-        result = std::move(args);
-        std::unique_lock<std::mutex> lock(mutex);
-        stats_read = true;
-        cv.notify_one();
-      });
+  bool PartialMatch(std::string_view s) const {
+    int rc = pcre2_match(code_.get(), reinterpret_cast<PCRE2_SPTR>(s.data()),
+                         s.size(),
+                         /*startoffset=*/0, /*options=*/0, match_data_.get(),
+                         /*mcontext=*/nullptr);
+    return rc >= 0;
+  }

-  {
-    std::unique_lock<std::mutex> lock(mutex);
-    cv.wait(lock, [&stats_read] { return stats_read; });
+  bool PartialMatchWithGroups(std::string_view s,
+                              std::vector<std::string_view>& out) const {
+    out.clear();
+    int rc = pcre2_match(code_.get(), reinterpret_cast<PCRE2_SPTR>(s.data()),
+                         s.size(),
+                         /*startoffset=*/0, /*options=*/0, match_data_.get(),
+                         /*mcontext=*/nullptr);
+    if (rc <= 0)
+      return false;
+    FillGroups(s, out);
+    return true;
   }
-  return result;
-}

-TracingSession::QueryServiceStateCallbackArgs
-TracingSession::QueryServiceStateBlocking() {
-  std::mutex mutex;
-  std::condition_variable cv;
-  QueryServiceStateCallbackArgs result;
-  bool status_read = false;
+  bool SearchWithOffset(std::string_view s,
+                        size_t start_offset,
+                        std::vector<std::string_view>& out) const {
+    out.clear();
+    if (start_offset > s.size())
+      return false;
+    int rc =
+        pcre2_match(code_.get(), reinterpret_cast<PCRE2_SPTR>(s.data()),
+                    s.size(), start_offset, /*options=*/0, match_data_.get(),
+                    /*mcontext=*/nullptr);
+    if (rc <= 0)
+      return false;
+    FillGroups(s, out);
+    return true;
+  }

-  QueryServiceState(
-      [&mutex, &result, &status_read, &cv](QueryServiceStateCallbackArgs args) {
-        result = std::move(args);
-        std::unique_lock<std::mutex> lock(mutex);
-        status_read = true;
-        cv.notify_one();
-      });
+ private:
+  RegexPcre2() = default;

-  {
-    std::unique_lock<std::mutex> lock(mutex);
-    cv.wait(lock, [&status_read] { return status_read; });
+  void FillGroups(std::string_view s,
+                  std::vector<std::string_view>& out) const {
+    uint32_t count = pcre2_get_ovector_count(match_data_.get());
+    size_t* ovector = pcre2_get_ovector_pointer(match_data_.get());
+    for (uint32_t i = 0; i < count; ++i) {
+      if (ovector[2 * i] == std::numeric_limits<PCRE2_SIZE>::max()) {
+        out.emplace_back();
+      } else {
+        out.emplace_back(s.data() + ovector[2 * i],
+                         ovector[2 * i + 1] - ovector[2 * i]);
+      }
+    }
   }
-  return result;
-}

-StartupTracingSession::~StartupTracingSession() = default;
+  ScopedPcre2Code code_;
+  // pcre2_match() requires a non-null match_data even when capture groups
+  // aren't needed (FullMatch, PartialMatch). A separate minimal match_data
+  // with ovecsize=1 could be used for those cases, but the savings are
+  // negligible: PCRE2's internal backtracking frame size depends on the
+  // pattern's capture groups regardless of ovector size. We reuse a single
+  // cached match_data created from the pattern to avoid per-call allocation.
+  mutable ScopedPcre2MatchData match_data_;
+};

+}  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/tracing_policy.cc
+
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_PCRE2) &&
+        // !defined(PERFETTO_REGEX_FORCE_STD)
+
+#endif  // SRC_BASE_REGEX_REGEX_PCRE2_H_
+// gen_amalgamated begin header: src/base/regex/regex_re2.h
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -50552,16 +55718,125 @@ StartupTracingSession::~StartupTracingSession() = default;
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/tracing/tracing_policy.h"
+#ifndef SRC_BASE_REGEX_REGEX_RE2_H_
+#define SRC_BASE_REGEX_REGEX_RE2_H_
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+// Guarded because gen_amalgamated inlines the body even when the include
+// site in regex.cc is preprocessed out. Also honor PERFETTO_REGEX_FORCE_STD:
+// targets that define it opt out of the optional backends and must not pull in
+// <re2/re2.h>, even when PERFETTO_RE2 is set.
+#if PERFETTO_BUILDFLAG(PERFETTO_RE2) && !defined(PERFETTO_REGEX_FORCE_STD)
+
+#include <memory>
+#include <string>
+#include <string_view>
+#include <vector>
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"
+
+// Using quotes instead of angle brackets because Bazel passes external repo
+// paths via -iquote (not -isystem), which only covers quoted includes.
+// See google/re2 commit baeed1c and bazelbuild/bazel#18974.
+#include "re2/re2.h"  // gen_amalgamated:keep

 namespace perfetto {
+namespace base {

-TracingPolicy::~TracingPolicy() = default;
+// RE2 backend for Regex. Used on non-Android builds (standalone, Chromium).
+// Header-only so the compiler can inline through unique_ptr<RegexImpl>.
+class RegexRe2 {
+ public:
+  RegexRe2(RegexRe2&&) noexcept = default;
+  RegexRe2& operator=(RegexRe2&&) noexcept = default;
+  ~RegexRe2() = default;
+
+  RegexRe2(const RegexRe2&) = delete;
+  RegexRe2& operator=(const RegexRe2&) = delete;
+
+  static StatusOr<RegexRe2> Create(std::string_view pattern,
+                                   bool case_insensitive) {
+    RegexRe2 result;
+    re2::RE2::Options options;
+    options.set_log_errors(false);
+    options.set_case_sensitive(!case_insensitive);
+    result.re_ = std::make_unique<re2::RE2>(std::string(pattern), options);
+    if (!result.re_->ok()) {
+      return ErrStatus("RE2 compile error: %s", result.re_->error().c_str());
+    }
+    result.groups_.resize(
+        static_cast<size_t>(result.re_->NumberOfCapturingGroups() + 1));
+    return std::move(result);
+  }
+
+  std::string GlobalReplace(std::string_view s,
+                            std::string_view replacement) const {
+    std::string out(s);
+    re2::RE2::GlobalReplace(&out, *re_, replacement);
+    return out;
+  }
+
+  bool FullMatch(std::string_view s) const {
+    return re2::RE2::FullMatch(s, *re_);
+  }
+
+  bool FullMatchWithGroups(std::string_view s,
+                           std::vector<std::string_view>& out) const {
+    out.clear();
+    int n = static_cast<int>(groups_.size());
+    if (!re_->Match(s, 0, s.size(), re2::RE2::ANCHOR_BOTH, groups_.data(), n))
+      return false;
+    out.assign(groups_.begin(), groups_.begin() + n);
+    return true;
+  }
+
+  bool PartialMatch(std::string_view s) const {
+    return re2::RE2::PartialMatch(s, *re_);
+  }
+
+  bool PartialMatchWithGroups(std::string_view s,
+                              std::vector<std::string_view>& out) const {
+    out.clear();
+    int n = static_cast<int>(groups_.size());
+    if (!re_->Match(s, 0, s.size(), re2::RE2::UNANCHORED, groups_.data(), n))
+      return false;
+    out.assign(groups_.begin(), groups_.begin() + n);
+    return true;
+  }
+
+  bool SearchWithOffset(std::string_view s,
+                        size_t start_offset,
+                        std::vector<std::string_view>& out) const {
+    out.clear();
+    if (start_offset > s.size())
+      return false;
+    int n = static_cast<int>(groups_.size());
+    if (!re_->Match(s, start_offset, s.size(), re2::RE2::UNANCHORED,
+                    groups_.data(), n))
+      return false;
+    out.assign(groups_.begin(), groups_.begin() + n);
+    return true;
+  }
+
+ private:
+  RegexRe2() = default;
+  std::unique_ptr<re2::RE2> re_;
+  // Must be absl::string_view, not std::string_view, to match RE2::Match()
+  // signature. These can be distinct types in different builds.
+  mutable std::vector<absl::string_view> groups_;
+};

+}  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/track.cc
+
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_RE2) &&
+        // !defined(PERFETTO_REGEX_FORCE_STD)
+
+#endif  // SRC_BASE_REGEX_REGEX_RE2_H_
+// gen_amalgamated begin header: src/base/regex/regex_std.h
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -50576,259 +55851,110 @@ TracingPolicy::~TracingPolicy() = default;
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/tracing/track.h"
-
-// gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/fnv_hash.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/string_splitter.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/thread_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/uuid.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_data_source.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_internal.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/counter_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/process_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/thread_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
+#ifndef SRC_BASE_REGEX_REGEX_STD_H_
+#define SRC_BASE_REGEX_REGEX_STD_H_

-namespace perfetto {
+#include <regex>
+#include <string>
+#include <string_view>
+#include <vector>

-// static
-uint64_t Track::process_uuid = 0;
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"

-protos::gen::TrackDescriptor Track::Serialize() const {
-  protos::gen::TrackDescriptor desc;
-  desc.set_uuid(uuid);
-  if (parent_uuid)
-    desc.set_parent_uuid(parent_uuid);
-  return desc;
-}
+namespace perfetto {
+namespace base {

-void Track::Serialize(protos::pbzero::TrackDescriptor* desc) const {
-  auto bytes = Serialize().SerializeAsString();
-  desc->AppendRawProtoBytes(bytes.data(), bytes.size());
-}
+// std::regex backend for Regex. Uses ECMAScript syntax for Perl-style
+// shorthands (\d, \w, \s).
+class RegexStd {
+ public:
+  RegexStd(RegexStd&&) noexcept = default;
+  RegexStd& operator=(RegexStd&&) noexcept = default;
+  ~RegexStd() = default;

-// static
-Track Track::ThreadScoped(const void* ptr, Track parent) {
-  if (parent.uuid == 0)
-    return Track::FromPointer(ptr, ThreadTrack::Current());
-  return Track::FromPointer(ptr, parent);
-}
+  RegexStd(const RegexStd&) = delete;
+  RegexStd& operator=(const RegexStd&) = delete;

-protos::gen::TrackDescriptor ProcessTrack::Serialize() const {
-  auto desc = Track::Serialize();
-  auto pd = desc.mutable_process();
-  pd->set_pid(static_cast<int32_t>(pid));
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  std::string cmdline;
-  if (base::ReadFile("/proc/self/cmdline", &cmdline)) {
-    // Since cmdline is a zero-terminated list of arguments, this ends up
-    // writing just the first element, i.e., the process name, into the process
-    // name field.
-    pd->set_process_name(cmdline.c_str());
-    base::StringSplitter splitter(std::move(cmdline), '\0');
-    while (splitter.Next()) {
-      pd->add_cmdline(
-          std::string(splitter.cur_token(), splitter.cur_token_size()));
+  static StatusOr<RegexStd> Create(std::string_view pattern,
+                                   bool case_insensitive) {
+    RegexStd result;
+    auto flags = std::regex::ECMAScript;
+    if (case_insensitive) {
+      flags |= std::regex::icase;
     }
+    result.re_ = std::regex(std::string(pattern), flags);
+    return std::move(result);
   }
-  // TODO(skyostil): Record command line on Windows, FreeBSD and Mac.
-#endif
-  return desc;
-}
-
-void ProcessTrack::Serialize(protos::pbzero::TrackDescriptor* desc) const {
-  auto bytes = Serialize().SerializeAsString();
-  desc->AppendRawProtoBytes(bytes.data(), bytes.size());
-}
-
-protos::gen::TrackDescriptor ThreadTrack::Serialize() const {
-  auto desc = Track::Serialize();
-  auto td = desc.mutable_thread();
-  td->set_pid(static_cast<int32_t>(pid));
-  td->set_tid(static_cast<int32_t>(tid));
-  if (disallow_merging_with_system_tracks) {
-    desc.set_disallow_merging_with_system_tracks(true);
-  }
-  std::string thread_name;
-  if (base::GetThreadName(thread_name))
-    td->set_thread_name(thread_name);
-  return desc;
-}
-
-// static
-ThreadTrack ThreadTrack::Current() {
-  return ThreadTrack(
-      internal::TracingMuxer::Get()->GetCurrentThreadId(),
-      internal::TrackEventInternal::GetDisallowMergingWithSystemTracks());
-}
-
-// static
-ThreadTrack ThreadTrack::ForThread(base::PlatformThreadId tid_) {
-  return ThreadTrack(
-      tid_, internal::TrackEventInternal::GetDisallowMergingWithSystemTracks());
-}
-
-void ThreadTrack::Serialize(protos::pbzero::TrackDescriptor* desc) const {
-  auto bytes = Serialize().SerializeAsString();
-  desc->AppendRawProtoBytes(bytes.data(), bytes.size());
-}

-protos::gen::TrackDescriptor NamedTrack::Serialize() const {
-  auto desc = NamedTrackBase::Serialize();
-  if (sibling_merge_behavior_ != perfetto::protos::gen::TrackDescriptor::
-                                     SIBLING_MERGE_BEHAVIOR_UNSPECIFIED) {
-    desc.set_sibling_merge_behavior(sibling_merge_behavior_);
-  }
-  if (sibling_merge_key_) {
-    desc.set_sibling_merge_key(sibling_merge_key_);
-  } else if (sibling_merge_key_int_.has_value()) {
-    desc.set_sibling_merge_key_int(*sibling_merge_key_int_);
+  std::string GlobalReplace(std::string_view s,
+                            std::string_view replacement) const {
+    return std::regex_replace(std::string(s), re_, std::string(replacement));
   }
-  return desc;
-}
-
-protos::gen::TrackDescriptor StateTrack::Serialize() const {
-  auto desc = NamedTrackBase::Serialize();
-  // Initialize the state submessage to mark this track as a StateTrack.
-  desc.mutable_state();
-  return desc;
-}

-protos::gen::TrackDescriptor CounterTrack::Serialize() const {
-  auto desc = Track::Serialize();
-  auto* counter = desc.mutable_counter();
-  if (static_name_) {
-    desc.set_static_name(static_name_.value);
-  } else {
-    desc.set_name(dynamic_name_.value);
+  bool FullMatch(std::string_view s) const {
+    return std::regex_match(s.data(), s.data() + s.size(), re_);
   }

-  if (category_)
-    counter->add_categories(category_);
-  if (unit_ != perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED)
-    counter->set_unit(static_cast<protos::gen::CounterDescriptor_Unit>(unit_));
-  {
-    // if |type| is set, we don't want to emit |unit_name|. Trace processor
-    // infers the track name from the type in that case.
-    if (type_ !=
-        perfetto::protos::gen::CounterDescriptor::COUNTER_UNSPECIFIED) {
-      counter->set_type(type_);
-    } else if (unit_name_) {
-      counter->set_unit_name(unit_name_);
-    }
+  bool FullMatchWithGroups(std::string_view s,
+                           std::vector<std::string_view>& out) const {
+    out.clear();
+    if (!std::regex_match(s.data(), s.data() + s.size(), match_data_, re_))
+      return false;
+    FillGroups(s.data(), out);
+    return true;
   }
-  if (unit_multiplier_ != 1)
-    counter->set_unit_multiplier(unit_multiplier_);
-  if (is_incremental_)
-    counter->set_is_incremental(is_incremental_);
-  return desc;
-}
-
-void CounterTrack::Serialize(protos::pbzero::TrackDescriptor* desc) const {
-  auto bytes = Serialize().SerializeAsString();
-  desc->AppendRawProtoBytes(bytes.data(), bytes.size());
-}

-namespace internal {
-namespace {
-
-uint64_t GetProcessStartTime() {
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  std::string stat;
-  if (!base::ReadFile("/proc/self/stat", &stat))
-    return 0u;
-  // The stat file is a single line split into space-separated fields as "pid
-  // (comm) state ppid ...". However because the command name can contain any
-  // characters (including parentheses and spaces), we need to skip past it
-  // before parsing the rest of the fields. To do that, we look for the last
-  // instance of ") " (parentheses followed by space) and parse forward from
-  // that point.
-  size_t comm_end = stat.rfind(") ");
-  if (comm_end == std::string::npos)
-    return 0u;
-  stat = stat.substr(comm_end + strlen(") "));
-  base::StringSplitter splitter(stat, ' ');
-  for (size_t skip = 0; skip < 20; skip++) {
-    if (!splitter.Next())
-      return 0u;
+  bool PartialMatch(std::string_view s) const {
+    return std::regex_search(s.data(), s.data() + s.size(), re_);
   }
-  return base::CStringToUInt64(splitter.cur_token()).value_or(0u);
-#else
-  return 0;
-#endif  // !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-}
-
-}  // namespace
-
-// static
-TrackRegistry* TrackRegistry::instance_;
-
-TrackRegistry::TrackRegistry() = default;
-TrackRegistry::~TrackRegistry() = default;

-// static
-void TrackRegistry::InitializeInstance(std::optional<uint64_t> process_uuid) {
-  if (instance_)
-    return;
-  instance_ = new TrackRegistry();
-  if (process_uuid) {
-    Track::process_uuid = *process_uuid;
-  } else {
-    Track::process_uuid = ComputeProcessUuid();
+  bool PartialMatchWithGroups(std::string_view s,
+                              std::vector<std::string_view>& out) const {
+    out.clear();
+    if (!std::regex_search(s.data(), s.data() + s.size(), match_data_, re_))
+      return false;
+    FillGroups(s.data(), out);
+    return true;
   }
-}

-// static
-uint64_t TrackRegistry::ComputeProcessUuid() {
-  // Use the process start time + pid as the unique identifier for this process.
-  // This ensures that if there are two independent copies of the Perfetto SDK
-  // in the same process (e.g., one in the app and another in a system
-  // framework), events emitted by each will be consistently interleaved on
-  // common thread and process tracks.
-  uint64_t random;
-  if (uint64_t start_time = GetProcessStartTime()) {
-    random = start_time;
-  } else {
-    // Fall back to a randomly generated identifier.
-    static uint64_t random_once = static_cast<uint64_t>(base::Uuidv4().lsb());
-    random = random_once;
+  bool SearchWithOffset(std::string_view s,
+                        size_t start_offset,
+                        std::vector<std::string_view>& out) const {
+    out.clear();
+    const char* begin = s.data() + start_offset;
+    const char* end = s.data() + s.size();
+    if (begin > end)
+      return false;
+    if (!std::regex_search(begin, end, match_data_, re_))
+      return false;
+    FillGroups(begin, out);
+    return true;
   }
-  return base::FnvHasher::Combine(random, Platform::GetCurrentProcessId());
-}
-
-void TrackRegistry::ResetForTesting() {
-  instance_->tracks_.clear();
-}

-void TrackRegistry::UpdateTrack(Track track,
-                                const std::string& serialized_desc) {
-  std::lock_guard<std::mutex> lock(mutex_);
-  tracks_[track.uuid] = {serialized_desc, track.parent_uuid};
-}
+ private:
+  RegexStd() = default;

-void TrackRegistry::EraseTrack(Track track) {
-  std::lock_guard<std::mutex> lock(mutex_);
-  tracks_.erase(track.uuid);
-}
+  void FillGroups(const char* base, std::vector<std::string_view>& out) const {
+    for (size_t i = 0; i < match_data_.size(); ++i) {
+      if (match_data_[i].matched) {
+        out.emplace_back(base + match_data_.position(i),
+                         static_cast<size_t>(match_data_.length(i)));
+      } else {
+        out.emplace_back();
+      }
+    }
+  }

-// static
-void TrackRegistry::WriteTrackDescriptor(
-    const SerializedTrackDescriptor& desc,
-    protozero::MessageHandle<protos::pbzero::TracePacket> packet) {
-  packet->AppendString(
-      perfetto::protos::pbzero::TracePacket::kTrackDescriptorFieldNumber, desc);
-}
+  std::regex re_;
+  mutable std::cmatch match_data_;
+};

-}  // namespace internal
+}  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/track_event_category_registry.cc
+
+#endif  // SRC_BASE_REGEX_REGEX_STD_H_
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -50843,145 +55969,158 @@ void TrackRegistry::WriteTrackDescriptor(
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/tracing/track_event_category_registry.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/regex.h"
+
+#include <optional>
+#include <string>
+#include <string_view>
+#include <vector>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_macros.h"
+
+// Unconditional: each header self-guards, so the amalgamator can see through.
+// gen_amalgamated expanded: #include "src/base/regex/regex_pcre2.h"
+// gen_amalgamated expanded: #include "src/base/regex/regex_re2.h"
+// gen_amalgamated expanded: #include "src/base/regex/regex_std.h"
+
+// Picks the regex backend and exposes it as PERFETTO_REGEX_BACKEND.
+// Preference order:
+//   1. PERFETTO_REGEX_FORCE_STD -> std::regex
+//   2. PCRE2 (when enabled)
+//   3. RE2   (when enabled)
+//   4. std::regex (fallback)
+#if defined(PERFETTO_REGEX_FORCE_STD)
+#define PERFETTO_REGEX_BACKEND ::perfetto::base::RegexStd
+#elif PERFETTO_BUILDFLAG(PERFETTO_PCRE2)
+#define PERFETTO_REGEX_BACKEND ::perfetto::base::RegexPcre2
+#elif PERFETTO_BUILDFLAG(PERFETTO_RE2)
+#define PERFETTO_REGEX_BACKEND ::perfetto::base::RegexRe2
+#else
+#define PERFETTO_REGEX_BACKEND ::perfetto::base::RegexStd
+#endif

 namespace perfetto {
-namespace {
+namespace base {

-static constexpr const char kLegacySlowPrefix[] = "disabled-by-default-";
-static constexpr const char kSlowTag[] = "slow";
+// Pimpl for Regex. Holds the selected backend; Regex reaches into `backend`
+// directly (Regex is a friend below).
+class RegexImpl {
+ public:
+  using Backend = PERFETTO_REGEX_BACKEND;
+  explicit RegexImpl(Backend b) : backend(std::move(b)) {}

-}  // namespace
+ private:
+  friend class Regex;
+  Backend backend;
+};
+#undef PERFETTO_REGEX_BACKEND

-// static
-Category Category::FromDynamicCategory(const char* name) {
-  if (GetNthNameSize(1, name, name)) {
-    Category group(Group(name));
-    PERFETTO_DCHECK(group.name);
-    return group;
-  }
-  Category category(name);
-  PERFETTO_DCHECK(category.name);
-  // Disabled-by-default categories get a "slow" tag.
-  if (!strncmp(name, kLegacySlowPrefix, strlen(kLegacySlowPrefix))) {
-    return category.SetTags(kSlowTag);
-  }
-  return category;
+Regex::Regex(Regex&&) noexcept = default;
+Regex& Regex::operator=(Regex&&) noexcept = default;
+Regex::~Regex() = default;
+
+StatusOr<Regex> Regex::Create(std::string_view pattern,
+                              Regex::CaseSensitivity cs) {
+  bool insensitive = (cs == CaseSensitivity::kInsensitive);
+  ASSIGN_OR_RETURN(auto backend,
+                   RegexImpl::Backend::Create(pattern, insensitive));
+  Regex regex;
+  regex.pattern_ = std::string(pattern);
+  regex.cs_ = cs;
+  regex.impl_ = std::make_unique<RegexImpl>(std::move(backend));
+  return std::move(regex);
 }

-Category Category::FromDynamicCategory(
-    const DynamicCategory& dynamic_category) {
-  return FromDynamicCategory(dynamic_category.name.c_str());
+Regex Regex::CreateOrCheck(std::string_view pattern,
+                           Regex::CaseSensitivity cs) {
+  auto re_or = Create(pattern, cs);
+  PERFETTO_CHECK(re_or.ok());
+  return std::move(*re_or);
 }

-namespace internal {
+std::string Regex::GlobalReplace(std::string_view s,
+                                 std::string_view replacement) const {
+  PERFETTO_DCHECK(impl_);
+  return impl_->backend.GlobalReplace(s, replacement);
+}

-perfetto::DynamicCategory NullCategory(const perfetto::DynamicCategory&) {
-  return perfetto::DynamicCategory{};
+bool Regex::FullMatch(std::string_view s) const {
+  PERFETTO_DCHECK(impl_);
+  return impl_->backend.FullMatch(s);
 }

-void TrackEventCategoryRegistry::EnableCategoryForInstance(
-    size_t category_index,
-    uint32_t instance_index) const {
-  PERFETTO_DCHECK(instance_index < kMaxDataSourceInstances);
-  PERFETTO_DCHECK(category_index < category_count_);
-  // Matches the acquire_load in DataSource::Trace().
-  state_storage_[category_index].fetch_or(
-      static_cast<uint8_t>(1u << instance_index), std::memory_order_release);
+bool Regex::FullMatchWithGroups(std::string_view s,
+                                std::vector<std::string_view>& out) const {
+  PERFETTO_DCHECK(impl_);
+  return impl_->backend.FullMatchWithGroups(s, out);
 }

-void TrackEventCategoryRegistry::DisableCategoryForInstance(
-    size_t category_index,
-    uint32_t instance_index) const {
-  PERFETTO_DCHECK(instance_index < kMaxDataSourceInstances);
-  PERFETTO_DCHECK(category_index < category_count_);
-  // Matches the acquire_load in DataSource::Trace().
-  state_storage_[category_index].fetch_and(
-      static_cast<uint8_t>(~(1u << instance_index)), std::memory_order_release);
+bool Regex::PartialMatch(std::string_view s) const {
+  PERFETTO_DCHECK(impl_);
+  return impl_->backend.PartialMatch(s);
 }

-}  // namespace internal
-}  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/track_event_legacy.cc
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+bool Regex::PartialMatchWithGroups(std::string_view s,
+                                   std::vector<std::string_view>& out) const {
+  PERFETTO_DCHECK(impl_);
+  return impl_->backend.PartialMatchWithGroups(s, out);
+}

-// gen_amalgamated expanded: #include "perfetto/tracing/track_event_legacy.h"
+// --- PartialMatchIterator ---

-// gen_amalgamated expanded: #include "perfetto/ext/base/fnv_hash.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/track.h"
+Regex::PartialMatchIterator::PartialMatchIterator(const Regex* re,
+                                                  std::string_view input)
+    : re_(re), input_(input) {}

-namespace perfetto {
-namespace legacy {
+std::optional<std::string_view> Regex::PartialMatchIterator::Next() {
+  return NextWithGroups(groups_);
+}

-template <>
-ThreadTrack ConvertThreadId(const PerfettoLegacyCurrentThreadId&) {
-  // Because of the short-circuit in PERFETTO_INTERNAL_LEGACY_EVENT, we should
-  // never get here.
-  PERFETTO_DCHECK(false);
-  return ThreadTrack::Current();
+std::optional<std::string_view> Regex::PartialMatchIterator::NextWithGroups(
+    std::vector<std::string_view>& groups) {
+  if (offset_ > input_.size())
+    return std::nullopt;
+  PERFETTO_DCHECK(re_->impl_);
+  if (!re_->impl_->backend.SearchWithOffset(input_, offset_, groups))
+    return std::nullopt;
+  if (groups.empty())
+    return std::nullopt;
+  std::string_view match = groups[0];
+  size_t match_end =
+      static_cast<size_t>(match.data() - input_.data()) + match.size();
+  // Advance past the match. If match is empty, advance by one to avoid
+  // infinite loops.
+  offset_ = match.size() == 0 ? match_end + 1 : match_end;
+  return match;
 }

-}  // namespace legacy
+Regex::PartialMatchIterator Regex::PartialMatchAll(std::string_view s) const {
+  return PartialMatchIterator(this, s);
+}

-namespace internal {
+// --- CopyableRegex ---

-void LegacyTraceId::Write(protos::pbzero::TrackEvent::LegacyEvent* event,
-                          uint32_t event_flags) const {
-  // Legacy flow events always use bind_id.
-  if (event_flags &
-      (legacy::kTraceEventFlagFlowOut | legacy::kTraceEventFlagFlowIn)) {
-    // Flow bind_ids don't have scopes, so we need to mangle in-process ones to
-    // avoid collisions.
-    if (id_flags_ & legacy::kTraceEventFlagHasLocalId) {
-      event->set_bind_id(raw_id_ ^ ProcessTrack::Current().uuid);
-    } else {
-      event->set_bind_id(raw_id_);
-    }
-    return;
-  }
+CopyableRegex::CopyableRegex(Regex regex) : regex_(std::move(regex)) {}

-  uint32_t scope_flags = id_flags_ & (legacy::kTraceEventFlagHasId |
-                                      legacy::kTraceEventFlagHasLocalId |
-                                      legacy::kTraceEventFlagHasGlobalId);
-  uint64_t id = raw_id_;
-  if (scope_ && scope_flags != legacy::kTraceEventFlagHasGlobalId) {
-    id = base::FnvHasher::Combine(id, scope_);
-  }
+CopyableRegex::CopyableRegex(const CopyableRegex& other)
+    : regex_(Regex::CreateOrCheck(other.regex_.pattern(),
+                                  other.regex_.case_sensitivity())) {}

-  switch (scope_flags) {
-    case legacy::kTraceEventFlagHasId:
-      event->set_unscoped_id(id);
-      break;
-    case legacy::kTraceEventFlagHasLocalId:
-      event->set_local_id(id);
-      break;
-    case legacy::kTraceEventFlagHasGlobalId:
-      event->set_global_id(id);
-      break;
+CopyableRegex& CopyableRegex::operator=(const CopyableRegex& other) {
+  if (this != &other) {
+    this->~CopyableRegex();
+    new (this) CopyableRegex(other);
   }
-  if (scope_)
-    event->set_id_scope(scope_);
+  return *this;
 }

-}  // namespace internal
+}  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/track_event_state_tracker.cc
+// gen_amalgamated begin source: src/base/clock_snapshots.cc
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -50996,282 +56135,131 @@ void LegacyTraceId::Write(protos::pbzero::TrackEvent::LegacyEvent* event,
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/tracing/track_event_state_tracker.h"
-
-// gen_amalgamated expanded: #include "perfetto/ext/base/fnv_hash.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/internal/track_event_internal.h"
-
-// gen_amalgamated expanded: #include "protos/perfetto/common/interceptor_descriptor.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/trace_packet.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
-
-namespace perfetto {
-
-using internal::TrackEventIncrementalState;
-
-TrackEventStateTracker::~TrackEventStateTracker() = default;
-TrackEventStateTracker::Delegate::~Delegate() = default;
-
-// static
-void TrackEventStateTracker::ProcessTracePacket(
-    Delegate& delegate,
-    SequenceState& sequence_state,
-    const protos::pbzero::TracePacket_Decoder& packet) {
-  UpdateIncrementalState(delegate, sequence_state, packet);
-
-  if (!packet.has_track_event())
-    return;
-  perfetto::protos::pbzero::TrackEvent::Decoder track_event(
-      packet.track_event());
-
-  auto clock_id = packet.timestamp_clock_id();
-  if (!packet.has_timestamp_clock_id())
-    clock_id = sequence_state.default_clock_id;
-  uint64_t timestamp = packet.timestamp();
-  // TODO(mohitms): Incorporate unit multiplier as well.
-  if (clock_id == internal::TrackEventIncrementalState::kClockIdIncremental) {
-    timestamp += sequence_state.most_recent_absolute_time_ns;
-    sequence_state.most_recent_absolute_time_ns = timestamp;
-  }
-
-  Track* track = &sequence_state.track;
-  if (track_event.has_track_uuid()) {
-    auto* session_state = delegate.GetSessionState();
-    if (!session_state)
-      return;  // Tracing must have ended.
-    track = &session_state->tracks[track_event.track_uuid()];
-  }
-
-  // We only log the first category of each event.
-  protozero::ConstChars category{};
-  uint64_t category_iid = 0;
-  if (auto iid_it = track_event.category_iids()) {
-    category_iid = *iid_it;
-    category.data = sequence_state.event_categories[category_iid].data();
-    category.size = sequence_state.event_categories[category_iid].size();
-  } else if (auto cat_it = track_event.categories()) {
-    category.data = reinterpret_cast<const char*>(cat_it->data());
-    category.size = cat_it->size();
-  }
-
-  protozero::ConstChars name{};
-  uint64_t name_iid = track_event.name_iid();
-  uint64_t name_hash = 0;
-  uint64_t duration = 0;
-  if (name_iid) {
-    name.data = sequence_state.event_names[name_iid].data();
-    name.size = sequence_state.event_names[name_iid].size();
-  } else if (track_event.has_name()) {
-    name.data = track_event.name().data;
-    name.size = track_event.name().size;
-  }
-
-  if (name.data) {
-    base::FnvHasher hash;
-    hash.Update(name.data, name.size);
-    name_hash = hash.digest();
-  }
-
-  size_t depth = track->stack.size();
-  switch (track_event.type()) {
-    case protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN: {
-      StackFrame frame;
-      frame.timestamp = timestamp;
-      frame.name_hash = name_hash;
-      if (track_event.has_track_uuid()) {
-        frame.name = name.ToStdString();
-        frame.category = category.ToStdString();
-      } else {
-        frame.name_iid = name_iid;
-        frame.category_iid = category_iid;
-      }
-      track->stack.push_back(std::move(frame));
-      break;
-    }
-    case protos::pbzero::TrackEvent::TYPE_SLICE_END:
-      if (!track->stack.empty()) {
-        const auto& prev_frame = track->stack.back();
-        if (prev_frame.name_iid) {
-          name.data = sequence_state.event_names[prev_frame.name_iid].data();
-          name.size = sequence_state.event_names[prev_frame.name_iid].size();
-        } else {
-          name.data = prev_frame.name.data();
-          name.size = prev_frame.name.size();
-        }
-        name_hash = prev_frame.name_hash;
-        if (prev_frame.category_iid) {
-          category.data =
-              sequence_state.event_categories[prev_frame.category_iid].data();
-          category.size =
-              sequence_state.event_categories[prev_frame.category_iid].size();
-        } else {
-          category.data = prev_frame.category.data();
-          category.size = prev_frame.category.size();
-        }
-        duration = timestamp - prev_frame.timestamp;
-        depth--;
-      }
-      break;
-    case protos::pbzero::TrackEvent::TYPE_INSTANT:
-      break;
-    case protos::pbzero::TrackEvent::TYPE_COUNTER:
-    case protos::pbzero::TrackEvent::TYPE_UNSPECIFIED:
-      // TODO(skyostil): Support counters.
-      return;
-  }
-
-  ParsedTrackEvent parsed_event{track_event};
-  parsed_event.timestamp_ns = timestamp;
-  parsed_event.duration_ns = duration;
-  parsed_event.stack_depth = depth;
-  parsed_event.category = category;
-  parsed_event.name = name;
-  parsed_event.name_hash = name_hash;
-  delegate.OnTrackEvent(*track, parsed_event);
-
-  if (track_event.type() == protos::pbzero::TrackEvent::TYPE_SLICE_END &&
-      !track->stack.empty()) {
-    track->stack.pop_back();
-  }
-}
+// gen_amalgamated expanded: #include "perfetto/ext/base/clock_snapshots.h"

-// static
-void TrackEventStateTracker::UpdateIncrementalState(
-    Delegate& delegate,
-    SequenceState& sequence_state,
-    const protos::pbzero::TracePacket_Decoder& packet) {
-#if PERFETTO_DCHECK_IS_ON()
-  if (!sequence_state.sequence_id) {
-    sequence_state.sequence_id = packet.trusted_packet_sequence_id();
-  } else {
-    PERFETTO_DCHECK(sequence_state.sequence_id ==
-                    packet.trusted_packet_sequence_id());
-  }
-#endif
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/builtin_clock.pbzero.h"

-  perfetto::protos::pbzero::ClockSnapshot::Decoder snapshot(
-      packet.clock_snapshot());
-  for (auto it = snapshot.clocks(); it; ++it) {
-    perfetto::protos::pbzero::ClockSnapshot::Clock::Decoder clock(*it);
-    // TODO(mohitms) : Handle the incremental clock other than default one.
-    if (clock.is_incremental() &&
-        clock.clock_id() ==
-            internal::TrackEventIncrementalState::kClockIdIncremental) {
-      sequence_state.most_recent_absolute_time_ns =
-          clock.timestamp() * clock.unit_multiplier_ns();
-      break;
-    }
-  }
+namespace perfetto::base {

-  if (packet.sequence_flags() &
-      perfetto::protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED) {
-    // Convert any existing event names and categories on the stack to
-    // non-interned strings so we can look up their names even after the
-    // incremental state is gone.
-    for (auto& frame : sequence_state.track.stack) {
-      if (frame.name_iid) {
-        frame.name = sequence_state.event_names[frame.name_iid];
-        frame.name_iid = 0u;
-      }
-      if (frame.category_iid) {
-        frame.category = sequence_state.event_categories[frame.category_iid];
-        frame.category_iid = 0u;
-      }
-    }
-    sequence_state.event_names.clear();
-    sequence_state.event_categories.clear();
-    sequence_state.debug_annotation_names.clear();
-    sequence_state.track.uuid = 0u;
-    sequence_state.track.index = 0u;
-  }
-  if (packet.has_interned_data()) {
-    perfetto::protos::pbzero::InternedData::Decoder interned_data(
-        packet.interned_data());
-    for (auto it = interned_data.event_names(); it; it++) {
-      perfetto::protos::pbzero::EventName::Decoder entry(*it);
-      sequence_state.event_names[entry.iid()] = entry.name().ToStdString();
-    }
-    for (auto it = interned_data.event_categories(); it; it++) {
-      perfetto::protos::pbzero::EventCategory::Decoder entry(*it);
-      sequence_state.event_categories[entry.iid()] = entry.name().ToStdString();
-    }
-    for (auto it = interned_data.debug_annotation_names(); it; it++) {
-      perfetto::protos::pbzero::DebugAnnotationName::Decoder entry(*it);
-      sequence_state.debug_annotation_names[entry.iid()] =
-          entry.name().ToStdString();
-    }
+ClockSnapshotVector CaptureClockSnapshots() {
+  ClockSnapshotVector snapshot_data;
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) &&   \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) &&     \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) && \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL) &&    \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_QNX)
+  struct {
+    clockid_t id;
+    protos::pbzero::BuiltinClock type;
+    struct timespec ts;
+  } clocks[] = {
+      {CLOCK_BOOTTIME, protos::pbzero::BUILTIN_CLOCK_BOOTTIME, {0, 0}},
+      {CLOCK_REALTIME_COARSE,
+       protos::pbzero::BUILTIN_CLOCK_REALTIME_COARSE,
+       {0, 0}},
+      {CLOCK_MONOTONIC_COARSE,
+       protos::pbzero::BUILTIN_CLOCK_MONOTONIC_COARSE,
+       {0, 0}},
+      {CLOCK_REALTIME, protos::pbzero::BUILTIN_CLOCK_REALTIME, {0, 0}},
+      {CLOCK_MONOTONIC, protos::pbzero::BUILTIN_CLOCK_MONOTONIC, {0, 0}},
+      {CLOCK_MONOTONIC_RAW,
+       protos::pbzero::BUILTIN_CLOCK_MONOTONIC_RAW,
+       {0, 0}},
+  };
+  // First snapshot all the clocks as atomically as we can.
+  for (auto& clock : clocks) {
+    if (clock_gettime(clock.id, &clock.ts) == -1)
+      PERFETTO_DLOG("clock_gettime failed for clock %d", clock.id);
   }
-  if (packet.has_trace_packet_defaults()) {
-    perfetto::protos::pbzero::TracePacketDefaults::Decoder defaults(
-        packet.trace_packet_defaults());
-    if (defaults.has_track_event_defaults()) {
-      perfetto::protos::pbzero::TrackEventDefaults::Decoder
-          track_event_defaults(defaults.track_event_defaults());
-      sequence_state.track.uuid = track_event_defaults.track_uuid();
-      if (defaults.has_timestamp_clock_id())
-        sequence_state.default_clock_id = defaults.timestamp_clock_id();
-    }
+  for (auto& clock : clocks) {
+    snapshot_data.push_back(ClockReading(
+        static_cast<uint32_t>(clock.type),
+        static_cast<uint64_t>(base::FromPosixTimespec(clock.ts).count())));
   }
-  if (packet.has_track_descriptor()) {
-    perfetto::protos::pbzero::TrackDescriptor::Decoder track_descriptor(
-        packet.track_descriptor());
-    auto* session_state = delegate.GetSessionState();
-    auto& track = session_state->tracks[track_descriptor.uuid()];
-    if (!track.index)
-      track.index = static_cast<uint32_t>(session_state->tracks.size() + 1);
-    track.uuid = track_descriptor.uuid();
+#else  // OS_APPLE || OS_WIN && OS_NACL
+  auto wall_time_ns = static_cast<uint64_t>(base::GetWallTimeNs().count());
+  // The default trace clock is boot time, so we always need to emit a path to
+  // it. However since we don't actually have a boot time source on these
+  // platforms, pretend that wall time equals boot time.
+  snapshot_data.push_back(
+      ClockReading(protos::pbzero::BUILTIN_CLOCK_BOOTTIME, wall_time_ns));
+  snapshot_data.push_back(
+      ClockReading(protos::pbzero::BUILTIN_CLOCK_MONOTONIC, wall_time_ns));
+#endif

-    if (track_descriptor.has_name()) {
-      track.name = track_descriptor.name().ToStdString();
-    } else if (track_descriptor.has_static_name()) {
-      track.name = track_descriptor.static_name().ToStdString();
-    }
-    track.pid = 0;
-    track.tid = 0;
-    if (track_descriptor.has_process()) {
-      perfetto::protos::pbzero::ProcessDescriptor::Decoder process(
-          track_descriptor.process());
-      track.pid = process.pid();
-      if (track.name.empty())
-        track.name = process.process_name().ToStdString();
-    } else if (track_descriptor.has_thread()) {
-      perfetto::protos::pbzero::ThreadDescriptor::Decoder thread(
-          track_descriptor.thread());
-      track.pid = thread.pid();
-      track.tid = thread.tid();
-      if (track.name.empty())
-        track.name = thread.thread_name().ToStdString();
-    }
-    delegate.OnTrackUpdated(track);
+#if PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_X86_64)
+  // X86-specific but OS-independent TSC clocksource
+  snapshot_data.push_back(
+      ClockReading(protos::pbzero::BUILTIN_CLOCK_TSC, base::Rdtsc()));
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_X86_64)

-    // Mirror properties to the default track of the sequence. Note that
-    // this does not catch updates to the default track written through other
-    // sequences.
-    if (track.uuid == sequence_state.track.uuid) {
-      sequence_state.track.index = track.index;
-      sequence_state.track.name = track.name;
-      sequence_state.track.pid = track.pid;
-      sequence_state.track.tid = track.tid;
-      sequence_state.track.user_data = track.user_data;
-    }
-  }
+  return snapshot_data;
 }

-TrackEventStateTracker::ParsedTrackEvent::ParsedTrackEvent(
-    const perfetto::protos::pbzero::TrackEvent::Decoder& track_event_)
-    : track_event(track_event_) {}
+}  // namespace perfetto::base
+// gen_amalgamated begin source: src/base/version.cc
+// gen_amalgamated begin header: include/perfetto/ext/base/version.h
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_VERSION_H_
+#define INCLUDE_PERFETTO_EXT_BASE_VERSION_H_
+
+namespace perfetto {
+namespace base {
+
+// The returned pointer is a static string and safe to pass around.
+// Returns a human readable string currently of the approximate form:
+// Perfetto v42.1-deadbeef0 (deadbeef03c641e4b4ea9cf38e9b5696670175a9)
+// However you should not depend on the format of this string.
+// It maybe not be possible to determine the version. In which case the
+// string will be of the approximate form:
+// Perfetto v0.0 (unknown)
+const char* GetVersionString();
+
+// The returned pointer is a static string and safe to pass around.
+// Returns the short code used to identity the version:
+// v42.1-deadbeef0
+// It maybe not be possible to determine the version. In which case
+// this returns nullptr.
+// This can be compared with equality to other
+// version codes to detect matched builds (for example to see if
+// trace_processor_shell and the UI were built at the same revision)
+// but you should not attempt to parse it as the format may change
+// without warning.
+const char* GetVersionCode();

+}  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/virtual_destructors.cc
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_VERSION_H_
+// gen_amalgamated begin header: gen/perfetto_version.gen.h
+// Generated by write_version_header.py
+
+#ifndef GEN_PERFETTO_VERSION_GEN_H_
+#define GEN_PERFETTO_VERSION_GEN_H_
+
+#define PERFETTO_VERSION_STRING() "v59.1-1e9c61e98"
+#define PERFETTO_VERSION_SCM_REVISION() "1e9c61e9844affd7dccd3d42d9ea9f3a3d10cf6d"
+
+#endif  // GEN_PERFETTO_VERSION_GEN_H_
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -51286,47 +56274,48 @@ TrackEventStateTracker::ParsedTrackEvent::ParsedTrackEvent(
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/tracing/internal/tracing_tls.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/tracing.h"
-// gen_amalgamated expanded: #include "perfetto/tracing/tracing_backend.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/version.h"

-// This translation unit contains the definitions for the destructor of pure
-// virtual interfaces for the src/public:public target. The alternative would be
-// introducing a one-liner .cc file for each pure virtual interface, which is
-// overkill. This is for compliance with -Wweak-vtables.
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#include <stdio.h>
+
+#if PERFETTO_BUILDFLAG(PERFETTO_VERSION_GEN)
+// gen_amalgamated expanded: #include "perfetto_version.gen.h"
+#else
+#define PERFETTO_VERSION_STRING() nullptr
+#define PERFETTO_VERSION_SCM_REVISION() "unknown"
+#endif

 namespace perfetto {
-namespace internal {
+namespace base {

-TracingTLS::~TracingTLS() {
-  // Avoid entering trace points while the thread is being torn down.
-  // This is the problem: when a thread exits, the at-thread-exit destroys the
-  // TracingTLS. As part of that the various TraceWriter for the active data
-  // sources are destroyd. A TraceWriter dtor will issue a PostTask on the IPC
-  // thread to issue a final flush and unregister its ID with the service.
-  // The PostTask, in chromium, might have a trace event that will try to
-  // re-enter the tracing system.
-  // We fix this by resetting the TLS key to the TracingTLS object that is
-  // being destroyed in the platform impl (platform_posix.cc,
-  // platform_windows.cc, chromium's platform.cc). We carefully rely on the fact
-  // that all the tracing path that will be invoked during thread exit will
-  // early out if |is_in_trace_point| == true and will not depend on the other
-  // TLS state that has been destroyed.
-  is_in_trace_point = true;
+const char* GetVersionCode() {
+  return PERFETTO_VERSION_STRING();
 }

-}  // namespace internal
-
-TracingProducerBackend::~TracingProducerBackend() = default;
-TracingConsumerBackend::~TracingConsumerBackend() = default;
-TracingBackend::~TracingBackend() = default;
+const char* GetVersionString() {
+  static const char* version_str = [] {
+    static constexpr size_t kMaxLen = 256;
+    const char* version_code = PERFETTO_VERSION_STRING();
+    if (version_code == nullptr) {
+      version_code = "v0.0";
+    }
+    char* version = new char[kMaxLen + 1];
+    snprintf(version, kMaxLen, "Perfetto %s (%s)", version_code,
+             PERFETTO_VERSION_SCM_REVISION());
+    return version;
+  }();
+  return version_str;
+}

+}  // namespace base
 }  // namespace perfetto
-// gen_amalgamated begin source: src/android_stats/statsd_logging_helper.cc
-// gen_amalgamated begin header: src/android_stats/statsd_logging_helper.h
-// gen_amalgamated begin header: src/android_stats/perfetto_atoms.h
+// gen_amalgamated begin source: src/protovm/allocator.cc
+// gen_amalgamated begin header: src/protovm/allocator.h
+// gen_amalgamated begin header: src/protovm/error_handling.h
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -51341,136 +56330,341 @@ TracingBackend::~TracingBackend() = default;
  * limitations under the License.
  */

-#ifndef SRC_ANDROID_STATS_PERFETTO_ATOMS_H_
-#define SRC_ANDROID_STATS_PERFETTO_ATOMS_H_
+#ifndef SRC_PROTOVM_ERROR_HANDLING_H_
+#define SRC_PROTOVM_ERROR_HANDLING_H_
+
+#include <cstdarg>
+#include <cstdint>
+#include <memory>
+#include <new>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+
+#if defined(__GNUC__) || defined(__clang__)
+#if defined(__clang__)
+#pragma clang diagnostic push
+// Fix 'error: #pragma system_header ignored in main file' for amalgamated
+// build.
+#pragma clang diagnostic ignored "-Wpragma-system-header-outside-header"
+#endif
+
+// Ignore GCC warning about a missing argument for a variadic macro parameter.
+#pragma GCC system_header
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+#endif

 namespace perfetto {
+namespace protovm {

-// This must match the values of the PerfettoUploadEvent enum in:
-// frameworks/proto_logging/stats/atoms.proto
-enum class PerfettoStatsdAtom {
-  kUndefined = 0,
+using Stacktrace = std::vector<std::string>;

-  // Checkpoints inside perfetto_cmd before tracing is finished.
-  kTraceBegin = 1,
-  kBackgroundTraceBegin = 2,
-  kCmdCloneTraceBegin = 55,
-  kCmdCloneTriggerTraceBegin = 56,
-  kOnConnect = 3,
-  kCmdOnSessionClone = 58,
-  kCmdOnTriggerSessionClone = 59,
+void LogStacktraceMessage(Stacktrace& stacktrace,
+                          const char* file_name,
+                          int file_line,
+                          const char* fmt,
+                          ...) PERFETTO_PRINTF_FORMAT(4, 5);

-  // Guardrails inside perfetto_cmd before tracing is finished.
-  kOnTimeout = 16,
+// overload for calls without message args
+void LogStacktraceMessage(Stacktrace& stacktrace,
+                          const char* file_name,
+                          int file_line);

-  // Checkpoints inside traced.
-  kTracedEnableTracing = 37,
-  kTracedStartTracing = 38,
-  kTracedDisableTracing = 39,
-  kTracedNotifyTracingDisabled = 40,
+#define PROTOVM_ABORT(fmt, ...)                                       \
+  do {                                                                \
+    auto status_abort = StatusOr<void>::Abort();                      \
+    LogStacktraceMessage(status_abort.stacktrace(),                   \
+                         ::perfetto::base::LoggingBasename(__FILE__), \
+                         __LINE__, fmt, ##__VA_ARGS__);               \
+    return status_abort;                                              \
+  } while (0)

-  // Trigger checkpoints inside traced.
-  // These atoms are special because, along with the UUID,
-  // they log the trigger name.
-  kTracedTriggerStartTracing = 41,
-  kTracedTriggerStopTracing = 42,
-  kTracedTriggerCloneSnapshot = 53,
+#define PROTOVM_RETURN(s, ...)                                          \
+  do {                                                                  \
+    if (s.IsAbort()) {                                                  \
+      LogStacktraceMessage(s.stacktrace(),                              \
+                           ::perfetto::base::LoggingBasename(__FILE__), \
+                           __LINE__, ##__VA_ARGS__);                    \
+    }                                                                   \
+    return s;                                                           \
+  } while (0)

-  // Guardrails inside traced.
-  kTracedEnableTracingExistingTraceSession = 18,
-  kTracedEnableTracingTooLongTrace = 19,
-  kTracedEnableTracingInvalidTriggerTimeout = 20,
-  kTracedEnableTracingDurationWithTrigger = 21,
-  kTracedEnableTracingStopTracingWriteIntoFile = 22,
-  kTracedEnableTracingDuplicateTriggerName = 23,
-  kTracedEnableTracingInvalidDeferredStart = 24,
-  kTracedEnableTracingInvalidBufferSize = 25,
-  kTracedEnableTracingBufferSizeTooLarge = 26,
-  kTracedEnableTracingTooManyBuffers = 27,
-  kTracedEnableTracingDuplicateSessionName = 28,
-  kTracedEnableTracingSessionNameTooRecent = 29,
-  kTracedEnableTracingTooManySessionsForUid = 30,
-  kTracedEnableTracingTooManyConcurrentSessions = 31,
-  kTracedEnableTracingInvalidFdOutputFile = 32,
-  kTracedEnableTracingFailedToCreateFile = 33,
-  kTracedEnableTracingOom = 34,
-  kTracedEnableTracingUnknown = 35,
-  kTracedStartTracingInvalidSessionState = 36,
-  kTracedEnableTracingInvalidFilter = 47,
-  kTracedEnableTracingOobTargetBuffer = 48,
-  kTracedEnableTracingInvalidTriggerMode = 52,
-  kTracedEnableTracingInvalidBrFilename = 54,
-  kTracedEnableTracingFailedSessionSemaphoreCheck = 57,
-  kTracedEnablePriorityBoostInvalidConfig = 61,
-  kTracedEnablePriorityBoostOtherError = 62,
-  kTracedEnableTracingExclusiveSessionRejected = 63,
-  kTracedEnableTracingExclusiveSessionNotAllowed = 64,
-  kTracedEnableTracingDuplicateBufferName = 65,
+#define PROTOVM_RETURN_IF_NOT_OK(s, ...)                                \
+  do {                                                                  \
+    if (s.IsAbort()) {                                                  \
+      LogStacktraceMessage(s.stacktrace(),                              \
+                           ::perfetto::base::LoggingBasename(__FILE__), \
+                           __LINE__, ##__VA_ARGS__);                    \
+      return s;                                                         \
+    }                                                                   \
+    if (!s.IsOk()) {                                                    \
+      return s;                                                         \
+    }                                                                   \
+  } while (0)

-  // Checkpoints inside perfetto_cmd after tracing has finished.
-  kOnTracingDisabled = 4,
-  kFinalizeTraceAndExit = 11,
-  kCmdFwReportBegin = 49,
-  // Will be removed once incidentd is no longer used.
-  kUploadIncidentBegin = 8,
-  kNotUploadingEmptyTrace = 17,
+#define PROTOVM_INTERNAL_CONCAT_IMPL(x, y) x##y
+#define PROTOVM_INTERNAL_MACRO_CONCAT(x, y) PROTOVM_INTERNAL_CONCAT_IMPL(x, y)

-  // Guardrails inside perfetto_cmd after tracing has finished.
-  kCmdFwReportEmptyTrace = 50,
-  // Will be removed once incidentd is no longer used.
-  kUploadIncidentFailure = 10,
+// Evaluates |rhs| which should return a StatusOr<T> and assigns this
+// to |lhs|. If the status is an error status, returns the status from the
+// current function.
+#define PROTOVM_ASSIGN_OR_RETURN(lhs, rhs)                       \
+  PROTOVM_INTERNAL_MACRO_CONCAT(auto status_or, __LINE__) = rhs; \
+  PROTOVM_RETURN_IF_NOT_OK(                                      \
+      PROTOVM_INTERNAL_MACRO_CONCAT(status_or, __LINE__));       \
+  lhs = std::move(PROTOVM_INTERNAL_MACRO_CONCAT(status_or, __LINE__).value())

-  // "Successful" terminal states inside perfetto_cmd.
-  kCmdFwReportHandoff = 51,
+enum class Status : uint8_t {
+  kOk,
+  kError,
+
+  // It causes an abort of the whole VM's program. Typically indicates a
+  // fundamental issue with the program and requires developer's intervention.
+  kAbort,
+};
+
+namespace internal {
+
+template <class T, class Arg, class... Args>
+static constexpr bool is_copy_or_move_ctor_arg_v =
+    sizeof...(Args) == 0 && std::is_same_v<T, std::decay_t<Arg>>;
+
+template <class T>
+inline constexpr std::size_t sizeof_v = sizeof(T);
+
+template <>
+inline constexpr std::size_t sizeof_v<void> = 0;
+
+template <class T>
+inline constexpr std::size_t alignof_v = alignof(T);
+
+template <>
+inline constexpr std::size_t alignof_v<void> = 0;
+
+}  // namespace internal
+
+template <class T>
+class StatusOr {
+ public:
+  // Factory methods to create empty instances (without value)
+  static StatusOr Ok() { return StatusOr{Status::kOk}; }
+  static StatusOr Error() { return StatusOr{Status::kError}; }
+  static StatusOr Abort() { return StatusOr{Status::kAbort}; }
+
+  StatusOr(const StatusOr&) = delete;
+
+  StatusOr(StatusOr&& other) : status_{other.status_} {
+    if (IsOk()) {
+      if constexpr (!std::is_same_v<T, void>) {
+        new (storage_) T(std::move(other.value()));
+      }
+    } else if (IsAbort()) {
+      new (storage_) std::unique_ptr<Stacktrace>(
+          std::move(other.GetStacktraceUniquePtr()));
+    }
+  }
+
+  // Intentionally implicit to allow idiomatic usage (return plain value to be
+  // implicitly converted to StatusOr)
+  template <
+      class... Args,
+      // sfinae to avoid clashing with copy/move constructors
+      std::enable_if_t<!internal::is_copy_or_move_ctor_arg_v<StatusOr, Args...>,
+                       int> = 0>
+  StatusOr(Args&&... args) : status_{Status::kOk} {
+    if constexpr (!std::is_same_v<T, void>) {
+      new (storage_) T(std::forward<Args>(args)...);
+    }
+  }
+
+  // Intentionally implicit to allow idiomatic usage (e.g. return StatusOr<A> to
+  // be implicitely converted to StatusOr<B>).
+  // When status is ok, the conversion is only allowed if B = void, otherwise it
+  // would require a conversion from A to non-void B.
+  template <class U, std::enable_if_t<!std::is_same_v<U, T>, int> = 0>
+  StatusOr(StatusOr<U>&& other) : status_{other.status_} {
+    if (IsOk()) {
+      PERFETTO_DCHECK((std::is_same_v<T, void>));
+    } else if (IsAbort()) {
+      new (storage_) std::unique_ptr<Stacktrace>(
+          std::move(other.GetStacktraceUniquePtr()));
+    }
+  }
+
+  ~StatusOr() {
+    if (IsOk()) {
+      if constexpr (!std::is_same_v<T, void>) {
+        value().~T();
+      }
+    }
+    if (IsAbort()) {
+      GetStacktraceUniquePtr().~unique_ptr();
+    }
+  }
+
+  bool IsOk() const { return status_ == Status::kOk; }
+  bool IsError() const { return status_ == Status::kError; }
+  bool IsAbort() const { return status_ == Status::kAbort; }
+
+  template <class U = T>
+  std::enable_if_t<!std::is_same_v<U, void>, U&> value() {
+    PERFETTO_DCHECK(IsOk());
+    return *std::launder(reinterpret_cast<T*>(storage_));
+  }
+
+  template <class U = T>
+  std::enable_if_t<!std::is_same_v<U, void>, const U&> value() const {
+    return const_cast<StatusOr*>(this)->value();
+  }
+
+  template <class U = T>
+  std::enable_if_t<!std::is_same_v<U, void>, U&> operator*() {
+    return value();
+  }
+
+  template <class U = T>
+  std::enable_if_t<!std::is_same_v<U, void>, const U&> operator*() const {
+    return value();
+  }
+
+  template <class U = T>
+  std::enable_if_t<!std::is_same_v<U, void>, U*> operator->() {
+    return &value();
+  }
+
+  template <class U = T>
+  std::enable_if_t<!std::is_same_v<U, void>, const U*> operator->() const {
+    return &value();
+  }
+
+  Stacktrace& stacktrace() {
+    PERFETTO_DCHECK(IsAbort());
+    return **std::launder(
+        reinterpret_cast<std::unique_ptr<Stacktrace>*>(storage_));
+  }
+
+ private:
+  template <class U>
+  friend class StatusOr;
+
+  // Intentionally private to encourage the use of factory methods, instead of
+  // plain status values. E.g. StatusOr::Abort() instead of Status::kAbort.
+  // Plain status value don't include stacktrace information in case of an
+  // abort.
+  explicit StatusOr(Status status) : status_{status} {
+    PERFETTO_DCHECK((std::is_same_v<T, void>) || !IsOk());
+    if (IsAbort()) {
+      new (storage_) std::unique_ptr<Stacktrace>{new Stacktrace{}};
+    }
+  }

-  // Deprecated as "success" is misleading; it simply means we were
-  // able to communicate with incidentd. Will be removed once
-  // incidentd is no longer used.
-  kUploadIncidentSuccess = 9,
+  std::unique_ptr<Stacktrace>& GetStacktraceUniquePtr() {
+    PERFETTO_DCHECK(IsAbort());
+    return *std::launder(
+        reinterpret_cast<std::unique_ptr<Stacktrace>*>(storage_));
+  }

-  // Contained trigger begin/success/failure. Replaced by
-  // |PerfettoTriggerAtom| to allow aggregation using a count metric
-  // and reduce spam.
-  // reserved 12, 13, 14;
+  Status status_;

-  // Contained that a guardrail in perfetto_cmd was hit. Replaced with
-  // kCmd* guardrails.
-  // reserved 15;
+  static constexpr size_t kStorageAlign =
+      std::max(internal::alignof_v<T>, alignof(std::unique_ptr<Stacktrace>));
+  static constexpr size_t kStorageSize =
+      std::max(internal::sizeof_v<T>, sizeof(std::unique_ptr<Stacktrace>));
+  alignas(kStorageAlign) unsigned char storage_[kStorageSize];
+};

-  // Contained status of Dropbox uploads. Removed as Perfetto no
-  // longer supports uploading traces using Dropbox.
-  // reserved 5, 6, 7;
+}  // namespace protovm
+}  // namespace perfetto

-  // Contained status of guardrail state initialization and upload limit in
-  // perfetto_cmd. Removed as perfetto no longer manages stateful guardrails
-  // reserved 44, 45, 46;
+#endif  // SRC_PROTOVM_ERROR_HANDLING_H_
+// gen_amalgamated begin header: src/protovm/node.h
+// gen_amalgamated begin header: src/protovm/owned_ptr.h
+/*
+ * Copyright (C) 2025 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.
+ */

-  // Contained the guardrail for user build tracing. Removed as this guardrail
-  // causes more problem than it solves these days.
-  // reserved 43;
-};
+#ifndef SRC_PROTOVM_OWNED_PTR_H_
+#define SRC_PROTOVM_OWNED_PTR_H_

-// This must match the values of the PerfettoTrigger::TriggerType enum in:
-// frameworks/proto_logging/stats/atoms.proto
-enum PerfettoTriggerAtom {
-  kUndefined = 0,
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"

-  kTracedLimitProbability = 5,
-  kTracedLimitMaxPer24h = 6,
+namespace perfetto {
+namespace protovm {

-  kTracedTrigger = 9,
+// OwnedPtr is essentially a std::unique_ptr that doesn't perform any deletion
+// itself. Its purpose is to clearly express ownership semantics for objects
+// that are managed with a custom allocators.
+//
+// It is not a unique_ptr, because that's not guaranteed to be standard layout
+//
+// OwnedPtr is extensively used within RwProto to manage the lifecycle and
+// clarify ownership of the internal, manually-allocated nodes.
+template <class T>
+class OwnedPtr {
+ public:
+  using pointer = T*;
+  OwnedPtr(const OwnedPtr&) = delete;
+  OwnedPtr(OwnedPtr&& other) {
+    p_ = other.p_;
+    other.p_ = nullptr;
+  }
+  OwnedPtr(T* p) noexcept : p_(p) {}  // NOLINT: explicit
+  ~OwnedPtr() noexcept { reset(); }
+  OwnedPtr& operator=(const OwnedPtr&) = delete;
+  OwnedPtr& operator=(OwnedPtr&& other) noexcept {
+    if (this == &other) {
+      return *this;
+    }
+    reset();
+    p_ = other.p_;
+    other.p_ = nullptr;
+    return *this;
+  }
+  T* get() const noexcept { return p_; }
+  T* release() noexcept {
+    T* p = p_;
+    p_ = nullptr;
+    return p;
+  }
+  typename std::add_lvalue_reference<T>::type operator*() const noexcept {
+    return *p_;
+  }
+  explicit operator bool() const noexcept { return get() != nullptr; }
+  T* operator->() const noexcept { return p_; }
+  void reset(T* p = pointer()) noexcept {
+    PERFETTO_DCHECK(p_ == nullptr);
+    p_ = p;
+  }

-  // Contained events of logging triggers through perfetto_cmd, probes and
-  // trigger_perfetto.
-  // Removed in W (Oct 2024) and replaced by |kTracedTrigger|.
-  // reserved 1, 2, 3, 4, 7, 8
+ private:
+  T* p_ = nullptr;
 };

+}  // namespace protovm
 }  // namespace perfetto

-#endif  // SRC_ANDROID_STATS_PERFETTO_ATOMS_H_
+#endif  // SRC_PROTOVM_OWNED_PTR_H_
+// gen_amalgamated begin header: src/protovm/scalar.h
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -51485,40 +56679,43 @@ enum PerfettoTriggerAtom {
  * limitations under the License.
  */

-#ifndef SRC_ANDROID_STATS_STATSD_LOGGING_HELPER_H_
-#define SRC_ANDROID_STATS_STATSD_LOGGING_HELPER_H_
-
-#include <stdint.h>
-#include <optional>
-#include <string>
-#include <vector>
+#ifndef SRC_PROTOVM_SCALAR_H_
+#define SRC_PROTOVM_SCALAR_H_

-// gen_amalgamated expanded: #include "src/android_stats/perfetto_atoms.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"

 namespace perfetto {
-namespace android_stats {
+namespace protovm {

-// Functions in this file are only active on built in the Android
-// tree. On other platforms (including Android standalone and Chromium
-// on Android) these functions are a noop.
+struct Scalar {
+  static Scalar Fixed32(uint32_t value) {
+    return Scalar{protozero::proto_utils::ProtoWireType::kFixed32, value};
+  }

-// Logs the upload event to statsd if built in the Android tree.
-void MaybeLogUploadEvent(PerfettoStatsdAtom atom,
-                         int64_t uuid_lsb,
-                         int64_t uuid_msb,
-                         const std::string& trigger_name = "");
+  static Scalar Fixed64(uint64_t value) {
+    return Scalar{protozero::proto_utils::ProtoWireType::kFixed64, value};
+  }

-// Logs the trigger events to statsd if built in the Android tree.
-void MaybeLogTriggerEvent(PerfettoTriggerAtom atom,
-                          int64_t uuid_lsb,
-                          const std::string& trigger);
+  static Scalar VarInt(uint64_t value) {
+    return Scalar{protozero::proto_utils::ProtoWireType::kVarInt, value};
+  }

-}  // namespace android_stats
+  bool operator==(const Scalar& other) const {
+    return wire_type == other.wire_type && value == other.value;
+  }
+
+  bool operator!=(const Scalar& other) const { return !(*this == other); }
+
+  protozero::proto_utils::ProtoWireType wire_type;
+  uint64_t value;
+};
+
+}  // namespace protovm
 }  // namespace perfetto

-#endif  // SRC_ANDROID_STATS_STATSD_LOGGING_HELPER_H_
+#endif  // SRC_PROTOVM_SCALAR_H_
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -51533,62 +56730,92 @@ void MaybeLogTriggerEvent(PerfettoTriggerAtom atom,
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/android_stats/statsd_logging_helper.h"
+#ifndef SRC_PROTOVM_NODE_H_
+#define SRC_PROTOVM_NODE_H_

-#include <cstdint>
-#include <string>
-#include <vector>
+#include <type_traits>
+#include <variant>

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "src/android_stats/perfetto_atoms.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/intrusive_tree.h"
+// gen_amalgamated expanded: #include "src/protovm/owned_ptr.h"
+// gen_amalgamated expanded: #include "src/protovm/scalar.h"

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
-    PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
-// gen_amalgamated expanded: #include "src/android_internal/lazy_library_loader.h"  // nogncheck
-// gen_amalgamated expanded: #include "src/android_internal/statsd_logging.h"       // nogncheck
-#endif
+namespace perfetto {
+namespace protovm {

-namespace perfetto::android_stats {
+struct Node;

-// Make sure we don't accidentally log on non-Android tree build. Note that even
-// removing this ifdef still doesn't make uploads work on OS_ANDROID.
-// PERFETTO_LAZY_LOAD will return a nullptr on non-Android and non-in-tree
-// builds as libperfetto_android_internal will not be available.
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
-    PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+struct MapNode {
+  struct Traits {
+    using KeyType = uint64_t;
+    static constexpr size_t NodeOffset() {
+      static_assert(std::is_standard_layout_v<MapNode>);
+      return offsetof(MapNode, node);
+    }
+    static const KeyType& GetKey(const MapNode& n) { return n.key; }
+  };

-void MaybeLogUploadEvent(PerfettoStatsdAtom atom,
-                         int64_t uuid_lsb,
-                         int64_t uuid_msb,
-                         const std::string& trigger_name) {
-  PERFETTO_LAZY_LOAD(android_internal::StatsdLogUploadEvent, log_event_fn);
-  if (log_event_fn) {
-    log_event_fn(atom, uuid_lsb, uuid_msb, trigger_name.c_str());
+  MapNode(uint64_t k, OwnedPtr<Node> v) : key{k}, value{std::move(v)} {}
+
+  base::IntrusiveTreeNode node{};
+  uint64_t key;
+  OwnedPtr<Node> value;
+};
+
+using IntrusiveMap = base::IntrusiveTree<MapNode, MapNode::Traits>;
+
+struct Node {
+  struct Bytes {
+    OwnedPtr<void> data;
+    size_t size;
+  };
+
+  struct Empty {};
+
+  struct MappedRepeatedField {
+    IntrusiveMap key_to_node;
+  };
+
+  struct IndexedRepeatedField {
+    IntrusiveMap index_to_node;
+  };
+
+  struct Message {
+    IntrusiveMap field_id_to_node;
+  };
+
+  template <class T>
+  T* GetIf() {
+    return std::get_if<T>(&value);
   }
-}

-void MaybeLogTriggerEvent(PerfettoTriggerAtom atom,
-                          int64_t uuid_lsb,
-                          const std::string& trigger_name) {
-  PERFETTO_LAZY_LOAD(android_internal::StatsdLogTriggerEvent, log_event_fn);
-  if (log_event_fn) {
-    log_event_fn(atom, uuid_lsb, trigger_name.c_str());
+  template <class T>
+  const T* GetIf() const {
+    return std::get_if<T>(&value);
   }
-}

-#else
-void MaybeLogUploadEvent(PerfettoStatsdAtom,
-                         int64_t,
-                         int64_t,
-                         const std::string&) {}
-void MaybeLogTriggerEvent(PerfettoTriggerAtom, int64_t, const std::string&) {}
-#endif
+  const char* GetTypeName() const;

-}  // namespace perfetto::android_stats
-// gen_amalgamated begin source: src/base/regex/regex.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/regex.h
+  std::variant<Bytes,
+               Empty,
+               IndexedRepeatedField,
+               MappedRepeatedField,
+               Message,
+               Scalar>
+      value;
+
+  // Flag needed to track the merge state of repeated fields
+  // (see implementation of Merge operation in RW proto)
+  bool has_been_merged = false;
+};
+
+}  // namespace protovm
+}  // namespace perfetto
+
+#endif  // SRC_PROTOVM_NODE_H_
+// gen_amalgamated begin header: src/protovm/slab_allocator.h
 /*
- * Copyright (C) 2026 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -51603,180 +56830,280 @@ void MaybeLogTriggerEvent(PerfettoTriggerAtom, int64_t, const std::string&) {}
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_REGEX_H_
-#define INCLUDE_PERFETTO_EXT_BASE_REGEX_H_
-
-#include <cstdint>
-#include <memory>
-#include <optional>
-#include <string>
-#include <string_view>
-#include <vector>
-
-// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"
+#ifndef SRC_PROTOVM_SLAB_ALLOCATOR_H_
+#define SRC_PROTOVM_SLAB_ALLOCATOR_H_

-namespace perfetto {
-namespace base {
+#include <cstdlib>
+#include <new>

-// Pimpl for the regex engine. Defined in src/base/regex/regex.cc.
-class RegexImpl;
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/flat_hash_map.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/intrusive_list.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/paged_memory.h"

-// A regular expression abstraction that hides the underlying engine.
+// An efficient allocator for elements with fixed size and alignment
+// requirements.
 //
-// Construct via the two static factories:
-//   - Create()        – fallible; returns StatusOr<Regex>.
-//   - CreateOrCheck() – infallible; crashes on invalid pattern.
-//                       Use for compile-time constant patterns only.
+// Design doc: go/perfetto-protovm-implementation
 //
-// Regex is movable but NOT copyable. Use CopyableRegex when copy semantics
-// are needed (e.g. for storage in copyable containers).
+// Key features:
 //
-// Patterns use ECMAScript syntax, supporting Perl-style shorthands (\d, \w,
-// \s).
+// - Slab allocation: Instead of requesting memory for each individual element,
+//   this allocator pre-allocates large memory chunks (slabs). Each slab is
+//   designed to hold multiple elements.
 //
-// Operations:
-//   GlobalReplace()          – global substitution; returns a new string.
-//   FullMatch()              – anchored, no groups.
-//   FullMatchWithGroups()    – anchored, with capture groups.
-//   PartialMatch()           – unanchored, no groups.
-//   PartialMatchWithGroups() – unanchored, with capture groups.
-//   PartialMatchAll()        – iterator over all non-overlapping matches.
-class Regex {
+// - Element free list: A free list tracks available elements within each
+//   individual slab, allowing for O(1) access time during allocation.
+//
+// - Slab intrusive lists: Slabs are managed within one of two intrusive lists.
+//   The "non-full slabs" list and the "full slabs" list. This organization
+//   allows "non-full" slabs (those with available space for new allocations) to
+//   be accessed in O(1) time.
+//
+// - Block-to-Slab hash map: A hash map links 4KB-aligned memory blocks to their
+//   corresponding slab. This enables O(1) mapping of an element back to its
+//   slab during deallocation.
+//
+// Allocation process:
+// 1. If there is no free slab
+//    1.1 Allocate a new slab, add it to the “non-full slabs” list, populate the
+//        block-to-slab map
+// 2. Pick any slab from the “non-full slabs” list
+// 3. Allocate the element
+// 4. If needed, move the slab to the “full slabs” list
+//
+// Deallocation process:
+// 1. Find slab from the block-to-slab map
+// 2. Free element
+// 3. If needed, move to the “non-full slabs” list
+// 4. If needed, deallocate the slab.
+
+namespace perfetto {
+namespace protovm {
+
+namespace internal {
+static constexpr size_t k4KiloBytes = static_cast<size_t>(4096);
+}
+
+template <size_t ElementSize, size_t ElementAlign, size_t Blocks4KB>
+class Slab {
  public:
-  // Controls whether matching is case-sensitive or case-insensitive.
-  enum class CaseSensitivity : uint8_t {
-    kSensitive,
-    kInsensitive,
+  struct IntrusiveListTraits {
+    static constexpr size_t node_offset() {
+      return offsetof(Slab, intrusive_list_node_);
+    }
   };

-  Regex(Regex&&) noexcept;
-  Regex& operator=(Regex&&) noexcept;
-  ~Regex();
+  Slab() : paged_memory_(base::PagedMemory::Allocate(ElementSize * kCapacity)) {
+    PERFETTO_CHECK(paged_memory_.IsValid());

-  // Regex is not copyable. Use CopyableRegex for copy semantics.
-  Regex(const Regex&) = delete;
-  Regex& operator=(const Regex&) = delete;
+    // Expect allocated memory to be always 4KB-aligned
+    PERFETTO_CHECK(reinterpret_cast<uintptr_t>(paged_memory_.Get()) %
+                       internal::k4KiloBytes ==
+                   0);

-  // Compiles |pattern| into a Regex.
-  // Returns an error if the pattern syntax is invalid.
-  static StatusOr<Regex> Create(
-      std::string_view pattern,
-      CaseSensitivity cs = CaseSensitivity::kSensitive);
+    ConstructSlots();
+    InitializeSlotsFreeList();
+  }

-  // Like Create(), but crashes (PERFETTO_CHECK) on an invalid pattern.
-  // Use only for patterns that are known at compile time.
-  static Regex CreateOrCheck(std::string_view pattern,
-                             CaseSensitivity cs = CaseSensitivity::kSensitive);
+  ~Slab() { DestroySlots(); }

-  // Replaces every occurrence of the regex in |s| with |replacement| and
-  // returns the resulting string.  |s| is unchanged.
-  std::string GlobalReplace(std::string_view s,
-                            std::string_view replacement) const;
+  void* Allocate() {
+    PERFETTO_DCHECK(next_free_slot_);
+    auto* slot = next_free_slot_;
+    next_free_slot_ = next_free_slot_->next;
+    ++size_;
+    return slot;
+  }

-  // Returns true if the regex matches the entirety of |s|.
-  bool FullMatch(std::string_view s) const;
+  void Free(void* p) {
+    auto* slot = static_cast<Slot*>(p);
+    PERFETTO_DCHECK(slot >= slots() && slot < slots() + kCapacity);
+    slot->next = next_free_slot_;
+    next_free_slot_ = slot;
+    --size_;
+  }

-  // Like FullMatch(), but also fills |out| with captured groups on success.
-  //   out[0]    – the full match.
-  //   out[1..N] – the parenthesised sub-groups.
-  // Returns false (and clears |out|) when there is no match.
-  //
-  // The string_views in |out| point into |s|; they are invalidated if |s| is
-  // modified or destroyed.
-  bool FullMatchWithGroups(std::string_view s,
-                           std::vector<std::string_view>& out) const;
+  bool IsFull() const { return size_ == kCapacity; }

-  // Returns true if the regex matches any substring of |s|.
-  bool PartialMatch(std::string_view s) const;
+  bool IsEmpty() const { return size_ == 0; }
+
+  const void* GetBeginAddress() const {
+    return const_cast<Slab*>(this)->slots();
+  }
+
+  const void* GetEndAddress() const {
+    return const_cast<Slab*>(this)->slots() + kCapacity;
+  }
+
+ private:
+  static constexpr size_t kCapacity =
+      Blocks4KB * (internal::k4KiloBytes / ElementSize);
+
+  static_assert(kCapacity >= 128,
+                "The configured number of 4KB blocks per slab seems too small, "
+                "resulting in a low slab capacity. Slab allocation is "
+                "expensive (involves syscalls), so a high elements-to-slab "
+                "ratio is desirable to amortize the cost.");
+
+  static_assert(ElementAlign <= internal::k4KiloBytes,
+                "SlabAllocator currently supports alignment <= 4KB");
+
+  union Slot {
+    Slot* next;
+    alignas(ElementAlign) unsigned char element[ElementSize];
+  };
+
+  void InitializeSlotsFreeList() {
+    next_free_slot_ = &slots()[0];
+
+    for (size_t i = 0; i + 1 < kCapacity; ++i) {
+      auto& slot = slots()[i];
+      auto& next_slot = slots()[i + 1];
+      slot.next = &next_slot;
+    }
+
+    auto& last_slot = slots()[kCapacity - 1];
+    last_slot.next = nullptr;
+  }
+
+  void ConstructSlots() {
+    auto* slot = slots();
+    for (size_t i = 0; i < kCapacity; ++i) {
+      new (&slot[i]) Slot();
+    }
+  }
+
+  void DestroySlots() {
+    auto* slot = slots();
+    for (size_t i = 0; i < kCapacity; ++i) {
+      slot[i].~Slot();
+    }
+  }
+
+  Slot* slots() {
+    return std::launder(reinterpret_cast<Slot*>(paged_memory_.Get()));
+  }
+
+  Slot* next_free_slot_{nullptr};
+  size_t size_{0};
+  base::IntrusiveListNode intrusive_list_node_;
+  base::PagedMemory paged_memory_;
+};
+
+template <size_t ElementSize, size_t ElementAlign, size_t Blocks4KBPerSlab = 16>
+class SlabAllocator {
+ public:
+  ~SlabAllocator() {
+    DeleteSlabs(slabs_non_full_);
+    DeleteSlabs(slabs_full_);
+  }

-  // Like PartialMatch(), but also fills |out| with captured groups on success.
-  //   out[0]    – the full match.
-  //   out[1..N] – the parenthesised sub-groups.
-  // Returns false (and clears |out|) when there is no match.
-  //
-  // The string_views in |out| point into |s|; they are invalidated if |s| is
-  // modified or destroyed.
-  bool PartialMatchWithGroups(std::string_view s,
-                              std::vector<std::string_view>& out) const;
+  void* Allocate() {
+    // Create new slab if needed
+    if (slabs_non_full_.empty()) {
+      auto* slab = new SlabType();
+      slabs_non_full_.PushFront(*slab);
+      ++slabs_non_full_size_;
+      InsertHashMapEntries(*slab);
+    }

-  // Iterator over all non-overlapping matches in a string.
-  class PartialMatchIterator {
-   public:
-    // Returns the next match, or std::nullopt when no more matches remain.
-    std::optional<std::string_view> Next();
+    // Allocate using any non-full slab
+    auto& slab = slabs_non_full_.front();
+    auto* allocated = slab.Allocate();
+    PERFETTO_CHECK(allocated);

-    // Returns the next match with capture groups. Groups are filled into
-    // |groups|. Returns std::nullopt when no more matches remain.
-    std::optional<std::string_view> NextWithGroups(
-        std::vector<std::string_view>& groups);
+    // Move to "full slabs" list if needed
+    if (slab.IsFull()) {
+      slabs_non_full_.Erase(slab);
+      --slabs_non_full_size_;
+      slabs_full_.PushFront(slab);
+    }

-   private:
-    friend class Regex;
-    PartialMatchIterator(const Regex* re, std::string_view input);
-    const Regex* re_;
-    std::string_view input_;
-    size_t offset_ = 0;
-    std::vector<std::string_view> groups_;  // Cached for Next().
-  };
+    return allocated;
+  }

-  // Returns an iterator that yields all non-overlapping matches in |s|.
-  PartialMatchIterator PartialMatchAll(std::string_view s) const;
+  void Free(void* p) {
+    auto& slab = FindSlabInHashMap(p);

-  // Accessors for pattern and case sensitivity (used by CopyableRegex).
-  const std::string& pattern() const { return pattern_; }
-  CaseSensitivity case_sensitivity() const { return cs_; }
+    // Move to "non-full slabs" list if needed
+    if (slab.IsFull()) {
+      slabs_full_.Erase(slab);
+      slabs_non_full_.PushFront(slab);
+      ++slabs_non_full_size_;
+    }

- private:
-  Regex() = default;
-  std::unique_ptr<RegexImpl> impl_;
-  std::string pattern_;
-  CaseSensitivity cs_ = CaseSensitivity::kSensitive;
-};
+    slab.Free(p);

-// A copyable wrapper around Regex. Copying re-compiles the pattern from the
-// stored string, so prefer moving when the source is no longer needed.
-//
-// Use this class when Regex needs to be stored in a copyable container
-// (e.g. std::optional in a copyable struct).
-class CopyableRegex {
- public:
-  explicit CopyableRegex(Regex regex);
+    // Deallocate the slab if it becomes empty and it's not the sole non-full
+    // slab.
+    //
+    // The "is not the sole non-full slab" condition avoids thrashing scenarios
+    // where a slab is repeatedly allocated and deallocated. For example:
+    // 1. Allocate element x -> a new slab is allocated.
+    // 2. Free element x -> slab becomes empty and is deallocated.
+    // 3. Allocate element y -> a new slab is allocated again.
+    // 4. And so on...
+    if (slab.IsEmpty() && slabs_non_full_size_ > 1) {
+      EraseHashMapEntries(slab);
+      slabs_non_full_.Erase(slab);
+      --slabs_non_full_size_;
+      delete &slab;
+    }
+  }

-  CopyableRegex(const CopyableRegex& other);
-  CopyableRegex& operator=(const CopyableRegex& other);
-  CopyableRegex(CopyableRegex&&) noexcept = default;
-  CopyableRegex& operator=(CopyableRegex&&) noexcept = default;
-  ~CopyableRegex() = default;
+ private:
+  using SlabType = Slab<ElementSize, ElementAlign, Blocks4KBPerSlab>;
+  using SlabList =
+      base::IntrusiveList<SlabType, typename SlabType::IntrusiveListTraits>;

-  std::string GlobalReplace(std::string_view s,
-                            std::string_view replacement) const {
-    return regex_.GlobalReplace(s, replacement);
+  void InsertHashMapEntries(SlabType& slab) {
+    for (auto p = reinterpret_cast<uintptr_t>(slab.GetBeginAddress());
+         p < reinterpret_cast<uintptr_t>(slab.GetEndAddress());
+         p += internal::k4KiloBytes) {
+      PERFETTO_DCHECK(p % internal::k4KiloBytes == 0);
+      block_4KB_aligned_to_slab_.Insert(p, &slab);
+    }
   }
-  bool FullMatch(std::string_view s) const { return regex_.FullMatch(s); }
-  bool FullMatchWithGroups(std::string_view s,
-                           std::vector<std::string_view>& out) const {
-    return regex_.FullMatchWithGroups(s, out);
+
+  void EraseHashMapEntries(const SlabType& slab) {
+    for (auto p = reinterpret_cast<uintptr_t>(slab.GetBeginAddress());
+         p < reinterpret_cast<uintptr_t>(slab.GetEndAddress());
+         p += internal::k4KiloBytes) {
+      PERFETTO_DCHECK(p % internal::k4KiloBytes == 0);
+      block_4KB_aligned_to_slab_.Erase(p);
+    }
   }
-  bool PartialMatch(std::string_view s) const { return regex_.PartialMatch(s); }
-  bool PartialMatchWithGroups(std::string_view s,
-                              std::vector<std::string_view>& out) const {
-    return regex_.PartialMatchWithGroups(s, out);
+
+  SlabType& FindSlabInHashMap(const void* ptr) {
+    auto ptr_4KB_aligned = reinterpret_cast<uintptr_t>(ptr) &
+                           ~(static_cast<uintptr_t>(internal::k4KiloBytes) - 1);
+    SlabType** slab = block_4KB_aligned_to_slab_.Find(ptr_4KB_aligned);
+    PERFETTO_CHECK(slab);
+    PERFETTO_CHECK(*slab);
+    return **slab;
   }
-  Regex::PartialMatchIterator PartialMatchAll(std::string_view s) const {
-    return regex_.PartialMatchAll(s);
+
+  void DeleteSlabs(SlabList& slabs) {
+    while (!slabs.empty()) {
+      auto& slab = slabs.front();
+      slabs.PopFront();
+      delete &slab;
+    }
   }

- private:
-  Regex regex_;
+  base::FlatHashMap<uintptr_t, SlabType*> block_4KB_aligned_to_slab_;
+  SlabList slabs_full_;
+  SlabList slabs_non_full_;
+  size_t slabs_non_full_size_{0};
 };

-}  // namespace base
+}  // namespace protovm
 }  // namespace perfetto

-#endif  // INCLUDE_PERFETTO_EXT_BASE_REGEX_H_
-// gen_amalgamated begin header: src/base/regex/regex_pcre2.h
+#endif  // SRC_PROTOVM_SLAB_ALLOCATOR_H_
 /*
- * Copyright (C) 2026 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -51791,199 +57118,85 @@ class CopyableRegex {
  * limitations under the License.
  */

-#ifndef SRC_BASE_REGEX_REGEX_PCRE2_H_
-#define SRC_BASE_REGEX_REGEX_PCRE2_H_
-
-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-
-// See note in regex_re2.h. Also honor PERFETTO_REGEX_FORCE_STD: targets that
-// define it (e.g. libperfetto_client_experimental) opt out of the optional
-// backends and must not pull in <pcre2.h>, even when PERFETTO_PCRE2 is set.
-#if PERFETTO_BUILDFLAG(PERFETTO_PCRE2) && !defined(PERFETTO_REGEX_FORCE_STD)
+#ifndef SRC_PROTOVM_ALLOCATOR_H_
+#define SRC_PROTOVM_ALLOCATOR_H_

-#include <limits>
-#include <memory>
-#include <string>
-#include <string_view>
-#include <vector>
+#include <algorithm>
+#include <cstdlib>

-// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/field.h"

-#define PCRE2_CODE_UNIT_WIDTH 8
-#include <pcre2.h>
+// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
+// gen_amalgamated expanded: #include "src/protovm/node.h"
+// gen_amalgamated expanded: #include "src/protovm/slab_allocator.h"

 namespace perfetto {
-namespace base {
-
-struct Pcre2CodeDeleter {
-  void operator()(pcre2_code* p) const { pcre2_code_free(p); }
-};
+namespace protovm {

-struct Pcre2MatchDataDeleter {
-  void operator()(pcre2_match_data* p) const { pcre2_match_data_free(p); }
-};
+// A centralized allocator to manage all the memory requests from RwProto. It
+// enforces a strict memory usage limit, thus allowing fine-grained control of
+// the overall proto VM's memory footprint.
+class Allocator {
+ public:
+  static constexpr size_t kNodeSize = std::max(sizeof(Node), sizeof(MapNode));
+  static constexpr size_t kNodeAlign =
+      std::max(alignof(Node), alignof(MapNode));

-using ScopedPcre2Code = std::unique_ptr<pcre2_code, Pcre2CodeDeleter>;
-using ScopedPcre2MatchData =
-    std::unique_ptr<pcre2_match_data, Pcre2MatchDataDeleter>;
+  explicit Allocator(size_t memory_limit_bytes);

-// PCRE2 backend for Regex. Used on Android when the use_pcre2 flag is enabled.
-// Header-only so the compiler can inline through unique_ptr<RegexImpl>.
-class RegexPcre2 {
- public:
-  RegexPcre2(RegexPcre2&&) noexcept = default;
-  RegexPcre2& operator=(RegexPcre2&&) noexcept = default;
-  ~RegexPcre2() = default;
+  template <class T, class... Args>
+  StatusOr<OwnedPtr<Node>> CreateNode(Args&&... args) {
+    auto p = Allocate();
+    PROTOVM_RETURN_IF_NOT_OK(p);

-  RegexPcre2(const RegexPcre2&) = delete;
-  RegexPcre2& operator=(const RegexPcre2&) = delete;
+    // 1. Construct node's inner value T passing args (struct aggregate
+    // initialization)
+    // 2. Construct node passing T (struct aggregate initialization)
+    new (*p) Node{T{std::forward<Args>(args)...}};

-  static StatusOr<RegexPcre2> Create(std::string_view pattern,
-                                     bool case_insensitive) {
-    RegexPcre2 result;
-    int error_code;
-    size_t error_offset;
-    uint32_t pcre2_flags = 0;
-    if (case_insensitive) {
-      pcre2_flags |= PCRE2_CASELESS;
-    }
-    result.code_.reset(pcre2_compile(
-        reinterpret_cast<PCRE2_SPTR>(pattern.data()), pattern.size(),
-        pcre2_flags, &error_code, &error_offset, nullptr));
-    if (!result.code_) {
-      PCRE2_UCHAR buffer[256];
-      pcre2_get_error_message(error_code, buffer, sizeof(buffer));
-      return ErrStatus("PCRE2 compile error at offset %zu: %s", error_offset,
-                       reinterpret_cast<char*>(buffer));
-    }
-    // JIT-compile for faster matching. Non-fatal if JIT is unavailable.
-    pcre2_jit_compile(result.code_.get(), PCRE2_JIT_COMPLETE);
-    result.match_data_.reset(
-        pcre2_match_data_create_from_pattern(result.code_.get(), nullptr));
-    return std::move(result);
+    return OwnedPtr<Node>(static_cast<Node*>(*p));
   }

-  std::string GlobalReplace(std::string_view s,
-                            std::string_view replacement) const {
-    std::string out;
-    size_t out_len = s.size() + replacement.size() * 2 + 64;
-    auto do_substitute = [&](size_t* len) {
-      out.resize(*len);
-      return pcre2_substitute(
-          code_.get(), reinterpret_cast<PCRE2_SPTR>(s.data()), s.size(),
-          /*startoffset=*/0,
-          PCRE2_SUBSTITUTE_GLOBAL | PCRE2_SUBSTITUTE_OVERFLOW_LENGTH,
-          /*match_data=*/nullptr, /*mcontext=*/nullptr,
-          reinterpret_cast<PCRE2_SPTR>(replacement.data()), replacement.size(),
-          reinterpret_cast<PCRE2_UCHAR*>(out.data()), len);
-    };
-    int rc = do_substitute(&out_len);
-    if (rc == PCRE2_ERROR_NOMEMORY) {
-      // PCRE2_SUBSTITUTE_OVERFLOW_LENGTH sets out_len to the required size.
-      rc = do_substitute(&out_len);
-    }
-    if (rc >= 0) {
-      out.resize(out_len);
-      return out;
-    }
-    return std::string(s);
-  }
+  StatusOr<OwnedPtr<MapNode>> CreateMapNode(uint64_t key, OwnedPtr<Node> value);

-  bool FullMatch(std::string_view s) const {
-    int rc = pcre2_match(code_.get(), reinterpret_cast<PCRE2_SPTR>(s.data()),
-                         s.size(),
-                         /*startoffset=*/0, PCRE2_ANCHORED | PCRE2_ENDANCHORED,
-                         match_data_.get(), /*mcontext=*/nullptr);
-    return rc >= 0;
-  }
+  StatusOr<Node::Bytes> AllocateAndCopyBytes(protozero::ConstBytes data);

-  bool FullMatchWithGroups(std::string_view s,
-                           std::vector<std::string_view>& out) const {
-    out.clear();
-    int rc = pcre2_match(code_.get(), reinterpret_cast<PCRE2_SPTR>(s.data()),
-                         s.size(),
-                         /*startoffset=*/0, PCRE2_ANCHORED | PCRE2_ENDANCHORED,
-                         match_data_.get(), /*mcontext=*/nullptr);
-    if (rc <= 0)
-      return false;
-    FillGroups(s, out);
-    return true;
-  }
+  // Deeply delete a node and all the referenced data. E.g. if the node holds a
+  // Node::Message, recursively delete the message fields and finally delete the
+  // node.
+  void Delete(Node* node);
+  void Delete(MapNode* node);

-  bool PartialMatch(std::string_view s) const {
-    int rc = pcre2_match(code_.get(), reinterpret_cast<PCRE2_SPTR>(s.data()),
-                         s.size(),
-                         /*startoffset=*/0, /*options=*/0, match_data_.get(),
-                         /*mcontext=*/nullptr);
-    return rc >= 0;
-  }
+  // Deeply delete a node's referenced data, but do not delete the node itself.
+  // E.g. if the node holds a Node::Message, recursively delete the message
+  // fields.
+  void DeleteReferencedData(Node* node);

-  bool PartialMatchWithGroups(std::string_view s,
-                              std::vector<std::string_view>& out) const {
-    out.clear();
-    int rc = pcre2_match(code_.get(), reinterpret_cast<PCRE2_SPTR>(s.data()),
-                         s.size(),
-                         /*startoffset=*/0, /*options=*/0, match_data_.get(),
-                         /*mcontext=*/nullptr);
-    if (rc <= 0)
-      return false;
-    FillGroups(s, out);
-    return true;
-  }
+  // Recursively delete a message's fields, but do not delete the message
+  // itself.
+  void DeleteReferencedData(Node::Message* message);

-  bool SearchWithOffset(std::string_view s,
-                        size_t start_offset,
-                        std::vector<std::string_view>& out) const {
-    out.clear();
-    if (start_offset > s.size())
-      return false;
-    int rc =
-        pcre2_match(code_.get(), reinterpret_cast<PCRE2_SPTR>(s.data()),
-                    s.size(), start_offset, /*options=*/0, match_data_.get(),
-                    /*mcontext=*/nullptr);
-    if (rc <= 0)
-      return false;
-    FillGroups(s, out);
-    return true;
-  }
+  // Delete the blob referenced by the Bytes struct, but do not delete the Bytes
+  // struct itself.
+  void DeleteReferencedData(Node::Bytes* bytes);

- private:
-  RegexPcre2() = default;
+  size_t GetMemoryUsageBytes() const { return used_memory_bytes_; }

-  void FillGroups(std::string_view s,
-                  std::vector<std::string_view>& out) const {
-    uint32_t count = pcre2_get_ovector_count(match_data_.get());
-    size_t* ovector = pcre2_get_ovector_pointer(match_data_.get());
-    for (uint32_t i = 0; i < count; ++i) {
-      if (ovector[2 * i] == std::numeric_limits<PCRE2_SIZE>::max()) {
-        out.emplace_back();
-      } else {
-        out.emplace_back(s.data() + ovector[2 * i],
-                         ovector[2 * i + 1] - ovector[2 * i]);
-      }
-    }
-  }
+ private:
+  void DeallocateBytes(OwnedPtr<void> p, size_t size);
+  StatusOr<void*> Allocate();

-  ScopedPcre2Code code_;
-  // pcre2_match() requires a non-null match_data even when capture groups
-  // aren't needed (FullMatch, PartialMatch). A separate minimal match_data
-  // with ovecsize=1 could be used for those cases, but the savings are
-  // negligible: PCRE2's internal backtracking frame size depends on the
-  // pattern's capture groups regardless of ovector size. We reuse a single
-  // cached match_data created from the pattern to avoid per-call allocation.
-  mutable ScopedPcre2MatchData match_data_;
+  size_t memory_limit_bytes_{0};
+  size_t used_memory_bytes_{0};
+  SlabAllocator<kNodeSize, kNodeAlign> slab_allocator_;
 };

-}  // namespace base
+}  // namespace protovm
 }  // namespace perfetto

-#endif  // PERFETTO_BUILDFLAG(PERFETTO_PCRE2) &&
-        // !defined(PERFETTO_REGEX_FORCE_STD)
-
-#endif  // SRC_BASE_REGEX_REGEX_PCRE2_H_
-// gen_amalgamated begin header: src/base/regex/regex_re2.h
+#endif  // SRC_PROTOVM_ALLOCATOR_H_
 /*
- * Copyright (C) 2026 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -51998,125 +57211,121 @@ class RegexPcre2 {
  * limitations under the License.
  */

-#ifndef SRC_BASE_REGEX_REGEX_RE2_H_
-#define SRC_BASE_REGEX_REGEX_RE2_H_
-
-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-
-// Guarded because gen_amalgamated inlines the body even when the include
-// site in regex.cc is preprocessed out. Also honor PERFETTO_REGEX_FORCE_STD:
-// targets that define it opt out of the optional backends and must not pull in
-// <re2/re2.h>, even when PERFETTO_RE2 is set.
-#if PERFETTO_BUILDFLAG(PERFETTO_RE2) && !defined(PERFETTO_REGEX_FORCE_STD)
-
-#include <memory>
-#include <string>
-#include <string_view>
-#include <vector>
-
-// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"
-
-// Using quotes instead of angle brackets because Bazel passes external repo
-// paths via -iquote (not -isystem), which only covers quoted includes.
-// See google/re2 commit baeed1c and bazelbuild/bazel#18974.
-#include "re2/re2.h"  // gen_amalgamated:keep
+// gen_amalgamated expanded: #include "src/protovm/allocator.h"

 namespace perfetto {
-namespace base {
-
-// RE2 backend for Regex. Used on non-Android builds (standalone, Chromium).
-// Header-only so the compiler can inline through unique_ptr<RegexImpl>.
-class RegexRe2 {
- public:
-  RegexRe2(RegexRe2&&) noexcept = default;
-  RegexRe2& operator=(RegexRe2&&) noexcept = default;
-  ~RegexRe2() = default;
+namespace protovm {

-  RegexRe2(const RegexRe2&) = delete;
-  RegexRe2& operator=(const RegexRe2&) = delete;
+Allocator::Allocator(size_t memory_limit_bytes)
+    : memory_limit_bytes_{memory_limit_bytes}, used_memory_bytes_{0} {}

-  static StatusOr<RegexRe2> Create(std::string_view pattern,
-                                   bool case_insensitive) {
-    RegexRe2 result;
-    re2::RE2::Options options;
-    options.set_log_errors(false);
-    options.set_case_sensitive(!case_insensitive);
-    result.re_ = std::make_unique<re2::RE2>(std::string(pattern), options);
-    if (!result.re_->ok()) {
-      return ErrStatus("RE2 compile error: %s", result.re_->error().c_str());
-    }
-    result.groups_.resize(
-        static_cast<size_t>(result.re_->NumberOfCapturingGroups() + 1));
-    return std::move(result);
+StatusOr<OwnedPtr<MapNode>> Allocator::CreateMapNode(uint64_t key,
+                                                     OwnedPtr<Node> value) {
+  PERFETTO_DCHECK(value);
+  auto p = Allocate();
+  if (!p.IsOk()) {
+    Delete(value.release());
+    PROTOVM_RETURN(p);
   }
+  new (*p) MapNode{key, std::move(value)};
+  return OwnedPtr<MapNode>(static_cast<MapNode*>(*p));
+}

-  std::string GlobalReplace(std::string_view s,
-                            std::string_view replacement) const {
-    std::string out(s);
-    re2::RE2::GlobalReplace(&out, *re_, replacement);
-    return out;
+StatusOr<Node::Bytes> Allocator::AllocateAndCopyBytes(
+    protozero::ConstBytes data) {
+  if (used_memory_bytes_ + data.size > memory_limit_bytes_) {
+    PROTOVM_ABORT(
+        "Failed to allocate %zu bytes. Memory limit: %zu bytes. Used: %zu "
+        "bytes.)",
+        data.size, memory_limit_bytes_, used_memory_bytes_);
   }

-  bool FullMatch(std::string_view s) const {
-    return re2::RE2::FullMatch(s, *re_);
+  if (data.size == 0) {
+    return Node::Bytes{nullptr, 0};
   }

-  bool FullMatchWithGroups(std::string_view s,
-                           std::vector<std::string_view>& out) const {
-    out.clear();
-    int n = static_cast<int>(groups_.size());
-    if (!re_->Match(s, 0, s.size(), re2::RE2::ANCHOR_BOTH, groups_.data(), n))
-      return false;
-    out.assign(groups_.begin(), groups_.begin() + n);
-    return true;
+  auto copy = OwnedPtr<void>{malloc(data.size)};
+  if (!copy) {
+    PROTOVM_ABORT("Failed to malloc %zu bytes", data.size);
   }
+  used_memory_bytes_ += data.size;
+  memcpy(copy.get(), data.data, data.size);

-  bool PartialMatch(std::string_view s) const {
-    return re2::RE2::PartialMatch(s, *re_);
-  }
+  return Node::Bytes{std::move(copy), data.size};
+}

-  bool PartialMatchWithGroups(std::string_view s,
-                              std::vector<std::string_view>& out) const {
-    out.clear();
-    int n = static_cast<int>(groups_.size());
-    if (!re_->Match(s, 0, s.size(), re2::RE2::UNANCHORED, groups_.data(), n))
-      return false;
-    out.assign(groups_.begin(), groups_.begin() + n);
-    return true;
+void Allocator::Delete(Node* node) {
+  DeleteReferencedData(node);
+  node->~Node();
+  slab_allocator_.Free(node);
+  used_memory_bytes_ -= kNodeSize;
+}
+
+void Allocator::Delete(MapNode* node) {
+  Delete(node->value.release());
+  node->~MapNode();
+  slab_allocator_.Free(node);
+  used_memory_bytes_ -= kNodeSize;
+}
+
+void Allocator::DeleteReferencedData(Node* node) {
+  if (auto* message = node->GetIf<Node::Message>()) {
+    DeleteReferencedData(message);
+  } else if (auto* indexed_fields = node->GetIf<Node::IndexedRepeatedField>()) {
+    for (auto it = indexed_fields->index_to_node.begin(); it;) {
+      auto& map_node = *it;
+      it = indexed_fields->index_to_node.Remove(it);
+      Delete(&map_node);
+    }
+  } else if (auto* mapped_fields = node->GetIf<Node::MappedRepeatedField>()) {
+    for (auto it = mapped_fields->key_to_node.begin(); it;) {
+      auto& map_node = *it;
+      it = mapped_fields->key_to_node.Remove(it);
+      Delete(&map_node);
+    }
+  } else if (auto* bytes = node->GetIf<Node::Bytes>()) {
+    DeleteReferencedData(bytes);
   }
+}

-  bool SearchWithOffset(std::string_view s,
-                        size_t start_offset,
-                        std::vector<std::string_view>& out) const {
-    out.clear();
-    if (start_offset > s.size())
-      return false;
-    int n = static_cast<int>(groups_.size());
-    if (!re_->Match(s, start_offset, s.size(), re2::RE2::UNANCHORED,
-                    groups_.data(), n))
-      return false;
-    out.assign(groups_.begin(), groups_.begin() + n);
-    return true;
+void Allocator::DeleteReferencedData(Node::Message* message) {
+  for (auto it = message->field_id_to_node.begin(); it;) {
+    auto& map_node = *it;
+    it = message->field_id_to_node.Remove(it);
+    Delete(&map_node);
   }
+}

- private:
-  RegexRe2() = default;
-  std::unique_ptr<re2::RE2> re_;
-  // Must be absl::string_view, not std::string_view, to match RE2::Match()
-  // signature. These can be distinct types in different builds.
-  mutable std::vector<absl::string_view> groups_;
-};
+void Allocator::DeleteReferencedData(Node::Bytes* bytes) {
+  DeallocateBytes(std::move(bytes->data), bytes->size);
+}

-}  // namespace base
-}  // namespace perfetto
+void Allocator::DeallocateBytes(OwnedPtr<void> p, size_t size) {
+  free(p.release());
+  used_memory_bytes_ -= size;
+}

-#endif  // PERFETTO_BUILDFLAG(PERFETTO_RE2) &&
-        // !defined(PERFETTO_REGEX_FORCE_STD)
+StatusOr<void*> Allocator::Allocate() {
+  if (used_memory_bytes_ + kNodeSize > memory_limit_bytes_) {
+    PROTOVM_ABORT(
+        "Failed to allocate element (%zu bytes). Memory limit: %zu [bytes]. "
+        "Used: %zu "
+        "[bytes].)",
+        kNodeSize, memory_limit_bytes_, used_memory_bytes_);
+  }
+  auto* p = slab_allocator_.Allocate();
+  if (!p) {
+    PROTOVM_ABORT("Failed to allocate node");
+  }
+  used_memory_bytes_ += kNodeSize;
+  return p;
+}

-#endif  // SRC_BASE_REGEX_REGEX_RE2_H_
-// gen_amalgamated begin header: src/base/regex/regex_std.h
+}  // namespace protovm
+}  // namespace perfetto
+// gen_amalgamated begin source: src/protovm/error_handling.cc
 /*
- * Copyright (C) 2026 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -52131,110 +57340,62 @@ class RegexRe2 {
  * limitations under the License.
  */

-#ifndef SRC_BASE_REGEX_REGEX_STD_H_
-#define SRC_BASE_REGEX_REGEX_STD_H_
-
-#include <regex>
-#include <string>
-#include <string_view>
-#include <vector>
-
-// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"
+// gen_amalgamated expanded: #include "src/protovm/error_handling.h"

 namespace perfetto {
-namespace base {
-
-// std::regex backend for Regex. Uses ECMAScript syntax for Perl-style
-// shorthands (\d, \w, \s).
-class RegexStd {
- public:
-  RegexStd(RegexStd&&) noexcept = default;
-  RegexStd& operator=(RegexStd&&) noexcept = default;
-  ~RegexStd() = default;
-
-  RegexStd(const RegexStd&) = delete;
-  RegexStd& operator=(const RegexStd&) = delete;
+namespace protovm {

-  static StatusOr<RegexStd> Create(std::string_view pattern,
-                                   bool case_insensitive) {
-    RegexStd result;
-    auto flags = std::regex::ECMAScript;
-    if (case_insensitive) {
-      flags |= std::regex::icase;
-    }
-    result.re_ = std::regex(std::string(pattern), flags);
-    return std::move(result);
-  }
+void LogStacktraceMessage(Stacktrace& stacktrace,
+                          const char* file_name,
+                          int file_line,
+                          const char* fmt,
+                          ...) {
+  const char* const fmt_file_info = "%s:%d ";
+  auto size_file_info =
+      std::snprintf(nullptr, 0, fmt_file_info, file_name, file_line);

-  std::string GlobalReplace(std::string_view s,
-                            std::string_view replacement) const {
-    return std::regex_replace(std::string(s), re_, std::string(replacement));
-  }
+  va_list args;
+  va_start(args, fmt);
+  auto size_message = std::vsnprintf(nullptr, 0, fmt, args);
+  va_end(args);

-  bool FullMatch(std::string_view s) const {
-    return std::regex_match(s.data(), s.data() + s.size(), re_);
-  }
+  auto s = std::string(
+      static_cast<std::size_t>(size_file_info + size_message + 1), '.');

-  bool FullMatchWithGroups(std::string_view s,
-                           std::vector<std::string_view>& out) const {
-    out.clear();
-    if (!std::regex_match(s.data(), s.data() + s.size(), match_data_, re_))
-      return false;
-    FillGroups(s.data(), out);
-    return true;
-  }
+  std::snprintf(s.data(), static_cast<size_t>(size_file_info) + 1,
+                fmt_file_info, file_name, file_line);

-  bool PartialMatch(std::string_view s) const {
-    return std::regex_search(s.data(), s.data() + s.size(), re_);
-  }
+  va_start(args, fmt);
+  std::vsnprintf(s.data() + size_file_info,
+                 static_cast<size_t>(size_message) + 1, fmt, args);
+  va_end(args);

-  bool PartialMatchWithGroups(std::string_view s,
-                              std::vector<std::string_view>& out) const {
-    out.clear();
-    if (!std::regex_search(s.data(), s.data() + s.size(), match_data_, re_))
-      return false;
-    FillGroups(s.data(), out);
-    return true;
-  }
+  s.pop_back();  // null-terminating char

-  bool SearchWithOffset(std::string_view s,
-                        size_t start_offset,
-                        std::vector<std::string_view>& out) const {
-    out.clear();
-    const char* begin = s.data() + start_offset;
-    const char* end = s.data() + s.size();
-    if (begin > end)
-      return false;
-    if (!std::regex_search(begin, end, match_data_, re_))
-      return false;
-    FillGroups(begin, out);
-    return true;
-  }
+  stacktrace.push_back(std::move(s));
+}

- private:
-  RegexStd() = default;
+void LogStacktraceMessage(Stacktrace& stacktrace,
+                          const char* file_name,
+                          int file_line) {
+  const char* const fmt = "%s:%d <no message>";
+  auto size_message = std::snprintf(nullptr, 0, fmt, file_name, file_line);
+  auto s = std::string(static_cast<size_t>(size_message) + 1, '.');
+  std::snprintf(s.data(), static_cast<size_t>(size_message) + 1, fmt, file_name,
+                file_line);

-  void FillGroups(const char* base, std::vector<std::string_view>& out) const {
-    for (size_t i = 0; i < match_data_.size(); ++i) {
-      if (match_data_[i].matched) {
-        out.emplace_back(base + match_data_.position(i),
-                         static_cast<size_t>(match_data_.length(i)));
-      } else {
-        out.emplace_back();
-      }
-    }
-  }
+  s.pop_back();  // null-terminating char

-  std::regex re_;
-  mutable std::cmatch match_data_;
-};
+  stacktrace.push_back(std::move(s));
+}

-}  // namespace base
+}  // namespace protovm
 }  // namespace perfetto
-
-#endif  // SRC_BASE_REGEX_REGEX_STD_H_
+// gen_amalgamated begin source: src/protovm/executor.cc
+// gen_amalgamated begin header: src/protovm/executor.h
+// gen_amalgamated begin header: src/protovm/ro_cursor.h
 /*
- * Copyright (C) 2026 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -52249,159 +57410,65 @@ class RegexStd {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/regex.h"
-
-#include <optional>
-#include <string>
-#include <string_view>
-#include <vector>
+#ifndef SRC_PROTOVM_RO_CURSOR_H_
+#define SRC_PROTOVM_RO_CURSOR_H_

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/flags.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/status_macros.h"
+#include <cstdint>
+#include <variant>

-// Unconditional: each header self-guards, so the amalgamator can see through.
-// gen_amalgamated expanded: #include "src/base/regex/regex_pcre2.h"
-// gen_amalgamated expanded: #include "src/base/regex/regex_re2.h"
-// gen_amalgamated expanded: #include "src/base/regex/regex_std.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"

-// Picks the regex backend and exposes it as PERFETTO_REGEX_BACKEND.
-// Preference order:
-//   1. PERFETTO_REGEX_FORCE_STD -> std::regex
-//   2. PCRE2 (when enabled and the runtime flag is on)
-//   3. RE2   (when enabled)
-//   4. std::regex (fallback)
-#if defined(PERFETTO_REGEX_FORCE_STD)
-#define PERFETTO_REGEX_BACKEND ::perfetto::base::RegexStd
-#elif PERFETTO_BUILDFLAG(PERFETTO_PCRE2) && PERFETTO_FLAGS_USE_PCRE2
-#define PERFETTO_REGEX_BACKEND ::perfetto::base::RegexPcre2
-#elif PERFETTO_BUILDFLAG(PERFETTO_RE2)
-#define PERFETTO_REGEX_BACKEND ::perfetto::base::RegexRe2
-#else
-#define PERFETTO_REGEX_BACKEND ::perfetto::base::RegexStd
-#endif
+// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
+// gen_amalgamated expanded: #include "src/protovm/scalar.h"

 namespace perfetto {
-namespace base {
+namespace protovm {

-// Pimpl for Regex. Holds the selected backend; Regex reaches into `backend`
-// directly (Regex is a friend below).
-class RegexImpl {
+class RoCursor {
  public:
-  using Backend = PERFETTO_REGEX_BACKEND;
-  explicit RegexImpl(Backend b) : backend(std::move(b)) {}
-
- private:
-  friend class Regex;
-  Backend backend;
-};
-#undef PERFETTO_REGEX_BACKEND
-
-Regex::Regex(Regex&&) noexcept = default;
-Regex& Regex::operator=(Regex&&) noexcept = default;
-Regex::~Regex() = default;
-
-StatusOr<Regex> Regex::Create(std::string_view pattern,
-                              Regex::CaseSensitivity cs) {
-  bool insensitive = (cs == CaseSensitivity::kInsensitive);
-  ASSIGN_OR_RETURN(auto backend,
-                   RegexImpl::Backend::Create(pattern, insensitive));
-  Regex regex;
-  regex.pattern_ = std::string(pattern);
-  regex.cs_ = cs;
-  regex.impl_ = std::make_unique<RegexImpl>(std::move(backend));
-  return std::move(regex);
-}
-
-Regex Regex::CreateOrCheck(std::string_view pattern,
-                           Regex::CaseSensitivity cs) {
-  auto re_or = Create(pattern, cs);
-  PERFETTO_CHECK(re_or.ok());
-  return std::move(*re_or);
-}
-
-std::string Regex::GlobalReplace(std::string_view s,
-                                 std::string_view replacement) const {
-  PERFETTO_DCHECK(impl_);
-  return impl_->backend.GlobalReplace(s, replacement);
-}
-
-bool Regex::FullMatch(std::string_view s) const {
-  PERFETTO_DCHECK(impl_);
-  return impl_->backend.FullMatch(s);
-}
-
-bool Regex::FullMatchWithGroups(std::string_view s,
-                                std::vector<std::string_view>& out) const {
-  PERFETTO_DCHECK(impl_);
-  return impl_->backend.FullMatchWithGroups(s, out);
-}
-
-bool Regex::PartialMatch(std::string_view s) const {
-  PERFETTO_DCHECK(impl_);
-  return impl_->backend.PartialMatch(s);
-}
-
-bool Regex::PartialMatchWithGroups(std::string_view s,
-                                   std::vector<std::string_view>& out) const {
-  PERFETTO_DCHECK(impl_);
-  return impl_->backend.PartialMatchWithGroups(s, out);
-}
-
-// --- PartialMatchIterator ---
-
-Regex::PartialMatchIterator::PartialMatchIterator(const Regex* re,
-                                                  std::string_view input)
-    : re_(re), input_(input) {}
-
-std::optional<std::string_view> Regex::PartialMatchIterator::Next() {
-  return NextWithGroups(groups_);
-}
-
-std::optional<std::string_view> Regex::PartialMatchIterator::NextWithGroups(
-    std::vector<std::string_view>& groups) {
-  if (offset_ > input_.size())
-    return std::nullopt;
-  PERFETTO_DCHECK(re_->impl_);
-  if (!re_->impl_->backend.SearchWithOffset(input_, offset_, groups))
-    return std::nullopt;
-  if (groups.empty())
-    return std::nullopt;
-  std::string_view match = groups[0];
-  size_t match_end =
-      static_cast<size_t>(match.data() - input_.data()) + match.size();
-  // Advance past the match. If match is empty, advance by one to avoid
-  // infinite loops.
-  offset_ = match.size() == 0 ? match_end + 1 : match_end;
-  return match;
-}
+  class RepeatedFieldIterator {
+   public:
+    RepeatedFieldIterator();
+    explicit RepeatedFieldIterator(protozero::ProtoDecoder decoder,
+                                   uint32_t field_id);
+    RepeatedFieldIterator& operator++();
+    RoCursor operator*();
+    explicit operator bool() const;

-Regex::PartialMatchIterator Regex::PartialMatchAll(std::string_view s) const {
-  return PartialMatchIterator(this, s);
-}
+   private:
+    protozero::Field Advance();

-// --- CopyableRegex ---
+    protozero::ProtoDecoder decoder_;
+    uint32_t field_id_;
+    protozero::Field field_;
+  };

-CopyableRegex::CopyableRegex(Regex regex) : regex_(std::move(regex)) {}
+  RoCursor();
+  explicit RoCursor(protozero::ConstBytes data);
+  explicit RoCursor(protozero::Field data);
+  StatusOr<void> EnterField(uint32_t field_id);
+  StatusOr<void> EnterRepeatedFieldAt(uint32_t field_id, uint32_t index);
+  StatusOr<RepeatedFieldIterator> IterateRepeatedField(uint32_t field_id) const;
+  bool IsScalar() const;
+  bool IsBytes() const;
+  StatusOr<Scalar> GetScalar() const;
+  StatusOr<protozero::ConstBytes> GetBytes() const;

-CopyableRegex::CopyableRegex(const CopyableRegex& other)
-    : regex_(Regex::CreateOrCheck(other.regex_.pattern(),
-                                  other.regex_.case_sensitivity())) {}
+ private:
+  StatusOr<protozero::ConstBytes> GetLengthDelimitedData() const;

-CopyableRegex& CopyableRegex::operator=(const CopyableRegex& other) {
-  if (this != &other) {
-    this->~CopyableRegex();
-    new (this) CopyableRegex(other);
-  }
-  return *this;
-}
+  std::variant<protozero::ConstBytes, protozero::Field> data_;
+};

-}  // namespace base
+}  // namespace protovm
 }  // namespace perfetto
-// gen_amalgamated begin source: src/base/clock_snapshots.cc
+
+#endif  // SRC_PROTOVM_RO_CURSOR_H_
+// gen_amalgamated begin header: src/protovm/rw_proto.h
+// gen_amalgamated begin header: src/protovm/rw_proto_cursor.h
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -52416,74 +57483,137 @@ CopyableRegex& CopyableRegex::operator=(const CopyableRegex& other) {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/clock_snapshots.h"
+#ifndef SRC_PROTOVM_RW_PROTO_CURSOR_H_
+#define SRC_PROTOVM_RW_PROTO_CURSOR_H_

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/base/time.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/builtin_clock.pbzero.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/field.h"

-namespace perfetto::base {
+// gen_amalgamated expanded: #include "src/protovm/allocator.h"
+// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
+// gen_amalgamated expanded: #include "src/protovm/node.h"

-ClockSnapshotVector CaptureClockSnapshots() {
-  ClockSnapshotVector snapshot_data;
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) &&   \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) &&     \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) && \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL) &&    \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_QNX)
-  struct {
-    clockid_t id;
-    protos::pbzero::BuiltinClock type;
-    struct timespec ts;
-  } clocks[] = {
-      {CLOCK_BOOTTIME, protos::pbzero::BUILTIN_CLOCK_BOOTTIME, {0, 0}},
-      {CLOCK_REALTIME_COARSE,
-       protos::pbzero::BUILTIN_CLOCK_REALTIME_COARSE,
-       {0, 0}},
-      {CLOCK_MONOTONIC_COARSE,
-       protos::pbzero::BUILTIN_CLOCK_MONOTONIC_COARSE,
-       {0, 0}},
-      {CLOCK_REALTIME, protos::pbzero::BUILTIN_CLOCK_REALTIME, {0, 0}},
-      {CLOCK_MONOTONIC, protos::pbzero::BUILTIN_CLOCK_MONOTONIC, {0, 0}},
-      {CLOCK_MONOTONIC_RAW,
-       protos::pbzero::BUILTIN_CLOCK_MONOTONIC_RAW,
-       {0, 0}},
+namespace perfetto {
+namespace protovm {
+
+class RwProtoCursor {
+ public:
+  enum MergeFlags : uint32_t {
+    kNone = 0,
+    kSkipSubmessages = 1 << 0,
+    kDelIfSrcEmpty = 1 << 1,
+  };
+
+  class RepeatedFieldIterator {
+   public:
+    RepeatedFieldIterator();
+    RepeatedFieldIterator(Allocator* allocator, IntrusiveMap::Iterator it);
+    RepeatedFieldIterator& operator++();
+    RwProtoCursor GetCursor();
+    explicit operator bool() const;
+
+   private:
+    Allocator* allocator_{nullptr};
+    IntrusiveMap::Iterator it_;
+  };
+
+  RwProtoCursor();
+  explicit RwProtoCursor(Node* node, Allocator* allocator);
+
+  bool operator==(const RwProtoCursor& other) const;
+  bool operator!=(const RwProtoCursor& other) const;
+
+  StatusOr<bool> IsRoot() const;
+  StatusOr<bool> HasField(uint32_t field_id);
+  StatusOr<void> EnterField(uint32_t field_id);
+  StatusOr<void> EnterRepeatedFieldAt(uint32_t field_id, uint32_t index);
+  StatusOr<RepeatedFieldIterator> IterateRepeatedField(uint32_t field_id);
+
+  // Enters a specific repeated field, treating it as a map of key-value pairs.
+  //
+  // The function operates on the principle that the repeated field (identified
+  // by <field_id>) can be viewed as a collection of key-value pairs, where:
+  //  - Each message element within the repetition is a "value".
+  //  - A specific subfield within each message element (identified by
+  //   <map_key_field_id>) serves as the unique key for that element.
+  //
+  // The function performs a lookup using the provided <key> and, if a match
+  // is found, moves the cursor into the corresponding repeated field.
+  //
+  // Example:
+  //
+  // // Message currently pointer by the cursor
+  // message CurrentMessage {
+  //   repeated KeyValuePair entries = <field_id>;
+  // }
+  //
+  // message KeyValuePair {
+  //   uint32 key = <map_key_field_id>;
+  //   string value_x = ...;
+  //   string value_y = ...;
+  // }
+  //
+  // In this example <field_id> points to CurrentMessage.entries,
+  // <map_key_field_id> points to KeyValuePair.key, and <key> = 10. So the
+  // function attempts to find and enter the KeyValuePair message where
+  // key == 10.
+  StatusOr<void> EnterRepeatedFieldByKey(uint32_t field_id,
+                                         uint32_t map_key_field_id,
+                                         uint64_t key);
+
+  StatusOr<Scalar> GetScalar() const;
+  StatusOr<void> SetBytes(protozero::ConstBytes data);
+  StatusOr<void> SetScalar(Scalar scalar);
+
+  // Perform a shallow (one level) merge of two messages.
+  // The cursor must currently point to a message and 'data' must contain a
+  // message. Merge fields from the 'data' message into the message pointed by
+  // cursor. Existing fields in the cursor's message are overwritten by fields
+  // from 'data'. Fields present in 'data' but not in the cursor's message are
+  // added/created. If skip_submessages is set to true, any message node already
+  // resolved in the dst are skipped, thus not overwritten by the parent merge.
+  StatusOr<void> Merge(protozero::ConstBytes data, uint32_t flags);
+
+  StatusOr<void> Delete();
+
+ private:
+  struct ParentLink {
+    Node* node;
+    IntrusiveMap* map;
+    MapNode* map_node;
   };
-  // First snapshot all the clocks as atomically as we can.
-  for (auto& clock : clocks) {
-    if (clock_gettime(clock.id, &clock.ts) == -1)
-      PERFETTO_DLOG("clock_gettime failed for clock %d", clock.id);
-  }
-  for (auto& clock : clocks) {
-    snapshot_data.push_back(ClockReading(
-        static_cast<uint32_t>(clock.type),
-        static_cast<uint64_t>(base::FromPosixTimespec(clock.ts).count())));
-  }
-#else  // OS_APPLE || OS_WIN && OS_NACL
-  auto wall_time_ns = static_cast<uint64_t>(base::GetWallTimeNs().count());
-  // The default trace clock is boot time, so we always need to emit a path to
-  // it. However since we don't actually have a boot time source on these
-  // platforms, pretend that wall time equals boot time.
-  snapshot_data.push_back(
-      ClockReading(protos::pbzero::BUILTIN_CLOCK_BOOTTIME, wall_time_ns));
-  snapshot_data.push_back(
-      ClockReading(protos::pbzero::BUILTIN_CLOCK_MONOTONIC, wall_time_ns));
-#endif

-#if PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_X86_64)
-  // X86-specific but OS-independent TSC clocksource
-  snapshot_data.push_back(
-      ClockReading(protos::pbzero::BUILTIN_CLOCK_TSC, base::Rdtsc()));
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_X86_64)
+  StatusOr<void> ConvertToMessageIfNeeded(Node* node);
+  StatusOr<OwnedPtr<Node>> CreateNodeFromField(protozero::Field field);
+  StatusOr<void> ConvertToMappedRepeatedFieldIfNeeded(
+      Node* node,
+      uint32_t map_key_field_id);
+  StatusOr<void> ConvertToIndexedRepeatedFieldIfNeeded(Node* node);
+  StatusOr<IntrusiveMap::Iterator> FindOrCreateMessageField(Node* node,
+                                                            uint32_t field_id);
+  StatusOr<IntrusiveMap::Iterator> FindOrCreateIndexedRepeatedField(
+      Node* node,
+      uint32_t index);
+  StatusOr<IntrusiveMap::Iterator> FindOrCreateMappedRepeatedField(
+      Node* node,
+      uint64_t key);
+  StatusOr<IntrusiveMap::Iterator> MapInsert(IntrusiveMap* map,
+                                             uint64_t key,
+                                             OwnedPtr<Node> map_value);
+  StatusOr<uint64_t> ReadScalarField(const Node& node, uint32_t field_id);

-  return snapshot_data;
-}
+  StatusOr<void> CheckIsValid() const;

-}  // namespace perfetto::base
-// gen_amalgamated begin source: src/base/version.cc
-// gen_amalgamated begin header: include/perfetto/ext/base/version.h
+  Node* node_ = nullptr;
+  ParentLink parent_link_ = {};
+  Allocator* allocator_ = nullptr;
+};
+
+}  // namespace protovm
+}  // namespace perfetto
+
+#endif  // SRC_PROTOVM_RW_PROTO_CURSOR_H_
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -52498,49 +57628,150 @@ ClockSnapshotVector CaptureClockSnapshots() {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_VERSION_H_
-#define INCLUDE_PERFETTO_EXT_BASE_VERSION_H_
+#ifndef SRC_PROTOVM_RW_PROTO_H_
+#define SRC_PROTOVM_RW_PROTO_H_

-namespace perfetto {
-namespace base {
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"

-// The returned pointer is a static string and safe to pass around.
-// Returns a human readable string currently of the approximate form:
-// Perfetto v42.1-deadbeef0 (deadbeef03c641e4b4ea9cf38e9b5696670175a9)
-// However you should not depend on the format of this string.
-// It maybe not be possible to determine the version. In which case the
-// string will be of the approximate form:
-// Perfetto v0.0 (unknown)
-const char* GetVersionString();
+// gen_amalgamated expanded: #include "src/protovm/allocator.h"
+// gen_amalgamated expanded: #include "src/protovm/node.h"
+// gen_amalgamated expanded: #include "src/protovm/rw_proto_cursor.h"

-// The returned pointer is a static string and safe to pass around.
-// Returns the short code used to identity the version:
-// v42.1-deadbeef0
-// It maybe not be possible to determine the version. In which case
-// this returns nullptr.
-// This can be compared with equality to other
-// version codes to detect matched builds (for example to see if
-// trace_processor_shell and the UI were built at the same revision)
-// but you should not attempt to parse it as the format may change
-// without warning.
-const char* GetVersionCode();
+namespace perfetto {
+namespace protovm {

-}  // namespace base
-}  // namespace perfetto
+// RwProto provides an API to create and manipulate protobuf messages without
+// requiring prior knowledge of the schema. Meaning that protobuf messages can
+// be dynamically built on-the-fly, without pre-compiled .proto definitions.
+//
+// Key features:
+//
+// - Schema-agnostic: RwProto dynamically learns the schema as fields and
+//   messages are added.
+//
+// - Tree-like representation: protobuf messages are represented internally as a
+//   tree of nodes, mirroring the nested structure of protobufs. This tree can
+//   be traversed and manipulated using a Cursor object.
+//
+// - Fine-grained Memory Management: a centralized allocator is used to manage
+//   memory for nodes, ensuring good data locality and fine-grained control over
+//   memory usage.
+//
+// Overview of RwProto's internal node structure:
+//
+//                             ┌─────────┐
+//                             │         │
+//                             │ Message │
+//                             │         │ Map<field_id, OwnedPtr<Node>>
+//                             └─┬┬┬┬┬┬──┘
+//                               │││││└────────────────────────────────┐
+//                               ││││└───────────────────────┐         │
+//               ┌───────────────┘││└──────────────┐         │         │
+//               │            ┌───┘└────┐          │         │         │
+//               │            │    ┌────▼────┐ ┌───▼───┐ ┌───▼───┐ ┌───▼────┐
+//               │            │    │         │ │       │ │       │ │        │
+//               │            │    │ Message │ │ Empty │ │ Bytes │ │ Scalar │
+//               │            │    │         │ │       │ │       │ │        │
+//               │            │    └────┬────┘ └───────┘ └───────┘ └────────┘
+//               │            │         ▼
+//               │            │        ...
+//               │            │
+//               │            │
+//               │   *********▼********
+//               │   *                *
+//               │   * Mapped         *
+//               │   * RepeatedField  *
+//               │   *                *
+//               │   ******************  Map<key, OwnedPtr<Node>>
+//               │           │ │
+//               │       ┌───┘ └────┐
+//               │       │          │
+//               │  ┌────▼────┐ ┌───▼───┐
+//               │  │         │ │       │
+//               │  │ Message │ │ Bytes │
+//               │  │         │ │       │
+//               │  └────┬────┘ └───────┘
+//               │       ▼
+//               │      ...
+//               │
+//
+//       ********▼*********
+//       *                *
+//       * Indexed        *
+//       * RepeatedField  *
+//       *                *
+//       ******************  Map<index, OwnedPtr<Node>>
+//             ││││
+//      ┌──────┘││└────────────────────┐
+//      │       │└───────────┐         │
+//      │       └──┐         │         │
+//      │          │         │         │
+// ┌────▼────┐ ┌───▼───┐ ┌───▼───┐ ┌───▼────┐
+// │         │ │       │ │       │ │        │
+// │ Message │ │ Empty │ │ Bytes │ │ Scalar │
+// │         │ │       │ │       │ │        │
+// └────┬────┘ └───────┘ └───────┘ └────────┘
+//      ▼
+//     ...
+//
+//
+// ┌─┐
+// │ │  Data node: corresponds to an actual field/message in the proto schema
+// └─┘             and eventually becomes part of the final serialized proto.
+//
+// ***
+// * *  Structural node: used to organize and provide access to message fields.
+// ***                   It is an internal helper and doesn't appear in the
+//                       final serialized proto.
+//
+//
+// Message: Represents a protobuf message. It maps field_id to corresponding
+//          node/value. It allows accessing fields as well as adding and
+//          removing them.
+//
+//
+// Empty: Represents an empty or uninitialized node. It is used as a
+//        placeholder before a node's type is determined. E.g. between an
+//        "enter field" and a "set field value" operation.
+//
+//
+// Bytes: Stores a sequence of bytes, typically representing a length-delimited
+//        protobuf field.
+//
+//
+// Scalar: Stores a scalar value.
+//
+//
+// IndexedRepeatedField: Represents a protobuf repeated field where elements
+//                       are accessed by index.
+//
+//
+// MappedRepeatedField: Represents a protobuf repeated field where elements
+//                      are accessed by key.
+class RwProto {
+ public:
+  using Cursor = RwProtoCursor;

-#endif  // INCLUDE_PERFETTO_EXT_BASE_VERSION_H_
-// gen_amalgamated begin header: gen/perfetto_version.gen.h
-// Generated by write_version_header.py
+  explicit RwProto(Allocator* allocator);
+  ~RwProto();
+  Cursor GetRoot();
+  void Serialize(protozero::Message*) const;

-#ifndef GEN_PERFETTO_VERSION_GEN_H_
-#define GEN_PERFETTO_VERSION_GEN_H_
+ private:
+  void SerializeField(uint32_t field_id,
+                      const Node& node,
+                      protozero::Message* proto) const;
+
+  Allocator* allocator_{nullptr};
+  Node root_;
+};

-#define PERFETTO_VERSION_STRING() "v58.2-add693d8b"
-#define PERFETTO_VERSION_SCM_REVISION() "add693d8b338ba9599dbcbc3e300b1ab8c000897"
+}  // namespace protovm
+}  // namespace perfetto

-#endif  // GEN_PERFETTO_VERSION_GEN_H_
+#endif  // SRC_PROTOVM_RW_PROTO_H_
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -52555,46 +57786,68 @@ const char* GetVersionCode();
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "perfetto/ext/base/version.h"
+#ifndef SRC_PROTOVM_EXECUTOR_H_
+#define SRC_PROTOVM_EXECUTOR_H_

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+#include <cstdint>
+#include <optional>

-#include <stdio.h>
+// gen_amalgamated expanded: #include "protos/perfetto/protovm/vm_program.pbzero.h"

-#if PERFETTO_BUILDFLAG(PERFETTO_VERSION_GEN)
-// gen_amalgamated expanded: #include "perfetto_version.gen.h"
-#else
-#define PERFETTO_VERSION_STRING() nullptr
-#define PERFETTO_VERSION_SCM_REVISION() "unknown"
-#endif
+// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
+// gen_amalgamated expanded: #include "src/protovm/ro_cursor.h"
+// gen_amalgamated expanded: #include "src/protovm/rw_proto.h"

 namespace perfetto {
-namespace base {
+namespace protovm {

-const char* GetVersionCode() {
-  return PERFETTO_VERSION_STRING();
-}
+using CursorEnum = perfetto::protos::pbzero::VmCursorEnum;

-const char* GetVersionString() {
-  static const char* version_str = [] {
-    static constexpr size_t kMaxLen = 256;
-    const char* version_code = PERFETTO_VERSION_STRING();
-    if (version_code == nullptr) {
-      version_code = "v0.0";
-    }
-    char* version = new char[kMaxLen + 1];
-    snprintf(version, kMaxLen, "Perfetto %s (%s)", version_code,
-             PERFETTO_VERSION_SCM_REVISION());
-    return version;
-  }();
-  return version_str;
-}
+struct Cursors {
+  RoCursor src;
+  RwProto::Cursor dst;
+  CursorEnum selected{CursorEnum::VM_CURSOR_UNSPECIFIED};
+  bool create_if_not_exist{false};

-}  // namespace base
+  // Used internally by Parser to detect illegal "del" operations.
+  // See detailed explanation in the Parser implementation.
+  const RwProto::Cursor* innermost_saved_dst{nullptr};
+};
+
+// Executor's methods are virtual to be overridden in tests
+class Executor {
+ public:
+  using MergeFlags = RwProto::Cursor::MergeFlags;
+
+  virtual ~Executor();
+  virtual StatusOr<void> EnterField(Cursors* cursors, uint32_t field_id) const;
+  virtual StatusOr<void> EnterRepeatedFieldAt(Cursors* cursors,
+                                              uint32_t field_id,
+                                              uint32_t index) const;
+  virtual StatusOr<void> EnterRepeatedFieldByKey(Cursors* cursors,
+                                                 uint32_t field_id,
+                                                 uint32_t map_key_field_id,
+                                                 uint64_t key) const;
+  virtual StatusOr<RoCursor::RepeatedFieldIterator> IterateRepeatedField(
+      RoCursor* src,
+      uint32_t field_id) const;
+  virtual StatusOr<RwProto::Cursor::RepeatedFieldIterator> IterateRepeatedField(
+      RwProto::Cursor* dst,
+      uint32_t field_id) const;
+  virtual StatusOr<uint64_t> ReadRegister(uint8_t reg_id) const;
+  virtual StatusOr<void> WriteRegister(const Cursors& cursors, uint8_t reg_id);
+  virtual StatusOr<void> Delete(RwProto::Cursor* dst) const;
+  virtual StatusOr<void> Merge(Cursors* cursors, uint32_t flags) const;
+  virtual StatusOr<void> Set(Cursors* cursors) const;
+
+ private:
+  std::array<std::optional<uint64_t>, 32> registers_;
+};
+
+}  // namespace protovm
 }  // namespace perfetto
-// gen_amalgamated begin source: src/protovm/allocator.cc
-// gen_amalgamated begin header: src/protovm/allocator.h
-// gen_amalgamated begin header: src/protovm/error_handling.h
+
+#endif  // SRC_PROTOVM_EXECUTOR_H_
 /*
  * Copyright (C) 2025 The Android Open Source Project
  *
@@ -52611,261 +57864,126 @@ const char* GetVersionString() {
  * limitations under the License.
  */

-#ifndef SRC_PROTOVM_ERROR_HANDLING_H_
-#define SRC_PROTOVM_ERROR_HANDLING_H_
-
-#include <cstdarg>
-#include <cstdint>
-#include <memory>
-#include <new>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-
-#if defined(__GNUC__) || defined(__clang__)
-#if defined(__clang__)
-#pragma clang diagnostic push
-// Fix 'error: #pragma system_header ignored in main file' for amalgamated
-// build.
-#pragma clang diagnostic ignored "-Wpragma-system-header-outside-header"
-#endif
-
-// Ignore GCC warning about a missing argument for a variadic macro parameter.
-#pragma GCC system_header
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-#endif
+// gen_amalgamated expanded: #include "src/protovm/executor.h"

 namespace perfetto {
 namespace protovm {

-using Stacktrace = std::vector<std::string>;
-
-void LogStacktraceMessage(Stacktrace& stacktrace,
-                          const char* file_name,
-                          int file_line,
-                          const char* fmt,
-                          ...) PERFETTO_PRINTF_FORMAT(4, 5);
-
-// overload for calls without message args
-void LogStacktraceMessage(Stacktrace& stacktrace,
-                          const char* file_name,
-                          int file_line);
-
-#define PROTOVM_ABORT(fmt, ...)                                       \
-  do {                                                                \
-    auto status_abort = StatusOr<void>::Abort();                      \
-    LogStacktraceMessage(status_abort.stacktrace(),                   \
-                         ::perfetto::base::LoggingBasename(__FILE__), \
-                         __LINE__, fmt, ##__VA_ARGS__);               \
-    return status_abort;                                              \
-  } while (0)
-
-#define PROTOVM_RETURN(s, ...)                                          \
-  do {                                                                  \
-    if (s.IsAbort()) {                                                  \
-      LogStacktraceMessage(s.stacktrace(),                              \
-                           ::perfetto::base::LoggingBasename(__FILE__), \
-                           __LINE__, ##__VA_ARGS__);                    \
-    }                                                                   \
-    return s;                                                           \
-  } while (0)
-
-#define PROTOVM_RETURN_IF_NOT_OK(s, ...)                                \
-  do {                                                                  \
-    if (s.IsAbort()) {                                                  \
-      LogStacktraceMessage(s.stacktrace(),                              \
-                           ::perfetto::base::LoggingBasename(__FILE__), \
-                           __LINE__, ##__VA_ARGS__);                    \
-      return s;                                                         \
-    }                                                                   \
-    if (!s.IsOk()) {                                                    \
-      return s;                                                         \
-    }                                                                   \
-  } while (0)
-
-#define PROTOVM_INTERNAL_CONCAT_IMPL(x, y) x##y
-#define PROTOVM_INTERNAL_MACRO_CONCAT(x, y) PROTOVM_INTERNAL_CONCAT_IMPL(x, y)
-
-// Evaluates |rhs| which should return a StatusOr<T> and assigns this
-// to |lhs|. If the status is an error status, returns the status from the
-// current function.
-#define PROTOVM_ASSIGN_OR_RETURN(lhs, rhs)                       \
-  PROTOVM_INTERNAL_MACRO_CONCAT(auto status_or, __LINE__) = rhs; \
-  PROTOVM_RETURN_IF_NOT_OK(                                      \
-      PROTOVM_INTERNAL_MACRO_CONCAT(status_or, __LINE__));       \
-  lhs = std::move(PROTOVM_INTERNAL_MACRO_CONCAT(status_or, __LINE__).value())
-
-enum class Status : uint8_t {
-  kOk,
-  kError,
-
-  // It causes an abort of the whole VM's program. Typically indicates a
-  // fundamental issue with the program and requires developer's intervention.
-  kAbort,
-};
-
-namespace internal {
-
-template <class T, class Arg, class... Args>
-static constexpr bool is_copy_or_move_ctor_arg_v =
-    sizeof...(Args) == 0 && std::is_same_v<T, std::decay_t<Arg>>;
-
-template <class T>
-inline constexpr std::size_t sizeof_v = sizeof(T);
+Executor::~Executor() = default;

-template <>
-inline constexpr std::size_t sizeof_v<void> = 0;
+StatusOr<void> Executor::EnterField(Cursors* cursors, uint32_t field_id) const {
+  if (cursors->selected == CursorEnum::VM_CURSOR_SRC) {
+    return cursors->src.EnterField(field_id);
+  }

-template <class T>
-inline constexpr std::size_t alignof_v = alignof(T);
+  auto has_field = cursors->dst.HasField(field_id);
+  PROTOVM_RETURN_IF_NOT_OK(has_field);

-template <>
-inline constexpr std::size_t alignof_v<void> = 0;
+  if (!has_field.value() && !cursors->create_if_not_exist) {
+    return StatusOr<void>::Error();
+  }

-}  // namespace internal
+  return cursors->dst.EnterField(field_id);
+}

-template <class T>
-class StatusOr {
- public:
-  // Factory methods to create empty instances (without value)
-  static StatusOr Ok() { return StatusOr{Status::kOk}; }
-  static StatusOr Error() { return StatusOr{Status::kError}; }
-  static StatusOr Abort() { return StatusOr{Status::kAbort}; }
+StatusOr<void> Executor::EnterRepeatedFieldAt(Cursors* cursors,
+                                              uint32_t field_id,
+                                              uint32_t index) const {
+  if (cursors->selected == CursorEnum::VM_CURSOR_SRC) {
+    return cursors->src.EnterRepeatedFieldAt(field_id, index);
+  }

-  StatusOr(const StatusOr&) = delete;
+  return cursors->dst.EnterRepeatedFieldAt(field_id, index);
+}

-  StatusOr(StatusOr&& other) : status_{other.status_} {
-    if (IsOk()) {
-      if constexpr (!std::is_same_v<T, void>) {
-        new (storage_) T(std::move(other.value()));
-      }
-    } else if (IsAbort()) {
-      new (storage_) std::unique_ptr<Stacktrace>(
-          std::move(other.GetStacktraceUniquePtr()));
-    }
+StatusOr<void> Executor::EnterRepeatedFieldByKey(Cursors* cursors,
+                                                 uint32_t field_id,
+                                                 uint32_t map_key_field_id,
+                                                 uint64_t key) const {
+  if (cursors->selected == CursorEnum::VM_CURSOR_SRC) {
+    PROTOVM_ABORT(
+        "Mapped repeated fields are currently supported only in RwProto");
   }

-  // Intentionally implicit to allow idiomatic usage (return plain value to be
-  // implicitly converted to StatusOr)
-  template <
-      class... Args,
-      // sfinae to avoid clashing with copy/move constructors
-      std::enable_if_t<!internal::is_copy_or_move_ctor_arg_v<StatusOr, Args...>,
-                       int> = 0>
-  StatusOr(Args&&... args) : status_{Status::kOk} {
-    if constexpr (!std::is_same_v<T, void>) {
-      new (storage_) T(std::forward<Args>(args)...);
-    }
-  }
+  auto has_field = cursors->dst.HasField(field_id);
+  PROTOVM_RETURN_IF_NOT_OK(has_field);

-  // Intentionally implicit to allow idiomatic usage (e.g. return StatusOr<A> to
-  // be implicitely converted to StatusOr<B>).
-  // When status is ok, the conversion is only allowed if B = void, otherwise it
-  // would require a conversion from A to non-void B.
-  template <class U, std::enable_if_t<!std::is_same_v<U, T>, int> = 0>
-  StatusOr(StatusOr<U>&& other) : status_{other.status_} {
-    if (IsOk()) {
-      PERFETTO_DCHECK((std::is_same_v<T, void>));
-    } else if (IsAbort()) {
-      new (storage_) std::unique_ptr<Stacktrace>(
-          std::move(other.GetStacktraceUniquePtr()));
-    }
+  if (!has_field.value() && !cursors->create_if_not_exist) {
+    return StatusOr<void>::Error();
   }

-  ~StatusOr() {
-    if (IsOk()) {
-      if constexpr (!std::is_same_v<T, void>) {
-        value().~T();
-      }
-    }
-    if (IsAbort()) {
-      GetStacktraceUniquePtr().~unique_ptr();
-    }
-  }
+  return cursors->dst.EnterRepeatedFieldByKey(field_id, map_key_field_id, key);
+}

-  bool IsOk() const { return status_ == Status::kOk; }
-  bool IsError() const { return status_ == Status::kError; }
-  bool IsAbort() const { return status_ == Status::kAbort; }
+StatusOr<RoCursor::RepeatedFieldIterator> Executor::IterateRepeatedField(
+    RoCursor* src,
+    uint32_t field_id) const {
+  return src->IterateRepeatedField(field_id);
+}

-  template <class U = T>
-  std::enable_if_t<!std::is_same_v<U, void>, U&> value() {
-    PERFETTO_DCHECK(IsOk());
-    return *std::launder(reinterpret_cast<T*>(storage_));
-  }
+StatusOr<RwProto::Cursor::RepeatedFieldIterator> Executor::IterateRepeatedField(
+    RwProto::Cursor* dst,
+    uint32_t field_id) const {
+  return dst->IterateRepeatedField(field_id);
+}

-  template <class U = T>
-  std::enable_if_t<!std::is_same_v<U, void>, const U&> value() const {
-    return const_cast<StatusOr*>(this)->value();
+StatusOr<uint64_t> Executor::ReadRegister(uint8_t reg_id) const {
+  if (reg_id >= registers_.size()) {
+    PROTOVM_ABORT("Register (id = %d) is out of bounds", reg_id);
+  }
+  if (!registers_[reg_id]) {
+    PROTOVM_ABORT("Register (id = %d) is not initialized)", reg_id);
   }
+  return StatusOr<uint64_t>{*registers_[reg_id]};
+}

-  template <class U = T>
-  std::enable_if_t<!std::is_same_v<U, void>, U&> operator*() {
-    return value();
+StatusOr<void> Executor::WriteRegister(const Cursors& cursors, uint8_t reg_id) {
+  if (reg_id >= registers_.size()) {
+    PROTOVM_ABORT("Register (id = %d) is out of bounds", reg_id);
   }

-  template <class U = T>
-  std::enable_if_t<!std::is_same_v<U, void>, const U&> operator*() const {
-    return value();
-  }
+  auto status_or_scalar = cursors.selected == CursorEnum::VM_CURSOR_SRC
+                              ? cursors.src.GetScalar()
+                              : cursors.dst.GetScalar();
+  PROTOVM_RETURN_IF_NOT_OK(status_or_scalar);

-  template <class U = T>
-  std::enable_if_t<!std::is_same_v<U, void>, U*> operator->() {
-    return &value();
-  }
+  registers_[reg_id] = status_or_scalar->value;
+  return StatusOr<void>::Ok();
+}

-  template <class U = T>
-  std::enable_if_t<!std::is_same_v<U, void>, const U*> operator->() const {
-    return &value();
-  }
+StatusOr<void> Executor::Delete(RwProto::Cursor* dst) const {
+  return dst->Delete();
+}

-  Stacktrace& stacktrace() {
-    PERFETTO_DCHECK(IsAbort());
-    return **std::launder(
-        reinterpret_cast<std::unique_ptr<Stacktrace>*>(storage_));
+StatusOr<void> Executor::Merge(Cursors* cursors, uint32_t flags) const {
+  if (!cursors->src.IsBytes()) {
+    PROTOVM_ABORT(
+        "Attempted MERGE operation but src cursor has incompatible data "
+        "type");
   }

- private:
-  template <class U>
-  friend class StatusOr;
+  return cursors->dst.Merge(*cursors->src.GetBytes(), flags);
+}

-  // Intentionally private to encourage the use of factory methods, instead of
-  // plain status values. E.g. StatusOr::Abort() instead of Status::kAbort.
-  // Plain status value don't include stacktrace information in case of an
-  // abort.
-  explicit StatusOr(Status status) : status_{status} {
-    PERFETTO_DCHECK((std::is_same_v<T, void>) || !IsOk());
-    if (IsAbort()) {
-      new (storage_) std::unique_ptr<Stacktrace>{new Stacktrace{}};
-    }
+StatusOr<void> Executor::Set(Cursors* cursors) const {
+  if (cursors->src.IsScalar()) {
+    auto status_or_scalar = cursors->src.GetScalar();
+    PROTOVM_RETURN_IF_NOT_OK(status_or_scalar);
+    return cursors->dst.SetScalar(*status_or_scalar);
   }

-  std::unique_ptr<Stacktrace>& GetStacktraceUniquePtr() {
-    PERFETTO_DCHECK(IsAbort());
-    return *std::launder(
-        reinterpret_cast<std::unique_ptr<Stacktrace>*>(storage_));
+  if (cursors->src.IsBytes()) {
+    auto status_or_bytes = cursors->src.GetBytes();
+    PROTOVM_RETURN_IF_NOT_OK(status_or_bytes);
+    return cursors->dst.SetBytes(*status_or_bytes);
   }

-  Status status_;
-
-  static constexpr size_t kStorageAlign =
-      std::max(internal::alignof_v<T>, alignof(std::unique_ptr<Stacktrace>));
-  static constexpr size_t kStorageSize =
-      std::max(internal::sizeof_v<T>, sizeof(std::unique_ptr<Stacktrace>));
-  alignas(kStorageAlign) unsigned char storage_[kStorageSize];
-};
+  PROTOVM_ABORT("Attempted SET operation but src cursor has no valid data");
+}

 }  // namespace protovm
 }  // namespace perfetto
-
-#endif  // SRC_PROTOVM_ERROR_HANDLING_H_
-// gen_amalgamated begin header: src/protovm/node.h
-// gen_amalgamated begin header: src/protovm/owned_ptr.h
+// gen_amalgamated begin source: src/protovm/node.cc
 /*
  * Copyright (C) 2025 The Android Open Source Project
  *
@@ -52882,68 +58000,38 @@ class StatusOr {
  * limitations under the License.
  */

-#ifndef SRC_PROTOVM_OWNED_PTR_H_
-#define SRC_PROTOVM_OWNED_PTR_H_
-
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "src/protovm/node.h"

 namespace perfetto {
 namespace protovm {

-// OwnedPtr is essentially a std::unique_ptr that doesn't perform any deletion
-// itself. Its purpose is to clearly express ownership semantics for objects
-// that are managed with a custom allocators.
-//
-// It is not a unique_ptr, because that's not guaranteed to be standard layout
-//
-// OwnedPtr is extensively used within RwProto to manage the lifecycle and
-// clarify ownership of the internal, manually-allocated nodes.
-template <class T>
-class OwnedPtr {
- public:
-  using pointer = T*;
-  OwnedPtr(const OwnedPtr&) = delete;
-  OwnedPtr(OwnedPtr&& other) {
-    p_ = other.p_;
-    other.p_ = nullptr;
-  }
-  OwnedPtr(T* p) noexcept : p_(p) {}  // NOLINT: explicit
-  ~OwnedPtr() noexcept { reset(); }
-  OwnedPtr& operator=(const OwnedPtr&) = delete;
-  OwnedPtr& operator=(OwnedPtr&& other) noexcept {
-    if (this == &other) {
-      return *this;
-    }
-    reset();
-    p_ = other.p_;
-    other.p_ = nullptr;
-    return *this;
-  }
-  T* get() const noexcept { return p_; }
-  T* release() noexcept {
-    T* p = p_;
-    p_ = nullptr;
-    return p;
-  }
-  typename std::add_lvalue_reference<T>::type operator*() const noexcept {
-    return *p_;
-  }
-  explicit operator bool() const noexcept { return get() != nullptr; }
-  T* operator->() const noexcept { return p_; }
-  void reset(T* p = pointer()) noexcept {
-    PERFETTO_DCHECK(p_ == nullptr);
-    p_ = p;
-  }
-
- private:
-  T* p_ = nullptr;
-};
+const char* Node::GetTypeName() const {
+  return std::visit(
+      [](auto&& value) {
+        using T = std::decay_t<decltype(value)>;
+        if constexpr (std::is_same_v<T, Node::Bytes>) {
+          return "Bytes";
+        } else if constexpr (std::is_same_v<T, Node::Empty>) {
+          return "Empty";
+        } else if constexpr (std::is_same_v<T, Node::IndexedRepeatedField>) {
+          return "IndexedRepeatedField";
+        } else if constexpr (std::is_same_v<T, Node::MappedRepeatedField>) {
+          return "MappedRepeatedField";
+        } else if constexpr (std::is_same_v<T, Node::Message>) {
+          return "Message";
+        } else if constexpr (std::is_same_v<T, Scalar>) {
+          return "Scalar";
+        } else {
+          return "<unknown>";
+        }
+      },
+      value);
+}

 }  // namespace protovm
 }  // namespace perfetto
-
-#endif  // SRC_PROTOVM_OWNED_PTR_H_
-// gen_amalgamated begin header: src/protovm/scalar.h
+// gen_amalgamated begin source: src/protovm/parser.cc
+// gen_amalgamated begin header: src/protovm/parser.h
 /*
  * Copyright (C) 2025 The Android Open Source Project
  *
@@ -52960,41 +58048,50 @@ class OwnedPtr {
  * limitations under the License.
  */

-#ifndef SRC_PROTOVM_SCALAR_H_
-#define SRC_PROTOVM_SCALAR_H_
+#ifndef SRC_PROTOVM_PARSER_H_
+#define SRC_PROTOVM_PARSER_H_

-// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
+// gen_amalgamated expanded: #include "protos/perfetto/protovm/vm_program.pbzero.h"
+
+// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
+// gen_amalgamated expanded: #include "src/protovm/executor.h"
+// gen_amalgamated expanded: #include "src/protovm/ro_cursor.h"
+// gen_amalgamated expanded: #include "src/protovm/rw_proto.h"

 namespace perfetto {
 namespace protovm {

-struct Scalar {
-  static Scalar Fixed32(uint32_t value) {
-    return Scalar{protozero::proto_utils::ProtoWireType::kFixed32, value};
-  }
-
-  static Scalar Fixed64(uint64_t value) {
-    return Scalar{protozero::proto_utils::ProtoWireType::kFixed64, value};
-  }
-
-  static Scalar VarInt(uint64_t value) {
-    return Scalar{protozero::proto_utils::ProtoWireType::kVarInt, value};
-  }
-
-  bool operator==(const Scalar& other) const {
-    return wire_type == other.wire_type && value == other.value;
-  }
+class Parser {
+ public:
+  Parser(protozero::ConstBytes program, Executor* executor);
+  StatusOr<void> Run(RoCursor src, RwProto::Cursor dst);

-  bool operator!=(const Scalar& other) const { return !(*this == other); }
+ private:
+  StatusOr<void> ParseInstructions(
+      protozero::RepeatedFieldIterator<protozero::ConstBytes> it_instruction);
+  StatusOr<void> ParseInstruction(
+      const protos::pbzero::VmInstruction::Decoder& instruction);
+  StatusOr<void> ParseMerge(
+      const protos::pbzero::VmInstruction::Decoder& instruction);
+  StatusOr<void> ParseRegLoad(
+      const protos::pbzero::VmInstruction::Decoder& instruction);
+  StatusOr<void> ParseSelect(
+      const protos::pbzero::VmInstruction::Decoder& instruction);
+  StatusOr<void> ParseSelectRec(
+      const protos::pbzero::VmInstruction::Decoder& instruction,
+      protozero::RepeatedFieldIterator<protozero::ConstBytes>
+          it_path_component);

-  protozero::proto_utils::ProtoWireType wire_type;
-  uint64_t value;
+  protos::pbzero::VmProgram::Decoder program_;
+  Cursors cursors_;
+  Executor* executor_;
 };

 }  // namespace protovm
 }  // namespace perfetto

-#endif  // SRC_PROTOVM_SCALAR_H_
+#endif  // SRC_PROTOVM_PARSER_H_
 /*
  * Copyright (C) 2025 The Android Open Source Project
  *
@@ -53011,96 +58108,260 @@ struct Scalar {
  * limitations under the License.
  */

-#ifndef SRC_PROTOVM_NODE_H_
-#define SRC_PROTOVM_NODE_H_
-
-#include <type_traits>
-#include <variant>
-
-// gen_amalgamated expanded: #include "perfetto/ext/base/intrusive_tree.h"
-// gen_amalgamated expanded: #include "src/protovm/owned_ptr.h"
-// gen_amalgamated expanded: #include "src/protovm/scalar.h"
+// gen_amalgamated expanded: #include "src/protovm/parser.h"
+// gen_amalgamated expanded: #include "protos/perfetto/protovm/vm_program.pbzero.h"
+// gen_amalgamated expanded: #include "src/protovm/error_handling.h"

 namespace perfetto {
 namespace protovm {

-struct Node;
+Parser::Parser(protozero::ConstBytes program, Executor* executor)
+    : program_{program}, executor_(executor) {}

-struct MapNode {
-  struct Traits {
-    using KeyType = uint64_t;
-    static constexpr size_t NodeOffset() {
-      static_assert(std::is_standard_layout_v<MapNode>);
-      return offsetof(MapNode, node);
+StatusOr<void> Parser::Run(RoCursor src, RwProto::Cursor dst) {
+  cursors_.src = src;
+  cursors_.dst = dst;
+  cursors_.innermost_saved_dst = nullptr;
+  return ParseInstructions(program_.instructions());
+}
+
+StatusOr<void> Parser::ParseInstructions(
+    protozero::RepeatedFieldIterator<protozero::ConstBytes> it_instruction) {
+  int instruction_index = 0;
+  for (; it_instruction; ++it_instruction) {
+    auto instruction = protos::pbzero::VmInstruction::Decoder(*it_instruction);
+    auto status = ParseInstruction(instruction);
+
+    if (status.IsAbort()) {
+      PROTOVM_RETURN(status, "instruction[%d]", instruction_index);
     }
-    static const KeyType& GetKey(const MapNode& n) { return n.key; }
-  };

-  MapNode(uint64_t k, OwnedPtr<Node> v) : key{k}, value{std::move(v)} {}
+    if (status.IsError()) {
+      using AbortLevel = perfetto::protos::pbzero::VmInstruction::AbortLevel;
+      auto abort_level =
+          instruction.has_abort_level()
+              ? static_cast<AbortLevel>(instruction.abort_level())
+              : AbortLevel::SKIP_CURRENT_INSTRUCTION_AND_BREAK_OUTER;
+      if (abort_level == AbortLevel::SKIP_CURRENT_INSTRUCTION_AND_BREAK_OUTER) {
+        return StatusOr<void>::Error();
+      }
+      if (abort_level == AbortLevel::ABORT) {
+        PROTOVM_ABORT(
+            "instruction[%d]: returned status = error and instruction's "
+            "abort level = 'abort'",
+            instruction_index);
+      }
+    }

-  base::IntrusiveTreeNode node{};
-  uint64_t key;
-  OwnedPtr<Node> value;
-};
+    ++instruction_index;
+  }

-using IntrusiveMap = base::IntrusiveTree<MapNode, MapNode::Traits>;
+  return StatusOr<void>::Ok();
+}

-struct Node {
-  struct Bytes {
-    OwnedPtr<void> data;
-    size_t size;
-  };
+StatusOr<void> Parser::ParseInstruction(
+    const protos::pbzero::VmInstruction::Decoder& instruction) {
+  if (instruction.has_select()) {
+    auto status = ParseSelect(instruction);
+    PROTOVM_RETURN(status, "select");
+  }

-  struct Empty {};
+  if (instruction.has_reg_load()) {
+    auto status = ParseRegLoad(instruction);
+    PROTOVM_RETURN_IF_NOT_OK(status, "reg_load");
+  } else if (instruction.has_del()) {
+    PROTOVM_ASSIGN_OR_RETURN(bool is_root, cursors_.dst.IsRoot());
+    if (cursors_.innermost_saved_dst != nullptr &&
+        cursors_.dst == *cursors_.innermost_saved_dst && !is_root) {
+      // Illegal operation
+      // For each "select" operation we effectively save a snapshot of the
+      // cursors on the stack, so that they can be restored when a frame is
+      // exited. This "del" operation is attempting to delete a "dst" cursor
+      // that is currently aliased in the parent stack frame. This would result
+      // in a dangling pointer when the current frame is exited and the aliased
+      // "dst" is restored.
+      PROTOVM_ABORT(
+          "del would invalidate a cursor saved by an enclosing select");
+    }
+    auto status = executor_->Delete(&cursors_.dst);
+    PROTOVM_RETURN_IF_NOT_OK(status, "del");
+  } else if (instruction.has_merge()) {
+    auto status = ParseMerge(instruction);
+    PROTOVM_RETURN_IF_NOT_OK(status, "merge");
+  } else if (instruction.has_set()) {
+    auto status = executor_->Set(&cursors_);
+    PROTOVM_RETURN_IF_NOT_OK(status, "set");
+  } else {
+    PROTOVM_ABORT("Unsupported instruction");
+  }

-  struct MappedRepeatedField {
-    IntrusiveMap key_to_node;
-  };
+  return ParseInstructions(instruction.nested_instructions());
+}

-  struct IndexedRepeatedField {
-    IntrusiveMap index_to_node;
-  };
+StatusOr<void> Parser::ParseMerge(
+    const protos::pbzero::VmInstruction::Decoder& instruction) {
+  protos::pbzero::VmOpMerge::Decoder merge(instruction.merge());
+  uint32_t flags = Executor::MergeFlags::kNone;
+  if (merge.skip_submessages())
+    flags |= Executor::MergeFlags::kSkipSubmessages;
+  if (merge.del_if_src_empty())
+    flags |= Executor::MergeFlags::kDelIfSrcEmpty;
+  auto status = executor_->Merge(&cursors_, flags);
+  PROTOVM_RETURN_IF_NOT_OK(status);
+  return status;
+}

-  struct Message {
-    IntrusiveMap field_id_to_node;
-  };
+StatusOr<void> Parser::ParseRegLoad(
+    const protos::pbzero::VmInstruction::Decoder& instruction) {
+  auto saved_selected = cursors_.selected;

-  template <class T>
-  T* GetIf() {
-    return std::get_if<T>(&value);
+  protos::pbzero::VmOpRegLoad::Decoder reg_load(instruction.reg_load());
+  cursors_.selected = !reg_load.has_cursor()
+                          ? CursorEnum::VM_CURSOR_SRC
+                          : static_cast<CursorEnum>(reg_load.cursor());
+  auto status = executor_->WriteRegister(
+      cursors_, static_cast<uint8_t>(reg_load.dst_register()));
+
+  cursors_.selected = saved_selected;
+
+  PROTOVM_RETURN_IF_NOT_OK(status);
+
+  return status;
+}
+
+StatusOr<void> Parser::ParseSelect(
+    const protos::pbzero::VmInstruction::Decoder& instruction) {
+  auto saved_cursors = cursors_;
+  cursors_.innermost_saved_dst = &saved_cursors.dst;
+
+  protos::pbzero::VmOpSelect::Decoder select(instruction.select());
+  cursors_.selected = !select.has_cursor()
+                          ? CursorEnum::VM_CURSOR_SRC
+                          : static_cast<CursorEnum>(select.cursor());
+  cursors_.create_if_not_exist = select.create_if_not_exist();
+
+  if (cursors_.selected == CursorEnum::VM_CURSOR_SRC &&
+      cursors_.create_if_not_exist) {
+    PROTOVM_ABORT(
+        "incompatible params: src cursor (read only) + create_if_not_exist");
   }

-  template <class T>
-  const T* GetIf() const {
-    return std::get_if<T>(&value);
+  auto status = ParseSelectRec(instruction, select.relative_path());
+  cursors_ = saved_cursors;
+
+  return status;
+}
+
+StatusOr<void> Parser::ParseSelectRec(
+    const protos::pbzero::VmInstruction::Decoder& instruction,
+    protozero::RepeatedFieldIterator<protozero::ConstBytes> it_path_component) {
+  protos::pbzero::VmOpSelect::Decoder select(instruction.select());
+
+  bool has_entered_all_path_components = !it_path_component;
+  if (has_entered_all_path_components) {
+    return ParseInstructions(instruction.nested_instructions());
   }

-  const char* GetTypeName() const;
+  auto curr_component =
+      protos::pbzero::VmOpSelect::PathComponent::Decoder(*it_path_component);
+  ++it_path_component;

-  std::variant<Bytes,
-               Empty,
-               IndexedRepeatedField,
-               MappedRepeatedField,
-               Message,
-               Scalar>
-      value;
+  std::optional<protos::pbzero::VmOpSelect::PathComponent::Decoder>
+      next_component =
+          it_path_component
+              ? std::make_optional(
+                    protos::pbzero::VmOpSelect::PathComponent::Decoder(
+                        *it_path_component))
+              : std::nullopt;

-  // Flag needed to track the merge state of repeated fields
-  // (see implementation of Merge operation in RW proto)
-  bool has_been_merged = false;
-};
+  if (!curr_component.has_field_id()) {
+    PROTOVM_ABORT("Invalid path. Expected path component with field_id.");
+  }
+
+  // iterate repeated field
+  if (curr_component.is_repeated()) {
+    if (cursors_.selected == CursorEnum::VM_CURSOR_SRC) {
+      auto status_or_it = executor_->IterateRepeatedField(
+          &cursors_.src, curr_component.field_id());
+      PROTOVM_RETURN_IF_NOT_OK(status_or_it, "iterate repeated field (id = %d)",
+                               static_cast<int>(curr_component.field_id()));
+      int index = 0;
+      for (auto it = *status_or_it; it; ++it) {
+        cursors_.src = *it;
+        auto status = ParseSelectRec(instruction, it_path_component);
+        PROTOVM_RETURN_IF_NOT_OK(status, "repeated field (id = %d, index = %d)",
+                                 static_cast<int>(curr_component.field_id()),
+                                 index);
+        ++index;
+      }
+    } else if (cursors_.selected == CursorEnum::VM_CURSOR_DST) {
+      auto status_or_it = executor_->IterateRepeatedField(
+          &cursors_.dst, curr_component.field_id());
+      PROTOVM_RETURN_IF_NOT_OK(status_or_it, "iterate repeated field (id = %d)",
+                               static_cast<int>(curr_component.field_id()));
+      int index = 0;
+      for (auto it = *status_or_it; it; ++it) {
+        cursors_.dst = it.GetCursor();
+        auto status = ParseSelectRec(instruction, it_path_component);
+        PROTOVM_RETURN_IF_NOT_OK(status, "repeated field (id = %d, index = %d)",
+                                 static_cast<int>(curr_component.field_id()),
+                                 index);
+        ++index;
+      }
+    } else {
+      PROTOVM_ABORT(
+          "Iteration over selected cursor (%d) is not supported. Should be "
+          "either SRC or DST cursor.",
+          static_cast<int>(cursors_.selected));
+    }
+    return StatusOr<void>::Ok();
+  }
+
+  // enter indexed repeated field
+  if (next_component && next_component->has_array_index()) {
+    ++it_path_component;
+    auto status_enter = executor_->EnterRepeatedFieldAt(
+        &cursors_, curr_component.field_id(), next_component->array_index());
+    PROTOVM_RETURN_IF_NOT_OK(status_enter, "enter indexed repeated field");
+    auto status_select = ParseSelectRec(instruction, it_path_component);
+    PROTOVM_RETURN(status_select, "repeated field (id = %d, index = %d)",
+                   static_cast<int>(curr_component.field_id()),
+                   static_cast<int>(next_component->array_index()));
+  }
+
+  // enter mapped repeated field
+  if (next_component && next_component->has_map_key_field_id()) {
+    ++it_path_component;
+    if (!next_component->has_register_to_match()) {
+      PROTOVM_ABORT(
+          "enter mapped repeated field: expected field 'register_to_match'");
+    }
+    auto reg_id = static_cast<uint8_t>(next_component->register_to_match());
+    auto key = executor_->ReadRegister(reg_id);
+    PROTOVM_RETURN_IF_NOT_OK(key, "enter mapped repeated field");
+    auto status_enter = executor_->EnterRepeatedFieldByKey(
+        &cursors_, curr_component.field_id(),
+        next_component->map_key_field_id(), static_cast<uint32_t>(*key));
+    PROTOVM_RETURN_IF_NOT_OK(status_enter, "enter mapped repeated field");
+    auto status_select = ParseSelectRec(instruction, it_path_component);
+    PROTOVM_RETURN(status_select, "mapped repeated field (id = %d, key = %d)",
+                   static_cast<int>(curr_component.field_id()),
+                   static_cast<int>(*key));
+  }
+
+  // enter field
+  auto status = executor_->EnterField(&cursors_, curr_component.field_id());
+  PROTOVM_RETURN_IF_NOT_OK(status, "enter field (id = %d)",
+                           static_cast<int>(curr_component.field_id()));
+
+  return ParseSelectRec(instruction, it_path_component);
+}

 }  // namespace protovm
 }  // namespace perfetto
-
-#endif  // SRC_PROTOVM_NODE_H_
-// gen_amalgamated begin header: src/protovm/slab_allocator.h
-// gen_amalgamated begin header: include/perfetto/ext/base/flat_hash_map.h
-// gen_amalgamated begin header: include/perfetto/ext/base/flat_hash_map_v1.h
-// gen_amalgamated begin header: include/perfetto/ext/base/murmur_hash.h
-// gen_amalgamated begin header: include/perfetto/ext/base/hash.h
+// gen_amalgamated begin source: src/protovm/ro_cursor.cc
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -53115,468 +58376,282 @@ struct Node {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_HASH_H_
-#define INCLUDE_PERFETTO_EXT_BASE_HASH_H_
+// gen_amalgamated expanded: #include "src/protovm/ro_cursor.h"

-#include <cstddef>
-#include <cstdint>
-#include <memory>
-#include <optional>
-#include <tuple>
-#include <utility>
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"

-namespace perfetto::base {
+namespace perfetto {
+namespace protovm {

-// ============================================================================
-// Absl-style hash customization point
-// ============================================================================
-//
-// To make a type hashable with Perfetto hash functions, define a friend
-// function template:
-//
-//   template <typename H>
-//   friend H PerfettoHashValue(H h, const MyType& value) {
-//     return H::Combine(std::move(h), value.field1, value.field2);
-//   }
-//
-// This function will be found via ADL (Argument Dependent Lookup) when hashing
-// your type. No forward declaration is needed - ADL finds it in your type's
-// namespace.
+RoCursor::RepeatedFieldIterator::RepeatedFieldIterator()
+    : decoder_{protozero::ConstBytes{}}, field_id_{0}, field_{Advance()} {
+  PERFETTO_DCHECK(!field_.valid());
+}

-// ============================================================================
-// Built-in PerfettoHashValue implementations for common standard library
-// types. These allow standard library types to work seamlessly with the
-// absl-style hash API without requiring users to define their own
-// implementations.
-// ============================================================================
+RoCursor::RepeatedFieldIterator::RepeatedFieldIterator(
+    protozero::ProtoDecoder decoder,
+    uint32_t field_id)
+    : decoder_(decoder), field_id_(field_id), field_{Advance()} {}

-// Hash function for std::optional - hashes the value if present, or a sentinel
-// if not.
-template <typename H, typename T>
-H PerfettoHashValue(H h, const std::optional<T>& value) {
-  if (value.has_value()) {
-    return H::Combine(H::Combine(std::move(h), true), *value);
-  }
-  return H::Combine(std::move(h), false, 0);
+RoCursor::RepeatedFieldIterator& RoCursor::RepeatedFieldIterator::operator++() {
+  field_ = Advance();
+  return *this;
 }

-// Hash function for std::pair - combines hashes of both elements.
-template <typename H, typename T1, typename T2>
-H PerfettoHashValue(H h, const std::pair<T1, T2>& value) {
-  return H::Combine(std::move(h), value.first, value.second);
+RoCursor RoCursor::RepeatedFieldIterator::operator*() {
+  return RoCursor(field_);
 }

-// Hash function for std::tuple - combines hashes of all elements.
-template <typename H, typename... Ts>
-H PerfettoHashValue(H h, const std::tuple<Ts...>& value) {
-  return std::apply(
-      [&h](const auto&... elements) {
-        return H::Combine(std::move(h), elements...);
-      },
-      value);
+RoCursor::RepeatedFieldIterator::operator bool() const {
+  return field_.valid();
 }

-// Hash function for pointers - hashes the pointer value as an integer.
-template <typename H, typename T>
-H PerfettoHashValue(H h, const std::unique_ptr<T>& ptr) {
-  return H::Combine(std::move(h), ptr.get());
-}
-template <typename H, typename T>
-H PerfettoHashValue(H h, const std::shared_ptr<T>& ptr) {
-  return H::Combine(std::move(h), ptr.get());
+protozero::Field RoCursor::RepeatedFieldIterator::Advance() {
+  auto field = decoder_.ReadField();
+  while (field) {
+    if (field.id() == field_id_) {
+      break;
+    }
+    field = decoder_.ReadField();
+  }
+  return field;
 }

-// This is for using already-hashed key into std::unordered_map and avoid the
-// cost of re-hashing. Example:
-// unordered_map<uint64_t, Value, AlreadyHashed> my_map.
-template <typename T>
-struct AlreadyHashed {
-  size_t operator()(const T& x) const { return static_cast<size_t>(x); }
-};
+RoCursor::RoCursor() = default;

-}  // namespace perfetto::base
+RoCursor::RoCursor(protozero::ConstBytes data) : data_{data} {}

-#endif  // INCLUDE_PERFETTO_EXT_BASE_HASH_H_
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+RoCursor::RoCursor(protozero::Field data) : data_{data} {}

-#ifndef INCLUDE_PERFETTO_EXT_BASE_MURMUR_HASH_H_
-#define INCLUDE_PERFETTO_EXT_BASE_MURMUR_HASH_H_
+StatusOr<void> RoCursor::EnterField(uint32_t field_id) {
+  auto status_or_data = GetLengthDelimitedData();
+  PROTOVM_RETURN_IF_NOT_OK(status_or_data);

-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-#include <limits>
-#include <string>
-#include <string_view>
-#include <type_traits>
-#include <utility>
+  protozero::ProtoDecoder decoder(*status_or_data);

-// gen_amalgamated expanded: #include "perfetto/ext/base/hash.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
-// gen_amalgamated expanded: #include "perfetto/public/compiler.h"
+  for (auto field = decoder.ReadField(); field.valid();
+       field = decoder.ReadField()) {
+    if (field.id() == field_id) {
+      data_ = field;
+      return StatusOr<void>::Ok();
+    }
+  }

-// We need <cmath> only for std::isnan here. But cmath is a quite heavy
-// header AND is not frequently used, so its include cost is generally
-// NOT amortized. OTOH this header is very frequently used.
-// The code below avoids pulling cmath in many translation units, reducing
-// compilation time. Under the hoods std::isnan uses __builtin_isnan if
-// available.
-#if PERFETTO_HAS_BUILTIN(__builtin_isnan)
-#define PERFETTO_IS_NAN(x) __builtin_isnan(x)
-#else
-#include <cmath>
-#define PERFETTO_IS_NAN(x) std::isnan(x)
-#endif
+  return StatusOr<void>::Error();
+}

-// This file provides an implementation of the 64-bit MurmurHash2 algorithm,
-// also known as MurmurHash64A. This algorithm, created by Austin Appleby, is a
-// fast, non-cryptographic hash function with excellent distribution properties,
-// making it ideal for use in hash tables.
-//
-// The file also includes related hashing utilities:
-// - A standalone `fmix64` finalizer from MurmurHash3, used for hashing
-//   individual numeric types.
-// - A hash combiner for creating a single hash from a sequence of values.
-//
-// NOTE: This implementation is NOT cryptographically secure. It must not be
-// used for security-sensitive applications like password storage or digital
-// signatures, as it is not designed to be resistant to malicious attacks.
+StatusOr<void> RoCursor::EnterRepeatedFieldAt(uint32_t field_id,
+                                              uint32_t index) {
+  auto status_or_data = GetLengthDelimitedData();
+  PROTOVM_RETURN_IF_NOT_OK(status_or_data);

-namespace perfetto::base {
+  protozero::ProtoDecoder decoder(*status_or_data);

-namespace murmur_internal {
+  uint32_t current_index = 0;

-// Finalizes an intermediate hash value using the `fmix64` routine from
-// MurmurHash3.
-//
-// This function's purpose is to thoroughly mix the bits of the hash state to
-// ensure the final result is well-distributed, which is critical for avoiding
-// collisions in hash tables.
-//
-// Args:
-//   h: The intermediate hash value to be finalized.
-//
-// Returns:
-//   The final, well-mixed 64-bit hash value.
-inline uint64_t MurmurHashMix(uint64_t h) {
-  h ^= h >> 33;
-  h *= 0xff51afd7ed558ccdULL;
-  h ^= h >> 33;
-  h *= 0xc4ceb9fe1a85ec53ULL;
-  h ^= h >> 33;
-  return h;
-}
+  for (auto field = decoder.ReadField(); field.valid();
+       field = decoder.ReadField()) {
+    if (field.id() != field_id) {
+      continue;
+    }

-// Computes a 64-bit hash for a block of memory using the MurmurHash64A
-// algorithm.
-//
-// The process involves four main steps:
-// 1. Initialization: The hash state is seeded with a value derived from the
-//    input length.
-// 2. Main Loop: Data is processed in 8-byte chunks, with each chunk being
-//    mixed into the hash state.
-// 3. Tail Processing: The final 1-7 bytes of data are handled.
-// 4. Finalization: The hash state is passed through a final mixing sequence to
-//    ensure good bit distribution.
-//
-// Args:
-//   input: A pointer to the data to be hashed.
-//   len:   The length of the data in bytes.
-//
-// Returns:
-//   The 64-bit MurmurHash64A hash of the input data.
-inline uint64_t MurmurHashBytes(const void* input, size_t len) {
-  // This implementation follows the canonical MurmurHash64A algorithm.
-  // The constants `kMulConstant` (m) and the shift value `47` (r) are from
-  // the original specification.
-  // The seed is inspired by the one used in DuckDB.
-  static constexpr uint64_t kSeed = 0xe17a1465U;
-  static constexpr uint64_t kMulConstant = 0xc6a4a7935bd1e995;
-  static constexpr int kShift = 47;
+    if (current_index == index) {
+      data_ = field.as_bytes();
+      return StatusOr<void>::Ok();
+    }

-  uint64_t h = kSeed ^ (len * kMulConstant);
-  const auto* data = static_cast<const uint8_t*>(input);
-  const size_t num_blocks = len / 8;
+    ++current_index;
+  }

-  // Process 8-byte (64-bit) chunks
-  for (size_t i = 0; i < num_blocks; ++i) {
-    uint64_t k;
-    memcpy(&k, data, sizeof(k));
-    data += sizeof(k);  // Advance the pointer by 8 bytes
+  return StatusOr<void>::Error();
+}

-    k *= kMulConstant;
-    k ^= k >> kShift;
-    k *= kMulConstant;
+StatusOr<RoCursor::RepeatedFieldIterator> RoCursor::IterateRepeatedField(
+    uint32_t field_id) const {
+  auto status_or_data = GetLengthDelimitedData();
+  PROTOVM_RETURN_IF_NOT_OK(status_or_data);
+  protozero::ProtoDecoder decoder(*status_or_data);
+  return RepeatedFieldIterator(decoder, field_id);
+}

-    h ^= k;
-    h *= kMulConstant;
+bool RoCursor::IsScalar() const {
+  auto* field = std::get_if<protozero::Field>(&data_);
+  if (!field) {
+    return false;
   }

-  // Process the remaining 1 to 7 bytes
-  // The 'byte_ptr' now points to the beginning of the tail.
-  switch (len & 7) {
-    case 7:
-      h ^= static_cast<uint64_t>(data[6]) << 48;
-      [[fallthrough]];
-    case 6:
-      h ^= static_cast<uint64_t>(data[5]) << 40;
-      [[fallthrough]];
-    case 5:
-      h ^= static_cast<uint64_t>(data[4]) << 32;
-      [[fallthrough]];
-    case 4:
-      h ^= static_cast<uint64_t>(data[3]) << 24;
-      [[fallthrough]];
-    case 3:
-      h ^= static_cast<uint64_t>(data[2]) << 16;
-      [[fallthrough]];
-    case 2:
-      h ^= static_cast<uint64_t>(data[1]) << 8;
-      [[fallthrough]];
-    case 1:
-      h ^= static_cast<uint64_t>(data[0]);
-      h *= kMulConstant;
-  }
+  return field->type() == protozero::proto_utils::ProtoWireType::kVarInt ||
+         field->type() == protozero::proto_utils::ProtoWireType::kFixed32 ||
+         field->type() == protozero::proto_utils::ProtoWireType::kFixed64;
+}

-  // Final mixing stage
-  h ^= h >> kShift;
-  h *= kMulConstant;
-  h ^= h >> kShift;
+bool RoCursor::IsBytes() const {
+  if (std::holds_alternative<protozero::ConstBytes>(data_)) {
+    return true;
+  }

-  return h;
+  auto& field = std::get<protozero::Field>(data_);
+  return field.type() ==
+         protozero::proto_utils::ProtoWireType::kLengthDelimited;
 }

-template <typename Float, typename Int>
-Int NormalizeFloatToInt(Float value) {
-  static_assert(std::is_floating_point_v<Float>);
-  static_assert(std::is_integral_v<Int>);
-
-  // Normalize floating point representations which can vary.
-  if (PERFETTO_UNLIKELY(value == 0.0)) {
-    // Turn negative zero into positive zero
-    value = 0.0;
-  } else if (PERFETTO_UNLIKELY(PERFETTO_IS_NAN(value))) {
-    // Turn arbtirary NaN representations to a consistent NaN repr.
-    value = std::numeric_limits<Float>::quiet_NaN();
+StatusOr<Scalar> RoCursor::GetScalar() const {
+  if (!IsScalar()) {
+    PROTOVM_ABORT("Attempted to access length-delimited field as a scalar");
   }
-  Int res;
-  static_assert(sizeof(Float) == sizeof(Int));
-  memcpy(&res, &value, sizeof(Float));
-  return res;
+
+  auto& field = std::get<protozero::Field>(data_);
+  return Scalar{field.type(), field.as_uint64()};
 }

-// Computes a 64-bit hash for a single built-in value without any combination.
-// This is the core primitive used by both MurmurHashValue and
-// MurmurHashCombiner::CombineOne for built-in types.
-//
-// NOTE: This function intentionally has no else branch for non-builtin types,
-// which will cause a compile error if called with an unsupported type. Callers
-// should check if the type is supported before calling this function.
-template <typename T>
-auto MurmurHashBuiltinValue(const T& value) {
-  if constexpr (std::is_enum_v<T>) {
-    return murmur_internal::MurmurHashMix(
-        static_cast<uint64_t>(static_cast<std::underlying_type_t<T>>(value)));
-  } else if constexpr (std::is_integral_v<T>) {
-    return murmur_internal::MurmurHashMix(static_cast<uint64_t>(value));
-  } else if constexpr (std::is_same_v<T, double>) {
-    return murmur_internal::MurmurHashMix(
-        murmur_internal::NormalizeFloatToInt<double, uint64_t>(value));
-  } else if constexpr (std::is_same_v<T, float>) {
-    return murmur_internal::MurmurHashMix(
-        murmur_internal::NormalizeFloatToInt<float, uint32_t>(value));
-  } else if constexpr (std::is_same_v<T, std::string> ||
-                       std::is_same_v<T, std::string_view> ||
-                       std::is_same_v<T, base::StringView>) {
-    return murmur_internal::MurmurHashBytes(value.data(), value.size());
-  } else if constexpr (std::is_same_v<T, const char*>) {
-    std::string_view view(value);
-    return murmur_internal::MurmurHashBytes(view.data(), view.size());
-  } else if constexpr (std::is_pointer_v<T>) {
-    return murmur_internal::MurmurHashMix(
-        static_cast<uint64_t>(reinterpret_cast<uintptr_t>(value)));
-  } else {
-    struct InvalidBuiltin {};
-    return InvalidBuiltin{};
+StatusOr<protozero::ConstBytes> RoCursor::GetBytes() const {
+  if (std::holds_alternative<protozero::ConstBytes>(data_)) {
+    return std::get<protozero::ConstBytes>(data_);
   }
-}

-// Helper to check if a type has a built-in MurmurHash implementation.
-template <typename T>
-constexpr bool HasMurmurHashBuiltinValue() {
-  return std::is_same_v<decltype(MurmurHashBuiltinValue(std::declval<T>())),
-                        uint64_t>;
-}
+  auto& field = std::get<protozero::Field>(data_);
+  if (field.type() != protozero::proto_utils::ProtoWireType::kLengthDelimited) {
+    PROTOVM_ABORT(
+        "Attempted to access field as length-delimited but actual wire "
+        "type is "
+        "%u",
+        static_cast<uint32_t>(field.type()));
+  }

-// Helper to check if two types are integeral and U is convertible to T.
-template <typename T, typename U>
-constexpr bool IsConvertibleIntegral() {
-  return std::is_integral_v<T> && std::is_integral_v<U> &&
-         std::is_convertible_v<U, T>;
+  return field.as_bytes();
 }

-// Helper to check if a type is string-like (i.e. string, c-string or string
-// views).
-template <typename T>
-constexpr bool IsStringLike() {
-  return std::is_same_v<T, std::string> ||
-         std::is_same_v<T, std::string_view> ||
-         std::is_same_v<T, base::StringView> || std::is_same_v<T, const char*>;
-}
+StatusOr<protozero::ConstBytes> RoCursor::GetLengthDelimitedData() const {
+  if (std::holds_alternative<protozero::Field>(data_)) {
+    auto& field = std::get<protozero::Field>(data_);
+    if (field.type() !=
+        protozero::proto_utils::ProtoWireType::kLengthDelimited) {
+      PROTOVM_ABORT(
+          "Attempted to access field as length-delimited, but actual "
+          "wire type is %u",
+          static_cast<uint32_t>(field.type()));
+    }
+    return field.as_bytes();
+  }

-// Helper to check if heterogeneous lookup is allowed between T and U.
-// Only allows it for convertible integral types and string-like types.
-template <typename T, typename U>
-constexpr bool AllowsHeterogeneousLookup() {
-  return IsConvertibleIntegral<T, U>() ||
-         (IsStringLike<T>() && IsStringLike<U>());
+  return std::get<protozero::ConstBytes>(data_);
 }

-// Helper to detect pointers in Combine(...).
-// Hashing pointers directly is prohibited as it often indicates a misuse
-// (e.g., passing ptr and len instead of a single std::string_view).
-template <typename... Args>
-constexpr bool HasPointerV = (std::is_pointer_v<Args> || ...);
+}  // namespace protovm
+}  // namespace perfetto
+// gen_amalgamated begin source: src/protovm/rw_proto.cc
+/*
+ * Copyright (C) 2025 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.
+ */

-}  // namespace murmur_internal
+// gen_amalgamated expanded: #include "src/protovm/rw_proto.h"

-// ============================================================================
-// MurmurHashCombiner - the core hasher state object
-// ============================================================================
-//
-// A helper class to create a 64-bit MurmurHash from a series of
-// structured fields.
-//
-// This class supports both the absl-style hasher API and a direct
-// member Combine() method.
-//
-// Absl-style API (for custom types with PerfettoHashValue):
-//   template <typename H>
-//   friend H PerfettoHashValue(H h, const MyType& value) {
-//     return H::Combine(std::move(h), value.field1, value.field2);
-//   }
-//
-// Direct API (for simple hash combining):
-//   MurmurHashCombiner combiner;
-//   combiner.Combine(field1, field2, ...);
-//   return combiner.digest();
-//
-// IMPORTANT: This is NOT a true streaming hash. It is an order-dependent
-// combiner. It does not guarantee that hashing two concatenated chunks of data
-// will produce the same result as hashing them separately in sequence.
-class MurmurHashCombiner {
- public:
-  MurmurHashCombiner() = default;
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"

-  // Static Combine - returns a new hasher with the combined state.
-  // This is used by the absl-style PerfettoHashValue API.
-  template <typename... Args>
-  static MurmurHashCombiner Combine(MurmurHashCombiner h, const Args&... args) {
-    h.Combine(args...);
-    return h;
-  }
+namespace perfetto {
+namespace protovm {

-  // Member Combine - combines values into this hasher's state.
-  // This is a convenient API for directly combining multiple values.
-  // The combination is order-dependent.
-  template <typename... Args>
-  void Combine(const Args&... args) {
-    static_assert(!murmur_internal::HasPointerV<Args...>,
-                  "MurmurHashCombiner::Combine() does not support pointers. "
-                  "If you want to hash the contents of a memory range, use a "
-                  "single hashable object (e.g., std::string_view). If you "
-                  "want to hash a pointer address, cast it to uintptr_t.");
-    // Uses a C++17 fold expression with CombineOne for each argument.
-    (CombineOne(args), ...);
-  }
+RwProto::RwProto(Allocator* allocator)
+    : allocator_{allocator}, root_{Node::Empty{}} {}

-  // Returns the digest (i.e. current state of the combiner).
-  uint64_t digest() const { return hash_; }
+RwProto::~RwProto() {
+  allocator_->DeleteReferencedData(&root_);
+}

- private:
-  // Combines a single value into the hasher state.
-  template <typename T>
-  void CombineOne(const T& value) {
-    if constexpr (murmur_internal::HasMurmurHashBuiltinValue<T>()) {
-      Update(murmur_internal::MurmurHashBuiltinValue(value));
-    } else {
-      // For custom types, use ADL to find the PerfettoHashValue function.
-      // This will cause a compile error with a clear message if the function
-      // is not defined.
-      hash_ = PerfettoHashValue(std::move(*this), value).digest();
-    }
+RwProto::Cursor RwProto::GetRoot() {
+  return Cursor{&root_, allocator_};
+}
+
+void RwProto::Serialize(protozero::Message* proto) const {
+  if (root_.GetIf<Node::Empty>()) {
+    return;
   }

-  // Low-level update with a pre-computed hash value. This uses a fast,
-  // order-dependent combination step inspired by the `hash_combine` function
-  // in the Boost C++ libraries.
-  void Update(uint64_t piece_hash) {
-    hash_ ^= piece_hash + 0x9e3779b9 + (hash_ << 6) + (hash_ >> 2);
+  if (auto* bytes = root_.GetIf<Node::Bytes>()) {
+    proto->AppendRawProtoBytes(bytes->data.get(), bytes->size);
+    return;
   }

-  static constexpr uint64_t kSeed = 0xe17a1465U;
-  uint64_t hash_ = kSeed;
-};
+  auto* message = root_.GetIf<Node::Message>();
+  PERFETTO_DCHECK(message);

-// Simple wrapper function around MurmurHashCombiner to improve clarity in
-// callsites to not have to instantiate the class, call Combine() then digest().
-template <typename... Args>
-uint64_t MurmurHashCombine(const Args&... value) {
-  return MurmurHashCombiner::Combine(MurmurHashCombiner{}, value...).digest();
+  for (auto it = message->field_id_to_node.begin(); it; ++it) {
+    auto field_id = static_cast<uint32_t>(it->key);
+    SerializeField(field_id, *it->value, proto);
+  }
 }

-// Simple wrapper function to compute a hash value for a single value.
-// This is the primitive hash operation that MurmurHash<T> delegates to.
-//
-// For built-in types (integers, floats, strings), this uses a fast path that
-// avoids the overhead of the MurmurHashCombiner. For custom types, it delegates
-// to MurmurHashCombiner which will use ADL to find the PerfettoHashValue.
-template <typename T>
-uint64_t MurmurHashValue(const T& value) {
-  if constexpr (murmur_internal::HasMurmurHashBuiltinValue<T>()) {
-    return murmur_internal::MurmurHashBuiltinValue(value);
-  } else {
-    return MurmurHashCombine(value);
+void RwProto::SerializeField(uint32_t field_id,
+                             const Node& node,
+                             protozero::Message* proto) const {
+  if (node.GetIf<Node::Empty>()) {
+    return;
+  }
+
+  if (auto* bytes = node.GetIf<Node::Bytes>()) {
+    proto->AppendBytes(field_id, bytes->data.get(), bytes->size);
+    return;
   }
-}

-// std::hash<T> drop-in class which uses MurmurHashValue as the primitive.
-// All specializations consistently delegate to MurmurHashValue.
-template <typename T>
-struct MurmurHash {
-  using is_transparent = void;
+  if (auto* scalar = node.GetIf<Scalar>()) {
+    if (scalar->wire_type == protozero::proto_utils::ProtoWireType::kFixed32) {
+      proto->AppendFixed(field_id, static_cast<uint32_t>(scalar->value));
+      return;
+    }

-  uint64_t operator()(const T& value) const { return MurmurHashValue(value); }
+    if (scalar->wire_type == protozero::proto_utils::ProtoWireType::kFixed64) {
+      proto->AppendFixed(field_id, static_cast<uint64_t>(scalar->value));
+      return;
+    }

-  // Heterogeneous lookup support. Only allowed for types where it makes sense
-  // (e.g. string-like types and convertible integral types).
-  template <typename U>
-  auto operator()(const U& value) const
-      -> std::enable_if_t<murmur_internal::AllowsHeterogeneousLookup<T, U>(),
-                          uint64_t> {
-    return MurmurHashValue(value);
+    proto->AppendVarInt(field_id, scalar->value);
+    return;
   }
-};

-}  // namespace perfetto::base
+  if (auto* message = node.GetIf<Node::Message>()) {
+    auto* message_proto =
+        proto->BeginNestedMessage<protozero::Message>(field_id);

-#endif  // INCLUDE_PERFETTO_EXT_BASE_MURMUR_HASH_H_
+    for (auto it = message->field_id_to_node.begin(); it; ++it) {
+      auto message_field_id = static_cast<uint32_t>(it->key);
+      SerializeField(message_field_id, *it->value, message_proto);
+    }
+  }
+
+  if (node.GetIf<Node::IndexedRepeatedField>() ||
+      node.GetIf<Node::MappedRepeatedField>()) {
+    const IntrusiveMap* map;
+    if (auto* indexed = node.GetIf<Node::IndexedRepeatedField>()) {
+      map = &indexed->index_to_node;
+    } else {
+      map = &node.GetIf<Node::MappedRepeatedField>()->key_to_node;
+    }
+
+    for (auto it = map->begin(); it; ++it) {
+      SerializeField(field_id, *it->value, proto);
+    }
+  }
+}
+
+}  // namespace protovm
+}  // namespace perfetto
+// gen_amalgamated begin source: src/protovm/rw_proto_cursor.cc
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -53591,1117 +58666,853 @@ struct MurmurHash {
  * limitations under the License.
  */

-#ifndef INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_V1_H_
-#define INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_V1_H_
+// gen_amalgamated expanded: #include "src/protovm/rw_proto_cursor.h"

-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/murmur_hash.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
-// gen_amalgamated expanded: #include "perfetto/public/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "src/protovm/error_handling.h"

-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-#include <limits>
-#include <memory>
-#include <type_traits>
-#include <utility>
+namespace perfetto {
+namespace protovm {

-namespace perfetto::base {
+RwProtoCursor::RepeatedFieldIterator::RepeatedFieldIterator()
+    : allocator_{nullptr}, it_{} {}

-// An open-addressing hashmap implementation.
-// Pointers are not stable, neither for keys nor values.
-// Has similar performances of a RobinHood hash (without the complications)
-// and 2x an unordered map.
-// Doc: http://go/perfetto-hashtables .
-//
-// When used to implement a string pool in TraceProcessor, the performance
-// characteristics obtained by replaying the set of strings seeen in a 4GB trace
-// (226M strings, 1M unique) are the following (see flat_hash_map_benchmark.cc):
-// This(Linear+AppendOnly)    879,383,676 ns    258.013M insertions/s
-// This(LinearProbe):         909,206,047 ns    249.546M insertions/s
-// This(QuadraticProbe):    1,083,844,388 ns    209.363M insertions/s
-// std::unordered_map:      6,203,351,870 ns    36.5811M insertions/s
-// tsl::robin_map:            931,403,397 ns    243.622M insertions/s
-// absl::flat_hash_map:       998,013,459 ns    227.379M insertions/s
-// FollyF14FastMap:         1,181,480,602 ns    192.074M insertions/s
-//
-// The structs below define the probing algorithm used to probe slots upon a
-// collision. They are guaranteed to visit all slots as our table size is always
-// a power of two (see https://en.wikipedia.org/wiki/Quadratic_probing).
+RwProtoCursor::RepeatedFieldIterator::RepeatedFieldIterator(
+    Allocator* allocator,
+    IntrusiveMap::Iterator it)
+    : allocator_{allocator}, it_{it} {}

-// Linear probing can be faster if the hashing is well distributed and the load
-// is not high. For TraceProcessor's StringPool this is the fastest. It can
-// degenerate badly if the hashing doesn't spread (e.g., if using directly pids
-// as keys, with a no-op hashing function).
-struct LinearProbe {
-  static inline size_t Calc(size_t key_hash, size_t step, size_t capacity) {
-    return (key_hash + step) & (capacity - 1);  // Linear probe
-  }
-};
+RwProtoCursor::RepeatedFieldIterator&
+RwProtoCursor::RepeatedFieldIterator::RepeatedFieldIterator::operator++() {
+  ++it_;
+  return *this;
+}

-// Generates the sequence: 0, 3, 10, 21, 36, 55, ...
-// Can be a bit (~5%) slower than LinearProbe because it's less cache hot, but
-// avoids degenerating badly if the hash function is bad and causes clusters.
-// A good default choice unless benchmarks prove otherwise.
-struct QuadraticProbe {
-  static inline size_t Calc(size_t key_hash, size_t step, size_t capacity) {
-    return (key_hash + 2 * step * step + step) & (capacity - 1);
-  }
-};
+RwProtoCursor RwProtoCursor::RepeatedFieldIterator::GetCursor() {
+  return RwProtoCursor{it_->value.get(), allocator_};
+}

-// Tends to perform in the middle between linear and quadratic.
-// It's a bit more cache-effective than the QuadraticProbe but can create more
-// clustering if the hash function doesn't spread well.
-// Generates the sequence: 0, 1, 3, 6, 10, 15, 21, ...
-struct QuadraticHalfProbe {
-  static inline size_t Calc(size_t key_hash, size_t step, size_t capacity) {
-    return (key_hash + (step * step + step) / 2) & (capacity - 1);
-  }
-};
+RwProtoCursor::RepeatedFieldIterator::operator bool() const {
+  return static_cast<bool>(it_);
+}

-// Non-templated base class to hold helpers for FlatHashMap.
-struct FlatHashMapV1Base {
- public:
-  // Helper to detect if a hasher has is_transparent defined.
-  template <typename, typename = void>
-  struct HasIsTransparent : std::false_type {};
+RwProtoCursor::RwProtoCursor() = default;

-  template <typename H>
-  struct HasIsTransparent<H, std::void_t<typename H::is_transparent>>
-      : std::true_type {};
+RwProtoCursor::RwProtoCursor(Node* node, Allocator* allocator)
+    : node_{node}, allocator_{allocator} {}

-  // Helper to check if a lookup key type K is allowed.
-  // Returns true if:
-  // 1. K can be implicitly converted to Key, OR
-  // 2. Hasher has is_transparent AND Hasher is invocable with K AND Key and K
-  // are equality comparable
-  template <typename K, typename Key, typename Hasher>
-  static constexpr bool IsLookupKeyAllowed() {
-    if constexpr (HasIsTransparent<Hasher>::value) {
-      return std::is_invocable_v<Hasher, const K&> &&
-             std::is_same_v<decltype(std::declval<const Key&>() ==
-                                     std::declval<const K&>()),
-                            bool>;
-    } else if constexpr (std::is_convertible_v<K, Key>) {
-      return true;
-    } else {
-      return false;
-    }
-  }
-};
+bool RwProtoCursor::operator==(const RwProtoCursor& other) const {
+  return node_ == other.node_;
+}
+bool RwProtoCursor::operator!=(const RwProtoCursor& other) const {
+  return !(*this == other);
+}

-template <typename Key,
-          typename Value,
-          typename Hasher = base::MurmurHash<Key>,
-          typename Probe = QuadraticProbe,
-          bool AppendOnly = false>
-class FlatHashMapV1 : protected FlatHashMapV1Base {
- public:
-  class Iterator {
-   public:
-    explicit Iterator(const FlatHashMapV1* map) : map_(map) {
-      FindNextNonFree();
-    }
-    ~Iterator() = default;
-    Iterator(const Iterator&) = default;
-    Iterator& operator=(const Iterator&) = default;
-    Iterator(Iterator&&) noexcept = default;
-    Iterator& operator=(Iterator&&) noexcept = default;
+StatusOr<bool> RwProtoCursor::IsRoot() const {
+  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());
+  return parent_link_.node == nullptr;
+}

-    Key& key() { return map_->keys_[idx_]; }
-    Value& value() { return map_->values_[idx_]; }
-    const Key& key() const { return map_->keys_[idx_]; }
-    const Value& value() const { return map_->values_[idx_]; }
+StatusOr<bool> RwProtoCursor::HasField(uint32_t field_id) {
+  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());

-    explicit operator bool() const { return idx_ != kEnd; }
-    Iterator& operator++() {
-      PERFETTO_DCHECK(idx_ < map_->capacity_);
-      ++idx_;
-      FindNextNonFree();
-      return *this;
-    }
+  // Eagerly decompose bytes because the field being tested will be entered
+  // later anyways. See Executor::EnterField().
+  auto status = ConvertToMessageIfNeeded(node_);
+  PROTOVM_RETURN_IF_NOT_OK(status);
+  auto* message = node_->GetIf<Node::Message>();
+  bool found = static_cast<bool>(message->field_id_to_node.Find(field_id));
+  return found;
+}

-   private:
-    static constexpr size_t kEnd = std::numeric_limits<size_t>::max();
+StatusOr<void> RwProtoCursor::EnterField(uint32_t field_id) {
+  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());

-    void FindNextNonFree() {
-      const auto& tags = map_->tags_;
-      for (; idx_ < map_->capacity_; idx_++) {
-        if (tags[idx_] != kFreeSlot && (AppendOnly || tags[idx_] != kTombstone))
-          return;
-      }
-      idx_ = kEnd;
-    }
+  auto status_or_it = FindOrCreateMessageField(node_, field_id);
+  PROTOVM_RETURN_IF_NOT_OK(status_or_it);
+  auto it = *status_or_it;

-    const FlatHashMapV1* map_ = nullptr;
-    size_t idx_ = 0;
-  };  // Iterator
-  static constexpr int kDefaultLoadLimitPct = 75;
-  explicit FlatHashMapV1(size_t initial_capacity = 0,
-                         int load_limit_pct = kDefaultLoadLimitPct)
-      : load_limit_percent_(load_limit_pct) {
-    if (initial_capacity > 0)
-      Reset(initial_capacity, true);
+  if (it->value->GetIf<Node::IndexedRepeatedField>()) {
+    PROTOVM_ABORT(
+        "Attempted to enter field (id=%u) as a simple field but it is an "
+        "indexed repeated field",
+        field_id);
   }

-  // We are calling Clear() so that the destructors for the inserted entries are
-  // called (unless they are trivial, in which case it will be a no-op).
-  ~FlatHashMapV1() { Clear(); }
+  if (it->value->GetIf<Node::MappedRepeatedField>()) {
+    PROTOVM_ABORT(
+        "Attempted to enter field (id=%u) as a simple field but it is a "
+        "mapped repeated field",
+        field_id);
+  }

-  FlatHashMapV1(FlatHashMapV1&& other) noexcept {
-    tags_ = std::move(other.tags_);
-    keys_ = std::move(other.keys_);
-    values_ = std::move(other.values_);
-    capacity_ = other.capacity_;
-    size_ = other.size_;
-    tombstones_ = other.tombstones_;
-    max_probe_length_ = other.max_probe_length_;
-    load_limit_ = other.load_limit_;
-    load_limit_percent_ = other.load_limit_percent_;
+  parent_link_ = {node_, &node_->GetIf<Node::Message>()->field_id_to_node,
+                  std::addressof(*it)};
+  node_ = it->value.get();
+  return StatusOr<void>::Ok();
+}

-    new (&other) FlatHashMapV1();
-  }
+StatusOr<void> RwProtoCursor::EnterRepeatedFieldAt(uint32_t field_id,
+                                                   uint32_t index) {
+  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());

-  FlatHashMapV1& operator=(FlatHashMapV1&& other) noexcept {
-    this->~FlatHashMapV1();
-    new (this) FlatHashMapV1(std::move(other));
-    return *this;
-  }
+  auto status_or_message_field = FindOrCreateMessageField(node_, field_id);
+  PROTOVM_RETURN_IF_NOT_OK(status_or_message_field);
+  auto message_field = *status_or_message_field;

-  FlatHashMapV1(const FlatHashMapV1&) = delete;
-  FlatHashMapV1& operator=(const FlatHashMapV1&) = delete;
+  auto status =
+      ConvertToIndexedRepeatedFieldIfNeeded(message_field->value.get());
+  PROTOVM_RETURN_IF_NOT_OK(status);

-  std::pair<Value*, bool> Insert(Key key, Value value) {
-    const size_t key_hash = Hasher{}(key);
-    const uint8_t tag = HashToTag(key_hash);
-    static constexpr size_t kSlotNotFound = std::numeric_limits<size_t>::max();
+  auto status_or_repeated_field =
+      FindOrCreateIndexedRepeatedField(message_field->value.get(), index);
+  PROTOVM_RETURN_IF_NOT_OK(status_or_repeated_field);

-    // This for loop does in reality at most two attempts:
-    // The first iteration either:
-    //  - Early-returns, because the key exists already,
-    //  - Finds an insertion slot and proceeds because the load is < limit.
-    // The second iteration is only hit in the unlikely case of this insertion
-    // bringing the table beyond the target |load_limit_| (or the edge case
-    // of the HT being full, if |load_limit_pct_| = 100).
-    // We cannot simply pre-grow the table before insertion, because we must
-    // guarantee that calling Insert() with a key that already exists doesn't
-    // invalidate iterators.
-    size_t insertion_slot;
-    size_t probe_len;
-    for (;;) {
-      PERFETTO_DCHECK((capacity_ & (capacity_ - 1)) == 0);  // Must be a pow2.
-      insertion_slot = kSlotNotFound;
-      // Start the iteration at the desired slot (key_hash % capacity_)
-      // searching either for a free slot or a tombstone. In the worst case we
-      // might end up scanning the whole array of slots. The Probe functions are
-      // guaranteed to visit all the slots within |capacity_| steps. If we find
-      // a free slot, we can stop the search immediately (a free slot acts as an
-      // "end of chain for entries having the same hash". If we find a
-      // tombstones (a deleted slot) we remember its position, but have to keep
-      // searching until a free slot to make sure we don't insert a duplicate
-      // key.
-      for (probe_len = 0; probe_len < capacity_;) {
-        const size_t idx = Probe::Calc(key_hash, probe_len, capacity_);
-        PERFETTO_DCHECK(idx < capacity_);
-        const uint8_t tag_idx = tags_[idx];
-        ++probe_len;
-        if (tag_idx == kFreeSlot) {
-          // Rationale for "insertion_slot == kSlotNotFound": if we encountered
-          // a tombstone while iterating we should reuse that rather than
-          // taking another slot.
-          if (AppendOnly || insertion_slot == kSlotNotFound)
-            insertion_slot = idx;
-          break;
-        }
-        // We should never encounter tombstones in AppendOnly mode.
-        PERFETTO_DCHECK(!(tag_idx == kTombstone && AppendOnly));
-        if (!AppendOnly && tag_idx == kTombstone) {
-          insertion_slot = idx;
-          continue;
-        }
-        if (tag_idx == tag && keys_[idx] == key) {
-          // The key is already in the map.
-          return std::make_pair(&values_[idx], false);
-        }
-      }  // for (idx)
+  parent_link_ = {
+      message_field->value.get(),
+      &message_field->value->GetIf<Node::IndexedRepeatedField>()->index_to_node,
+      std::addressof(**status_or_repeated_field)};
+  node_ = (*status_or_repeated_field)->value.get();

-      // If we got to this point the key does not exist (otherwise we would have
-      // hit the return above) and we are going to insert a new entry.
-      // Before doing so, ensure we stay under the target load limit.
-      if (PERFETTO_UNLIKELY(size_ >= load_limit_)) {
-        MaybeGrowAndRehash(/*grow=*/true);
-        continue;
-      }
-      // If there are too many tombstones, it's worth doing a rehash to
-      // clean them up. This is to avoid the case where we have a table full
-      // of tombstones which would cause lookups to be very slow.
-      bool is_many_tombstones = tombstones_ > size_ && size_ > 128;
-      bool is_tombstones_plus_size_too_high = tombstones_ + size_ > load_limit_;
-      if (PERFETTO_UNLIKELY(is_many_tombstones ||
-                            is_tombstones_plus_size_too_high)) {
-        MaybeGrowAndRehash(/*grow=*/false);
-        continue;
-      }
-      PERFETTO_DCHECK(insertion_slot != kSlotNotFound);
-      break;
-    }  // for (attempt)
+  return StatusOr<void>::Ok();
+}

-    PERFETTO_CHECK(insertion_slot < capacity_);
+StatusOr<RwProtoCursor::RepeatedFieldIterator>
+RwProtoCursor::IterateRepeatedField(uint32_t field_id) {
+  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());

-    // We found a free slot (or a tombstone). Proceed with the insertion.
-    if (tags_[insertion_slot] == kTombstone) {
-      PERFETTO_DCHECK(tombstones_ > 0);
-      tombstones_--;
-    }
-    Value* value_idx = &values_[insertion_slot];
-    new (&keys_[insertion_slot]) Key(std::move(key));
-    new (value_idx) Value(std::move(value));
-    tags_[insertion_slot] = tag;
-    PERFETTO_DCHECK(probe_len > 0 && probe_len <= capacity_);
-    max_probe_length_ = std::max(max_probe_length_, probe_len);
-    size_++;
+  auto status_convertion_to_message = ConvertToMessageIfNeeded(node_);
+  PROTOVM_RETURN_IF_NOT_OK(status_convertion_to_message);

-    return std::make_pair(value_idx, true);
-  }
+  auto* message = node_->GetIf<Node::Message>();
+  auto it = message->field_id_to_node.Find(field_id);

-  template <typename K = Key>
-  Value* Find(const K& key) const {
-    const size_t idx = FindInternal(key);
-    if (idx == kNotFound)
-      return nullptr;
-    return &values_[idx];
+  if (!it) {
+    return RepeatedFieldIterator{};
   }

-  template <typename K = Key>
-  bool Erase(const K& key) {
-    if (AppendOnly)
-      PERFETTO_FATAL("Erase() not supported because AppendOnly=true");
-    size_t idx = FindInternal(key);
-    if (idx == kNotFound)
-      return false;
-    EraseInternal(idx);
-    return true;
-  }
+  auto* field = it->value.get();
+  auto status_convertion_to_repeated_field =
+      ConvertToIndexedRepeatedFieldIfNeeded(field);
+  PROTOVM_RETURN_IF_NOT_OK(status_convertion_to_repeated_field);

-  void Clear() {
-    // Avoid trivial heap operations on zero-capacity std::move()-d objects.
-    if (PERFETTO_UNLIKELY(capacity_ == 0))
-      return;
+  return RepeatedFieldIterator{
+      allocator_,
+      field->GetIf<Node::IndexedRepeatedField>()->index_to_node.begin()};
+}

-    for (size_t i = 0; i < capacity_; ++i) {
-      const uint8_t tag = tags_[i];
-      if (tag != kFreeSlot && (AppendOnly || tag != kTombstone)) {
-        keys_[i].~Key();
-        values_[i].~Value();
-      }
-    }
-    Reset(capacity_, false);
-  }
+StatusOr<void> RwProtoCursor::EnterRepeatedFieldByKey(uint32_t field_id,
+                                                      uint32_t map_key_field_id,
+                                                      uint64_t key) {
+  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());

-  Value& operator[](Key key) {
-    auto it_and_inserted = Insert(std::move(key), Value{});
-    return *it_and_inserted.first;
-  }
+  auto status_or_message_field = FindOrCreateMessageField(node_, field_id);
+  PROTOVM_RETURN_IF_NOT_OK(status_or_message_field);
+  auto message_field = *status_or_message_field;

-  Iterator GetIterator() { return Iterator(this); }
-  const Iterator GetIterator() const { return Iterator(this); }
+  auto status_conversion = ConvertToMappedRepeatedFieldIfNeeded(
+      message_field->value.get(), map_key_field_id);
+  PROTOVM_RETURN_IF_NOT_OK(status_conversion);

-  size_t size() const { return size_; }
-  size_t capacity() const { return capacity_; }
+  auto status_or_repeated_field =
+      FindOrCreateMappedRepeatedField(message_field->value.get(), key);
+  PROTOVM_RETURN_IF_NOT_OK(status_or_repeated_field);

-  // "protected" here is only for the flat_hash_map_benchmark.cc. Everything
-  // below is by all means private.
- protected:
-  enum ReservedTags : uint8_t { kFreeSlot = 0, kTombstone = 1 };
-  static constexpr size_t kNotFound = std::numeric_limits<size_t>::max();
+  parent_link_ = {
+      message_field->value.get(),
+      &message_field->value->GetIf<Node::MappedRepeatedField>()->key_to_node,
+      std::addressof(**status_or_repeated_field)};
+  node_ = (*status_or_repeated_field)->value.get();

-  template <typename K = Key>
-  size_t FindInternal(const K& key) const {
-    static_assert(
-        IsLookupKeyAllowed<K, Key, Hasher>(),
-        "Heterogeneous lookup requires Hasher to define is_transparent and "
-        "support hashing the lookup key type. For same-type lookup, Key and K "
-        "must match exactly.");
-    const size_t key_hash = Hasher{}(key);
-    const uint8_t tag = HashToTag(key_hash);
-    PERFETTO_DCHECK((capacity_ & (capacity_ - 1)) == 0);  // Must be a pow2.
-    PERFETTO_DCHECK(max_probe_length_ <= capacity_);
-    for (size_t i = 0; i < max_probe_length_; ++i) {
-      const size_t idx = Probe::Calc(key_hash, i, capacity_);
-      const uint8_t tag_idx = tags_[idx];
+  return StatusOr<void>::Ok();
+}

-      if (tag_idx == kFreeSlot)
-        return kNotFound;
-      // HashToTag() never returns kTombstone, so the tag-check below cannot
-      // possibly match. Also we just want to skip tombstones.
-      if (tag_idx == tag && keys_[idx] == key) {
-        PERFETTO_DCHECK(tag_idx > kTombstone);
-        return idx;
-      }
-    }  // for (idx)
-    return kNotFound;
+StatusOr<Scalar> RwProtoCursor::GetScalar() const {
+  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());
+
+  auto* scalar = node_->GetIf<Scalar>();
+  if (!scalar) {
+    PROTOVM_ABORT("Attempted \"get scalar\" operation but node has type %s",
+                  node_->GetTypeName());
   }
+  return *scalar;
+}

-  void EraseInternal(size_t idx) {
-    PERFETTO_DCHECK(tags_[idx] > kTombstone);
-    PERFETTO_DCHECK(size_ > 0);
-    tags_[idx] = kTombstone;
-    keys_[idx].~Key();
-    values_[idx].~Value();
-    size_--;
-    tombstones_++;
-    PERFETTO_DCHECK(size_ + tombstones_ <= capacity_);
+StatusOr<void> RwProtoCursor::SetBytes(protozero::ConstBytes data) {
+  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());
+
+  if (bool is_compatible = node_->GetIf<Node::Empty>() ||
+                           node_->GetIf<Node::Bytes>() ||
+                           node_->GetIf<Node::Message>();
+      !is_compatible) {
+    PROTOVM_ABORT("Attempted \"set bytes\" operation but node has type %s",
+                  node_->GetTypeName());
   }

-  PERFETTO_NO_INLINE void MaybeGrowAndRehash(bool grow) {
-    PERFETTO_DCHECK(size_ <= capacity_);
-    const size_t old_capacity = capacity_;
+  auto status_or_bytes = allocator_->AllocateAndCopyBytes(data);
+  PROTOVM_RETURN_IF_NOT_OK(status_or_bytes);

-    // Grow quickly up to 1MB, then chill.
-    const size_t old_size_bytes = old_capacity * (sizeof(Key) + sizeof(Value));
-    const size_t grow_factor = old_size_bytes < (1024u * 1024u) ? 8 : 2;
-    const size_t new_capacity =
-        grow ? std::max(old_capacity * grow_factor, size_t(1024))
-             : old_capacity;
+  allocator_->DeleteReferencedData(node_);
+  node_->value = Node::Bytes{std::move(*status_or_bytes)};

-    auto old_tags(std::move(tags_));
-    auto old_keys(std::move(keys_));
-    auto old_values(std::move(values_));
-    size_t old_size = size_;
+  return StatusOr<void>::Ok();
+}

-    // This must be a CHECK (i.e. not just a DCHECK) to prevent UAF attacks on
-    // 32-bit archs that try to double the size of the table until wrapping.
-    PERFETTO_CHECK(new_capacity >= old_capacity);
-    Reset(new_capacity, true);
+StatusOr<void> RwProtoCursor::SetScalar(Scalar scalar) {
+  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());

-    size_t new_size = 0;  // Recompute the size.
-    for (size_t i = 0; i < old_capacity; ++i) {
-      const uint8_t old_tag = old_tags[i];
-      if (old_tag != kFreeSlot && old_tag != kTombstone) {
-        Insert(std::move(old_keys[i]), std::move(old_values[i]));
-        old_keys[i].~Key();  // Destroy the old objects.
-        old_values[i].~Value();
-        new_size++;
-      }
-    }
-    PERFETTO_DCHECK(new_size == old_size);
-    PERFETTO_DCHECK(tombstones_ == 0);
-    size_ = new_size;
+  if (bool is_compatible =
+          node_->GetIf<Node::Empty>() || node_->GetIf<Scalar>();
+      !is_compatible) {
+    PROTOVM_ABORT("Attempted \"set scalar\" operation but node has type %s",
+                  node_->GetTypeName());
   }

-  // Doesn't call destructors. Use Clear() for that.
-  PERFETTO_NO_INLINE void Reset(size_t n, bool reallocate) {
-    PERFETTO_DCHECK((n & (n - 1)) == 0);  // Must be a pow2.
-
-    capacity_ = n;
-    max_probe_length_ = 0;
-    size_ = 0;
-    tombstones_ = 0;
-    load_limit_ = n * static_cast<size_t>(load_limit_percent_) / 100;
-    load_limit_ = std::min(load_limit_, n);
+  node_->value = scalar;
+  return StatusOr<void>::Ok();
+}

-    if (reallocate) {
-      tags_.reset(new uint8_t[n]);
-      keys_ = AlignedAllocTyped<Key[]>(n);  // Deliberately not 0-initialized.
-      values_ =
-          AlignedAllocTyped<Value[]>(n);  // Deliberately not 0-initialized.
-    }
+StatusOr<void> RwProtoCursor::Merge(protozero::ConstBytes data,
+                                    uint32_t flags) {
+  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());

-    // Only clear the tags if not nullptr.
-    if (tags_) {
-      memset(&tags_[0], 0, n);  // Clear all tags.
-    }
+  if (bool is_compatible = node_->GetIf<Node::Empty>() ||
+                           node_->GetIf<Node::Message>() ||
+                           node_->GetIf<Node::Bytes>();
+      !is_compatible) {
+    PROTOVM_ABORT("Attempted MERGE operation but node has type %s",
+                  node_->GetTypeName());
   }

-  static inline uint8_t HashToTag(size_t full_hash) {
-    uint8_t tag = full_hash >> (sizeof(full_hash) * 8 - 8);
-    // Ensure the hash is always >= 2. We use 0, 1 for kFreeSlot and kTombstone.
-    tag += (tag <= kTombstone) << 1;
-    PERFETTO_DCHECK(tag > kTombstone);
-    return tag;
+  if (data.size == 0) {
+    return StatusOr<void>::Ok();
   }

-  size_t capacity_ = 0;
-  size_t size_ = 0;
-  size_t tombstones_ = 0;
-  size_t max_probe_length_ = 0;
-  size_t load_limit_ = 0;  // Updated every time |capacity_| changes.
-  int load_limit_percent_ =
-      kDefaultLoadLimitPct;  // Load factor limit in % of |capacity_|.
+  auto status_convert_to_message = ConvertToMessageIfNeeded(node_);
+  PROTOVM_RETURN_IF_NOT_OK(status_convert_to_message);
+  auto* message = node_->GetIf<Node::Message>();

-  // These arrays have always the |capacity_| elements.
-  // Note: AlignedUniquePtr just allocates memory, doesn't invoke any ctor/dtor.
-  std::unique_ptr<uint8_t[]> tags_;
-  AlignedUniquePtr<Key[]> keys_;
-  AlignedUniquePtr<Value[]> values_;
-};
+  protozero::ProtoDecoder decoder(data);

-}  // namespace perfetto::base
+  for (auto field = decoder.ReadField(); field.valid();
+       field = decoder.ReadField()) {
+    auto it = message->field_id_to_node.Find(field.id());

-#endif  // INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_V1_H_
-/*
- * Copyright (C) 2025 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.
- */
+    bool del_if_src_empty = (flags & kDelIfSrcEmpty) != 0;
+    if (it && del_if_src_empty &&
+        field.type() ==
+            protozero::proto_utils::ProtoWireType::kLengthDelimited &&
+        field.size() == 0) {
+      // Implements remove submessage semantics: empty src field means delete
+      // the dst field
+      message->field_id_to_node.Remove(*it);
+      allocator_->Delete(&*it);
+      continue;
+    }

-#ifndef INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_H_
-#define INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_H_
+    bool skip_submessages = (flags & kSkipSubmessages) != 0;
+    if (skip_submessages && it && it->value->GetIf<Node::Message>()) {
+      // Implements deep merge semantics: skip this message that was already
+      // merged by a previous operation
+      continue;
+    }

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/bits.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/flat_hash_map_v1.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/murmur_hash.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
-// gen_amalgamated expanded: #include "perfetto/public/compiler.h"
+    auto status_or_map_value = CreateNodeFromField(field);
+    PROTOVM_RETURN_IF_NOT_OK(status_or_map_value);

-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-#include <functional>
-#include <limits>
-#include <memory>
-#include <string>
-#include <string_view>
-#include <type_traits>
-#include <utility>
+    if (!it) {
+      auto status_or_it = MapInsert(&message->field_id_to_node, field.id(),
+                                    std::move(*status_or_map_value));
+      PROTOVM_RETURN_IF_NOT_OK(status_or_it);
+      (*status_or_it)->value->has_been_merged = true;
+      continue;
+    }

-#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
-#include <immintrin.h>
-#endif
+    if (it->value->GetIf<Node::MappedRepeatedField>()) {
+      PROTOVM_ABORT(
+          "Merge operation of mapped repeated field is not supported (field id "
+          "= %u)",
+          field.id());
+    }

-namespace perfetto::base {
+    // Promote field as repeated if needed:
+    // If this is the second time that the same field ID is being merged, then
+    // they are a repeated fields and they should live within an
+    // IndexedRepeatedField node.
+    if (it->value->has_been_merged) {
+      auto status_convert_to_indexed =
+          ConvertToIndexedRepeatedFieldIfNeeded(it->value.get());
+      PROTOVM_RETURN_IF_NOT_OK(status_convert_to_indexed);
+    }

-// A Swiss Table-style open-addressing hashmap implementation.
-// Inspired by absl::flat_hash_map, this uses a metadata array of control bytes
-// to enable fast SIMD-accelerated probing.
-//
-// Key design choices:
-// - Control bytes: Each slot has a 1-byte tag (7-bit H2 hash or special marker)
-//   stored in a separate array, enabling fast group-based matching.
-// - SIMD acceleration: On x64, uses SSE instructions to match 16 control bytes
-//   in parallel. Falls back to SWAR (SIMD Within A Register) on other platforms
-//   matching 8 bytes at a time.
-// - Triangular probing: Uses the sequence 0, 16, 48, 96, ... (like Absl) to
-//   probe groups of slots, ensuring good cache behavior.
-// - Pointers are NOT stable: Neither keys nor values have stable addresses
-//   across insertions that trigger rehashing.
-//
-// See also: FlatHashMapV1 in flat_hash_map_v1.h for the older implementation
-// using traditional open-addressing with configurable probing strategies.
+    if (auto* indexed_fields = it->value->GetIf<Node::IndexedRepeatedField>()) {
+      // Implements merge semantics for repeated fields: all existing fields are
+      // removed and replaced with the newly received fields.
+      if (!it->value->has_been_merged) {
+        // Optimization opportunity: reuse the existing nodes to avoid N
+        // allocation-deallocation pairs, where N is the number of newly
+        // received repeated fields.
+        allocator_->DeleteReferencedData(it->value.get());
+        it->value->has_been_merged = true;
+      }
+      MapInsert(&indexed_fields->index_to_node,
+                indexed_fields->index_to_node.Size(),
+                std::move(*status_or_map_value));
+      continue;
+    }

-// Non-templated base class to hold helpers for FlatHashMapV2.
-namespace flat_hash_map_v2_internal {
+    // Optimization oppurtunity: reuse the existing node to avoid one
+    // allocation-deallocation pair
+    allocator_->Delete(it->value.release());
+    it->value = std::move(*status_or_map_value);
+    it->value->has_been_merged = true;
+  }

-// Helper to detect if a hasher has is_transparent defined.
-template <typename, typename = void>
-struct HasIsTransparent : std::false_type {};
+  // Reset the merge flags
+  for (auto& field : message->field_id_to_node) {
+    field.value->has_been_merged = false;
+  }

-template <typename H>
-struct HasIsTransparent<H, std::void_t<typename H::is_transparent>>
-    : std::true_type {};
+  return StatusOr<void>::Ok();
+}

-// Equality comparator trait.
-template <typename T>
-struct HashEq : public std::equal_to<T> {};
+StatusOr<void> RwProtoCursor::Delete() {
+  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());

-// Specialization for std::string to compare via std::string_view.
-//
-// This exists because after benchmarking, it turns out libc++ has an
-// "optimization" for std::string equality that does something byte by
-// byte comparision for short strings but this is slower than just memcmp.
-//
-// This helps close the gap to absl::flat_hash_map for string keys.
-template <>
-struct HashEq<std::string> {
-  bool operator()(const std::string& a, const std::string& b) const {
-    return std::string_view(a) == std::string_view(b);
-  }
-  bool operator()(const std::string& a, const std::string_view& b) const {
-    return std::string_view(a) == b;
-  }
-  bool operator()(const std::string& a, base::StringView b) const {
-    return base::StringView(a) == b;
+  PROTOVM_ASSIGN_OR_RETURN(bool is_root, IsRoot());
+  if (is_root) {
+    node_->value = Node::Empty{};
+    return StatusOr<void>::Ok();
   }
-};

-// Specialization for double to prevent spurious -Wfloat-equal warnings.
-//
-// Trace processor legitimately has a need to compare with exact equality
-template <>
-struct HashEq<double> {
-  bool operator()(double a, double b) const { return std::equal_to<>()(a, b); }
-};
+  PERFETTO_DCHECK(parent_link_.map);
+  PERFETTO_DCHECK(parent_link_.map_node);

-// Helper to check if a lookup key type K is allowed.
-// Returns true if:
-// 1. K can be implicitly converted to Key, OR
-// 2. Hasher has is_transparent AND Hasher is invocable with K AND Key and K
-// are equality comparable
-template <typename K, typename Key, typename Hasher>
-static constexpr bool IsLookupKeyAllowed() {
-  if constexpr (HasIsTransparent<Hasher>::value) {
-    return std::is_invocable_v<Hasher, const K&> &&
-           std::is_invocable_v<std::equal_to<>, const Key&, const K&>;
-  } else if constexpr (std::is_convertible_v<K, Key>) {
-    return true;
-  } else {
-    return false;
-  }
+  parent_link_.map->Remove(*parent_link_.map_node);
+  allocator_->Delete(parent_link_.map_node);
+
+  // Delete operation invalidates cursor
+  node_ = nullptr;
+  parent_link_ = {};
+
+  return StatusOr<void>::Ok();
 }

-// Swiss Table control byte encoding:
-// - Empty:   0x80 (10000000) - MSB set, easy to detect with sign bit
-// - Deleted: 0xFE (11111110) - MSB set
-// - Full:    0x00-0x7F - MSB clear, stores 7-bit H2 hash
-static constexpr uint8_t kFreeSlot = 0x80;   // Empty slot
-static constexpr uint8_t kTombstone = 0xFE;  // Deleted slot
+StatusOr<void> RwProtoCursor::ConvertToMessageIfNeeded(Node* node) {
+  if (node->GetIf<Node::Message>()) {
+    return StatusOr<void>::Ok();
+  }

-// The default load limit percent before growing the table.
-static constexpr int kDefaultLoadLimitPct = 75;
+  if (node->GetIf<Node::Empty>()) {
+    node->value = Node::Message{};
+    return StatusOr<void>::Ok();
+  }

-}  // namespace flat_hash_map_v2_internal
+  auto* bytes = node->GetIf<Node::Bytes>();
+  if (!bytes) {
+    PROTOVM_ABORT("Attempted conversion to message but node has type %s",
+                  node->GetTypeName());
+  }

-template <typename Key,
-          typename Value,
-          typename Hasher = base::MurmurHash<Key>,
-          typename Eq = flat_hash_map_v2_internal::HashEq<Key>>
-class FlatHashMapV2 {
- private:
-  // Import constants from internal namespace.
-  static constexpr uint8_t kFreeSlot = flat_hash_map_v2_internal::kFreeSlot;
-  static constexpr uint8_t kTombstone = flat_hash_map_v2_internal::kTombstone;
-  static constexpr int kDefaultLoadLimitPct =
-      flat_hash_map_v2_internal::kDefaultLoadLimitPct;
+  Node::Message message;

-  // Slot structure holds both key and value
-  struct Slot {
-    Key key;
-    Value value;
-  };
+  protozero::ProtoDecoder decoder(protozero::ConstBytes{
+      static_cast<const uint8_t*>(bytes->data.get()), bytes->size});

- public:
-  class Iterator {
-   public:
-    explicit Iterator(const uint8_t* ctrl, const uint8_t* ctrl_end, Slot* slot)
-        : ctrl_(ctrl), ctrl_end_(ctrl_end), slot_(slot) {
-      FindNextNonFree();
+  for (auto field = decoder.ReadField(); field.valid();
+       field = decoder.ReadField()) {
+    auto status_or_map_value = CreateNodeFromField(field);
+    if (!status_or_map_value.IsOk()) {
+      allocator_->DeleteReferencedData(&message);
+      PROTOVM_RETURN(status_or_map_value);
     }
-    ~Iterator() = default;
-    Iterator(const Iterator&) = default;
-    Iterator& operator=(const Iterator&) = default;
-    Iterator(Iterator&&) noexcept = default;
-    Iterator& operator=(Iterator&&) noexcept = default;

-    const Key& key() { return slot_->key; }
-    Value& value() { return slot_->value; }
-    const Key& key() const { return slot_->key; }
-    const Value& value() const { return slot_->value; }
+    auto it = message.field_id_to_node.Find(field.id());

-    explicit operator bool() const { return ctrl_ != ctrl_end_; }
-    Iterator& operator++() {
-      PERFETTO_DCHECK(ctrl_ != ctrl_end_);
-      ++ctrl_;
-      ++slot_;
-      FindNextNonFree();
-      return *this;
+    // First occurrence of this field id. Just insert a new field into the
+    // map.
+    if (!it) {
+      auto status_or_it = MapInsert(&message.field_id_to_node, field.id(),
+                                    std::move(*status_or_map_value));
+      if (!status_or_it.IsOk()) {
+        allocator_->DeleteReferencedData(&message);
+        PROTOVM_RETURN(status_or_it, "Insert message field (id = %u)",
+                       field.id());
+      }
+      continue;
     }

-   private:
-    void FindNextNonFree() {
-      for (; ctrl_ != ctrl_end_; ++ctrl_, ++slot_) {
-        const uint8_t cur_ctrl = *ctrl_;
-        if (cur_ctrl != kFreeSlot && cur_ctrl != kTombstone)
-          return;
-      }
+    // Nth occurrence of this field id:
+    // 1. Make sure we have an IndexedRepeatedField node
+    // 2. Append into the IndexedRepeatedField's map
+    auto status_conversion =
+        ConvertToIndexedRepeatedFieldIfNeeded(it->value.get());
+    if (!status_conversion.IsOk()) {
+      allocator_->DeleteReferencedData(&message);
+      allocator_->Delete(status_or_map_value->release());
+      PROTOVM_RETURN(status_conversion);
     }
-    const uint8_t* ctrl_ = nullptr;
-    const uint8_t* ctrl_end_ = nullptr;
-    Slot* slot_ = nullptr;
-  };  // Iterator

-  explicit FlatHashMapV2(size_t initial_capacity = 0,
-                         int load_limit_pct = kDefaultLoadLimitPct)
-      : load_limit_percent_(load_limit_pct) {
-    if (initial_capacity > 0) {
-      Reset(initial_capacity, true);
+    auto& index_to_node =
+        it->value->GetIf<Node::IndexedRepeatedField>()->index_to_node;
+    auto status_or_it = MapInsert(&index_to_node, index_to_node.Size(),
+                                  std::move(*status_or_map_value));
+    if (!status_or_it.IsOk()) {
+      allocator_->DeleteReferencedData(&message);
+      PROTOVM_RETURN(status_or_it,
+                     "Insert repeated field (id = %u, index = %d)", field.id(),
+                     static_cast<int>(index_to_node.Size()));
     }
   }

-  // We are calling Clear() so that the destructors for the inserted entries are
-  // called (unless they are trivial, in which case it will be a no-op).
-  ~FlatHashMapV2() { Clear(); }
+  allocator_->DeleteReferencedData(node);
+  node->value = message;

-  FlatHashMapV2(FlatHashMapV2&& other) noexcept
-      : storage_(std::move(other.storage_)),
-        capacity_(other.capacity_),
-        size_(other.size_),
-        growth_info_(other.growth_info_),
-        load_limit_percent_(other.load_limit_percent_),
-        ctrl_(other.ctrl_),
-        slots_(other.slots_) {
-    new (&other) FlatHashMapV2();
+  return StatusOr<void>::Ok();
+}
+
+StatusOr<OwnedPtr<Node>> RwProtoCursor::CreateNodeFromField(
+    protozero::Field field) {
+  if (field.type() == protozero::proto_utils::ProtoWireType::kLengthDelimited) {
+    auto status_or_bytes = allocator_->AllocateAndCopyBytes(field.as_bytes());
+    PROTOVM_RETURN_IF_NOT_OK(status_or_bytes);
+    auto status_or_node =
+        allocator_->CreateNode<Node::Bytes>(std::move(*status_or_bytes));
+    if (!status_or_node.IsOk()) {
+      allocator_->DeleteReferencedData(&status_or_bytes.value());
+      PROTOVM_RETURN(status_or_node);
+    }
+
+    return std::move(*status_or_node);
   }

-  FlatHashMapV2& operator=(FlatHashMapV2&& other) noexcept {
-    this->~FlatHashMapV2();
-    new (this) FlatHashMapV2(std::move(other));
-    return *this;
+  auto status_or_node =
+      allocator_->CreateNode<Scalar>(field.type(), field.as_uint64());
+  if (!status_or_node.IsOk()) {
+    PROTOVM_RETURN(status_or_node);
+  }
+
+  return std::move(*status_or_node);
+}
+
+StatusOr<void> RwProtoCursor::ConvertToMappedRepeatedFieldIfNeeded(
+    Node* node,
+    uint32_t map_key_field_id) {
+  if (node->GetIf<Node::MappedRepeatedField>()) {
+    return StatusOr<void>::Ok();
+  }
+
+  if (node->GetIf<Node::Empty>()) {
+    node->value = Node::MappedRepeatedField{};
+    return StatusOr<void>::Ok();
+  }
+
+  // If the current node contains a message (either raw bytes or a decomposed
+  // Node::Message), convert it into a Node::MappedRepeatedField.
+  //
+  // The process is:
+  // 1. Extract the key (identified by 'map_key_field_id') from the message data
+  // within the current 'node'.
+  // 2. Allocate a new node. The original message content from the current node
+  // is moved into this new node.
+  // 3. Repurpose the current node, replacing it with an empty
+  // Node::MappedRepeatedField
+  //    (intrusive tree that stores message fields ordered by key).
+  // 4. Insert the new node (containing Node::Message) into the repurposed
+  // current node (Node::MappedRepeatedField) using the key extracted in step 1.
+  if (node->GetIf<Node::Bytes>() || node->GetIf<Node::Message>()) {
+    auto status_or_key = ReadScalarField(*node, map_key_field_id);
+    PROTOVM_RETURN_IF_NOT_OK(status_or_key);
+
+    auto status_or_map_value = allocator_->CreateNode<Node::Empty>();
+    PROTOVM_RETURN_IF_NOT_OK(status_or_map_value);
+
+    auto map_value = std::move(*status_or_map_value);
+    map_value->value = std::move(node->value);
+    node->value = Node::MappedRepeatedField{};
+
+    auto status_or_it =
+        MapInsert(&node->GetIf<Node::MappedRepeatedField>()->key_to_node,
+                  *status_or_key, std::move(map_value));
+    PROTOVM_RETURN(status_or_it);
   }

-  FlatHashMapV2(const FlatHashMapV2&) = delete;
-  FlatHashMapV2& operator=(const FlatHashMapV2&) = delete;
+  // If the current node is a Node::IndexedRepeatedField (intrusive tree with
+  // message fields ordered by index), this block converts it into a
+  // Node::MappedRepeatedField (intrusive tree with message fields ordered by
+  // key).
+  //
+  // The process is:
+  // 1. Initialize an empty Node::MappedRepeatedField
+  // 2. For each field in the current node (Node::IndexedRepeatedField):
+  //    2.1. Extract the key (identified by 'map_key_field_id'). Note that the
+  //         iterated fields must be messages.
+  //    2.2. Remove the field from the current node
+  //         (Node::IndexedRepeatedField).
+  //    2.3. Insert the field into the Node::MappedRepeatedField using the key
+  //         extracted in step 2.1.
+  // 3. Assign the Node::MappedRepeatedField to the current node, i.e. repurpose
+  //    current node from Node::IndexedRepeatedField to
+  //    Node::MappedRepeatedField)
+  if (auto* indexed = node->GetIf<Node::IndexedRepeatedField>()) {
+    Node::MappedRepeatedField mapped_repeated_field;
+
+    for (auto it = indexed->index_to_node.begin(); it;) {
+      auto& map_entry = *it;
+      auto& value_node = *it->value;
+
+      it = indexed->index_to_node.Remove(it);

-  template <typename K = Key>
-  PERFETTO_ALWAYS_INLINE Value* Find(const K& key) const {
-    size_t key_hash = Hasher{}(key);
-    uint8_t h2 = H2(key_hash);
-    FindResult res = FindSlotIgnoringTombstones<false>(key, key_hash, h2);
-    if (PERFETTO_UNLIKELY(res.needs_insert)) {
-      return nullptr;
-    }
-    return &slots_[res.idx].value;
-  }
+      auto status_or_key = ReadScalarField(value_node, map_key_field_id);
+      PROTOVM_RETURN_IF_NOT_OK(status_or_key);

-  template <typename K = Key>
-  bool Erase(const K& key) {
-    size_t key_hash = Hasher{}(key);
-    uint8_t h2 = H2(key_hash);
-    FindResult res = FindSlotIgnoringTombstones<false>(key, key_hash, h2);
-    if (PERFETTO_UNLIKELY(res.needs_insert)) {
-      return false;
+      map_entry.key = *status_or_key;
+      mapped_repeated_field.key_to_node.Insert(map_entry);
     }
-    PERFETTO_DCHECK(size_ > 0);
-    SetCtrl(res.idx, kTombstone);
-    slots_[res.idx].key.~Key();
-    slots_[res.idx].value.~Value();
-    size_--;
-    growth_info_.has_tombstones = 1;
-    return true;
-  }

-  PERFETTO_ALWAYS_INLINE std::pair<Value*, bool> Insert(Key key, Value value) {
-    size_t key_hash = Hasher{}(key);
-    uint8_t h2 = H2(key_hash);
-    FindResult res = FindSlotIgnoringTombstones<true>(key, key_hash, h2);
-    if (PERFETTO_UNLIKELY(!res.needs_insert)) {
-      return {&slots_[res.idx].value, false};
-    }
-    if (PERFETTO_UNLIKELY(growth_info_.growth_left == 0)) {
-      GrowAndRehash();
-      // After rehash, table has no tombstones. Find an empty slot directly
-      // instead of recursing (which would prevent inlining).
-      res.idx = FindFirstEmptyOrTombstone(key_hash);
-    }
-    PERFETTO_DCHECK(res.idx != kNotFound);
-    size_t insert_idx = res.idx;
-    bool is_freeslot = true;
-    if (PERFETTO_UNLIKELY(growth_info_.has_tombstones)) {
-      insert_idx = FindFirstEmptyOrTombstone(key_hash);
-      is_freeslot = ctrl_[insert_idx] != kTombstone;
-    }
-    new (&slots_[insert_idx].key) Key(std::move(key));
-    new (&slots_[insert_idx].value) Value(std::move(value));
-    SetCtrl(insert_idx, h2);
-    size_++;
-    if (is_freeslot) {
-      growth_info_.growth_left--;
-    }
-    return {&slots_[insert_idx].value, true};
+    node->value = mapped_repeated_field;
+
+    return StatusOr<void>::Ok();
   }

-  Value& operator[](Key key) {
-    auto it_and_inserted = Insert(std::move(key), Value{});
-    return *it_and_inserted.first;
+  PROTOVM_ABORT(
+      "Attempted to access field as MappedRepeatedField but node has type "
+      "%s",
+      node->GetTypeName());
+}
+
+StatusOr<void> RwProtoCursor::ConvertToIndexedRepeatedFieldIfNeeded(
+    Node* node) {
+  if (node->GetIf<Node::IndexedRepeatedField>()) {
+    return StatusOr<void>::Ok();
   }

-  void Clear() {
-    // Avoid trivial heap operations on zero-capacity std::move()-d objects.
-    if (PERFETTO_UNLIKELY(capacity_ == 0)) {
-      return;
-    }
-    for (size_t i = 0; i < capacity_; ++i) {
-      const uint8_t tag = ctrl_[i];
-      if (tag == kFreeSlot || tag == kTombstone) {
-        continue;
-      }
-      slots_[i].key.~Key();
-      slots_[i].value.~Value();
-    }
-    Reset(capacity_, false);
+  if (node->GetIf<Node::MappedRepeatedField>()) {
+    PROTOVM_ABORT(
+        "Attempted \"convert to indexed repeated field\" operation but "
+        "node has type %s",
+        node->GetTypeName());
   }

-  Iterator GetIterator() { return Iterator(ctrl_, ctrl_ + capacity_, slots_); }
-  Iterator GetIterator() const {
-    return Iterator(ctrl_, ctrl_ + capacity_, slots_);
+  if (node->GetIf<Node::Empty>()) {
+    node->value = Node::IndexedRepeatedField{};
+    return StatusOr<void>::Ok();
   }

-  size_t size() const { return size_; }
-  size_t capacity() const { return capacity_; }
+  auto status_or_map_value = allocator_->CreateNode<Node::Empty>();
+  PROTOVM_RETURN_IF_NOT_OK(status_or_map_value);

- private:
-  // Result struct for FindOrPrepareInsert - avoids bit manipulation overhead
-  struct FindResult {
-    uint64_t idx : 63;
-    uint64_t needs_insert : 1;
-  };
+  auto map_value = std::move(*status_or_map_value);
+  map_value->value = std::move(node->value);
+  node->value = Node::IndexedRepeatedField{};

-  // Tracks growth capacity and whether any deletions have occurred.
-  // Using bitfields like absl's GrowthInfo to avoid manual bit manipulation.
-  struct GrowthInfo {
-    uint64_t growth_left : 63;
-    uint64_t has_tombstones : 1;
-  };
+  auto status_or_it =
+      MapInsert(&node->GetIf<Node::IndexedRepeatedField>()->index_to_node, 0,
+                std::move(map_value));
+  PROTOVM_RETURN(status_or_it);
+}

-  // Not found sentinel (must fit in 63-bit FindResult.idx)
-  static constexpr size_t kNotFound = std::numeric_limits<size_t>::max() >> 1;
+StatusOr<IntrusiveMap::Iterator> RwProtoCursor::FindOrCreateMessageField(
+    Node* node,
+    uint32_t field_id) {
+  auto status = ConvertToMessageIfNeeded(node);
+  PROTOVM_RETURN_IF_NOT_OK(status);

-  // Abstraction over a group of control bytes that enables batch operations.
-  // On x64, uses SSE to match 16 control bytes in parallel.
-  // On other platforms, uses SWAR (SIMD Within A Register) for 8 bytes.
-  //
-  // Match() returns an iterator over slots whose control byte matches h2.
-  // MatchEmpty() returns an iterator over empty slots (kFreeSlot).
-  // MatchEmptyOrDeleted() returns an iterator over empty or deleted slots.
-#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
-  struct Group {
-   public:
-    // Group size 16 for x64 SSE
-    static constexpr size_t kSize = 16;
-
-    // Iterates over set bits in a match mask. Each set bit indicates a slot
-    // in the group that matched the search criteria. Call Next() to get the
-    // index of each matching slot.
-    struct Iterator {
-     public:
-      PERFETTO_ALWAYS_INLINE explicit Iterator(uint16_t mask) : mask_(mask) {}
-      PERFETTO_ALWAYS_INLINE explicit operator bool() const { return mask_; }
-
-      PERFETTO_ALWAYS_INLINE size_t Next() {
-        auto idx = static_cast<size_t>(CountTrailZeros(mask_));
-        mask_ &= static_cast<uint16_t>(mask_ - 1);
-        return idx;
-      }
+  auto* message = node->GetIf<Node::Message>();
+  auto it = message->field_id_to_node.Find(field_id);
+  if (it) {
+    return it;
+  }

-     private:
-      uint16_t mask_;
-    };
+  auto status_or_map_value = allocator_->CreateNode<Node::Empty>();
+  PROTOVM_RETURN_IF_NOT_OK(status_or_map_value);

-    PERFETTO_ALWAYS_INLINE explicit Group(const uint8_t* pos) {
-      ctrl_ = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pos));
-    }
+  auto status_or_it = MapInsert(&message->field_id_to_node, field_id,
+                                std::move(*status_or_map_value));
+  PROTOVM_RETURN(status_or_it);
+}

-    PERFETTO_ALWAYS_INLINE Iterator Match(uint8_t h2) const {
-      auto match = _mm_cmpeq_epi8(ctrl_, _mm_set1_epi8(static_cast<char>(h2)));
-      return Iterator(static_cast<uint16_t>(_mm_movemask_epi8(match)));
-    }
+StatusOr<IntrusiveMap::Iterator>
+RwProtoCursor::FindOrCreateIndexedRepeatedField(Node* node, uint32_t index) {
+  auto& index_to_node =
+      node->GetIf<Node::IndexedRepeatedField>()->index_to_node;

-    PERFETTO_ALWAYS_INLINE Iterator MatchEmpty() const {
-      return Iterator(static_cast<uint16_t>(
-          _mm_movemask_epi8(_mm_sign_epi8(ctrl_, ctrl_))));
-    }
+  auto it = index_to_node.Find(index);
+  if (it) {
+    return it;
+  }

-    PERFETTO_ALWAYS_INLINE Iterator MatchEmptyOrDeleted() const {
-      return Iterator(static_cast<uint16_t>(_mm_movemask_epi8(ctrl_)));
-    }
+  bool requires_creation_and_is_not_simple_append =
+      index > index_to_node.Size();
+  if (requires_creation_and_is_not_simple_append) {
+    PROTOVM_ABORT(
+        "Attempted to insert repeated field at arbitrary position (only "
+        "append operation is supported)");
+  }

-   private:
-    __m128i ctrl_;
-  };
-#else
-  // SWAR fallback: processes 8 control bytes at a time using 64-bit arithmetic.
-  struct Group {
-   public:
-    // Group size 8 for ARM and other platforms
-    static constexpr size_t kSize = 8;
-
-    // Iterates over set bits in a sparse 64-bit mask. Each set MSB indicates
-    // a matching byte in the group. Call Next() to get the index of each
-    // matching slot.
-    struct Iterator {
-     public:
-      PERFETTO_ALWAYS_INLINE explicit Iterator(uint64_t mask) : mask_(mask) {}
-      PERFETTO_ALWAYS_INLINE explicit operator bool() const { return mask_; }
-      PERFETTO_ALWAYS_INLINE size_t Next() {
-        // Count zeros and divide by 8 (shift 3)
-        // 0x80 (Byte 0) -> CTZ 7  -> 7>>3 = 0
-        // 0x8000 (Byte 1) -> CTZ 15 -> 15>>3 = 1
-        size_t idx = static_cast<size_t>(CountTrailZeros(mask_) >> 3);
-        mask_ &= mask_ - 1;  // Clear lowest set bit
-        return idx;
-      }
+  auto status_or_map_value = allocator_->CreateNode<Node::Empty>();
+  PROTOVM_RETURN_IF_NOT_OK(status_or_map_value);

-     private:
-      uint64_t mask_;
-    };
+  return MapInsert(&node->GetIf<Node::IndexedRepeatedField>()->index_to_node,
+                   index, std::move(*status_or_map_value));
+}

-    PERFETTO_ALWAYS_INLINE explicit Group(const uint8_t* pos) {
-      memcpy(&ctrl_, pos, sizeof(ctrl_));
-    }
+StatusOr<IntrusiveMap::Iterator> RwProtoCursor::FindOrCreateMappedRepeatedField(
+    Node* node,
+    uint64_t key) {
+  auto it = node->GetIf<Node::MappedRepeatedField>()->key_to_node.Find(key);

-    PERFETTO_ALWAYS_INLINE Iterator Match(uint8_t h2) const {
-      uint64_t x = ctrl_ ^ (kLsbs * h2);
-      return Iterator((x - kLsbs) & ~x & kMsbs);
-    }
+  if (it) {
+    return it;
+  }

-    PERFETTO_ALWAYS_INLINE Iterator MatchEmpty() const {
-      // 0x80 check (Empty)
-      return Iterator((ctrl_ & ~(ctrl_ << 6)) & kMsbs);
-    }
+  auto status_or_map_value = allocator_->CreateNode<Node::Empty>();
+  PROTOVM_RETURN_IF_NOT_OK(status_or_map_value);

-    PERFETTO_ALWAYS_INLINE Iterator MatchEmptyOrDeleted() const {
-      // 0x80 or 0xFE check (Empty or Deleted)
-      return Iterator((ctrl_ & ~(ctrl_ << 7)) & kMsbs);
-    }
+  return MapInsert(&node->GetIf<Node::MappedRepeatedField>()->key_to_node, key,
+                   std::move(*status_or_map_value));
+}

-   private:
-    static constexpr uint64_t kLsbs = 0x0101010101010101ULL;
-    static constexpr uint64_t kMsbs = 0x8080808080808080ULL;
+StatusOr<IntrusiveMap::Iterator> RwProtoCursor::MapInsert(
+    IntrusiveMap* map,
+    uint64_t key,
+    OwnedPtr<Node> map_value) {
+  auto status_or_map_node =
+      allocator_->CreateMapNode(key, std::move(map_value));
+  PROTOVM_RETURN_IF_NOT_OK(status_or_map_node, "Failed to allocate node");

-    uint64_t ctrl_;
-  };
-#endif
+  auto [it, inserted] = map->Insert(*status_or_map_node->get());
+  if (!inserted) {
+    allocator_->Delete(status_or_map_node->release());
+    PROTOVM_ABORT(
+        "Failed to insert intrusive map entry (key = %d). Duplicated key?",
+        static_cast<int>(key));
+  }

-  // The number of cloned control bytes after the main control byte array.
-  static constexpr size_t kNumClones = Group::kSize - 1;
+  status_or_map_node->release();
+  return it;
+}

-  // Searches for a key in the table. This function IGNORES tombstones during
-  // the search - it only stops at empty slots (kFreeSlot) or matching keys.
-  //
-  // Why ignore tombstones? In Swiss Tables, tombstones mark deleted entries but
-  // must be skipped during lookup because the key we're searching for may have
-  // been inserted AFTER the tombstone was created (i.e., the key's probe
-  // sequence may have skipped over that tombstone). Only an empty slot
-  // definitively proves the key doesn't exist.
-  //
-  // Returns FindResult with idx and whether the key needs to be inserted:
-  // - If key is found: {idx, false} where idx is the slot containing the key.
-  // - If key not found and ForInsert=true: {empty_idx, true} where empty_idx
-  //   is the index of the first EMPTY slot encountered.
-  // - If key not found and ForInsert=false: {kNotFound, true}.
-  //
-  // IMPORTANT for insertion (ForInsert=true): The returned empty_idx is NOT
-  // necessarily the best slot to insert into! There may be an earlier tombstone
-  // in the probe sequence that should be reused to avoid wasting slots. When
-  // has_tombstones is set, the caller must make a SECOND pass by calling
-  // FindFirstEmptyOrTombstone() to find the actual insertion slot.
-  template <bool ForInsert, typename K = Key>
-  PERFETTO_ALWAYS_INLINE FindResult
-  FindSlotIgnoringTombstones(const K& key, size_t key_hash, uint8_t h2) const {
-    static_assert(
-        flat_hash_map_v2_internal::IsLookupKeyAllowed<K, Key, Hasher>(),
-        "Heterogeneous lookup requires Hasher to define is_transparent and "
-        "support hashing the lookup key type. For same-type lookup, Key and K "
-        "must match exactly.");
+StatusOr<uint64_t> RwProtoCursor::ReadScalarField(const Node& node,
+                                                  uint32_t field_id) {
+  if (auto* bytes = node.GetIf<Node::Bytes>()) {
+    protozero::ProtoDecoder decoder(protozero::ConstBytes{
+        static_cast<const uint8_t*>(bytes->data.get()), bytes->size});

-    if (PERFETTO_UNLIKELY(ctrl_ == nullptr)) {
-      return {kNotFound, true};
+    auto field = decoder.ReadField();
+    while (field.valid() && field.id() != field_id) {
+      field = decoder.ReadField();
     }

-    const size_t cap_mask = capacity_ - 1;
-    size_t offset = H1(key_hash) & cap_mask;
-    size_t probe_size = 0;
-    const uint8_t* ctrl = ctrl_;
-
-    // Prefetch control bytes (like Absl's prefetch_heap_block).
-    // Use locality hint 3 (high temporal locality) for better L1 cache usage.
-    __builtin_prefetch(ctrl_ + offset, 0, 3);
-
-    while (true) {
-      // Prefetch slots at current probe offset (like Absl).
-      __builtin_prefetch(slots_ + offset, 0, 3);
-
-      Group group(ctrl + offset);
-
-      // Match H2 tags in this group.
-      for (auto it = group.Match(h2); PERFETTO_LIKELY(it);) {
-        // Must mask because offset + it.Next() can exceed capacity when
-        // group straddles the table boundary (using cloned control bytes).
-        size_t idx = (offset + it.Next()) & cap_mask;
-        if (PERFETTO_LIKELY(Eq{}(slots_[idx].key, key))) {
-          return {idx, false};  // Found
-        }
-      }
+    if (!field.valid()) {
+      PROTOVM_ABORT(
+          "Attempted to read scalar field (id=%u) but it is not present",
+          field_id);
+    }

-      // Check for empty slot (NOT tombstones). If we find an empty slot, the
-      // key cannot exist in the table (empty slots terminate probe chains).
-      if (auto it = group.MatchEmpty(); PERFETTO_LIKELY(it)) {
-        if constexpr (ForInsert) {
-          size_t empty_idx = (offset + it.Next()) & cap_mask;
-          return {empty_idx, true};  // Not found - empty slot for insertion
-        } else {
-          return {kNotFound, true};  // Not found - no need to compute slot
-        }
-      }
+    if (field.type() ==
+        protozero::proto_utils::ProtoWireType::kLengthDelimited) {
+      PROTOVM_ABORT("Attempted to length-delimited field (id=%u) as scalar",
+                    field_id);
+    }

-      // Triangular probing (like Absl): 0, 16, 48, 96, ...
-      probe_size += Group::kSize;
-      offset = (offset + probe_size) & cap_mask;
+    return field.as_uint64();
+  }

-      // Should never happen with load limit.
-      PERFETTO_DCHECK(probe_size <= capacity_);
+  if (auto* message = node.GetIf<Node::Message>()) {
+    auto it = message->field_id_to_node.Find(field_id);
+    if (!it) {
+      PROTOVM_ABORT(
+          "Attempted to read scalar field (id=%u) but it is not present",
+          field_id);
     }
-  }

-  // Find first empty OR tombstone slot for insertion.
-  // Called when has_tombstones is set to find an earlier tombstone that can
-  // be reused instead of taking a new empty slot.
-  size_t FindFirstEmptyOrTombstone(size_t key_hash) const {
-    const size_t cap_mask = capacity_ - 1;
-    size_t offset = H1(key_hash) & cap_mask;
-    size_t probe_size = 0;
-    while (true) {
-      Group group(ctrl_ + offset);
-      if (auto it = group.MatchEmptyOrDeleted(); PERFETTO_LIKELY(it)) {
-        return (offset + it.Next()) & cap_mask;
-      }
-      probe_size += Group::kSize;
-      offset = (offset + probe_size) & cap_mask;
+    auto* scalar = it->value->GetIf<Scalar>();
+    if (!scalar) {
+      PROTOVM_ABORT(
+          "Attempted to read scalar field (id=%u) from node with type %s",
+          field_id, it->value->GetTypeName());
     }
+
+    return scalar->value;
   }

-  PERFETTO_NO_INLINE void GrowAndRehash() {
-    // Grow factor must be a power of 2 because probing uses bitwise AND
-    // for modulo arithmetic (capacity must remain a power of 2).
-    static constexpr size_t kGrowFactor = 2;
-    static_assert((kGrowFactor & (kGrowFactor - 1)) == 0,
-                  "kGrowFactor must be a power of 2");
+  PROTOVM_ABORT(
+      "Attempted to read scalar field (id=%u) but parent node has type %s",
+      field_id, node.GetTypeName());
+}

-    PERFETTO_DCHECK(size_ <= capacity_);
+StatusOr<void> RwProtoCursor::CheckIsValid() const {
+  if (!node_) {
+    PROTOVM_ABORT("Attempted to access invalid cursor");
+  }
+  return StatusOr<void>::Ok();
+}

-    size_t old_capacity = capacity_;
-    size_t old_size = size_;
-    uint8_t* old_ctrl = ctrl_;
-    Slot* old_slots = slots_;
-    std::unique_ptr<uint8_t[]> old_storage(std::move(storage_));
+}  // namespace protovm
+}  // namespace perfetto
+// gen_amalgamated begin source: src/protovm/vm.cc
+// gen_amalgamated begin header: src/protovm/vm.h
+/*
+ * Copyright (C) 2025 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 must be a CHECK (i.e. not just a DCHECK) to prevent UAF attacks on
-    // 32-bit archs that try to double the size of the table until wrapping.
-    size_t new_capacity = old_capacity * kGrowFactor;
-    PERFETTO_CHECK(new_capacity >= old_capacity);
-    Reset(new_capacity, true);
+#ifndef SRC_PROTOVM_VM_H_
+#define SRC_PROTOVM_VM_H_

-    size_t new_size = 0;
-    for (size_t i = 0; i < old_capacity; ++i) {
-      if (uint8_t t = old_ctrl[i]; t == kFreeSlot || t == kTombstone) {
-        continue;
-      }
-      Insert(std::move(old_slots[i].key), std::move(old_slots[i].value));
-      old_slots[i].key.~Key();  // Destroy the old objects.
-      old_slots[i].value.~Value();
-      new_size++;
-    }
-    PERFETTO_DCHECK(new_size == old_size);
-    size_ = new_size;
-  }
+#include <memory>

-  // Doesn't call destructors. Use Clear() for that.
-  PERFETTO_NO_INLINE void Reset(size_t n, bool reallocate) {
-    // Must be a pow2.
-    PERFETTO_CHECK((n & (n - 1)) == 0);
+// gen_amalgamated expanded: #include "perfetto/protozero/field.h"

-    // Always ensure at least 128 capacity to avoid too frequent growths.
-    capacity_ = std::max<size_t>(n, 128u);
-    size_ = 0;
-    growth_info_.growth_left =
-        (capacity_ * static_cast<size_t>(load_limit_percent_)) / 100;
-    growth_info_.has_tombstones = 0;
+// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
+// gen_amalgamated expanded: #include "src/protovm/executor.h"
+// gen_amalgamated expanded: #include "src/protovm/parser.h"

-    if (reallocate) {
-      // See memory layout comment above |storage_|.
-      size_t slots_offset =
-          base::AlignUp(capacity_ + kNumClones, alignof(Slot));
-      storage_.reset(new uint8_t[slots_offset + (capacity_ * sizeof(Slot))]);
-      ctrl_ = storage_.get();
-      slots_ = reinterpret_cast<Slot*>(storage_.get() + slots_offset);
-    }
-    if (ctrl_) {
-      // Initialize all control bytes (including clones) to empty (kFreeSlot)
-      memset(ctrl_, kFreeSlot, capacity_ + kNumClones);
-    }
-  }
+namespace perfetto {
+namespace protovm {

-  // Swiss Table hash splitting (matching absl):
-  // H1 = upper bits for bucket index
-  // H2 = lower 7 bits for tag
-  // This ensures H1 and H2 are independent, avoiding tag collisions within
-  // buckets. The seed XOR prevents clustering when hash values have patterns
-  // (e.g., sequential keys)
-  static constexpr size_t H1(size_t hash) { return (hash >> 7); }
-  static constexpr uint8_t H2(size_t hash) { return hash & 0x7F; }
+// A VM that executes programs defined by data sources at registration time.
+// Used by traced to apply patches (packets overwritten in the ring buffer) to
+// an incremental state packet, thus allowing efficient incremental tracing of
+// Layers/Windows/Views without requiring periodic invalidation and achieve
+// perfect interning.
+//
+// Overview of the VM's architecture and interactions:
+//
+//
+//         ***********
+//         *         *
+//         * Program *
+//         *         *
+//         ***********
+//              │
+//              │
+//              │
+//          ┌───┴──┐            ┌────────┐
+//          │      │            │        │
+//          │Parser├────────────┤Executor│
+//          │      │            │        │
+//          └──────┘            └─┬────┬─┘
+//                                │    │
+//                          ┌─────┘    └─────┐
+//                          │                │
+//                          │                │
+//                      ┌───┴────┐       ┌───┴────┐
+//                      │        │       │        │
+//                      │RoCursor│       │RwProto │
+//                      │        │       │::Cursor│
+//                      └────┬───┘       │        │
+//                           │           └───┬────┘
+//                           │               │
+//                           │               │
+//                           │               │
+//                      *********      ***************
+//                      *       *      *             *
+//                      * Patch *      * Incremental *
+//                      *       *      *    state    *
+//                      *********      *             *
+//                                     ***************
+//
+//  ┌─┐
+//  │ │  VM's component
+//  └─┘
+//
+//  ***
+//  * *  Data
+//  ***
+//
+//
+// Parser: Understands the instructions semantic and controls the program flow.
+//         It delegates to the Executor operations like navigating through the
+//         patch and incremental state data, reading values, and manipulating
+//         fields.
+//
+//
+// Executor: Thin glue layer that mainly forwards data back and forth between
+//           the Parser and cursors. Mainly useful for testing, as it can
+//           be easily mocked and allows to test the Parser in isolation.
+//
+//
+// RoCursor: Provides read-only access to the incoming data (the patch) to be
+//           applied. It allows to traverse the proto message structure of the
+//           patch, iterating over fields and extracting field values.
+//
+//
+// RwProto::Cursor: Provides read-write access to the incremental state. It
+//                  allows traversing the proto message structure of the
+//                  incremental state, as well as deleting/inserting/merging
+//                  fields.
+class Vm {
+ public:
+  Vm(protozero::ConstBytes program,
+     size_t memory_limit_bytes,
+     protozero::ConstBytes initial_incremental_state = {nullptr, 0});
+  StatusOr<void> ApplyPatch(protozero::ConstBytes packet);
+  void SerializeIncrementalState(protozero::Message*) const;
+  std::string SerializeProgram() const;
+  std::unique_ptr<Vm> CloneReadOnly() const;
+  uint64_t GetMemoryUsageBytes() const;

-  // Set control byte and update clone if needed
-  PERFETTO_ALWAYS_INLINE void SetCtrl(size_t i, uint8_t h) {
-    ctrl_[i] = h;
-    // Update clone if this is one of the first kNumClones entries
-    if (PERFETTO_UNLIKELY(i < kNumClones)) {
-      ctrl_[capacity_ + i] = h;
+ private:
+  struct ReadWriteState {
+    ReadWriteState(std::string& program,
+                   size_t memory_limit_bytes,
+                   protozero::ConstBytes initial_incremental_state)
+        : executor{},
+          parser{protozero::ConstBytes{
+                     reinterpret_cast<const uint8_t*>(program.data()),
+                     program.size()},
+                 &executor},
+          allocator{memory_limit_bytes},
+          incremental_state{&allocator} {
+      if (initial_incremental_state.data) {
+        incremental_state.GetRoot().SetBytes(initial_incremental_state);
+      }
     }
-  }

-  // Owns the actual memory with the following layout:
-  //
-  // [Control bytes]
-  //   |capacity_| bytes for control bytes.
-  //   kNumClones (15 or 7) bytes for control byte clones (*).
-  //   No alignment required (accessed at arbitrary byte offsets).
-  //
-  // [Padding for Slot alignment]
-  //
-  // [Slots]
-  //   capacity_ * sizeof(Slot): contains key-value pairs.
-  //   Must be aligned to alignof(Slot).
-  //
-  // (*) Control byte clones: The first kNumClones control bytes are duplicated
-  // at the end of the control array. This allows SIMD operations to read a full
-  // group (16 or 8 bytes) starting from any position without bounds checking,
-  // even near the end of the array.
-  std::unique_ptr<uint8_t[]> storage_;
+    Executor executor;
+    Parser parser;
+    Allocator allocator;
+    RwProto incremental_state;
+  };

-  size_t capacity_ = 0;
-  size_t size_ = 0;
+  struct ReadOnlyState {
+    explicit ReadOnlyState(std::string incremental_state)
+        : serialized_incremental_state(std::move(incremental_state)) {}

-  // Slots remaining + has_deleted flag
-  GrowthInfo growth_info_{0, 0};
+    std::string serialized_incremental_state;
+  };

-  // Load factor limit in % of |capacity_|.
-  int load_limit_percent_ = kDefaultLoadLimitPct;
+  // Constructor used to produce read-only copies of the ProtoVm.
+  // Private to avoid unintended copies. It should be used only for
+  // CloneReadOnly().
+  Vm(const Vm&);

-  // Cached pointers for fast access (like absl::flat_hash_map)
-  // These are updated whenever storage is allocated/reallocated.
-  uint8_t* ctrl_ = nullptr;  // Points to control bytes
-  Slot* slots_ = nullptr;    // Points to slot array
-};
+  // Constructor used only for cloning
+  explicit Vm(std::string incremental_state);

-// Alias FlatHashMap to FlatHashMapV1 for backward compatibility.
-//
-// TODO(lalitm): Once FlatHashMapV2 is fully tested and verified, switch this
-// to FlatHashMapV2.
-template <typename Key,
-          typename Value,
-          typename Hasher = MurmurHash<Key>,
-          typename Probe = QuadraticProbe,
-          bool AppendOnly = false>
-using FlatHashMap = FlatHashMapV1<Key, Value, Hasher, Probe, AppendOnly>;
+  std::string SerializeIncrementalStateAsString() const;

-}  // namespace perfetto::base
+  std::string owned_program_;
+  std::variant<ReadWriteState, ReadOnlyState> state_;
+};

-#endif  // INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_H_
+}  // namespace protovm
+}  // namespace perfetto
+
+#endif  // SRC_PROTOVM_VM_H_
 /*
  * Copyright (C) 2025 The Android Open Source Project
  *
@@ -54718,280 +59529,81 @@ using FlatHashMap = FlatHashMapV1<Key, Value, Hasher, Probe, AppendOnly>;
  * limitations under the License.
  */

-#ifndef SRC_PROTOVM_SLAB_ALLOCATOR_H_
-#define SRC_PROTOVM_SLAB_ALLOCATOR_H_
-
-#include <cstdlib>
-#include <new>
-
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/flat_hash_map.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/intrusive_list.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/paged_memory.h"
+// gen_amalgamated expanded: #include "src/protovm/vm.h"

-// An efficient allocator for elements with fixed size and alignment
-// requirements.
-//
-// Design doc: go/perfetto-protovm-implementation
-//
-// Key features:
-//
-// - Slab allocation: Instead of requesting memory for each individual element,
-//   this allocator pre-allocates large memory chunks (slabs). Each slab is
-//   designed to hold multiple elements.
-//
-// - Element free list: A free list tracks available elements within each
-//   individual slab, allowing for O(1) access time during allocation.
-//
-// - Slab intrusive lists: Slabs are managed within one of two intrusive lists.
-//   The "non-full slabs" list and the "full slabs" list. This organization
-//   allows "non-full" slabs (those with available space for new allocations) to
-//   be accessed in O(1) time.
-//
-// - Block-to-Slab hash map: A hash map links 4KB-aligned memory blocks to their
-//   corresponding slab. This enables O(1) mapping of an element back to its
-//   slab during deallocation.
-//
-// Allocation process:
-// 1. If there is no free slab
-//    1.1 Allocate a new slab, add it to the “non-full slabs” list, populate the
-//        block-to-slab map
-// 2. Pick any slab from the “non-full slabs” list
-// 3. Allocate the element
-// 4. If needed, move the slab to the “full slabs” list
-//
-// Deallocation process:
-// 1. Find slab from the block-to-slab map
-// 2. Free element
-// 3. If needed, move to the “non-full slabs” list
-// 4. If needed, deallocate the slab.
+// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
+// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
+// gen_amalgamated expanded: #include "src/protovm/ro_cursor.h"
+// gen_amalgamated expanded: #include "src/protovm/rw_proto.h"

 namespace perfetto {
 namespace protovm {

-namespace internal {
-static constexpr size_t k4KiloBytes = static_cast<size_t>(4096);
-}
-
-template <size_t ElementSize, size_t ElementAlign, size_t Blocks4KB>
-class Slab {
- public:
-  struct IntrusiveListTraits {
-    static constexpr size_t node_offset() {
-      return offsetof(Slab, intrusive_list_node_);
-    }
-  };
-
-  Slab() : paged_memory_(base::PagedMemory::Allocate(ElementSize * kCapacity)) {
-    PERFETTO_CHECK(paged_memory_.IsValid());
-
-    // Expect allocated memory to be always 4KB-aligned
-    PERFETTO_CHECK(reinterpret_cast<uintptr_t>(paged_memory_.Get()) %
-                       internal::k4KiloBytes ==
-                   0);
-
-    ConstructSlots();
-    InitializeSlotsFreeList();
-  }
-
-  ~Slab() { DestroySlots(); }
-
-  void* Allocate() {
-    PERFETTO_DCHECK(next_free_slot_);
-    auto* slot = next_free_slot_;
-    next_free_slot_ = next_free_slot_->next;
-    ++size_;
-    return slot;
-  }
-
-  void Free(void* p) {
-    auto* slot = static_cast<Slot*>(p);
-    PERFETTO_DCHECK(slot >= slots() && slot < slots() + kCapacity);
-    slot->next = next_free_slot_;
-    next_free_slot_ = slot;
-    --size_;
-  }
-
-  bool IsFull() const { return size_ == kCapacity; }
-
-  bool IsEmpty() const { return size_ == 0; }
-
-  const void* GetBeginAddress() const {
-    return const_cast<Slab*>(this)->slots();
-  }
-
-  const void* GetEndAddress() const {
-    return const_cast<Slab*>(this)->slots() + kCapacity;
-  }
-
- private:
-  static constexpr size_t kCapacity =
-      Blocks4KB * (internal::k4KiloBytes / ElementSize);
-
-  static_assert(kCapacity >= 128,
-                "The configured number of 4KB blocks per slab seems too small, "
-                "resulting in a low slab capacity. Slab allocation is "
-                "expensive (involves syscalls), so a high elements-to-slab "
-                "ratio is desirable to amortize the cost.");
-
-  static_assert(ElementAlign <= internal::k4KiloBytes,
-                "SlabAllocator currently supports alignment <= 4KB");
-
-  union Slot {
-    Slot* next;
-    alignas(ElementAlign) unsigned char element[ElementSize];
-  };
-
-  void InitializeSlotsFreeList() {
-    next_free_slot_ = &slots()[0];
-
-    for (size_t i = 0; i + 1 < kCapacity; ++i) {
-      auto& slot = slots()[i];
-      auto& next_slot = slots()[i + 1];
-      slot.next = &next_slot;
-    }
-
-    auto& last_slot = slots()[kCapacity - 1];
-    last_slot.next = nullptr;
-  }
-
-  void ConstructSlots() {
-    auto* slot = slots();
-    for (size_t i = 0; i < kCapacity; ++i) {
-      new (&slot[i]) Slot();
-    }
-  }
-
-  void DestroySlots() {
-    auto* slot = slots();
-    for (size_t i = 0; i < kCapacity; ++i) {
-      slot[i].~Slot();
-    }
-  }
-
-  Slot* slots() {
-    return std::launder(reinterpret_cast<Slot*>(paged_memory_.Get()));
-  }
-
-  Slot* next_free_slot_{nullptr};
-  size_t size_{0};
-  base::IntrusiveListNode intrusive_list_node_;
-  base::PagedMemory paged_memory_;
-};
-
-template <size_t ElementSize, size_t ElementAlign, size_t Blocks4KBPerSlab = 16>
-class SlabAllocator {
- public:
-  ~SlabAllocator() {
-    DeleteSlabs(slabs_non_full_);
-    DeleteSlabs(slabs_full_);
-  }
-
-  void* Allocate() {
-    // Create new slab if needed
-    if (slabs_non_full_.empty()) {
-      auto* slab = new SlabType();
-      slabs_non_full_.PushFront(*slab);
-      ++slabs_non_full_size_;
-      InsertHashMapEntries(*slab);
-    }
-
-    // Allocate using any non-full slab
-    auto& slab = slabs_non_full_.front();
-    auto* allocated = slab.Allocate();
-    PERFETTO_CHECK(allocated);
+Vm::Vm(protozero::ConstBytes program,
+       size_t memory_limit_bytes,
+       protozero::ConstBytes initial_incremental_state)
+    : owned_program_(program.ToStdString()),
+      state_(std::in_place_type_t<ReadWriteState>{},
+             owned_program_,
+             memory_limit_bytes,
+             initial_incremental_state) {}

-    // Move to "full slabs" list if needed
-    if (slab.IsFull()) {
-      slabs_non_full_.Erase(slab);
-      --slabs_non_full_size_;
-      slabs_full_.PushFront(slab);
-    }
+Vm::Vm(const Vm& other)
+    : owned_program_(other.SerializeProgram()),
+      state_(std::in_place_type_t<ReadOnlyState>{},
+             other.SerializeIncrementalStateAsString()) {}

-    return allocated;
+StatusOr<void> Vm::ApplyPatch(protozero::ConstBytes packet) {
+  ReadWriteState* rw_state = std::get_if<ReadWriteState>(&state_);
+  if (!rw_state) {
+    return StatusOr<void>::Abort();
   }
+  auto src = RoCursor(packet);
+  auto dst = rw_state->incremental_state.GetRoot();
+  return rw_state->parser.Run(src, dst);
+}

-  void Free(void* p) {
-    auto& slab = FindSlabInHashMap(p);
-
-    // Move to "non-full slabs" list if needed
-    if (slab.IsFull()) {
-      slabs_full_.Erase(slab);
-      slabs_non_full_.PushFront(slab);
-      ++slabs_non_full_size_;
-    }
-
-    slab.Free(p);
-
-    // Deallocate the slab if it becomes empty and it's not the sole non-full
-    // slab.
-    //
-    // The "is not the sole non-full slab" condition avoids thrashing scenarios
-    // where a slab is repeatedly allocated and deallocated. For example:
-    // 1. Allocate element x -> a new slab is allocated.
-    // 2. Free element x -> slab becomes empty and is deallocated.
-    // 3. Allocate element y -> a new slab is allocated again.
-    // 4. And so on...
-    if (slab.IsEmpty() && slabs_non_full_size_ > 1) {
-      EraseHashMapEntries(slab);
-      slabs_non_full_.Erase(slab);
-      --slabs_non_full_size_;
-      delete &slab;
-    }
+void Vm::SerializeIncrementalState(protozero::Message* proto) const {
+  if (const ReadOnlyState* state = std::get_if<ReadOnlyState>(&state_); state) {
+    proto->AppendRawProtoBytes(state->serialized_incremental_state.data(),
+                               state->serialized_incremental_state.size());
+    return;
   }

- private:
-  using SlabType = Slab<ElementSize, ElementAlign, Blocks4KBPerSlab>;
-  using SlabList =
-      base::IntrusiveList<SlabType, typename SlabType::IntrusiveListTraits>;
+  const ReadWriteState* state = std::get_if<ReadWriteState>(&state_);
+  state->incremental_state.Serialize(proto);
+}

-  void InsertHashMapEntries(SlabType& slab) {
-    for (auto p = reinterpret_cast<uintptr_t>(slab.GetBeginAddress());
-         p < reinterpret_cast<uintptr_t>(slab.GetEndAddress());
-         p += internal::k4KiloBytes) {
-      PERFETTO_DCHECK(p % internal::k4KiloBytes == 0);
-      block_4KB_aligned_to_slab_.Insert(p, &slab);
-    }
-  }
+std::string Vm::SerializeIncrementalStateAsString() const {
+  protozero::HeapBuffered<protozero::Message> proto;
+  SerializeIncrementalState(proto.get());
+  return proto.SerializeAsString();
+}

-  void EraseHashMapEntries(const SlabType& slab) {
-    for (auto p = reinterpret_cast<uintptr_t>(slab.GetBeginAddress());
-         p < reinterpret_cast<uintptr_t>(slab.GetEndAddress());
-         p += internal::k4KiloBytes) {
-      PERFETTO_DCHECK(p % internal::k4KiloBytes == 0);
-      block_4KB_aligned_to_slab_.Erase(p);
-    }
-  }
+std::string Vm::SerializeProgram() const {
+  return owned_program_;
+}

-  SlabType& FindSlabInHashMap(const void* ptr) {
-    auto ptr_4KB_aligned = reinterpret_cast<uintptr_t>(ptr) &
-                           ~(static_cast<uintptr_t>(internal::k4KiloBytes) - 1);
-    SlabType** slab = block_4KB_aligned_to_slab_.Find(ptr_4KB_aligned);
-    PERFETTO_CHECK(slab);
-    PERFETTO_CHECK(*slab);
-    return **slab;
-  }
+std::unique_ptr<Vm> Vm::CloneReadOnly() const {
+  return std::unique_ptr<Vm>(new Vm(*this));
+}

-  void DeleteSlabs(SlabList& slabs) {
-    while (!slabs.empty()) {
-      auto& slab = slabs.front();
-      slabs.PopFront();
-      delete &slab;
-    }
+uint64_t Vm::GetMemoryUsageBytes() const {
+  if (const ReadOnlyState* state = std::get_if<ReadOnlyState>(&state_); state) {
+    return owned_program_.size() + state->serialized_incremental_state.size();
   }

-  base::FlatHashMap<uintptr_t, SlabType*> block_4KB_aligned_to_slab_;
-  SlabList slabs_full_;
-  SlabList slabs_non_full_;
-  size_t slabs_non_full_size_{0};
-};
+  const ReadWriteState* state = std::get_if<ReadWriteState>(&state_);
+  return owned_program_.size() + state->allocator.GetMemoryUsageBytes();
+}

 }  // namespace protovm
 }  // namespace perfetto
-
-#endif  // SRC_PROTOVM_SLAB_ALLOCATOR_H_
+// gen_amalgamated begin source: src/protozero/filtering/filter_bytecode_parser.cc
+// gen_amalgamated begin header: src/protozero/filtering/filter_bytecode_parser.h
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -55006,85 +59618,166 @@ class SlabAllocator {
  * limitations under the License.
  */

-#ifndef SRC_PROTOVM_ALLOCATOR_H_
-#define SRC_PROTOVM_ALLOCATOR_H_
-
-#include <algorithm>
-#include <cstdlib>
+#ifndef SRC_PROTOZERO_FILTERING_FILTER_BYTECODE_PARSER_H_
+#define SRC_PROTOZERO_FILTERING_FILTER_BYTECODE_PARSER_H_

-// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
+#include <stddef.h>
+#include <stdint.h>

-// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
-// gen_amalgamated expanded: #include "src/protovm/node.h"
-// gen_amalgamated expanded: #include "src/protovm/slab_allocator.h"
+#include <vector>

-namespace perfetto {
-namespace protovm {
+namespace protozero {

-// A centralized allocator to manage all the memory requests from RwProto. It
-// enforces a strict memory usage limit, thus allowing fine-grained control of
-// the overall proto VM's memory footprint.
-class Allocator {
+// Loads the proto-encoded bytecode in memory and allows fast lookups for tuples
+// (msg_index, field_id) to tell if a given field should be allowed or not and,
+// in the case of nested fields, what is the next message index to recurse into.
+// This class does two things:
+// 1. Expands the array of varint from the proto into a vector<uint32_t>. This
+//    is to avoid performing varint decoding on every lookup, at the cost of
+//    some extra memory (2KB-4KB). Note that the expanded vector is not just a
+//    1:1 copy of the proto one (more below). This is to avoid O(Fields) linear
+//    lookup complexity.
+// 2. Creates an index of offsets to remember the start word for each message.
+//    This is so we can jump to O(1) to the N-th message when recursing into a
+//    nested fields, without having to scan and find the (N-1)-th END_OF_MESSAGE
+//    marker.
+// Overall lookups are O(1) for field ids < 128 (kDirectlyIndexLimit) and O(N),
+// with N being the number of allowed field ranges for other fields.
+// See comments around |word_| below for the structure of the word vector.
+class FilterBytecodeParser {
  public:
-  static constexpr size_t kNodeSize = std::max(sizeof(Node), sizeof(MapNode));
-  static constexpr size_t kNodeAlign =
-      std::max(alignof(Node), alignof(MapNode));
+  static constexpr uint32_t kSimpleField = 0x7fffffff;
+  static constexpr uint32_t kFilterStringField = 0x7ffffffe;
+  // FilterStringFieldWithType uses a range: 0x7fff0000 to 0x7ffffffd.
+  // The semantic type is encoded in the lower 16 bits.
+  static constexpr uint32_t kFilterStringFieldWithType = 0x7fff0000;
+  static constexpr uint32_t kFilterStringFieldWithTypeMask = 0x7fff0000;
+  static constexpr uint32_t kSemanticTypeMask = 0xffff;

-  explicit Allocator(size_t memory_limit_bytes);
+  // Result of a Query() operation
+  struct QueryResult {
+    bool allowed;  // Whether the field is allowed at all or no.

-  template <class T, class... Args>
-  StatusOr<OwnedPtr<Node>> CreateNode(Args&&... args) {
-    auto p = Allocate();
-    PROTOVM_RETURN_IF_NOT_OK(p);
+    // If |allowed|==true && nested_msg_field() == true, this tells the message
+    // index of the nested field that should be used when recursing in the
+    // parser.
+    uint32_t nested_msg_index;

-    // 1. Construct node's inner value T passing args (struct aggregate
-    // initialization)
-    // 2. Construct node passing T (struct aggregate initialization)
-    new (*p) Node{T{std::forward<Args>(args)...}};
+    // The semantic type of the field (only meaningful when
+    // filter_string_field() returns true). A value of 0 means unspecified.
+    uint32_t semantic_type;

-    return OwnedPtr<Node>(static_cast<Node*>(*p));
-  }
+    // If |allowed|==true, specifies if the field is of a simple type (varint,
+    // fixed32/64, string or byte).
+    bool simple_field() const { return nested_msg_index == kSimpleField; }

-  StatusOr<OwnedPtr<MapNode>> CreateMapNode(uint64_t key, OwnedPtr<Node> value);
+    // If |allowed|==true, specifies if this field is a string field that needs
+    // to be filtered.
+    bool filter_string_field() const {
+      // Range: [kFilterStringFieldWithType, kFilterStringField] i.e.
+      // [0x7fff0000, 0x7ffffffe]. This excludes kSimpleField (0x7fffffff).
+      return nested_msg_index >= kFilterStringFieldWithType &&
+             nested_msg_index <= kFilterStringField;
+    }

-  StatusOr<Node::Bytes> AllocateAndCopyBytes(protozero::ConstBytes data);
+    // If |allowed|==true, specifies if the field is a nested field that needs
+    // recursion. The caller is expected to use |nested_msg_index| for the next
+    // Query() calls.
+    bool nested_msg_field() const {
+      static_assert(kFilterStringFieldWithType < kFilterStringField,
+                    "kFilterStringFieldWithType < kFilterStringField");
+      static_assert(kFilterStringField < kSimpleField,
+                    "kFilterStringField < kSimpleField");
+      return nested_msg_index < kFilterStringFieldWithType;
+    }
+  };

-  // Deeply delete a node and all the referenced data. E.g. if the node holds a
-  // Node::Message, recursively delete the message fields and finally delete the
-  // node.
-  void Delete(Node* node);
-  void Delete(MapNode* node);
+  // Loads a filter. The filter data consists of a sequence of varints which
+  // contains the filter opcodes and a final checksum.
+  // If a filter is already loaded, it is replaced.
+  //
+  // Optionally accepts an overlay (pass nullptr/0 if not needed). The overlay
+  // can add new fields or upgrade existing fields (e.g., to FilterString).
+  // The overlay uses the same opcode format as the base bytecode, but with an
+  // explicit message index prefix:
+  //   [msg_index, (field_id << 3) | opcode, argument] ... [checksum]
+  // Entry sizes depend on the opcode (same as base bytecode).
+  // Entries must be sorted by (msg_index, field_id). The base bytecode and
+  // overlay are merged during loading, maintaining sorted field order.
+  // See /rfcs/0011-subset-string-filter-rules.md for design details.
+  bool Load(const void* filter_data,
+            size_t len,
+            const void* overlay_data = nullptr,
+            size_t overlay_len = 0);

-  // Deeply delete a node's referenced data, but do not delete the node itself.
-  // E.g. if the node holds a Node::Message, recursively delete the message
-  // fields.
-  void DeleteReferencedData(Node* node);
+  // Checks wheter a given field is allowed or not.
+  // msg_index = 0 is the index of the root message, where all queries should
+  // start from (typically perfetto.protos.Trace).
+  QueryResult Query(uint32_t msg_index, uint32_t field_id) const;

-  // Recursively delete a message's fields, but do not delete the message
-  // itself.
-  void DeleteReferencedData(Node::Message* message);
+  void Reset();
+  void set_suppress_logs_for_fuzzer(bool x) { suppress_logs_for_fuzzer_ = x; }

-  // Delete the blob referenced by the Bytes struct, but do not delete the Bytes
-  // struct itself.
-  void DeleteReferencedData(Node::Bytes* bytes);
+ private:
+  static constexpr uint32_t kDirectlyIndexLimit = 128;
+  static constexpr uint32_t kAllowed = 1u << 31u;

-  size_t GetMemoryUsageBytes() const { return used_memory_bytes_; }
+  bool LoadInternal(const uint8_t* filter_data,
+                    size_t len,
+                    const uint8_t* overlay_data,
+                    size_t overlay_len);
+
+  // The state of all fields for all messages is stored in one contiguous array.
+  // This is to avoid memory fragmentation and allocator overhead.
+  // We expect a high number of messages (hundreds), but each message is small.
+  // For each message we store two sets of uint32:
+  // 1. A set of "directly indexed" fields, for field ids < 128.
+  // 2. The remainder is a set of ranges.
+  // So each message descriptor consists of a sequence of words as follows:
+  //
+  // [0] -> how many directly indexed fields are stored next (up to 128)
+  //
+  // [1..N] -> One word per field id (See "field state" below).
+  //
+  // [N + 1] -> Start of field id range 1
+  // [N + 2] -> End of field id range 1 (exclusive, STL-style).
+  // [N + 3] -> Field state for fields in range 1 (below)
+  //
+  // [N + 4] -> Start of field id range 2
+  // [N + 5] -> End of field id range 2 (exclusive, STL-style).
+  // [N + 6] -> Field state for fields in range 2 (below)
+
+  // The "field state" word is as follows:
+  // Bit 31: 1 if the field is allowed, 0 if disallowed.
+  //         Only directly indexed fields can be 0 (it doesn't make sense to add
+  //         a range and then say "btw it's NOT allowed".. don't add it then.
+  //         0 is only used for filling gaps in the directly indexed bucket.
+  // Bits [30..0] (only when MSB == allowed):
+  //  0x7fffffff: The field is "simple" (varint, fixed32/64, string, bytes) and
+  //      can be directly passed through in output. No recursion is needed.
+  //  0x7ffffffe: The field is string field which needs to be filtered.
+  //  0x7fff0000-0x7ffffffd: The field is a string field with a semantic type.
+  //      The lower 16 bits encode the semantic type.
+  //  [0, 0x7ffeffff]: The field is a nested submessage. The value is the index
+  //     that must be passed as first argument to the next Query() calls.
+  //     Note that the message index is purely a monotonic counter in the
+  std::vector<uint32_t> words_;

- private:
-  void DeallocateBytes(OwnedPtr<void> p, size_t size);
-  StatusOr<void*> Allocate();
+  // One entry for each message index stored in the filter plus a sentinel at
+  // the end. Maps each message index to the offset in |words_| where the
+  // Nth message start.
+  // message_offset_.size() - 2 == the max message id that can be parsed.
+  std::vector<uint32_t> message_offset_;

-  size_t memory_limit_bytes_{0};
-  size_t used_memory_bytes_{0};
-  SlabAllocator<kNodeSize, kNodeAlign> slab_allocator_;
+  bool suppress_logs_for_fuzzer_ = false;
 };

-}  // namespace protovm
-}  // namespace perfetto
+}  // namespace protozero

-#endif  // SRC_PROTOVM_ALLOCATOR_H_
+#endif  // SRC_PROTOZERO_FILTERING_FILTER_BYTECODE_PARSER_H_
+// gen_amalgamated begin header: src/protozero/filtering/filter_bytecode_common.h
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -55099,121 +59792,52 @@ class Allocator {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/protovm/allocator.h"
-
-namespace perfetto {
-namespace protovm {
-
-Allocator::Allocator(size_t memory_limit_bytes)
-    : memory_limit_bytes_{memory_limit_bytes}, used_memory_bytes_{0} {}
-
-StatusOr<OwnedPtr<MapNode>> Allocator::CreateMapNode(uint64_t key,
-                                                     OwnedPtr<Node> value) {
-  PERFETTO_DCHECK(value);
-  auto p = Allocate();
-  if (!p.IsOk()) {
-    Delete(value.release());
-    PROTOVM_RETURN(p);
-  }
-  new (*p) MapNode{key, std::move(value)};
-  return OwnedPtr<MapNode>(static_cast<MapNode*>(*p));
-}
-
-StatusOr<Node::Bytes> Allocator::AllocateAndCopyBytes(
-    protozero::ConstBytes data) {
-  if (used_memory_bytes_ + data.size > memory_limit_bytes_) {
-    PROTOVM_ABORT(
-        "Failed to allocate %zu bytes. Memory limit: %zu bytes. Used: %zu "
-        "bytes.)",
-        data.size, memory_limit_bytes_, used_memory_bytes_);
-  }
+#ifndef SRC_PROTOZERO_FILTERING_FILTER_BYTECODE_COMMON_H_
+#define SRC_PROTOZERO_FILTERING_FILTER_BYTECODE_COMMON_H_

-  if (data.size == 0) {
-    return Node::Bytes{nullptr, 0};
-  }
+#include <stdint.h>

-  auto copy = OwnedPtr<void>{malloc(data.size)};
-  if (!copy) {
-    PROTOVM_ABORT("Failed to malloc %zu bytes", data.size);
-  }
-  used_memory_bytes_ += data.size;
-  memcpy(copy.get(), data.data, data.size);
+namespace protozero {

-  return Node::Bytes{std::move(copy), data.size};
-}
+enum FilterOpcode : uint32_t {
+  // The immediate value is 0 in this case.
+  kFilterOpcode_EndOfMessage = 0,

-void Allocator::Delete(Node* node) {
-  DeleteReferencedData(node);
-  node->~Node();
-  slab_allocator_.Free(node);
-  used_memory_bytes_ -= kNodeSize;
-}
+  // The immediate value is the id of the allowed field.
+  kFilterOpcode_SimpleField = 1,

-void Allocator::Delete(MapNode* node) {
-  Delete(node->value.release());
-  node->~MapNode();
-  slab_allocator_.Free(node);
-  used_memory_bytes_ -= kNodeSize;
-}
+  // The immediate value is the start of the range. The next word (without
+  // any shifting) is the length of the range.
+  kFilterOpcode_SimpleFieldRange = 2,

-void Allocator::DeleteReferencedData(Node* node) {
-  if (auto* message = node->GetIf<Node::Message>()) {
-    DeleteReferencedData(message);
-  } else if (auto* indexed_fields = node->GetIf<Node::IndexedRepeatedField>()) {
-    for (auto it = indexed_fields->index_to_node.begin(); it;) {
-      auto& map_node = *it;
-      it = indexed_fields->index_to_node.Remove(it);
-      Delete(&map_node);
-    }
-  } else if (auto* mapped_fields = node->GetIf<Node::MappedRepeatedField>()) {
-    for (auto it = mapped_fields->key_to_node.begin(); it;) {
-      auto& map_node = *it;
-      it = mapped_fields->key_to_node.Remove(it);
-      Delete(&map_node);
-    }
-  } else if (auto* bytes = node->GetIf<Node::Bytes>()) {
-    DeleteReferencedData(bytes);
-  }
-}
+  // The immediate value is the id of the allowed field. The next word
+  // (without any shifting) is the index of the filter that should be used to
+  // recurse into the nested message.
+  kFilterOpcode_NestedField = 3,

-void Allocator::DeleteReferencedData(Node::Message* message) {
-  for (auto it = message->field_id_to_node.begin(); it;) {
-    auto& map_node = *it;
-    it = message->field_id_to_node.Remove(it);
-    Delete(&map_node);
-  }
-}
+  // The immediate value is the id of the allowed field. The behaviour of this
+  // opcode is the same as kFilterOpcode_SimpleField, with the further semantic
+  // that the field is a string and needs to be processed using the string
+  // filtering rules.
+  kFilterOpcode_FilterString = 4,

-void Allocator::DeleteReferencedData(Node::Bytes* bytes) {
-  DeallocateBytes(std::move(bytes->data), bytes->size);
-}
+  // The immediate value is the id of the allowed field. The next word (without
+  // any shifting) is the semantic type of the string field. The behaviour is
+  // similar to kFilterOpcode_FilterString, but carries semantic type info so
+  // that the string filter can apply type-specific rules.
+  kFilterOpcode_FilterStringWithType = 5,
+};

-void Allocator::DeallocateBytes(OwnedPtr<void> p, size_t size) {
-  free(p.release());
-  used_memory_bytes_ -= size;
-}
+// Constants for extracting opcode and field_id from bytecode words.
+// Bytecode words are encoded as: (field_id << kOpcodeShift) | opcode
+constexpr uint32_t kOpcodeMask = 0x7u;
+constexpr uint32_t kOpcodeShift = 3;

-StatusOr<void*> Allocator::Allocate() {
-  if (used_memory_bytes_ + kNodeSize > memory_limit_bytes_) {
-    PROTOVM_ABORT(
-        "Failed to allocate element (%zu bytes). Memory limit: %zu [bytes]. "
-        "Used: %zu "
-        "[bytes].)",
-        kNodeSize, memory_limit_bytes_, used_memory_bytes_);
-  }
-  auto* p = slab_allocator_.Allocate();
-  if (!p) {
-    PROTOVM_ABORT("Failed to allocate node");
-  }
-  used_memory_bytes_ += kNodeSize;
-  return p;
-}
+}  // namespace protozero

-}  // namespace protovm
-}  // namespace perfetto
-// gen_amalgamated begin source: src/protovm/error_handling.cc
+#endif  // SRC_PROTOZERO_FILTERING_FILTER_BYTECODE_COMMON_H_
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -55228,280 +59852,464 @@ StatusOr<void*> Allocator::Allocate() {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
-
-namespace perfetto {
-namespace protovm {
+// gen_amalgamated expanded: #include "src/protozero/filtering/filter_bytecode_parser.h"

-void LogStacktraceMessage(Stacktrace& stacktrace,
-                          const char* file_name,
-                          int file_line,
-                          const char* fmt,
-                          ...) {
-  const char* const fmt_file_info = "%s:%d ";
-  auto size_file_info =
-      std::snprintf(nullptr, 0, fmt_file_info, file_name, file_line);
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <vector>

-  va_list args;
-  va_start(args, fmt);
-  auto size_message = std::vsnprintf(nullptr, 0, fmt, args);
-  va_end(args);
+// gen_amalgamated expanded: #include "perfetto/base/endian.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/fnv_hash.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+// gen_amalgamated expanded: #include "perfetto/public/compiler.h"
+// gen_amalgamated expanded: #include "src/protozero/filtering/filter_bytecode_common.h"

-  auto s = std::string(
-      static_cast<std::size_t>(size_file_info + size_message + 1), '.');
+namespace protozero {

-  std::snprintf(s.data(), static_cast<size_t>(size_file_info) + 1,
-                fmt_file_info, file_name, file_line);
+namespace {

-  va_start(args, fmt);
-  std::vsnprintf(s.data() + size_file_info,
-                 static_cast<size_t>(size_message) + 1, fmt, args);
-  va_end(args);
+// Parses varint-encoded bytecode and verifies checksum.
+// Returns true on success, with the checksum removed from |words|.
+bool ParseAndVerifyChecksum(const uint8_t* data,
+                            size_t len,
+                            std::vector<uint32_t>* words,
+                            bool suppress_logs) {
+  if (len == 0) {
+    return false;
+  }
+  words->reserve(len);  // An overestimation, but avoids reallocations.

-  s.pop_back();  // null-terminating char
+  // Parse varints while computing checksum in a single pass.
+  // The last word is the checksum itself and should not be hashed.
+  perfetto::base::FnvHasher hasher;
+  uint32_t actual_csum = 0;

-  stacktrace.push_back(std::move(s));
+  using BytecodeDecoder =
+      PackedRepeatedFieldIterator<proto_utils::ProtoWireType::kVarInt,
+                                  uint32_t>;
+  bool has_checksum = false;
+  bool packed_parse_err = false;
+  for (BytecodeDecoder it(data, len, &packed_parse_err); it;) {
+    uint32_t word = *it++;
+    if (bool is_last_word = !it; is_last_word) {
+      actual_csum = word;
+      has_checksum = true;
+      break;
+    }
+    words->emplace_back(word);
+    hasher.Update(perfetto::base::HostToLE(word));
+  }
+  if (packed_parse_err || !has_checksum) {
+    words->clear();
+    return false;
+  }
+  auto expected_csum = static_cast<uint32_t>(hasher.digest());
+  if (expected_csum != actual_csum) {
+    if (!suppress_logs) {
+      PERFETTO_ELOG("Filter bytecode checksum failed. Expected: %x, actual: %x",
+                    expected_csum, actual_csum);
+    }
+    words->clear();
+    return false;
+  }
+  return true;
 }

-void LogStacktraceMessage(Stacktrace& stacktrace,
-                          const char* file_name,
-                          int file_line) {
-  const char* const fmt = "%s:%d <no message>";
-  auto size_message = std::snprintf(nullptr, 0, fmt, file_name, file_line);
-  auto s = std::string(static_cast<size_t>(size_message) + 1, '.');
-  std::snprintf(s.data(), static_cast<size_t>(size_message) + 1, fmt, file_name,
-                file_line);
-
-  s.pop_back();  // null-terminating char
+struct OverlayEntry {
+  uint32_t msg_index;
+  uint32_t field_id;
+  uint32_t message_id;
+};

-  stacktrace.push_back(std::move(s));
+// Returns the message_id for an overlay entry based on opcode and argument.
+// Returns 0 if the opcode is invalid.
+uint32_t GetMessageIdForOverlay(uint32_t opcode, uint32_t argument) {
+  switch (opcode) {
+    case kFilterOpcode_SimpleField:
+      return FilterBytecodeParser::kSimpleField;
+    case kFilterOpcode_FilterString:
+      return FilterBytecodeParser::kFilterStringField;
+    case kFilterOpcode_FilterStringWithType:
+      // For FilterStringWithType, incorporate semantic type into message_id.
+      return FilterBytecodeParser::kFilterStringFieldWithType |
+             (argument & FilterBytecodeParser::kSemanticTypeMask);
+    default:
+      return 0;  // Invalid opcode
+  }
 }

-}  // namespace protovm
-}  // namespace perfetto
-// gen_amalgamated begin source: src/protovm/executor.cc
-// gen_amalgamated begin header: src/protovm/executor.h
-// gen_amalgamated begin header: src/protovm/ro_cursor.h
-/*
- * Copyright (C) 2025 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_PROTOVM_RO_CURSOR_H_
-#define SRC_PROTOVM_RO_CURSOR_H_
+}  // namespace

-#include <cstdint>
-#include <variant>
+void FilterBytecodeParser::Reset() {
+  bool suppress = suppress_logs_for_fuzzer_;
+  *this = FilterBytecodeParser();
+  suppress_logs_for_fuzzer_ = suppress;
+}

-// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+bool FilterBytecodeParser::Load(const void* filter_data,
+                                size_t len,
+                                const void* overlay_data,
+                                size_t overlay_len) {
+  Reset();
+  bool res =
+      LoadInternal(static_cast<const uint8_t*>(filter_data), len,
+                   static_cast<const uint8_t*>(overlay_data), overlay_len);
+  // If load fails, don't leave the parser in a half broken state.
+  if (!res)
+    Reset();
+  return res;
+}

-// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
-// gen_amalgamated expanded: #include "src/protovm/scalar.h"
+bool FilterBytecodeParser::LoadInternal(const uint8_t* filter_data,
+                                        size_t len,
+                                        const uint8_t* overlay_data,
+                                        size_t overlay_len) {
+  // First unpack the varints into a plain uint32 vector, so it's easy to
+  // iterate through them and look ahead.
+  std::vector<uint32_t> words;
+  if (!ParseAndVerifyChecksum(filter_data, len, &words,
+                              suppress_logs_for_fuzzer_))
+    return false;

-namespace perfetto {
-namespace protovm {
+  // Parse the overlay (if provided).
+  std::vector<OverlayEntry> overlay;
+  if (overlay_data && overlay_len > 0) {
+    std::vector<uint32_t> overlay_words;
+    if (!ParseAndVerifyChecksum(overlay_data, overlay_len, &overlay_words,
+                                suppress_logs_for_fuzzer_)) {
+      return false;
+    }

-class RoCursor {
- public:
-  class RepeatedFieldIterator {
-   public:
-    RepeatedFieldIterator();
-    explicit RepeatedFieldIterator(protozero::ProtoDecoder decoder,
-                                   uint32_t field_id);
-    RepeatedFieldIterator& operator++();
-    RoCursor operator*();
-    explicit operator bool() const;
+    // Each entry is exactly 3 words: [msg_index, field_word, argument]
+    // where field_id = field_word >> 3. The argument is 0 when not needed.
+    if (overlay_words.size() % 3 != 0) {
+      PERFETTO_DLOG("overlay error: size %zu not multiple of 3",
+                    overlay_words.size());
+      return false;
+    }

-   private:
-    protozero::Field Advance();
+    for (size_t i = 0; i < overlay_words.size(); i += 3) {
+      overlay.emplace_back();

-    protozero::ProtoDecoder decoder_;
-    uint32_t field_id_;
-    protozero::Field field_;
-  };
+      uint32_t opcode = overlay_words[i + 1] & kOpcodeMask;
+      uint32_t message_id =
+          GetMessageIdForOverlay(opcode, overlay_words[i + 2]);
+      if (message_id == 0) {
+        PERFETTO_DLOG("overlay error: invalid opcode %u at index %zu", opcode,
+                      i + 1);
+        return false;
+      }

-  RoCursor();
-  explicit RoCursor(protozero::ConstBytes data);
-  explicit RoCursor(protozero::Field data);
-  StatusOr<void> EnterField(uint32_t field_id);
-  StatusOr<void> EnterRepeatedFieldAt(uint32_t field_id, uint32_t index);
-  StatusOr<RepeatedFieldIterator> IterateRepeatedField(uint32_t field_id) const;
-  bool IsScalar() const;
-  bool IsBytes() const;
-  StatusOr<Scalar> GetScalar() const;
-  StatusOr<protozero::ConstBytes> GetBytes() const;
+      OverlayEntry& entry = overlay.back();
+      entry.msg_index = overlay_words[i];
+      entry.field_id = overlay_words[i + 1] >> kOpcodeShift;
+      entry.message_id = message_id;

- private:
-  StatusOr<protozero::ConstBytes> GetLengthDelimitedData() const;
+      if (overlay.size() == 1) {
+        continue;
+      }
+      // Validate that overlay entries are sorted by (msg_index, field_id).
+      const OverlayEntry& prev_entry = overlay[overlay.size() - 2];
+      if (entry.msg_index < prev_entry.msg_index ||
+          (entry.msg_index == prev_entry.msg_index &&
+           entry.field_id <= prev_entry.field_id)) {
+        PERFETTO_DLOG(
+            "overlay error: entries not sorted at index %zu (msg %u, "
+            "field %u) after (msg %u, field %u)",
+            i, entry.msg_index, entry.field_id, prev_entry.msg_index,
+            prev_entry.field_id);
+        return false;
+      }
+    }
+  }

-  std::variant<protozero::ConstBytes, protozero::Field> data_;
-};
+  // Temporary storage for each message. Cleared on every END_OF_MESSAGE.
+  std::vector<uint32_t> direct_indexed_fields;
+  std::vector<uint32_t> ranges;
+  uint32_t max_msg_index = 0;
+  uint32_t current_msg_index = 0;
+  size_t overlay_idx = 0;

-}  // namespace protovm
-}  // namespace perfetto
+  auto add_directly_indexed_field = [&](uint32_t field_id, uint32_t msg_id) {
+    PERFETTO_DCHECK(field_id > 0 && field_id < kDirectlyIndexLimit);
+    direct_indexed_fields.resize(std::max(direct_indexed_fields.size(),
+                                          static_cast<size_t>(field_id) + 1));
+    direct_indexed_fields[field_id] = kAllowed | msg_id;
+  };

-#endif  // SRC_PROTOVM_RO_CURSOR_H_
-// gen_amalgamated begin header: src/protovm/rw_proto.h
-// gen_amalgamated begin header: src/protovm/rw_proto_cursor.h
-/*
- * Copyright (C) 2025 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.
- */
+  auto add_range = [&](uint32_t id_start, uint32_t id_end, uint32_t msg_id) {
+    PERFETTO_DCHECK(id_end > id_start);
+    PERFETTO_DCHECK(id_start >= kDirectlyIndexLimit);
+    ranges.emplace_back(id_start);
+    ranges.emplace_back(id_end);
+    ranges.emplace_back(kAllowed | msg_id);
+  };

-#ifndef SRC_PROTOVM_RW_PROTO_CURSOR_H_
-#define SRC_PROTOVM_RW_PROTO_CURSOR_H_
+  // Merges overlay entries into the current message being built.
+  //
+  // This function processes overlay entries for the current message up to (and
+  // including) the given |field_id|. Since both base bytecode and overlay are
+  // sorted by (msg_index, field_id), we can use a two-pointer merge approach:
+  // - Entries with field_id < the given field_id are ADDed as new fields
+  // - An entry with field_id == the given field_id is an UPGRADE (returned)
+  // - Entries with field_id > the given field_id are left for later
+  //
+  // Pass std::numeric_limits<uint32_t>::max() as field_id to drain all
+  // remaining entries for the current message (called at EndOfMessage).
+  //
+  // Returns:
+  // - The msg_id to use if there's an exact match (upgrade case)
+  // - 0 if no match (use the base bytecode's msg_id)
+  // - std::numeric_limits<uint32_t>::max() on error
+  constexpr uint32_t kOverlayError = std::numeric_limits<uint32_t>::max();
+  auto process_overlay = [&](uint32_t field_id) -> uint32_t {
+    while (overlay_idx < overlay.size()) {
+      const OverlayEntry& entry = overlay[overlay_idx];

-// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
+      // Stop if this entry is for a later message or a later field.
+      if (entry.msg_index > current_msg_index ||
+          (entry.msg_index == current_msg_index && entry.field_id > field_id)) {
+        break;
+      }

-// gen_amalgamated expanded: #include "src/protovm/allocator.h"
-// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
-// gen_amalgamated expanded: #include "src/protovm/node.h"
+      // Message indexes are dense and we verified above that we are sorted
+      // so this must be for the current message.
+      PERFETTO_DCHECK(entry.msg_index == current_msg_index);

-namespace perfetto {
-namespace protovm {
+      // Exact match - this is an upgrade. Return the msg_id for the caller.
+      if (entry.field_id == field_id) {
+        ++overlay_idx;
+        return entry.message_id;
+      }

-class RwProtoCursor {
- public:
-  enum MergeFlags : uint32_t {
-    kNone = 0,
-    kSkipSubmessages = 1 << 0,
-    kDelIfSrcEmpty = 1 << 1,
+      // entry_field < field_id: This is a new field to ADD.
+      if (entry.field_id < kDirectlyIndexLimit) {
+        add_directly_indexed_field(entry.field_id, entry.message_id);
+      } else {
+        add_range(entry.field_id, entry.field_id + 1, entry.message_id);
+      }
+      ++overlay_idx;
+    }
+    // No match found.
+    return 0;
   };

-  class RepeatedFieldIterator {
-   public:
-    RepeatedFieldIterator();
-    RepeatedFieldIterator(Allocator* allocator, IntrusiveMap::Iterator it);
-    RepeatedFieldIterator& operator++();
-    RwProtoCursor GetCursor();
-    explicit operator bool() const;
+  bool is_eom = true;
+  for (size_t i = 0; i < words.size(); ++i) {
+    const uint32_t word = words[i];
+    const bool has_next_word = i < words.size() - 1;
+    const uint32_t opcode = word & kOpcodeMask;
+    const uint32_t field_id = word >> kOpcodeShift;

-   private:
-    Allocator* allocator_{nullptr};
-    IntrusiveMap::Iterator it_;
-  };
+    is_eom = opcode == kFilterOpcode_EndOfMessage;
+    if (field_id == 0 && opcode != kFilterOpcode_EndOfMessage) {
+      PERFETTO_DLOG("bytecode error @ word %zu, invalid field id (0)", i);
+      return false;
+    }

-  RwProtoCursor();
-  explicit RwProtoCursor(Node* node, Allocator* allocator);
+    if (opcode == kFilterOpcode_SimpleField ||
+        opcode == kFilterOpcode_NestedField ||
+        opcode == kFilterOpcode_FilterString ||
+        opcode == kFilterOpcode_FilterStringWithType) {
+      // Field words are organized as follow:
+      // MSB: 1 if allowed, 0 if not allowed.
+      // Remaining bits:
+      //   Message index in the case of nested (non-simple) messages.
+      //   0x7fff0000-0x7ffffffe for string fields with semantic type.
+      //   0x7ffffffe in the case of string fields which need filtering.
+      //   0x7fffffff in the case of simple fields.
+      uint32_t msg_id;
+      if (opcode == kFilterOpcode_SimpleField) {
+        msg_id = kSimpleField;
+      } else if (opcode == kFilterOpcode_FilterString) {
+        msg_id = kFilterStringField;
+      } else if (opcode == kFilterOpcode_FilterStringWithType) {
+        // The next word in the bytecode contains the semantic type.
+        if (!has_next_word) {
+          PERFETTO_DLOG(
+              "bytecode error @ word %zu: unterminated filter string with type",
+              i);
+          return false;
+        }
+        uint32_t semantic_type = words[++i];
+        msg_id =
+            kFilterStringFieldWithType | (semantic_type & kSemanticTypeMask);
+      } else {  // FILTER_OPCODE_NESTED_FIELD
+        // The next word in the bytecode contains the message index.
+        if (!has_next_word) {
+          PERFETTO_DLOG("bytecode error @ word %zu: unterminated nested field",
+                        i);
+          return false;
+        }
+        msg_id = words[++i];
+        max_msg_index = std::max(max_msg_index, msg_id);
+      }

-  bool operator==(const RwProtoCursor& other) const;
-  bool operator!=(const RwProtoCursor& other) const;
+      // Process overlay: add any fields that come before this one, and check
+      // if this field should be upgraded.
+      uint32_t overlay_msg_id = process_overlay(field_id);
+      if (overlay_msg_id == kOverlayError) {
+        return false;
+      }
+      if (overlay_msg_id != 0) {
+        msg_id = overlay_msg_id;
+      }

-  StatusOr<bool> IsRoot() const;
-  StatusOr<bool> HasField(uint32_t field_id);
-  StatusOr<void> EnterField(uint32_t field_id);
-  StatusOr<void> EnterRepeatedFieldAt(uint32_t field_id, uint32_t index);
-  StatusOr<RepeatedFieldIterator> IterateRepeatedField(uint32_t field_id);
+      if (field_id < kDirectlyIndexLimit) {
+        add_directly_indexed_field(field_id, msg_id);
+      } else {
+        // In the case of a large field id (rare) we waste an extra word and
+        // represent it as a range. Doesn't make sense to introduce extra
+        // complexity to deal with rare cases like this.
+        add_range(field_id, field_id + 1, msg_id);
+      }
+    } else if (opcode == kFilterOpcode_SimpleFieldRange) {
+      if (!has_next_word) {
+        PERFETTO_DLOG("bytecode error @ word %zu: unterminated range", i);
+        return false;
+      }
+      const uint32_t range_len = words[++i];
+      const uint32_t range_end = field_id + range_len;  // STL-style, excl.
+      uint32_t id = field_id;

-  // Enters a specific repeated field, treating it as a map of key-value pairs.
-  //
-  // The function operates on the principle that the repeated field (identified
-  // by <field_id>) can be viewed as a collection of key-value pairs, where:
-  //  - Each message element within the repetition is a "value".
-  //  - A specific subfield within each message element (identified by
-  //   <map_key_field_id>) serves as the unique key for that element.
-  //
-  // The function performs a lookup using the provided <key> and, if a match
-  // is found, moves the cursor into the corresponding repeated field.
-  //
-  // Example:
-  //
-  // // Message currently pointer by the cursor
-  // message CurrentMessage {
-  //   repeated KeyValuePair entries = <field_id>;
-  // }
-  //
-  // message KeyValuePair {
-  //   uint32 key = <map_key_field_id>;
-  //   string value_x = ...;
-  //   string value_y = ...;
-  // }
-  //
-  // In this example <field_id> points to CurrentMessage.entries,
-  // <map_key_field_id> points to KeyValuePair.key, and <key> = 10. So the
-  // function attempts to find and enter the KeyValuePair message where
-  // key == 10.
-  StatusOr<void> EnterRepeatedFieldByKey(uint32_t field_id,
-                                         uint32_t map_key_field_id,
-                                         uint64_t key);
+      // Here's the subtle complexity: at the bytecode level, we don't know
+      // anything about the kDirectlyIndexLimit. It is legit to define a range
+      // that spans across the direct-indexing threshold (e.g. 126-132). In that
+      // case we want to add all the elements < the indexing to the O(1) bucket
+      // and add only the remaining range as a non-indexed range.
+      for (; id < range_end && id < kDirectlyIndexLimit; ++id)
+        add_directly_indexed_field(id, kAllowed | kSimpleField);
+      PERFETTO_DCHECK(id >= kDirectlyIndexLimit || id == range_end);
+      if (id < range_end)
+        add_range(id, range_end, kSimpleField);
+    } else if (opcode == kFilterOpcode_EndOfMessage) {
+      // Drain any remaining overlay entries for this message.
+      if (process_overlay(std::numeric_limits<uint32_t>::max()) ==
+          kOverlayError) {
+        return false;
+      }

-  StatusOr<Scalar> GetScalar() const;
-  StatusOr<void> SetBytes(protozero::ConstBytes data);
-  StatusOr<void> SetScalar(Scalar scalar);
+      // Verify that the ranges are non-overlapping. Assumes that the ranges
+      // are sorted (they are, because the bytecode is sorted).
+      for (size_t r = 0; r + 3 < ranges.size(); r += 3) {
+        const uint32_t prev_range_end = ranges[r + 1];
+        const uint32_t curr_range_start = ranges[r + 3];
+        if (curr_range_start < prev_range_end) {
+          PERFETTO_DLOG(
+              "bytecode error @ message %u: overlapping ranges [%u, %u) "
+              "and [%u, ...)",
+              current_msg_index, ranges[r], prev_range_end, curr_range_start);
+          return false;
+        }
+      }

-  // Perform a shallow (one level) merge of two messages.
-  // The cursor must currently point to a message and 'data' must contain a
-  // message. Merge fields from the 'data' message into the message pointed by
-  // cursor. Existing fields in the cursor's message are overwritten by fields
-  // from 'data'. Fields present in 'data' but not in the cursor's message are
-  // added/created. If skip_submessages is set to true, any message node already
-  // resolved in the dst are skipped, thus not overwritten by the parent merge.
-  StatusOr<void> Merge(protozero::ConstBytes data, uint32_t flags);
+      // For each message append:
+      // 1. The "header" word telling how many directly indexed fields there
+      //    are.
+      // 2. The words for the directly indexed fields (id < 128).
+      // 3. The rest of the fields, encoded as ranges.
+      // Also update the |message_offset_| index to remember the word offset for
+      // the current message.
+      message_offset_.emplace_back(static_cast<uint32_t>(words_.size()));
+      words_.emplace_back(static_cast<uint32_t>(direct_indexed_fields.size()));
+      words_.insert(words_.end(), direct_indexed_fields.begin(),
+                    direct_indexed_fields.end());
+      words_.insert(words_.end(), ranges.begin(), ranges.end());
+      direct_indexed_fields.clear();
+      ranges.clear();
+      ++current_msg_index;
+    } else {
+      PERFETTO_DLOG("bytecode error @ word %zu: invalid opcode (%x)", i, word);
+      return false;
+    }
+  }  // (for word in bytecode).

-  StatusOr<void> Delete();
+  if (!is_eom) {
+    PERFETTO_DLOG(
+        "bytecode error: end of message not the last word in the bytecode");
+    return false;
+  }

- private:
-  struct ParentLink {
-    Node* node;
-    IntrusiveMap* map;
-    MapNode* map_node;
-  };
+  if (overlay_idx != overlay.size()) {
+    PERFETTO_DLOG("bytecode error: overlay contains %zu unconsumed entries",
+                  overlay.size() - overlay_idx);
+    return false;
+  }

-  StatusOr<void> ConvertToMessageIfNeeded(Node* node);
-  StatusOr<OwnedPtr<Node>> CreateNodeFromField(protozero::Field field);
-  StatusOr<void> ConvertToMappedRepeatedFieldIfNeeded(
-      Node* node,
-      uint32_t map_key_field_id);
-  StatusOr<void> ConvertToIndexedRepeatedFieldIfNeeded(Node* node);
-  StatusOr<IntrusiveMap::Iterator> FindOrCreateMessageField(Node* node,
-                                                            uint32_t field_id);
-  StatusOr<IntrusiveMap::Iterator> FindOrCreateIndexedRepeatedField(
-      Node* node,
-      uint32_t index);
-  StatusOr<IntrusiveMap::Iterator> FindOrCreateMappedRepeatedField(
-      Node* node,
-      uint64_t key);
-  StatusOr<IntrusiveMap::Iterator> MapInsert(IntrusiveMap* map,
-                                             uint64_t key,
-                                             OwnedPtr<Node> map_value);
-  StatusOr<uint64_t> ReadScalarField(const Node& node, uint32_t field_id);
+  if (max_msg_index > 0 && max_msg_index >= message_offset_.size()) {
+    PERFETTO_DLOG(
+        "bytecode error: a message index (%u) is out of range "
+        "(num_messages=%zu)",
+        max_msg_index, message_offset_.size());
+    return false;
+  }

-  StatusOr<void> CheckIsValid() const;
+  // Add a final entry to |message_offset_| so we can tell where the last
+  // message ends without an extra branch in the Query() hotpath.
+  message_offset_.emplace_back(static_cast<uint32_t>(words_.size()));

-  Node* node_ = nullptr;
-  ParentLink parent_link_ = {};
-  Allocator* allocator_ = nullptr;
-};
+  return true;
+}

-}  // namespace protovm
-}  // namespace perfetto
+FilterBytecodeParser::QueryResult FilterBytecodeParser::Query(
+    uint32_t msg_index,
+    uint32_t field_id) const {
+  FilterBytecodeParser::QueryResult res{false, 0u, 0u};
+  if (static_cast<uint64_t>(msg_index) + 1 >=
+      static_cast<uint64_t>(message_offset_.size())) {
+    return res;
+  }
+  const uint32_t start_offset = message_offset_[msg_index];
+  // These are DCHECKs and not just CHECKS because the |words_| is populated
+  // by the LoadInternal call above. These cannot be violated with a malformed
+  // bytecode.
+  PERFETTO_DCHECK(start_offset < words_.size());
+  const uint32_t* word = &words_[start_offset];
+  const uint32_t end_off = message_offset_[msg_index + 1];
+  const uint32_t* const end = words_.data() + end_off;
+  PERFETTO_DCHECK(end > word && end <= words_.data() + words_.size());
+  const uint32_t num_directly_indexed = *(word++);
+  PERFETTO_DCHECK(num_directly_indexed <= kDirectlyIndexLimit);
+  PERFETTO_DCHECK(word + num_directly_indexed <= end);
+  uint32_t field_state = 0;
+  if (PERFETTO_LIKELY(field_id < num_directly_indexed)) {
+    PERFETTO_DCHECK(&word[field_id] < end);
+    field_state = word[field_id];
+  } else {
+    for (word = word + num_directly_indexed; word + 2 < end;) {
+      const uint32_t range_start = *(word++);
+      const uint32_t range_end = *(word++);
+      const uint32_t range_state = *(word++);
+      if (field_id >= range_start && field_id < range_end) {
+        field_state = range_state;
+        break;
+      }
+    }  // for (word in ranges)
+  }  // if (field_id >= num_directly_indexed)

-#endif  // SRC_PROTOVM_RW_PROTO_CURSOR_H_
+  res.allowed = (field_state & kAllowed) != 0;
+  res.nested_msg_index = field_state & ~kAllowed;
+  // Extract semantic type if this is a FilterStringFieldWithType.
+  // Exclude kFilterStringField (0x7ffffffe) which doesn't have semantic type.
+  if ((res.nested_msg_index & kFilterStringFieldWithTypeMask) ==
+          kFilterStringFieldWithType &&
+      res.nested_msg_index != kFilterStringField) {
+    res.semantic_type = res.nested_msg_index & kSemanticTypeMask;
+  }
+  PERFETTO_DCHECK(!res.nested_msg_field() ||
+                  res.nested_msg_index < message_offset_.size() - 1);
+  return res;
+}
+
+}  // namespace protozero
+// gen_amalgamated begin source: src/protozero/filtering/string_filter.cc
+// gen_amalgamated begin header: src/protozero/filtering/string_filter.h
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * 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.
@@ -55516,228 +60324,136 @@ class RwProtoCursor {
  * limitations under the License.
  */

-#ifndef SRC_PROTOVM_RW_PROTO_H_
-#define SRC_PROTOVM_RW_PROTO_H_
+#ifndef SRC_PROTOZERO_FILTERING_STRING_FILTER_H_
+#define SRC_PROTOZERO_FILTERING_STRING_FILTER_H_

-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <vector>

-// gen_amalgamated expanded: #include "src/protovm/allocator.h"
-// gen_amalgamated expanded: #include "src/protovm/node.h"
-// gen_amalgamated expanded: #include "src/protovm/rw_proto_cursor.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/regex.h"
+// gen_amalgamated expanded: #include "perfetto/public/compiler.h"

-namespace perfetto {
-namespace protovm {
+namespace protozero {

-// RwProto provides an API to create and manipulate protobuf messages without
-// requiring prior knowledge of the schema. Meaning that protobuf messages can
-// be dynamically built on-the-fly, without pre-compiled .proto definitions.
-//
-// Key features:
-//
-// - Schema-agnostic: RwProto dynamically learns the schema as fields and
-//   messages are added.
-//
-// - Tree-like representation: protobuf messages are represented internally as a
-//   tree of nodes, mirroring the nested structure of protobufs. This tree can
-//   be traversed and manipulated using a Cursor object.
-//
-// - Fine-grained Memory Management: a centralized allocator is used to manage
-//   memory for nodes, ensuring good data locality and fine-grained control over
-//   memory usage.
-//
-// Overview of RwProto's internal node structure:
-//
-//                             ┌─────────┐
-//                             │         │
-//                             │ Message │
-//                             │         │ Map<field_id, OwnedPtr<Node>>
-//                             └─┬┬┬┬┬┬──┘
-//                               │││││└────────────────────────────────┐
-//                               ││││└───────────────────────┐         │
-//               ┌───────────────┘││└──────────────┐         │         │
-//               │            ┌───┘└────┐          │         │         │
-//               │            │    ┌────▼────┐ ┌───▼───┐ ┌───▼───┐ ┌───▼────┐
-//               │            │    │         │ │       │ │       │ │        │
-//               │            │    │ Message │ │ Empty │ │ Bytes │ │ Scalar │
-//               │            │    │         │ │       │ │       │ │        │
-//               │            │    └────┬────┘ └───────┘ └───────┘ └────────┘
-//               │            │         ▼
-//               │            │        ...
-//               │            │
-//               │            │
-//               │   *********▼********
-//               │   *                *
-//               │   * Mapped         *
-//               │   * RepeatedField  *
-//               │   *                *
-//               │   ******************  Map<key, OwnedPtr<Node>>
-//               │           │ │
-//               │       ┌───┘ └────┐
-//               │       │          │
-//               │  ┌────▼────┐ ┌───▼───┐
-//               │  │         │ │       │
-//               │  │ Message │ │ Bytes │
-//               │  │         │ │       │
-//               │  └────┬────┘ └───────┘
-//               │       ▼
-//               │      ...
-//               │
-//
-//       ********▼*********
-//       *                *
-//       * Indexed        *
-//       * RepeatedField  *
-//       *                *
-//       ******************  Map<index, OwnedPtr<Node>>
-//             ││││
-//      ┌──────┘││└────────────────────┐
-//      │       │└───────────┐         │
-//      │       └──┐         │         │
-//      │          │         │         │
-// ┌────▼────┐ ┌───▼───┐ ┌───▼───┐ ┌───▼────┐
-// │         │ │       │ │       │ │        │
-// │ Message │ │ Empty │ │ Bytes │ │ Scalar │
-// │         │ │       │ │       │ │        │
-// └────┬────┘ └───────┘ └───────┘ └────────┘
-//      ▼
-//     ...
-//
-//
-// ┌─┐
-// │ │  Data node: corresponds to an actual field/message in the proto schema
-// └─┘             and eventually becomes part of the final serialized proto.
-//
-// ***
-// * *  Structural node: used to organize and provide access to message fields.
-// ***                   It is an internal helper and doesn't appear in the
-//                       final serialized proto.
-//
-//
-// Message: Represents a protobuf message. It maps field_id to corresponding
-//          node/value. It allows accessing fields as well as adding and
-//          removing them.
-//
-//
-// Empty: Represents an empty or uninitialized node. It is used as a
-//        placeholder before a node's type is determined. E.g. between an
-//        "enter field" and a "set field value" operation.
-//
-//
-// Bytes: Stores a sequence of bytes, typically representing a length-delimited
-//        protobuf field.
-//
-//
-// Scalar: Stores a scalar value.
-//
-//
-// IndexedRepeatedField: Represents a protobuf repeated field where elements
-//                       are accessed by index.
-//
-//
-// MappedRepeatedField: Represents a protobuf repeated field where elements
-//                      are accessed by key.
-class RwProto {
+// Performs filtering of strings in an "iptables" style. See the comments in
+// |TraceConfig.TraceFilter| for information on how this class works.
+class StringFilter {
  public:
-  using Cursor = RwProtoCursor;
-
-  explicit RwProto(Allocator* allocator);
-  ~RwProto();
-  Cursor GetRoot();
-  void Serialize(protozero::Message*) const;
+  // Bitmask for semantic types. Supports up to 128 semantic types.
+  class SemanticTypeMask {
+   public:
+    using Word = uint64_t;
+    static constexpr size_t kBitsPerWord = sizeof(Word) * 8;
+    static constexpr size_t kLimit = 128;

- private:
-  void SerializeField(uint32_t field_id,
-                      const Node& node,
-                      protozero::Message* proto) const;
+    // Returns a SemanticTypeMask with only bit 0 set (UNSPECIFIED only).
+    // UNSPECIFIED is its own category and only matches if bit 0 is set.
+    static constexpr SemanticTypeMask Unspecified() {
+      return SemanticTypeMask(Word(1), Word(0));
+    }

-  Allocator* allocator_{nullptr};
-  Node root_;
-};
+    // Creates a SemanticTypeMask from raw word values (for testing).
+    static constexpr SemanticTypeMask FromWords(Word w0, Word w1) {
+      return SemanticTypeMask(w0, w1);
+    }

-}  // namespace protovm
-}  // namespace perfetto
+    // Default constructor: no bits set.
+    constexpr SemanticTypeMask() = default;

-#endif  // SRC_PROTOVM_RW_PROTO_H_
-/*
- * Copyright (C) 2025 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.
- */
+    // Sets the bit for |semantic_type|.
+    PERFETTO_ALWAYS_INLINE void Set(uint32_t semantic_type) {
+      PERFETTO_DCHECK(semantic_type < kLimit);
+      uint32_t word_index = semantic_type / kBitsPerWord;
+      uint32_t bit_index = semantic_type % kBitsPerWord;
+      words_[word_index] |= Word(1) << bit_index;
+    }

-#ifndef SRC_PROTOVM_EXECUTOR_H_
-#define SRC_PROTOVM_EXECUTOR_H_
+    // Returns true if the bit for |semantic_type| is set.
+    PERFETTO_ALWAYS_INLINE bool IsSet(uint32_t semantic_type) const {
+      // If beyond supported range, return true (safe default: apply rule).
+      if (PERFETTO_UNLIKELY(semantic_type >= kLimit)) {
+        return true;
+      }
+      uint32_t word_index = semantic_type / kBitsPerWord;
+      uint32_t bit_index = semantic_type % kBitsPerWord;
+      return (words_[word_index] & (Word(1) << bit_index)) != 0;
+    }

-#include <cstdint>
-#include <optional>
+   private:
+    constexpr SemanticTypeMask(Word w0, Word w1) : words_{w0, w1} {}

-// gen_amalgamated expanded: #include "protos/perfetto/protovm/vm_program.pbzero.h"
+    std::array<Word, 2> words_ = {};
+  };

-// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
-// gen_amalgamated expanded: #include "src/protovm/ro_cursor.h"
-// gen_amalgamated expanded: #include "src/protovm/rw_proto.h"
+  enum class Policy : uint8_t {
+    kMatchRedactGroups = 1,
+    kAtraceMatchRedactGroups = 2,
+    kMatchBreak = 3,
+    kAtraceMatchBreak = 4,
+    kAtraceRepeatedSearchRedactGroups = 5,
+  };

-namespace perfetto {
-namespace protovm {
+  // Adds a new rule for filtering strings.
+  //
+  // If `name` is non-empty and a rule with the same name already exists, it
+  // will be replaced; otherwise the rule is appended.
+  //
+  // `semantic_type_mask` is a bitmask indicating which semantic types this rule
+  // applies to. UNSPECIFIED (0) is its own category and only matches if bit 0
+  // is explicitly set. Defaults to matching only UNSPECIFIED.
+  void AddRule(
+      Policy policy,
+      std::string_view pattern,
+      std::string atrace_payload_starts_with,
+      std::string name = {},
+      SemanticTypeMask semantic_type_mask = SemanticTypeMask::Unspecified());

-using CursorEnum = perfetto::protos::pbzero::VmCursorEnum;
+  // Tries to filter the given string. Returns true if the string was modified
+  // in any way, false otherwise. Uses semantic_type=0 (unspecified).
+  bool MaybeFilter(char* ptr, size_t len) const {
+    return MaybeFilter(ptr, len, /*semantic_type=*/0);
+  }

-struct Cursors {
-  RoCursor src;
-  RwProto::Cursor dst;
-  CursorEnum selected{CursorEnum::VM_CURSOR_UNSPECIFIED};
-  bool create_if_not_exist{false};
+  // Tries to filter the given string with a specific semantic type.
+  // Only rules that match the semantic type (or have no type restriction)
+  // are applied.
+  bool MaybeFilter(char* ptr, size_t len, uint32_t semantic_type) const {
+    if (len == 0 || rules_.empty()) {
+      return false;
+    }
+    return MaybeFilterInternal(ptr, len, semantic_type);
+  }

-  // Used internally by Parser to detect illegal "del" operations.
-  // See detailed explanation in the Parser implementation.
-  const RwProto::Cursor* innermost_saved_dst{nullptr};
-};
+ private:
+  struct Rule {
+    Policy policy;
+    std::optional<perfetto::base::CopyableRegex> pattern;
+    std::string atrace_payload_starts_with;
+    std::string name;
+    SemanticTypeMask semantic_type_mask = SemanticTypeMask::Unspecified();
+  };

-// Executor's methods are virtual to be overridden in tests
-class Executor {
- public:
-  using MergeFlags = RwProto::Cursor::MergeFlags;
+  bool MaybeFilterInternal(char* ptr, size_t len, uint32_t semantic_type) const;

-  virtual ~Executor();
-  virtual StatusOr<void> EnterField(Cursors* cursors, uint32_t field_id) const;
-  virtual StatusOr<void> EnterRepeatedFieldAt(Cursors* cursors,
-                                              uint32_t field_id,
-                                              uint32_t index) const;
-  virtual StatusOr<void> EnterRepeatedFieldByKey(Cursors* cursors,
-                                                 uint32_t field_id,
-                                                 uint32_t map_key_field_id,
-                                                 uint64_t key) const;
-  virtual StatusOr<RoCursor::RepeatedFieldIterator> IterateRepeatedField(
-      RoCursor* src,
-      uint32_t field_id) const;
-  virtual StatusOr<RwProto::Cursor::RepeatedFieldIterator> IterateRepeatedField(
-      RwProto::Cursor* dst,
-      uint32_t field_id) const;
-  virtual StatusOr<uint64_t> ReadRegister(uint8_t reg_id) const;
-  virtual StatusOr<void> WriteRegister(const Cursors& cursors, uint8_t reg_id);
-  virtual StatusOr<void> Delete(RwProto::Cursor* dst) const;
-  virtual StatusOr<void> Merge(Cursors* cursors, uint32_t flags) const;
-  virtual StatusOr<void> Set(Cursors* cursors) const;
+  // All rules, in the order they were added.
+  std::vector<Rule> rules_;

- private:
-  std::array<std::optional<uint64_t>, 32> registers_;
+  // Reusable buffer for regex match groups, avoiding heap allocations on each
+  // filter call.
+  mutable std::vector<std::string_view> matches_;
 };

-}  // namespace protovm
-}  // namespace perfetto
+}  // namespace protozero

-#endif  // SRC_PROTOVM_EXECUTOR_H_
+#endif  // SRC_PROTOZERO_FILTERING_STRING_FILTER_H_
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * 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.
@@ -55752,128 +60468,199 @@ class Executor {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/protovm/executor.h"
-
-namespace perfetto {
-namespace protovm {
-
-Executor::~Executor() = default;
+// gen_amalgamated expanded: #include "src/protozero/filtering/string_filter.h"

-StatusOr<void> Executor::EnterField(Cursors* cursors, uint32_t field_id) const {
-  if (cursors->selected == CursorEnum::VM_CURSOR_SRC) {
-    return cursors->src.EnterField(field_id);
-  }
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>

-  auto has_field = cursors->dst.HasField(field_id);
-  PROTOVM_RETURN_IF_NOT_OK(has_field);
+// gen_amalgamated expanded: #include "perfetto/public/compiler.h"

-  if (!has_field.value() && !cursors->create_if_not_exist) {
-    return StatusOr<void>::Error();
-  }
+namespace protozero {
+namespace {

-  return cursors->dst.EnterField(field_id);
-}
+constexpr std::string_view kRedacted = "P60REDACTED";
+constexpr char kRedactedDash = '-';

-StatusOr<void> Executor::EnterRepeatedFieldAt(Cursors* cursors,
-                                              uint32_t field_id,
-                                              uint32_t index) const {
-  if (cursors->selected == CursorEnum::VM_CURSOR_SRC) {
-    return cursors->src.EnterRepeatedFieldAt(field_id, index);
+// Returns a pointer to the first character after the tgid pipe character in
+// the atrace string given by [ptr, end). Returns null if no such character
+// exists.
+//
+// Examples:
+// E|1024 -> nullptr
+// foobarbaz -> nullptr
+// B|1024|x -> pointer to x
+PERFETTO_ALWAYS_INLINE const char* FindAtracePayloadPtr(const char* ptr,
+                                                        const char* end) {
+  // Don't even bother checking any strings which are so short that they could
+  // not contain a post-tgid section. This filters out strings like "E|" which
+  // emitted by Bionic.
+  //
+  // Also filter out any other strings starting with "E" as they never contain
+  // anything past the tgid: this removes >half of the strings for ~zero cost.
+  static constexpr size_t kEarliestSecondPipeIndex = 2;
+  const char* search_start = ptr + kEarliestSecondPipeIndex;
+  if (PERFETTO_UNLIKELY(search_start >= end || *ptr == 'E')) {
+    return nullptr;
   }

-  return cursors->dst.EnterRepeatedFieldAt(field_id, index);
+  // We skipped past the first '|' character by starting at the character at
+  // index 2. Just find the next pipe character (i.e. the one after tgid) using
+  // memchr.
+  const char* pipe = static_cast<const char*>(
+      memchr(search_start, '|', size_t(end - search_start)));
+  return pipe ? pipe + 1 : nullptr;
 }

-StatusOr<void> Executor::EnterRepeatedFieldByKey(Cursors* cursors,
-                                                 uint32_t field_id,
-                                                 uint32_t map_key_field_id,
-                                                 uint64_t key) const {
-  if (cursors->selected == CursorEnum::VM_CURSOR_SRC) {
-    PROTOVM_ABORT(
-        "Mapped repeated fields are currently supported only in RwProto");
-  }
-
-  auto has_field = cursors->dst.HasField(field_id);
-  PROTOVM_RETURN_IF_NOT_OK(has_field);
+PERFETTO_ALWAYS_INLINE bool StartsWith(const char* ptr,
+                                       const char* end,
+                                       const std::string& starts_with) {
+  // Verify that the atrace string has enough characters to match against all
+  // the characters in the "starts with" string.
+  size_t len = starts_with.size();
+  if (PERFETTO_UNLIKELY(ptr + len > end))
+    return false;

-  if (!has_field.value() && !cursors->create_if_not_exist) {
-    return StatusOr<void>::Error();
-  }
+  // Empty string matches everything.
+  if (PERFETTO_UNLIKELY(len == 0))
+    return true;

-  return cursors->dst.EnterRepeatedFieldByKey(field_id, map_key_field_id, key);
-}
+  // Quick rejection: check first character before expensive memcmp.
+  // This is very effective since most strings don't match.
+  if (PERFETTO_LIKELY(*ptr != *starts_with.data()))
+    return false;

-StatusOr<RoCursor::RepeatedFieldIterator> Executor::IterateRepeatedField(
-    RoCursor* src,
-    uint32_t field_id) const {
-  return src->IterateRepeatedField(field_id);
+  // If first char matches, do full memcmp for remaining characters.
+  return memcmp(ptr + 1, starts_with.data() + 1, len - 1) == 0;
 }

-StatusOr<RwProto::Cursor::RepeatedFieldIterator> Executor::IterateRepeatedField(
-    RwProto::Cursor* dst,
-    uint32_t field_id) const {
-  return dst->IterateRepeatedField(field_id);
-}
+void RedactMatches(const std::vector<std::string_view>& matches) {
+  // Go through every group in the matches.
+  for (size_t i = 1; i < matches.size(); ++i) {
+    const auto& match = matches[i];
+    // Skip unmatched optional groups: empty string_view may have nullptr
+    // data(), and passing nullptr to memcpy/memset is UB even with size 0.
+    if (match.empty())
+      continue;

-StatusOr<uint64_t> Executor::ReadRegister(uint8_t reg_id) const {
-  if (reg_id >= registers_.size()) {
-    PROTOVM_ABORT("Register (id = %d) is out of bounds", reg_id);
-  }
-  if (!registers_[reg_id]) {
-    PROTOVM_ABORT("Register (id = %d) is not initialized)", reg_id);
-  }
-  return StatusOr<uint64_t>{*registers_[reg_id]};
-}
+    // Overwrite the match with characters from |kRedacted|. If match is
+    // smaller, we will not use all of |kRedacted| but that's fine (i.e. we
+    // will overwrite with a truncated |kRedacted|).
+    size_t match_len = match.size();
+    size_t redacted_len = std::min(match_len, kRedacted.size());
+    memcpy(const_cast<char*>(match.data()), kRedacted.data(), redacted_len);

-StatusOr<void> Executor::WriteRegister(const Cursors& cursors, uint8_t reg_id) {
-  if (reg_id >= registers_.size()) {
-    PROTOVM_ABORT("Register (id = %d) is out of bounds", reg_id);
+    // Overwrite any characters after |kRedacted| with |kRedactedDash|.
+    memset(const_cast<char*>(match.data()) + redacted_len, kRedactedDash,
+           match_len - redacted_len);
   }
-
-  auto status_or_scalar = cursors.selected == CursorEnum::VM_CURSOR_SRC
-                              ? cursors.src.GetScalar()
-                              : cursors.dst.GetScalar();
-  PROTOVM_RETURN_IF_NOT_OK(status_or_scalar);
-
-  registers_[reg_id] = status_or_scalar->value;
-  return StatusOr<void>::Ok();
 }

-StatusOr<void> Executor::Delete(RwProto::Cursor* dst) const {
-  return dst->Delete();
-}
+}  // namespace

-StatusOr<void> Executor::Merge(Cursors* cursors, uint32_t flags) const {
-  if (!cursors->src.IsBytes()) {
-    PROTOVM_ABORT(
-        "Attempted MERGE operation but src cursor has incompatible data "
-        "type");
+void StringFilter::AddRule(Policy policy,
+                           std::string_view pattern_str,
+                           std::string atrace_payload_starts_with,
+                           std::string name,
+                           SemanticTypeMask semantic_type_mask) {
+  perfetto::base::CopyableRegex re(
+      perfetto::base::Regex::CreateOrCheck(std::string(pattern_str)));
+  Rule new_rule{policy, std::move(re), std::move(atrace_payload_starts_with),
+                std::move(name), semantic_type_mask};
+  // If name is non-empty, look for existing rule with same name and replace.
+  if (!new_rule.name.empty()) {
+    for (Rule& existing : rules_) {
+      if (existing.name == new_rule.name) {
+        existing = std::move(new_rule);
+        return;
+      }
+    }
   }
-
-  return cursors->dst.Merge(*cursors->src.GetBytes(), flags);
+  rules_.push_back(std::move(new_rule));
 }

-StatusOr<void> Executor::Set(Cursors* cursors) const {
-  if (cursors->src.IsScalar()) {
-    auto status_or_scalar = cursors->src.GetScalar();
-    PROTOVM_RETURN_IF_NOT_OK(status_or_scalar);
-    return cursors->dst.SetScalar(*status_or_scalar);
-  }
+bool StringFilter::MaybeFilterInternal(char* ptr,
+                                       size_t len,
+                                       uint32_t semantic_type) const {
+  bool atrace_find_tried = false;
+  const char* atrace_payload_ptr = nullptr;
+  std::string_view input(ptr, len);

-  if (cursors->src.IsBytes()) {
-    auto status_or_bytes = cursors->src.GetBytes();
-    PROTOVM_RETURN_IF_NOT_OK(status_or_bytes);
-    return cursors->dst.SetBytes(*status_or_bytes);
+  for (const Rule& rule : rules_) {
+    if (!rule.semantic_type_mask.IsSet(semantic_type)) {
+      continue;
+    }
+    switch (rule.policy) {
+      case Policy::kMatchBreak:
+        if (PERFETTO_UNLIKELY(rule.pattern->FullMatch(input)))
+          return false;
+        break;
+      case Policy::kMatchRedactGroups:
+        if (PERFETTO_UNLIKELY(
+                rule.pattern->FullMatchWithGroups(input, matches_))) {
+          RedactMatches(matches_);
+          return true;
+        }
+        break;
+      case Policy::kAtraceMatchBreak:
+        atrace_payload_ptr = atrace_find_tried
+                                 ? atrace_payload_ptr
+                                 : FindAtracePayloadPtr(ptr, ptr + len);
+        atrace_find_tried = true;
+        if (atrace_payload_ptr &&
+            StartsWith(atrace_payload_ptr, ptr + len,
+                       rule.atrace_payload_starts_with) &&
+            rule.pattern->FullMatch(input)) {
+          return false;
+        }
+        break;
+      case Policy::kAtraceMatchRedactGroups:
+        atrace_payload_ptr = atrace_find_tried
+                                 ? atrace_payload_ptr
+                                 : FindAtracePayloadPtr(ptr, ptr + len);
+        atrace_find_tried = true;
+        if (atrace_payload_ptr &&
+            StartsWith(atrace_payload_ptr, ptr + len,
+                       rule.atrace_payload_starts_with) &&
+            PERFETTO_UNLIKELY(
+                rule.pattern->FullMatchWithGroups(input, matches_))) {
+          RedactMatches(matches_);
+          return true;
+        }
+        break;
+      case Policy::kAtraceRepeatedSearchRedactGroups:
+        atrace_payload_ptr = atrace_find_tried
+                                 ? atrace_payload_ptr
+                                 : FindAtracePayloadPtr(ptr, ptr + len);
+        atrace_find_tried = true;
+        if (atrace_payload_ptr && StartsWith(atrace_payload_ptr, ptr + len,
+                                             rule.atrace_payload_starts_with)) {
+          bool has_any_matches = false;
+          for (auto iter = rule.pattern->PartialMatchAll(input);
+               iter.NextWithGroups(matches_);) {
+            RedactMatches(matches_);
+            has_any_matches = true;
+          }
+          if (has_any_matches) {
+            return true;
+          }
+        }
+        break;
+    }
   }
-
-  PROTOVM_ABORT("Attempted SET operation but src cursor has no valid data");
+  return false;
 }

-}  // namespace protovm
-}  // namespace perfetto
-// gen_amalgamated begin source: src/protovm/node.cc
+}  // namespace protozero
+// gen_amalgamated begin source: src/protozero/filtering/message_filter.cc
+// gen_amalgamated begin header: src/protozero/filtering/message_filter.h
+// gen_amalgamated begin header: src/protozero/filtering/message_tokenizer.h
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -55888,100 +60675,191 @@ StatusOr<void> Executor::Set(Cursors* cursors) const {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/protovm/node.h"
+#ifndef SRC_PROTOZERO_FILTERING_MESSAGE_TOKENIZER_H_
+#define SRC_PROTOZERO_FILTERING_MESSAGE_TOKENIZER_H_

-namespace perfetto {
-namespace protovm {
+#include <stdint.h>

-const char* Node::GetTypeName() const {
-  return std::visit(
-      [](auto&& value) {
-        using T = std::decay_t<decltype(value)>;
-        if constexpr (std::is_same_v<T, Node::Bytes>) {
-          return "Bytes";
-        } else if constexpr (std::is_same_v<T, Node::Empty>) {
-          return "Empty";
-        } else if constexpr (std::is_same_v<T, Node::IndexedRepeatedField>) {
-          return "IndexedRepeatedField";
-        } else if constexpr (std::is_same_v<T, Node::MappedRepeatedField>) {
-          return "MappedRepeatedField";
-        } else if constexpr (std::is_same_v<T, Node::Message>) {
-          return "Message";
-        } else if constexpr (std::is_same_v<T, Scalar>) {
-          return "Scalar";
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+
+namespace protozero {
+
+// A helper class for schema-less tokenizing of protobuf messages.
+// This class takes a stream of proto-encoded bytes, pushed one by one in input
+// via Push(octet), and returns a stream of tokens (each Push() call can return
+// 0 or 1 token).
+// A "token" contains metadata about a field, specifically: its ID, its wire
+// type and:
+//  - For varint and fixed32/64 fields: its payload.
+//  - For string and bytes fields: the length of its payload.
+//    In this case the caller is supposed to "eat" those N bytes before calling
+//    Push() again.
+// Note that this class cannot differentiate between a string/bytes field or
+// a submessage, because they are encoded in the same way. The caller is
+// supposed to know whether a field can be recursed into by just keep calling
+// Push() or is a string that should be skipped.
+// This is inline to allow the compiler to see through the Push method and
+// avoid a function call for each byte.
+class MessageTokenizer {
+ public:
+  struct Token {
+    uint32_t field_id;  // 0 == not valid.
+    proto_utils::ProtoWireType type;
+
+    // For kLengthDelimited, |value| represent the length of the payload.
+    uint64_t value;
+
+    inline bool valid() const { return field_id != 0; }
+    bool operator==(const Token& o) const {
+      return field_id == o.field_id && type == o.type && value == o.value;
+    }
+  };
+
+  // Pushes a byte in input and returns a token, only when getting to the last
+  // byte of each field. Specifically:
+  // - For varint and fixed32 fields, the Token is returned after the last byte
+  //   of the numeric payload is pushed.
+  // - For length-delimited fields, this returns after the last byte of the
+  //   length is pushed (i.e. right before the payload starts). The caller is
+  //   expected to either skip the next |value| bytes (in the case of a string
+  //   or bytes fields) or keep calling Push, in the case of a submessage.
+  inline Token Push(uint8_t octet) {
+    using protozero::proto_utils::ProtoWireType;
+
+    // Parsing a fixed32/64 field is the only case where we don't have to do
+    // any varint decoding. This is why this block is before the remaining
+    // switch statement below (all the rest is a varint).
+    if (PERFETTO_UNLIKELY(state_ == kFixedIntValue)) {
+      PERFETTO_DCHECK(fixed_int_bits_ == 32 || fixed_int_bits_ == 64);
+      fixed_int_value_ |= static_cast<uint64_t>(octet) << fixed_int_shift_;
+      fixed_int_shift_ += 8;
+      if (fixed_int_shift_ < fixed_int_bits_)
+        return Token{};  // Intermediate byte of a fixed32/64.
+      auto wire_type = fixed_int_bits_ == 32 ? ProtoWireType::kFixed32
+                                             : ProtoWireType::kFixed64;
+      uint64_t fixed_int_value = fixed_int_value_;
+      fixed_int_value_ = fixed_int_shift_ = fixed_int_bits_ = 0;
+      state_ = kFieldPreamble;
+      return Token{field_id_, wire_type, fixed_int_value};
+    }
+
+    // At this point either we are: (i) parsing a field preamble; (ii) parsing a
+    // varint field paylod; (iii) parsing the length of a length-delimited
+    // field. In all cases, we need to decode a varint before proceeding.
+    varint_ |= static_cast<uint64_t>(octet & 0x7F) << varint_shift_;
+    if (octet & 0x80) {
+      varint_shift_ += 7;
+      if (PERFETTO_UNLIKELY(varint_shift_ >= 64)) {
+        varint_shift_ = 0;
+        state_ = kInvalidVarInt;
+      }
+      return Token{};  // Still parsing a varint.
+    }
+
+    uint64_t varint = varint_;
+    varint_ = 0;
+    varint_shift_ = 0;
+
+    switch (state_) {
+      case kFieldPreamble: {
+        auto field_type = static_cast<uint32_t>(varint & 7u);  // 7 = 0..0111
+        field_id_ = static_cast<uint32_t>(varint >> 3);
+
+        // The field type is legit, now check it's well formed and within
+        // boundaries.
+        if (field_type == static_cast<uint32_t>(ProtoWireType::kVarInt)) {
+          state_ = kVarIntValue;
+        } else if (field_type ==
+                       static_cast<uint32_t>(ProtoWireType::kFixed32) ||
+                   field_type ==
+                       static_cast<uint32_t>(ProtoWireType::kFixed64)) {
+          state_ = kFixedIntValue;
+          fixed_int_shift_ = 0;
+          fixed_int_value_ = 0;
+          fixed_int_bits_ =
+              field_type == static_cast<uint32_t>(ProtoWireType::kFixed32) ? 32
+                                                                           : 64;
+        } else if (field_type ==
+                   static_cast<uint32_t>(ProtoWireType::kLengthDelimited)) {
+          state_ = kLenDelimited;
         } else {
-          return "<unknown>";
+          state_ = kInvalidFieldType;
         }
-      },
-      value);
-}
+        return Token{};
+      }

-}  // namespace protovm
-}  // namespace perfetto
-// gen_amalgamated begin source: src/protovm/parser.cc
-// gen_amalgamated begin header: src/protovm/parser.h
-/*
- * Copyright (C) 2025 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.
- */
+      case kVarIntValue: {
+        // Return the varint field payload and go back to the next field.
+        state_ = kFieldPreamble;
+        return Token{field_id_, ProtoWireType::kVarInt, varint};
+      }
+
+      case kLenDelimited: {
+        const auto payload_len = varint;
+        if (payload_len > protozero::proto_utils::kMaxMessageLength) {
+          state_ = kMessageTooBig;
+          return Token{};
+        }
+        state_ = kFieldPreamble;
+        // At this point the caller is expected to consume the next
+        // |payload_len| bytes.
+        return Token{field_id_, ProtoWireType::kLengthDelimited, payload_len};
+      }

-#ifndef SRC_PROTOVM_PARSER_H_
-#define SRC_PROTOVM_PARSER_H_
+      case kFixedIntValue:
+        // Unreachable because of the if before the switch.
+        PERFETTO_DCHECK(false);
+        break;

-// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
-// gen_amalgamated expanded: #include "protos/perfetto/protovm/vm_program.pbzero.h"
+      // Unrecoverable error states.
+      case kInvalidFieldType:
+      case kMessageTooBig:
+      case kInvalidVarInt:
+        break;
+    }  // switch(state_)

-// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
-// gen_amalgamated expanded: #include "src/protovm/executor.h"
-// gen_amalgamated expanded: #include "src/protovm/ro_cursor.h"
-// gen_amalgamated expanded: #include "src/protovm/rw_proto.h"
+    return Token{};  // Keep GCC happy.
+  }

-namespace perfetto {
-namespace protovm {
+  // Returns true if the tokenizer FSM has reached quiescence (i.e. if we are
+  // NOT in the middle of parsing a field).
+  bool idle() const {
+    return state_ == kFieldPreamble && varint_shift_ == 0 &&
+           fixed_int_shift_ == 0;
+  }

-class Parser {
- public:
-  Parser(protozero::ConstBytes program, Executor* executor);
-  StatusOr<void> Run(RoCursor src, RwProto::Cursor dst);
+  // Only for reporting parser errors in the trace.
+  uint32_t state() const { return static_cast<uint32_t>(state_); }

  private:
-  StatusOr<void> ParseInstructions(
-      protozero::RepeatedFieldIterator<protozero::ConstBytes> it_instruction);
-  StatusOr<void> ParseInstruction(
-      const protos::pbzero::VmInstruction::Decoder& instruction);
-  StatusOr<void> ParseMerge(
-      const protos::pbzero::VmInstruction::Decoder& instruction);
-  StatusOr<void> ParseRegLoad(
-      const protos::pbzero::VmInstruction::Decoder& instruction);
-  StatusOr<void> ParseSelect(
-      const protos::pbzero::VmInstruction::Decoder& instruction);
-  StatusOr<void> ParseSelectRec(
-      const protos::pbzero::VmInstruction::Decoder& instruction,
-      protozero::RepeatedFieldIterator<protozero::ConstBytes>
-          it_path_component);
+  enum State {
+    kFieldPreamble = 0,  // Parsing the varint for the field preamble.
+    kVarIntValue = 1,    // Parsing the payload of a varint field.
+    kFixedIntValue = 2,  // Parsing the payload of a fixed32/64 field.
+    kLenDelimited = 3,   // Parsing the length of a length-delimited field.

-  protos::pbzero::VmProgram::Decoder program_;
-  Cursors cursors_;
-  Executor* executor_;
+    // Unrecoverable error states:
+    kInvalidFieldType = 4,  // Encountered an invalid field type.
+    kMessageTooBig = 5,     // Size of the length delimited message was too big.
+    kInvalidVarInt = 6,     // Varint larger than 64 bits.
+  };
+
+  State state_ = kFieldPreamble;
+  uint32_t field_id_ = 0;
+  uint64_t varint_ = 0;
+  uint32_t varint_shift_ = 0;
+  uint32_t fixed_int_shift_ = 0;
+  uint32_t fixed_int_bits_ = 0;
+  uint64_t fixed_int_value_ = 0;
 };

-}  // namespace protovm
-}  // namespace perfetto
+}  // namespace protozero

-#endif  // SRC_PROTOVM_PARSER_H_
+#endif  // SRC_PROTOZERO_FILTERING_MESSAGE_TOKENIZER_H_
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -55996,260 +60874,249 @@ class Parser {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/protovm/parser.h"
-// gen_amalgamated expanded: #include "protos/perfetto/protovm/vm_program.pbzero.h"
-// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
+#ifndef SRC_PROTOZERO_FILTERING_MESSAGE_FILTER_H_
+#define SRC_PROTOZERO_FILTERING_MESSAGE_FILTER_H_

-namespace perfetto {
-namespace protovm {
+#include <stdint.h>

-Parser::Parser(protozero::ConstBytes program, Executor* executor)
-    : program_{program}, executor_(executor) {}
+#include <memory>
+#include <string>
+#include <unordered_map>

-StatusOr<void> Parser::Run(RoCursor src, RwProto::Cursor dst) {
-  cursors_.src = src;
-  cursors_.dst = dst;
-  cursors_.innermost_saved_dst = nullptr;
-  return ParseInstructions(program_.instructions());
-}
+// gen_amalgamated expanded: #include "src/protozero/filtering/filter_bytecode_parser.h"
+// gen_amalgamated expanded: #include "src/protozero/filtering/message_tokenizer.h"
+// gen_amalgamated expanded: #include "src/protozero/filtering/string_filter.h"

-StatusOr<void> Parser::ParseInstructions(
-    protozero::RepeatedFieldIterator<protozero::ConstBytes> it_instruction) {
-  int instruction_index = 0;
-  for (; it_instruction; ++it_instruction) {
-    auto instruction = protos::pbzero::VmInstruction::Decoder(*it_instruction);
-    auto status = ParseInstruction(instruction);
+namespace protozero {

-    if (status.IsAbort()) {
-      PROTOVM_RETURN(status, "instruction[%d]", instruction_index);
-    }
+// A class to filter binary-encoded proto messages using an allow-list of field
+// ids, also known as "filter bytecode". The filter determines which fields are
+// allowed to be passed through in output and strips all the other fields.
+// See go/trace-filtering for full design.
+// This class takes in input:
+// 1) The filter bytecode, loaded once via the LoadFilterBytecode() method.
+// 2) A proto-encoded binary message. The message doesn't have to be contiguous,
+//    it can be passed as an array of arbitrarily chunked fragments.
+// The FilterMessage*() method returns in output a proto message, stripping out
+// all unknown fields. If the input is malformed (e.g., unknown proto field wire
+// types, lengths out of bound) the whole filtering failed and the |error| flag
+// of the FilteredMessage object is set to true.
+// The filtering operation is based on rewriting a copy of the message into a
+// self-allocated buffer, which is then returned in the output. The input buffer
+// is NOT altered.
+// Note also that the process of rewriting the protos gets rid of most redundant
+// varint encoding (if present). So even if all fields are allow-listed, the
+// output might NOT be bitwise identical to the input (but it will be
+// semantically equivalent).
+// Furthermore the enable_field_usage_tracking() method allows to keep track of
+// a histogram of allowed / denied fields. It slows down filtering and is
+// intended only on host tools.
+class MessageFilter {
+ public:
+  class Config {
+   public:
+    // Loads filter bytecode. If a filter is already loaded, it is replaced.
+    // Optionally accepts an overlay (pass nullptr/0 if not needed).
+    // See FilterBytecodeParser::Load() for overlay format details.
+    bool LoadFilterBytecode(const void* filter_data,
+                            size_t len,
+                            const void* overlay_data = nullptr,
+                            size_t overlay_len = 0);

-    if (status.IsError()) {
-      using AbortLevel = perfetto::protos::pbzero::VmInstruction::AbortLevel;
-      auto abort_level =
-          instruction.has_abort_level()
-              ? static_cast<AbortLevel>(instruction.abort_level())
-              : AbortLevel::SKIP_CURRENT_INSTRUCTION_AND_BREAK_OUTER;
-      if (abort_level == AbortLevel::SKIP_CURRENT_INSTRUCTION_AND_BREAK_OUTER) {
-        return StatusOr<void>::Error();
-      }
-      if (abort_level == AbortLevel::ABORT) {
-        PROTOVM_ABORT(
-            "instruction[%d]: returned status = error and instruction's "
-            "abort level = 'abort'",
-            instruction_index);
-      }
-    }
+    bool SetFilterRoot(std::initializer_list<uint32_t> field_ids);

-    ++instruction_index;
-  }
+    const FilterBytecodeParser& filter() const { return filter_; }
+    const StringFilter& string_filter() const { return string_filter_; }
+    StringFilter& string_filter() { return string_filter_; }
+    uint32_t root_msg_index() const { return root_msg_index_; }

-  return StatusOr<void>::Ok();
-}
+   private:
+    FilterBytecodeParser filter_;
+    StringFilter string_filter_;
+    uint32_t root_msg_index_ = 0;
+  };

-StatusOr<void> Parser::ParseInstruction(
-    const protos::pbzero::VmInstruction::Decoder& instruction) {
-  if (instruction.has_select()) {
-    auto status = ParseSelect(instruction);
-    PROTOVM_RETURN(status, "select");
+  MessageFilter();
+  explicit MessageFilter(Config);
+  ~MessageFilter();
+
+  struct InputSlice {
+    const void* data;
+    size_t len;
+  };
+
+  struct FilteredMessage {
+    FilteredMessage(std::unique_ptr<uint8_t[]> d, size_t s)
+        : data(std::move(d)), size(s) {}
+    std::unique_ptr<uint8_t[]> data;
+    size_t size;  // The used bytes in |data|. This is <= sizeof(data).
+    bool error = false;
+  };
+
+  // Loads the filter bytecode that will be used to filter any subsequent
+  // message. Must be called before the first call to FilterMessage*().
+  // |filter_data| must point to a byte buffer for a proto-encoded ProtoFilter
+  // message (see proto_filter.proto).
+  // Optionally accepts an overlay (pass nullptr/0 if not needed).
+  bool LoadFilterBytecode(const void* filter_data,
+                          size_t len,
+                          const void* overlay_data = nullptr,
+                          size_t overlay_len = 0) {
+    return config_.LoadFilterBytecode(filter_data, len, overlay_data,
+                                      overlay_len);
   }

-  if (instruction.has_reg_load()) {
-    auto status = ParseRegLoad(instruction);
-    PROTOVM_RETURN_IF_NOT_OK(status, "reg_load");
-  } else if (instruction.has_del()) {
-    PROTOVM_ASSIGN_OR_RETURN(bool is_root, cursors_.dst.IsRoot());
-    if (cursors_.innermost_saved_dst != nullptr &&
-        cursors_.dst == *cursors_.innermost_saved_dst && !is_root) {
-      // Illegal operation
-      // For each "select" operation we effectively save a snapshot of the
-      // cursors on the stack, so that they can be restored when a frame is
-      // exited. This "del" operation is attempting to delete a "dst" cursor
-      // that is currently aliased in the parent stack frame. This would result
-      // in a dangling pointer when the current frame is exited and the aliased
-      // "dst" is restored.
-      PROTOVM_ABORT(
-          "del would invalidate a cursor saved by an enclosing select");
-    }
-    auto status = executor_->Delete(&cursors_.dst);
-    PROTOVM_RETURN_IF_NOT_OK(status, "del");
-  } else if (instruction.has_merge()) {
-    auto status = ParseMerge(instruction);
-    PROTOVM_RETURN_IF_NOT_OK(status, "merge");
-  } else if (instruction.has_set()) {
-    auto status = executor_->Set(&cursors_);
-    PROTOVM_RETURN_IF_NOT_OK(status, "set");
-  } else {
-    PROTOVM_ABORT("Unsupported instruction");
+  // This affects the filter starting point of the subsequent FilterMessage*()
+  // calls. By default the filtering process starts from the message @ index 0,
+  // the root message passed to proto_filter when generating the bytecode
+  // (in typical tracing use-cases, this is perfetto.protos.Trace). However, the
+  // caller (TracingServiceImpl) might want to filter packets from the 2nd level
+  // (perfetto.protos.TracePacket) because the root level is prepended after
+  // the fact. This call allows to change the root message for the filter.
+  // The argument |field_ids| is an array of proto field ids and determines the
+  // path to the new root. For instance, in the case of [1,2,3] SetFilterRoot
+  // will identify the sub-message for the field "root.1.2.3" and use that.
+  // In order for this to succeed all the fields in the path must be allowed
+  // in the filter and must be a nested message type.
+  bool SetFilterRoot(std::initializer_list<uint32_t> field_ids) {
+    return config_.SetFilterRoot(field_ids);
   }

-  return ParseInstructions(instruction.nested_instructions());
-}
+  // Takes an input message, fragmented in arbitrary slices, and returns a
+  // filtered message in output.
+  FilteredMessage FilterMessageFragments(const InputSlice*, size_t num_slices);

-StatusOr<void> Parser::ParseMerge(
-    const protos::pbzero::VmInstruction::Decoder& instruction) {
-  protos::pbzero::VmOpMerge::Decoder merge(instruction.merge());
-  uint32_t flags = Executor::MergeFlags::kNone;
-  if (merge.skip_submessages())
-    flags |= Executor::MergeFlags::kSkipSubmessages;
-  if (merge.del_if_src_empty())
-    flags |= Executor::MergeFlags::kDelIfSrcEmpty;
-  auto status = executor_->Merge(&cursors_, flags);
-  PROTOVM_RETURN_IF_NOT_OK(status);
-  return status;
-}
+  // Helper for tests, where the input is a contiguous buffer.
+  FilteredMessage FilterMessage(const void* data, size_t len) {
+    InputSlice slice{data, len};
+    return FilterMessageFragments(&slice, 1);
+  }

-StatusOr<void> Parser::ParseRegLoad(
-    const protos::pbzero::VmInstruction::Decoder& instruction) {
-  auto saved_selected = cursors_.selected;
+  // When enabled returns a map of "field path" to "usage counter".
+  // The key (std::string) is a binary buffer (i.e. NOT an ASCII/UTF-8 string)
+  // which contains a varint for each field. Consider the following:
+  // message Root { Sub1 f1 = 1; };
+  // message Sub1 { Sub2 f2 = 7;}
+  // message Sub2 { string f3 = 5; }
+  // The field .f1.f2.f3 will be encoded as \x01\0x07\x05.
+  // The value is the number of times that field has been encountered. If the
+  // field is not allow-listed in the bytecode (the field is stripped in output)
+  // the count will be negative.
+  void enable_field_usage_tracking(bool x) { track_field_usage_ = x; }
+  const std::unordered_map<std::string, int32_t>& field_usage() const {
+    return field_usage_;
+  }

-  protos::pbzero::VmOpRegLoad::Decoder reg_load(instruction.reg_load());
-  cursors_.selected = !reg_load.has_cursor()
-                          ? CursorEnum::VM_CURSOR_SRC
-                          : static_cast<CursorEnum>(reg_load.cursor());
-  auto status = executor_->WriteRegister(
-      cursors_, static_cast<uint8_t>(reg_load.dst_register()));
+  const Config& config() const { return config_; }
+  Config& config() { return config_; }

-  cursors_.selected = saved_selected;
+  // Returns the helper class used to perform string filtering.
+  StringFilter& string_filter() { return config_.string_filter(); }

-  PROTOVM_RETURN_IF_NOT_OK(status);
+ private:
+  // This is called by FilterMessageFragments().
+  // Inlining allows the compiler turn the per-byte call/return into a for loop,
+  // while, at the same time, keeping the code easy to read and reason about.
+  // It gives a 20-25% speedup (265ms vs 215ms for a 25MB trace).
+  void FilterOneByte(uint8_t octet) PERFETTO_ALWAYS_INLINE;

-  return status;
-}
+  // No-inline because this is a slowpath (only when usage tracking is enabled).
+  void IncrementCurrentFieldUsage(uint32_t field_id,
+                                  bool allowed) PERFETTO_NO_INLINE;

-StatusOr<void> Parser::ParseSelect(
-    const protos::pbzero::VmInstruction::Decoder& instruction) {
-  auto saved_cursors = cursors_;
-  cursors_.innermost_saved_dst = &saved_cursors.dst;
+  // Gets into an error state which swallows all the input and emits no output.
+  void SetUnrecoverableErrorState();

-  protos::pbzero::VmOpSelect::Decoder select(instruction.select());
-  cursors_.selected = !select.has_cursor()
-                          ? CursorEnum::VM_CURSOR_SRC
-                          : static_cast<CursorEnum>(select.cursor());
-  cursors_.create_if_not_exist = select.create_if_not_exist();
+  // We keep track of the nest of messages in a stack. Each StackState
+  // object corresponds to a level of nesting in the proto message structure.
+  // Every time a new field of type len-delimited that has a corresponding
+  // sub-message in the bytecode is encountered, a new StackState is pushed in
+  // |stack_|. stack_[0] is a sentinel to prevent over-popping without adding
+  // extra branches in the fastpath.
+  // |stack_|. stack_[1] is the state of the root message.
+  struct StackState {
+    uint32_t in_bytes = 0;  // Number of input bytes processed.

-  if (cursors_.selected == CursorEnum::VM_CURSOR_SRC &&
-      cursors_.create_if_not_exist) {
-    PROTOVM_ABORT(
-        "incompatible params: src cursor (read only) + create_if_not_exist");
-  }
+    // When |in_bytes| reaches this value, the current state should be popped.
+    // This is set when recursing into nested submessages. This is 0 only for
+    // stack_[0] (we don't know the size of the root message upfront).
+    uint32_t in_bytes_limit = 0;

-  auto status = ParseSelectRec(instruction, select.relative_path());
-  cursors_ = saved_cursors;
+    // This is set when a len-delimited message is encountered, either a string
+    // or a nested submessage that is NOT allow-listed in the bytecode.
+    // This causes input bytes to be consumed without being parsed from the
+    // input stream. If |passthrough_eaten_bytes| == true, they will be copied
+    // as-is in output (e.g. in the case of an allowed string/bytes field).
+    uint32_t eat_next_bytes = 0;

-  return status;
-}
+    // Keeps tracks of the stream_writer output counter (out_.written()) then
+    // the StackState is pushed. This is used to work out, when popping, how
+    // many bytes have been written for the current submessage.
+    uint32_t out_bytes_written_at_start = 0;

-StatusOr<void> Parser::ParseSelectRec(
-    const protos::pbzero::VmInstruction::Decoder& instruction,
-    protozero::RepeatedFieldIterator<protozero::ConstBytes> it_path_component) {
-  protos::pbzero::VmOpSelect::Decoder select(instruction.select());
+    uint32_t field_id = 0;   // The proto field id for the current message.
+    uint32_t msg_index = 0;  // The index of the message filter in the bytecode.

-  bool has_entered_all_path_components = !it_path_component;
-  if (has_entered_all_path_components) {
-    return ParseInstructions(instruction.nested_instructions());
-  }
+    // This is a pointer to the proto preamble for the current submessage
+    // (it's nullptr for stack_[0] and non-null elsewhere). This will be filled
+    // with the actual size of the message (out_.written() -
+    // |out_bytes_written_at_start|) when finishing (popping) the message.
+    // This must be filled using WriteRedundantVarint(). Note that the
+    // |size_field_len| is variable and depends on the actual length of the
+    // input message. If the output message has roughly the same size of the
+    // input message, the length will not be redundant.
+    // In other words: the length of the field is reserved when the submessage
+    // starts. At that point we know the upper-bound for the output message
+    // (a filtered submessage can be <= the original one, but not >). So we
+    // reserve as many bytes it takes to write the input length in varint.
+    // Then, when the message is finalized and we know the actual output size
+    // we backfill the field.
+    // Consider the example of a submessage where the input size = 130 (>127,
+    // 2 varint bytes) and the output is 120 bytes. The length will be 2 bytes
+    // wide even though could have been encoded with just one byte.
+    uint8_t* size_field = nullptr;
+    uint32_t size_field_len = 0;

-  auto curr_component =
-      protos::pbzero::VmOpSelect::PathComponent::Decoder(*it_path_component);
-  ++it_path_component;
+    // The pointer to the start of the string to update the string if it is
+    // filtered.
+    uint8_t* filter_string_ptr = nullptr;

-  std::optional<protos::pbzero::VmOpSelect::PathComponent::Decoder>
-      next_component =
-          it_path_component
-              ? std::make_optional(
-                    protos::pbzero::VmOpSelect::PathComponent::Decoder(
-                        *it_path_component))
-              : std::nullopt;
+    // The semantic type of the string field being filtered (0 = unspecified).
+    uint32_t filter_string_semantic_type = 0;

-  if (!curr_component.has_field_id()) {
-    PROTOVM_ABORT("Invalid path. Expected path component with field_id.");
-  }
+    // How |eat_next_bytes| should be handled. It seems that keeping this field
+    // at the end rather than next to |eat_next_bytes| makes the filter a little
+    // (but measurably) faster. (likely something related with struct layout vs
+    // cache sizes).
+    enum FilterAction {
+      kDrop,
+      kPassthrough,
+      kFilterString,
+    };
+    FilterAction action = FilterAction::kDrop;
+  };

-  // iterate repeated field
-  if (curr_component.is_repeated()) {
-    if (cursors_.selected == CursorEnum::VM_CURSOR_SRC) {
-      auto status_or_it = executor_->IterateRepeatedField(
-          &cursors_.src, curr_component.field_id());
-      PROTOVM_RETURN_IF_NOT_OK(status_or_it, "iterate repeated field (id = %d)",
-                               static_cast<int>(curr_component.field_id()));
-      int index = 0;
-      for (auto it = *status_or_it; it; ++it) {
-        cursors_.src = *it;
-        auto status = ParseSelectRec(instruction, it_path_component);
-        PROTOVM_RETURN_IF_NOT_OK(status, "repeated field (id = %d, index = %d)",
-                                 static_cast<int>(curr_component.field_id()),
-                                 index);
-        ++index;
-      }
-    } else if (cursors_.selected == CursorEnum::VM_CURSOR_DST) {
-      auto status_or_it = executor_->IterateRepeatedField(
-          &cursors_.dst, curr_component.field_id());
-      PROTOVM_RETURN_IF_NOT_OK(status_or_it, "iterate repeated field (id = %d)",
-                               static_cast<int>(curr_component.field_id()));
-      int index = 0;
-      for (auto it = *status_or_it; it; ++it) {
-        cursors_.dst = it.GetCursor();
-        auto status = ParseSelectRec(instruction, it_path_component);
-        PROTOVM_RETURN_IF_NOT_OK(status, "repeated field (id = %d, index = %d)",
-                                 static_cast<int>(curr_component.field_id()),
-                                 index);
-        ++index;
-      }
-    } else {
-      PROTOVM_ABORT(
-          "Iteration over selected cursor (%d) is not supported. Should be "
-          "either SRC or DST cursor.",
-          static_cast<int>(cursors_.selected));
-    }
-    return StatusOr<void>::Ok();
-  }
+  uint32_t out_written() { return static_cast<uint32_t>(out_ - &out_buf_[0]); }

-  // enter indexed repeated field
-  if (next_component && next_component->has_array_index()) {
-    ++it_path_component;
-    auto status_enter = executor_->EnterRepeatedFieldAt(
-        &cursors_, curr_component.field_id(), next_component->array_index());
-    PROTOVM_RETURN_IF_NOT_OK(status_enter, "enter indexed repeated field");
-    auto status_select = ParseSelectRec(instruction, it_path_component);
-    PROTOVM_RETURN(status_select, "repeated field (id = %d, index = %d)",
-                   static_cast<int>(curr_component.field_id()),
-                   static_cast<int>(next_component->array_index()));
-  }
+  Config config_;

-  // enter mapped repeated field
-  if (next_component && next_component->has_map_key_field_id()) {
-    ++it_path_component;
-    if (!next_component->has_register_to_match()) {
-      PROTOVM_ABORT(
-          "enter mapped repeated field: expected field 'register_to_match'");
-    }
-    auto reg_id = static_cast<uint8_t>(next_component->register_to_match());
-    auto key = executor_->ReadRegister(reg_id);
-    PROTOVM_RETURN_IF_NOT_OK(key, "enter mapped repeated field");
-    auto status_enter = executor_->EnterRepeatedFieldByKey(
-        &cursors_, curr_component.field_id(),
-        next_component->map_key_field_id(), static_cast<uint32_t>(*key));
-    PROTOVM_RETURN_IF_NOT_OK(status_enter, "enter mapped repeated field");
-    auto status_select = ParseSelectRec(instruction, it_path_component);
-    PROTOVM_RETURN(status_select, "mapped repeated field (id = %d, key = %d)",
-                   static_cast<int>(curr_component.field_id()),
-                   static_cast<int>(*key));
-  }
+  std::unique_ptr<uint8_t[]> out_buf_;
+  uint8_t* out_ = nullptr;
+  uint8_t* out_end_ = nullptr;

-  // enter field
-  auto status = executor_->EnterField(&cursors_, curr_component.field_id());
-  PROTOVM_RETURN_IF_NOT_OK(status, "enter field (id = %d)",
-                           static_cast<int>(curr_component.field_id()));
+  MessageTokenizer tokenizer_;
+  std::vector<StackState> stack_;

-  return ParseSelectRec(instruction, it_path_component);
-}
+  bool error_ = false;
+  bool track_field_usage_ = false;
+  std::unordered_map<std::string, int32_t> field_usage_;
+};

-}  // namespace protovm
-}  // namespace perfetto
-// gen_amalgamated begin source: src/protovm/ro_cursor.cc
+}  // namespace protozero
+
+#endif  // SRC_PROTOZERO_FILTERING_MESSAGE_FILTER_H_
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -56264,173 +61131,367 @@ StatusOr<void> Parser::ParseSelectRec(
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/protovm/ro_cursor.h"
+// gen_amalgamated expanded: #include "src/protozero/filtering/message_filter.h"

+// gen_amalgamated expanded: #include "perfetto/base/endian.h"
 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+// gen_amalgamated expanded: #include "src/protozero/filtering/string_filter.h"
+
+namespace protozero {
+
+namespace {

-namespace perfetto {
-namespace protovm {
+// Inline helpers to append proto fields in output. They are the equivalent of
+// the protozero::Message::AppendXXX() fields but don't require building and
+// maintaining a full protozero::Message object or dealing with scattered
+// output slices.
+// All these functions assume there is enough space in the output buffer, which
+// should be always the case assuming that we don't end up generating more
+// output than input.

-RoCursor::RepeatedFieldIterator::RepeatedFieldIterator()
-    : decoder_{protozero::ConstBytes{}}, field_id_{0}, field_{Advance()} {
-  PERFETTO_DCHECK(!field_.valid());
+inline void AppendVarInt(uint32_t field_id, uint64_t value, uint8_t** out) {
+  *out = proto_utils::WriteVarInt(proto_utils::MakeTagVarInt(field_id), *out);
+  *out = proto_utils::WriteVarInt(value, *out);
 }

-RoCursor::RepeatedFieldIterator::RepeatedFieldIterator(
-    protozero::ProtoDecoder decoder,
-    uint32_t field_id)
-    : decoder_(decoder), field_id_(field_id), field_{Advance()} {}
+// For fixed32 / fixed64.
+template <typename INT_T /* uint32_t | uint64_t*/>
+inline void AppendFixed(uint32_t field_id, INT_T value, uint8_t** out) {
+  *out = proto_utils::WriteVarInt(proto_utils::MakeTagFixed<INT_T>(field_id),
+                                  *out);
+  INT_T le_value = perfetto::base::HostToLE(value);
+  memcpy(*out, &le_value, sizeof(value));
+  *out += sizeof(value);
+}

-RoCursor::RepeatedFieldIterator& RoCursor::RepeatedFieldIterator::operator++() {
-  field_ = Advance();
-  return *this;
+// For length-delimited (string, bytes) fields. Note: this function appends only
+// the proto preamble and the varint field that states the length of the payload
+// not the payload itself.
+// In the case of submessages, the caller needs to re-write the length at the
+// end in the in the returned memory area.
+// The problem here is that, because of filtering, the length of a submessage
+// might be < original length (the original length is still an upper-bound).
+// Returns a pair with: (1) the pointer where the final length should be written
+// into, (2) the length of the size field.
+// The caller must write a redundant varint to match the original size (i.e.
+// needs to use WriteRedundantVarInt()).
+inline std::pair<uint8_t*, uint32_t> AppendLenDelim(uint32_t field_id,
+                                                    uint32_t len,
+                                                    uint8_t** out) {
+  *out = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
+                                  *out);
+  uint8_t* size_field_start = *out;
+  *out = proto_utils::WriteVarInt(len, *out);
+  const size_t size_field_len = static_cast<size_t>(*out - size_field_start);
+  return std::make_pair(size_field_start, size_field_len);
 }
+}  // namespace

-RoCursor RoCursor::RepeatedFieldIterator::operator*() {
-  return RoCursor(field_);
+MessageFilter::MessageFilter(Config config) : config_(std::move(config)) {
+  // Push a state on the stack for the implicit root message.
+  stack_.emplace_back();
 }

-RoCursor::RepeatedFieldIterator::operator bool() const {
-  return field_.valid();
+MessageFilter::MessageFilter() : MessageFilter(Config()) {}
+
+MessageFilter::~MessageFilter() = default;
+
+bool MessageFilter::Config::LoadFilterBytecode(const void* filter_data,
+                                               size_t len,
+                                               const void* overlay_data,
+                                               size_t overlay_len) {
+  return filter_.Load(filter_data, len, overlay_data, overlay_len);
 }

-protozero::Field RoCursor::RepeatedFieldIterator::Advance() {
-  auto field = decoder_.ReadField();
-  while (field) {
-    if (field.id() == field_id_) {
-      break;
-    }
-    field = decoder_.ReadField();
+bool MessageFilter::Config::SetFilterRoot(
+    std::initializer_list<uint32_t> field_ids) {
+  uint32_t root_msg_idx = 0;
+  for (uint32_t field_id : field_ids) {
+    auto res = filter_.Query(root_msg_idx, field_id);
+    if (!res.allowed || !res.nested_msg_field())
+      return false;
+    root_msg_idx = res.nested_msg_index;
   }
-  return field;
+  root_msg_index_ = root_msg_idx;
+  return true;
 }

-RoCursor::RoCursor() = default;
+MessageFilter::FilteredMessage MessageFilter::FilterMessageFragments(
+    const InputSlice* slices,
+    size_t num_slices) {
+  // First compute the upper bound for the output. The filtered message cannot
+  // be > the original message.
+  uint32_t total_len = 0;
+  for (size_t i = 0; i < num_slices; ++i)
+    total_len += slices[i].len;
+  out_buf_.reset(new uint8_t[total_len]);
+  out_ = out_buf_.get();
+  out_end_ = out_ + total_len;

-RoCursor::RoCursor(protozero::ConstBytes data) : data_{data} {}
+  // Reset the parser state.
+  tokenizer_ = MessageTokenizer();
+  error_ = false;
+  stack_.clear();
+  stack_.resize(2);
+  // stack_[0] is a sentinel and should never be hit in nominal cases. If we
+  // end up there we will just keep consuming the input stream and detecting
+  // at the end, without hurting the fastpath.
+  stack_[0].in_bytes_limit = UINT32_MAX;
+  stack_[0].eat_next_bytes = UINT32_MAX;
+  // stack_[1] is the actual root message.
+  stack_[1].in_bytes_limit = total_len;
+  stack_[1].msg_index = config_.root_msg_index();

-RoCursor::RoCursor(protozero::Field data) : data_{data} {}
+  // Process the input data and write the output.
+  for (size_t slice_idx = 0; slice_idx < num_slices; ++slice_idx) {
+    const InputSlice& slice = slices[slice_idx];
+    const uint8_t* data = static_cast<const uint8_t*>(slice.data);
+    for (size_t i = 0; i < slice.len; ++i)
+      FilterOneByte(data[i]);
+  }

-StatusOr<void> RoCursor::EnterField(uint32_t field_id) {
-  auto status_or_data = GetLengthDelimitedData();
-  PROTOVM_RETURN_IF_NOT_OK(status_or_data);
+  // Construct the output object.
+  PERFETTO_CHECK(out_ >= out_buf_.get() && out_ <= out_end_);
+  auto used_size = static_cast<size_t>(out_ - out_buf_.get());
+  FilteredMessage res{std::move(out_buf_), used_size};
+  res.error = error_;
+  if (stack_.size() != 1 || !tokenizer_.idle() ||
+      stack_[0].in_bytes != total_len) {
+    res.error = true;
+  }
+  return res;
+}

-  protozero::ProtoDecoder decoder(*status_or_data);
+void MessageFilter::FilterOneByte(uint8_t octet) {
+  PERFETTO_DCHECK(!stack_.empty());

-  for (auto field = decoder.ReadField(); field.valid();
-       field = decoder.ReadField()) {
-    if (field.id() == field_id) {
-      data_ = field;
-      return StatusOr<void>::Ok();
+  auto* state = &stack_.back();
+  StackState next_state{};
+  bool push_next_state = false;
+
+  if (state->eat_next_bytes > 0) {
+    // This is the case where the previous tokenizer_.Push() call returned a
+    // length delimited message which is NOT a submessage (a string or a bytes
+    // field). We just want to consume it, and pass it through/filter strings
+    // if the field was allowed.
+    --state->eat_next_bytes;
+    if (state->action == StackState::kPassthrough) {
+      *(out_++) = octet;
+    } else if (state->action == StackState::kFilterString) {
+      *(out_++) = octet;
+      if (state->eat_next_bytes == 0) {
+        config_.string_filter().MaybeFilter(
+            reinterpret_cast<char*>(state->filter_string_ptr),
+            static_cast<size_t>(out_ - state->filter_string_ptr),
+            state->filter_string_semantic_type);
+      }
     }
-  }
+  } else {
+    MessageTokenizer::Token token = tokenizer_.Push(octet);
+    // |token| will not be valid() in most cases and this is WAI. When pushing
+    // a varint field, only the last byte yields a token, all the other bytes
+    // return an invalid token, they just update the internal tokenizer state.
+    if (token.valid()) {
+      auto filter = config_.filter().Query(state->msg_index, token.field_id);
+      switch (token.type) {
+        case proto_utils::ProtoWireType::kVarInt:
+          if (filter.allowed && filter.simple_field())
+            AppendVarInt(token.field_id, token.value, &out_);
+          break;
+        case proto_utils::ProtoWireType::kFixed32:
+          if (filter.allowed && filter.simple_field())
+            AppendFixed(token.field_id, static_cast<uint32_t>(token.value),
+                        &out_);
+          break;
+        case proto_utils::ProtoWireType::kFixed64:
+          if (filter.allowed && filter.simple_field())
+            AppendFixed(token.field_id, static_cast<uint64_t>(token.value),
+                        &out_);
+          break;
+        case proto_utils::ProtoWireType::kLengthDelimited:
+          // Here we have two cases:
+          // A. A simple string/bytes field: we just want to consume the next
+          //    bytes (the string payload), optionally passing them through in
+          //    output if the field is allowed.
+          // B. This is a nested submessage. In this case we want to recurse and
+          //    push a new state on the stack.
+          // Note that we can't tell the difference between a
+          // "non-allowed string" and a "non-allowed submessage". But it doesn't
+          // matter because in both cases we just want to skip the next N bytes.
+          const auto submessage_len = static_cast<uint32_t>(token.value);
+          auto in_bytes_left = state->in_bytes_limit - state->in_bytes - 1;
+          if (PERFETTO_UNLIKELY(submessage_len > in_bytes_left)) {
+            // This is a malicious / malformed string/bytes/submessage that
+            // claims to be larger than the outer message that contains it.
+            return SetUnrecoverableErrorState();
+          }

-  return StatusOr<void>::Error();
-}
+          if (filter.allowed && filter.nested_msg_field() &&
+              submessage_len > 0) {
+            // submessage_len == 0 is the edge case of a message with a 0-len
+            // (but present) submessage. In this case, if allowed, we don't want
+            // to push any further state (doing so would desync the FSM) but we
+            // still want to emit it.
+            // At this point |submessage_len| is only an upper bound. The
+            // final message written in output can be <= the one in input,
+            // only some of its fields might be allowed (also remember that
+            // this class implicitly removes redundancy varint encoding of
+            // len-delimited field lengths). The final length varint (the
+            // return value of AppendLenDelim()) will be filled when popping
+            // from |stack_|.
+            auto size_field =
+                AppendLenDelim(token.field_id, submessage_len, &out_);
+            push_next_state = true;
+            next_state.field_id = token.field_id;
+            next_state.msg_index = filter.nested_msg_index;
+            next_state.in_bytes_limit = submessage_len;
+            next_state.size_field = size_field.first;
+            next_state.size_field_len = size_field.second;
+            next_state.out_bytes_written_at_start = out_written();
+          } else {
+            // A string or bytes field, or a 0 length submessage.
+            state->eat_next_bytes = submessage_len;
+            if (filter.allowed && filter.filter_string_field()) {
+              state->action = StackState::kFilterString;
+              AppendLenDelim(token.field_id, submessage_len, &out_);
+              state->filter_string_ptr = out_;
+              state->filter_string_semantic_type = filter.semantic_type;
+            } else if (filter.allowed) {
+              state->action = StackState::kPassthrough;
+              AppendLenDelim(token.field_id, submessage_len, &out_);
+            } else {
+              state->action = StackState::kDrop;
+            }
+          }
+          break;
+      }  // switch(type)

-StatusOr<void> RoCursor::EnterRepeatedFieldAt(uint32_t field_id,
-                                              uint32_t index) {
-  auto status_or_data = GetLengthDelimitedData();
-  PROTOVM_RETURN_IF_NOT_OK(status_or_data);
+      if (PERFETTO_UNLIKELY(track_field_usage_)) {
+        IncrementCurrentFieldUsage(token.field_id, filter.allowed);
+      }
+    }  // if (token.valid)
+  }  // if (eat_next_bytes == 0)

-  protozero::ProtoDecoder decoder(*status_or_data);
+  ++state->in_bytes;
+  while (state->in_bytes >= state->in_bytes_limit) {
+    PERFETTO_DCHECK(state->in_bytes == state->in_bytes_limit);
+    push_next_state = false;

-  uint32_t current_index = 0;
+    // We can't possibly write more than we read.
+    const uint32_t msg_bytes_written = static_cast<uint32_t>(
+        out_written() - state->out_bytes_written_at_start);
+    PERFETTO_DCHECK(msg_bytes_written <= state->in_bytes_limit);

-  for (auto field = decoder.ReadField(); field.valid();
-       field = decoder.ReadField()) {
-    if (field.id() != field_id) {
-      continue;
-    }
+    // Backfill the length field of the
+    proto_utils::WriteRedundantVarInt(msg_bytes_written, state->size_field,
+                                      state->size_field_len);

-    if (current_index == index) {
-      data_ = field.as_bytes();
-      return StatusOr<void>::Ok();
+    const uint32_t in_bytes_processes_for_last_msg = state->in_bytes;
+    stack_.pop_back();
+    PERFETTO_CHECK(!stack_.empty());
+    state = &stack_.back();
+    state->in_bytes += in_bytes_processes_for_last_msg;
+    if (PERFETTO_UNLIKELY(!tokenizer_.idle())) {
+      // If we hit this case, it means that we got to the end of a submessage
+      // while decoding a field. We can't recover from this and we don't want to
+      // propagate a broken sub-message.
+      return SetUnrecoverableErrorState();
     }
-
-    ++current_index;
   }

-  return StatusOr<void>::Error();
+  if (push_next_state) {
+    PERFETTO_DCHECK(tokenizer_.idle());
+    stack_.emplace_back(std::move(next_state));
+    state = &stack_.back();
+  }
 }

-StatusOr<RoCursor::RepeatedFieldIterator> RoCursor::IterateRepeatedField(
-    uint32_t field_id) const {
-  auto status_or_data = GetLengthDelimitedData();
-  PROTOVM_RETURN_IF_NOT_OK(status_or_data);
-  protozero::ProtoDecoder decoder(*status_or_data);
-  return RepeatedFieldIterator(decoder, field_id);
+void MessageFilter::SetUnrecoverableErrorState() {
+  error_ = true;
+  stack_.clear();
+  stack_.resize(1);
+  auto& state = stack_[0];
+  state.eat_next_bytes = UINT32_MAX;
+  state.in_bytes_limit = UINT32_MAX;
+  state.action = StackState::kDrop;
+  out_ = out_buf_.get();  // Reset the write pointer.
 }

-bool RoCursor::IsScalar() const {
-  auto* field = std::get_if<protozero::Field>(&data_);
-  if (!field) {
-    return false;
-  }
+void MessageFilter::IncrementCurrentFieldUsage(uint32_t field_id,
+                                               bool allowed) {
+  // Slowpath. Used mainly in offline tools and tests to workout used fields in
+  // a proto.
+  PERFETTO_DCHECK(track_field_usage_);

-  return field->type() == protozero::proto_utils::ProtoWireType::kVarInt ||
-         field->type() == protozero::proto_utils::ProtoWireType::kFixed32 ||
-         field->type() == protozero::proto_utils::ProtoWireType::kFixed64;
-}
+  // Field path contains a concatenation of varints, one for each nesting level.
+  // e.g. y in message Root { Sub x = 2; }; message Sub { SubSub y = 7; }
+  // is encoded as [varint(2) + varint(7)].
+  // We use varint to take the most out of SSO (small string opt). In most cases
+  // the path will fit in the on-stack 22 bytes, requiring no heap.
+  std::string field_path;

-bool RoCursor::IsBytes() const {
-  if (std::holds_alternative<protozero::ConstBytes>(data_)) {
-    return true;
-  }
+  auto append_field_id = [&field_path](uint32_t id) {
+    uint8_t buf[10];
+    uint8_t* end = proto_utils::WriteVarInt(id, buf);
+    field_path.append(reinterpret_cast<char*>(buf),
+                      static_cast<size_t>(end - buf));
+  };

-  auto& field = std::get<protozero::Field>(data_);
-  return field.type() ==
-         protozero::proto_utils::ProtoWireType::kLengthDelimited;
+  // Append all the ancestors IDs from the state stack.
+  // The first entry of the stack has always ID 0 and we skip it (we don't know
+  // the ID of the root message itself).
+  PERFETTO_DCHECK(stack_.size() >= 2 && stack_[1].field_id == 0);
+  for (size_t i = 2; i < stack_.size(); ++i)
+    append_field_id(stack_[i].field_id);
+  // Append the id of the field in the current message.
+  append_field_id(field_id);
+  field_usage_[field_path] += allowed ? 1 : -1;
 }

-StatusOr<Scalar> RoCursor::GetScalar() const {
-  if (!IsScalar()) {
-    PROTOVM_ABORT("Attempted to access length-delimited field as a scalar");
-  }
-
-  auto& field = std::get<protozero::Field>(data_);
-  return Scalar{field.type(), field.as_uint64()};
-}
+}  // namespace protozero
+// gen_amalgamated begin source: src/protozero/filtering/message_filter_config.cc
+// gen_amalgamated begin header: src/protozero/filtering/message_filter_config.h
+/*
+ * Copyright (C) 2026 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.
+ */

-StatusOr<protozero::ConstBytes> RoCursor::GetBytes() const {
-  if (std::holds_alternative<protozero::ConstBytes>(data_)) {
-    return std::get<protozero::ConstBytes>(data_);
-  }
+#ifndef SRC_PROTOZERO_FILTERING_MESSAGE_FILTER_CONFIG_H_
+#define SRC_PROTOZERO_FILTERING_MESSAGE_FILTER_CONFIG_H_

-  auto& field = std::get<protozero::Field>(data_);
-  if (field.type() != protozero::proto_utils::ProtoWireType::kLengthDelimited) {
-    PROTOVM_ABORT(
-        "Attempted to access field as length-delimited but actual wire "
-        "type is "
-        "%u",
-        static_cast<uint32_t>(field.type()));
-  }
+// gen_amalgamated expanded: #include "perfetto/base/status.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/trace_config.gen.h"
+// gen_amalgamated expanded: #include "src/protozero/filtering/message_filter.h"

-  return field.as_bytes();
-}
+namespace protozero {

-StatusOr<protozero::ConstBytes> RoCursor::GetLengthDelimitedData() const {
-  if (std::holds_alternative<protozero::Field>(data_)) {
-    auto& field = std::get<protozero::Field>(data_);
-    if (field.type() !=
-        protozero::proto_utils::ProtoWireType::kLengthDelimited) {
-      PROTOVM_ABORT(
-          "Attempted to access field as length-delimited, but actual "
-          "wire type is %u",
-          static_cast<uint32_t>(field.type()));
-    }
-    return field.as_bytes();
-  }
+// Loads a MessageFilter from the trace_filter field of a TraceConfig.
+// This sets up the string filter rules (both base and v54 chains) and loads
+// the bytecode (v1/v2 + v54 overlay).
+// On failure, returns a non-ok status with a descriptive error message.
+// On success, the caller may optionally call SetFilterRoot() to adjust the
+// root message (e.g. for per-packet filtering in the tracing service).
+perfetto::base::Status LoadMessageFilterConfig(
+    const perfetto::protos::gen::TraceConfig::TraceFilter& filt,
+    MessageFilter* filter);

-  return std::get<protozero::ConstBytes>(data_);
-}
+}  // namespace protozero

-}  // namespace protovm
-}  // namespace perfetto
-// gen_amalgamated begin source: src/protovm/rw_proto.cc
+#endif  // SRC_PROTOZERO_FILTERING_MESSAGE_FILTER_CONFIG_H_
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -56445,101 +61506,119 @@ StatusOr<protozero::ConstBytes> RoCursor::GetLengthDelimitedData() const {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/protovm/rw_proto.h"
+// gen_amalgamated expanded: #include "src/protozero/filtering/message_filter_config.h"

-// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+#include <cstdint>
+#include <optional>
+#include <string>

-namespace perfetto {
-namespace protovm {
+// gen_amalgamated expanded: #include "perfetto/base/status.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/trace_config.gen.h"
+// gen_amalgamated expanded: #include "src/protozero/filtering/message_filter.h"
+// gen_amalgamated expanded: #include "src/protozero/filtering/string_filter.h"

-RwProto::RwProto(Allocator* allocator)
-    : allocator_{allocator}, root_{Node::Empty{}} {}
+namespace protozero {

-RwProto::~RwProto() {
-  allocator_->DeleteReferencedData(&root_);
-}
+namespace {

-RwProto::Cursor RwProto::GetRoot() {
-  return Cursor{&root_, allocator_};
-}
+using TraceFilter = perfetto::protos::gen::TraceConfig::TraceFilter;
+using StringFilterRule = TraceFilter::StringFilterRule;

-void RwProto::Serialize(protozero::Message* proto) const {
-  if (root_.GetIf<Node::Empty>()) {
-    return;
+std::optional<StringFilter::Policy> ConvertPolicy(
+    TraceFilter::StringFilterPolicy policy) {
+  switch (policy) {
+    case TraceFilter::SFP_UNSPECIFIED:
+      return std::nullopt;
+    case TraceFilter::SFP_MATCH_REDACT_GROUPS:
+      return StringFilter::Policy::kMatchRedactGroups;
+    case TraceFilter::SFP_ATRACE_MATCH_REDACT_GROUPS:
+      return StringFilter::Policy::kAtraceMatchRedactGroups;
+    case TraceFilter::SFP_MATCH_BREAK:
+      return StringFilter::Policy::kMatchBreak;
+    case TraceFilter::SFP_ATRACE_MATCH_BREAK:
+      return StringFilter::Policy::kAtraceMatchBreak;
+    case TraceFilter::SFP_ATRACE_REPEATED_SEARCH_REDACT_GROUPS:
+      return StringFilter::Policy::kAtraceRepeatedSearchRedactGroups;
   }
+  return std::nullopt;
+}

-  if (auto* bytes = root_.GetIf<Node::Bytes>()) {
-    proto->AppendRawProtoBytes(bytes->data.get(), bytes->size);
-    return;
+std::optional<StringFilter::SemanticTypeMask> ConvertSemanticTypeMask(
+    const StringFilterRule& rule) {
+  // UNSPECIFIED (0) is treated as its own category - it only matches rules
+  // that explicitly include bit 0 in their mask. If no semantic types are
+  // specified, default to matching only UNSPECIFIED (bit 0).
+  if (rule.semantic_type().empty()) {
+    return StringFilter::SemanticTypeMask::Unspecified();
   }
-
-  auto* message = root_.GetIf<Node::Message>();
-  PERFETTO_DCHECK(message);
-
-  for (auto it = message->field_id_to_node.begin(); it; ++it) {
-    auto field_id = static_cast<uint32_t>(it->key);
-    SerializeField(field_id, *it->value, proto);
+  StringFilter::SemanticTypeMask mask;
+  for (const auto& type : rule.semantic_type()) {
+    auto semantic_type = static_cast<uint32_t>(type);
+    if (semantic_type >= StringFilter::SemanticTypeMask::kLimit) {
+      return std::nullopt;
+    }
+    mask.Set(semantic_type);
   }
+  return mask;
 }

-void RwProto::SerializeField(uint32_t field_id,
-                             const Node& node,
-                             protozero::Message* proto) const {
-  if (node.GetIf<Node::Empty>()) {
-    return;
-  }
+}  // namespace

-  if (auto* bytes = node.GetIf<Node::Bytes>()) {
-    proto->AppendBytes(field_id, bytes->data.get(), bytes->size);
-    return;
-  }
+perfetto::base::Status LoadMessageFilterConfig(const TraceFilter& filt,
+                                               MessageFilter* filter) {
+  StringFilter& string_filter = filter->string_filter();

-  if (auto* scalar = node.GetIf<Scalar>()) {
-    if (scalar->wire_type == protozero::proto_utils::ProtoWireType::kFixed32) {
-      proto->AppendFixed(field_id, static_cast<uint32_t>(scalar->value));
-      return;
+  auto add_rule = [&](const auto& rule) -> perfetto::base::Status {
+    auto policy = ConvertPolicy(rule.policy());
+    if (!policy.has_value()) {
+      return perfetto::base::ErrStatus(
+          "Trace filter has invalid string filtering rules");
     }
-
-    if (scalar->wire_type == protozero::proto_utils::ProtoWireType::kFixed64) {
-      proto->AppendFixed(field_id, static_cast<uint64_t>(scalar->value));
-      return;
+    auto semantic_type = ConvertSemanticTypeMask(rule);
+    if (!semantic_type.has_value()) {
+      return perfetto::base::ErrStatus(
+          "Trace filter has invalid semantic types in string filtering rules");
     }
+    string_filter.AddRule(*policy, rule.regex_pattern(),
+                          rule.atrace_payload_starts_with(), rule.name(),
+                          *semantic_type);
+    return perfetto::base::OkStatus();
+  };

-    proto->AppendVarInt(field_id, scalar->value);
-    return;
+  // Load base string filter chain.
+  for (const auto& rule : filt.string_filter_chain().rules()) {
+    auto status = add_rule(rule);
+    if (!status.ok())
+      return status;
   }

-  if (auto* message = node.GetIf<Node::Message>()) {
-    auto* message_proto =
-        proto->BeginNestedMessage<protozero::Message>(field_id);
-
-    for (auto it = message->field_id_to_node.begin(); it; ++it) {
-      auto message_field_id = static_cast<uint32_t>(it->key);
-      SerializeField(message_field_id, *it->value, message_proto);
-    }
+  // Load v54 string filter chain. Rules with matching names will replace
+  // existing rules; others will be appended.
+  for (const auto& rule : filt.string_filter_chain_v54().rules()) {
+    auto status = add_rule(rule);
+    if (!status.ok())
+      return status;
   }

-  if (node.GetIf<Node::IndexedRepeatedField>() ||
-      node.GetIf<Node::MappedRepeatedField>()) {
-    const IntrusiveMap* map;
-    if (auto* indexed = node.GetIf<Node::IndexedRepeatedField>()) {
-      map = &indexed->index_to_node;
-    } else {
-      map = &node.GetIf<Node::MappedRepeatedField>()->key_to_node;
-    }
-
-    for (auto it = map->begin(); it; ++it) {
-      SerializeField(field_id, *it->value, proto);
-    }
+  const std::string& bytecode_v1 = filt.bytecode();
+  const std::string& bytecode_v2 = filt.bytecode_v2();
+  const std::string& bytecode = bytecode_v2.empty() ? bytecode_v1 : bytecode_v2;
+  const std::string& overlay = filt.bytecode_overlay_v54();
+  if (!filter->LoadFilterBytecode(bytecode.data(), bytecode.size(),
+                                  overlay.empty() ? nullptr : overlay.data(),
+                                  overlay.size())) {
+    return perfetto::base::ErrStatus("Trace filter bytecode invalid");
   }
+
+  return perfetto::base::OkStatus();
 }

-}  // namespace protovm
-}  // namespace perfetto
-// gen_amalgamated begin source: src/protovm/rw_proto_cursor.cc
+}  // namespace protozero
+// gen_amalgamated begin source: src/tracing/service/proto_group_rewriter.cc
+// gen_amalgamated begin header: src/tracing/service/proto_group_rewriter.h
+// gen_amalgamated begin header: include/perfetto/ext/base/small_vector.h
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -56554,695 +61633,875 @@ void RwProto::SerializeField(uint32_t field_id,
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/protovm/rw_proto_cursor.h"
-
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
-
-namespace perfetto {
-namespace protovm {
-
-RwProtoCursor::RepeatedFieldIterator::RepeatedFieldIterator()
-    : allocator_{nullptr}, it_{} {}
-
-RwProtoCursor::RepeatedFieldIterator::RepeatedFieldIterator(
-    Allocator* allocator,
-    IntrusiveMap::Iterator it)
-    : allocator_{allocator}, it_{it} {}
-
-RwProtoCursor::RepeatedFieldIterator&
-RwProtoCursor::RepeatedFieldIterator::RepeatedFieldIterator::operator++() {
-  ++it_;
-  return *this;
-}
-
-RwProtoCursor RwProtoCursor::RepeatedFieldIterator::GetCursor() {
-  return RwProtoCursor{it_->value.get(), allocator_};
-}
-
-RwProtoCursor::RepeatedFieldIterator::operator bool() const {
-  return static_cast<bool>(it_);
-}
-
-RwProtoCursor::RwProtoCursor() = default;
-
-RwProtoCursor::RwProtoCursor(Node* node, Allocator* allocator)
-    : node_{node}, allocator_{allocator} {}
-
-bool RwProtoCursor::operator==(const RwProtoCursor& other) const {
-  return node_ == other.node_;
-}
-bool RwProtoCursor::operator!=(const RwProtoCursor& other) const {
-  return !(*this == other);
-}
+#ifndef INCLUDE_PERFETTO_EXT_BASE_SMALL_VECTOR_H_
+#define INCLUDE_PERFETTO_EXT_BASE_SMALL_VECTOR_H_

-StatusOr<bool> RwProtoCursor::IsRoot() const {
-  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());
-  return parent_link_.node == nullptr;
-}
+#include <algorithm>
+#include <type_traits>
+#include <utility>

-StatusOr<bool> RwProtoCursor::HasField(uint32_t field_id) {
-  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"

-  // Eagerly decompose bytes because the field being tested will be entered
-  // later anyways. See Executor::EnterField().
-  auto status = ConvertToMessageIfNeeded(node_);
-  PROTOVM_RETURN_IF_NOT_OK(status);
-  auto* message = node_->GetIf<Node::Message>();
-  bool found = static_cast<bool>(message->field_id_to_node.Find(field_id));
-  return found;
-}
+namespace perfetto {
+namespace base {

-StatusOr<void> RwProtoCursor::EnterField(uint32_t field_id) {
-  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());
+// Uses inline storage first, switches to dynamic storage when it overflows.
+template <typename T, size_t kSize>
+class SmallVector {
+ public:
+  static constexpr size_t kInlineSize = kSize;

-  auto status_or_it = FindOrCreateMessageField(node_, field_id);
-  PROTOVM_RETURN_IF_NOT_OK(status_or_it);
-  auto it = *status_or_it;
+  explicit SmallVector() = default;

-  if (it->value->GetIf<Node::IndexedRepeatedField>()) {
-    PROTOVM_ABORT(
-        "Attempted to enter field (id=%u) as a simple field but it is an "
-        "indexed repeated field",
-        field_id);
+  ~SmallVector() {
+    clear();
+    if (PERFETTO_UNLIKELY(is_using_heap()))
+      AlignedFree(begin_);
+    begin_ = end_ = end_of_storage_ = nullptr;
   }

-  if (it->value->GetIf<Node::MappedRepeatedField>()) {
-    PROTOVM_ABORT(
-        "Attempted to enter field (id=%u) as a simple field but it is a "
-        "mapped repeated field",
-        field_id);
+  // Move operators.
+  SmallVector(SmallVector&& other) noexcept(
+      std::is_nothrow_move_constructible<T>::value) {
+    if (other.is_using_heap()) {
+      // Move the heap content, no need to move the individual objects as their
+      // location won't change.
+      begin_ = other.begin_;
+      end_ = other.end_;
+      end_of_storage_ = other.end_of_storage_;
+    } else {
+      const size_t other_size = other.size();
+      PERFETTO_DCHECK(other_size <= capacity());
+      for (size_t i = 0; i < other_size; i++) {
+        // Move the entries and destroy the ones in the moved-from object.
+        new (&begin_[i]) T(std::move(other.begin_[i]));
+        other.begin_[i].~T();
+      }
+      end_ = begin_ + other_size;
+    }
+    auto* const other_inline_storage = other.inline_storage_begin();
+    other.end_ = other.begin_ = other_inline_storage;
+    other.end_of_storage_ = other_inline_storage + kInlineSize;
   }

-  parent_link_ = {node_, &node_->GetIf<Node::Message>()->field_id_to_node,
-                  std::addressof(*it)};
-  node_ = it->value.get();
-  return StatusOr<void>::Ok();
-}
-
-StatusOr<void> RwProtoCursor::EnterRepeatedFieldAt(uint32_t field_id,
-                                                   uint32_t index) {
-  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());
-
-  auto status_or_message_field = FindOrCreateMessageField(node_, field_id);
-  PROTOVM_RETURN_IF_NOT_OK(status_or_message_field);
-  auto message_field = *status_or_message_field;
+  SmallVector& operator=(SmallVector&& other) noexcept(
+      std::is_nothrow_move_constructible<T>::value) {
+    this->~SmallVector();
+    new (this) SmallVector<T, kSize>(std::move(other));
+    return *this;
+  }

-  auto status =
-      ConvertToIndexedRepeatedFieldIfNeeded(message_field->value.get());
-  PROTOVM_RETURN_IF_NOT_OK(status);
+  // Copy operators.
+  SmallVector(const SmallVector& other) {
+    const size_t other_size = other.size();
+    if (other_size > capacity())
+      Grow(other_size);
+    // Copy-construct the elements.
+    for (size_t i = 0; i < other_size; ++i)
+      new (&begin_[i]) T(other.begin_[i]);
+    end_ = begin_ + other_size;
+  }

-  auto status_or_repeated_field =
-      FindOrCreateIndexedRepeatedField(message_field->value.get(), index);
-  PROTOVM_RETURN_IF_NOT_OK(status_or_repeated_field);
+  SmallVector& operator=(const SmallVector& other) {
+    if (PERFETTO_UNLIKELY(this == &other))
+      return *this;
+    this->~SmallVector();
+    new (this) SmallVector<T, kSize>(other);
+    return *this;
+  }

-  parent_link_ = {
-      message_field->value.get(),
-      &message_field->value->GetIf<Node::IndexedRepeatedField>()->index_to_node,
-      std::addressof(**status_or_repeated_field)};
-  node_ = (*status_or_repeated_field)->value.get();
+  T* data() { return begin_; }
+  const T* data() const { return begin_; }

-  return StatusOr<void>::Ok();
-}
+  T* begin() { return begin_; }
+  const T* begin() const { return begin_; }

-StatusOr<RwProtoCursor::RepeatedFieldIterator>
-RwProtoCursor::IterateRepeatedField(uint32_t field_id) {
-  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());
+  T* end() { return end_; }
+  const T* end() const { return end_; }

-  auto status_convertion_to_message = ConvertToMessageIfNeeded(node_);
-  PROTOVM_RETURN_IF_NOT_OK(status_convertion_to_message);
+  size_t size() const { return static_cast<size_t>(end_ - begin_); }

-  auto* message = node_->GetIf<Node::Message>();
-  auto it = message->field_id_to_node.Find(field_id);
+  bool empty() const { return end_ == begin_; }

-  if (!it) {
-    return RepeatedFieldIterator{};
+  size_t capacity() const {
+    return static_cast<size_t>(end_of_storage_ - begin_);
   }

-  auto* field = it->value.get();
-  auto status_convertion_to_repeated_field =
-      ConvertToIndexedRepeatedFieldIfNeeded(field);
-  PROTOVM_RETURN_IF_NOT_OK(status_convertion_to_repeated_field);
-
-  return RepeatedFieldIterator{
-      allocator_,
-      field->GetIf<Node::IndexedRepeatedField>()->index_to_node.begin()};
-}
-
-StatusOr<void> RwProtoCursor::EnterRepeatedFieldByKey(uint32_t field_id,
-                                                      uint32_t map_key_field_id,
-                                                      uint64_t key) {
-  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());
+  T& front() {
+    PERFETTO_DCHECK(!empty());
+    return begin_[0];
+  }
+  const T& front() const {
+    PERFETTO_DCHECK(!empty());
+    return begin_[0];
+  }

-  auto status_or_message_field = FindOrCreateMessageField(node_, field_id);
-  PROTOVM_RETURN_IF_NOT_OK(status_or_message_field);
-  auto message_field = *status_or_message_field;
+  T& back() {
+    PERFETTO_DCHECK(!empty());
+    return end_[-1];
+  }
+  const T& back() const {
+    PERFETTO_DCHECK(!empty());
+    return end_[-1];
+  }

-  auto status_conversion = ConvertToMappedRepeatedFieldIfNeeded(
-      message_field->value.get(), map_key_field_id);
-  PROTOVM_RETURN_IF_NOT_OK(status_conversion);
+  T& operator[](size_t index) {
+    PERFETTO_DCHECK(index < size());
+    return begin_[index];
+  }

-  auto status_or_repeated_field =
-      FindOrCreateMappedRepeatedField(message_field->value.get(), key);
-  PROTOVM_RETURN_IF_NOT_OK(status_or_repeated_field);
+  const T& operator[](size_t index) const {
+    PERFETTO_DCHECK(index < size());
+    return begin_[index];
+  }

-  parent_link_ = {
-      message_field->value.get(),
-      &message_field->value->GetIf<Node::MappedRepeatedField>()->key_to_node,
-      std::addressof(**status_or_repeated_field)};
-  node_ = (*status_or_repeated_field)->value.get();
+  template <typename... Args>
+  void emplace_back(Args&&... args) {
+    T* end = end_;
+    if (PERFETTO_UNLIKELY(end == end_of_storage_))
+      end = Grow();
+    new (end) T(std::forward<Args>(args)...);
+    end_ = end + 1;
+  }

-  return StatusOr<void>::Ok();
-}
+  void pop_back() {
+    PERFETTO_DCHECK(!empty());
+    back().~T();
+    --end_;
+  }

-StatusOr<Scalar> RwProtoCursor::GetScalar() const {
-  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());
+  // Clear without reverting back to inline storage.
+  void clear() {
+    while (!empty())
+      pop_back();
+  }

-  auto* scalar = node_->GetIf<Scalar>();
-  if (!scalar) {
-    PROTOVM_ABORT("Attempted \"get scalar\" operation but node has type %s",
-                  node_->GetTypeName());
+ private:
+  PERFETTO_NO_INLINE T* Grow(size_t desired_capacity = 0) {
+    size_t cur_size = size();
+    size_t new_capacity = desired_capacity;
+    if (desired_capacity <= cur_size)
+      new_capacity = std::max(capacity() * 2, size_t(128));
+    T* new_storage =
+        static_cast<T*>(AlignedAlloc(alignof(T), new_capacity * sizeof(T)));
+    for (size_t i = 0; i < cur_size; ++i) {
+      // Move the elements into the new heap buffer and destroy the old ones.
+      new (&new_storage[i]) T(std::move(begin_[i]));
+      begin_[i].~T();
+    }
+    if (is_using_heap())
+      AlignedFree(begin_);
+    begin_ = new_storage;
+    end_ = new_storage + cur_size;
+    end_of_storage_ = new_storage + new_capacity;
+    return end_;
   }
-  return *scalar;
-}

-StatusOr<void> RwProtoCursor::SetBytes(protozero::ConstBytes data) {
-  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());
+  T* inline_storage_begin() { return reinterpret_cast<T*>(inline_storage_); }
+  bool is_using_heap() { return begin_ != inline_storage_begin(); }

-  if (bool is_compatible = node_->GetIf<Node::Empty>() ||
-                           node_->GetIf<Node::Bytes>() ||
-                           node_->GetIf<Node::Message>();
-      !is_compatible) {
-    PROTOVM_ABORT("Attempted \"set bytes\" operation but node has type %s",
-                  node_->GetTypeName());
-  }
+  T* begin_ = inline_storage_begin();
+  T* end_ = begin_;
+  T* end_of_storage_ = begin_ + kInlineSize;

-  auto status_or_bytes = allocator_->AllocateAndCopyBytes(data);
-  PROTOVM_RETURN_IF_NOT_OK(status_or_bytes);
+  alignas(T) char inline_storage_[sizeof(T) * kInlineSize];
+};

-  allocator_->DeleteReferencedData(node_);
-  node_->value = Node::Bytes{std::move(*status_or_bytes)};
+}  // namespace base
+}  // namespace perfetto

-  return StatusOr<void>::Ok();
-}
+#endif  // INCLUDE_PERFETTO_EXT_BASE_SMALL_VECTOR_H_
+/*
+ * Copyright (C) 2026 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.
+ */

-StatusOr<void> RwProtoCursor::SetScalar(Scalar scalar) {
-  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());
+#ifndef SRC_TRACING_SERVICE_PROTO_GROUP_REWRITER_H_
+#define SRC_TRACING_SERVICE_PROTO_GROUP_REWRITER_H_

-  if (bool is_compatible =
-          node_->GetIf<Node::Empty>() || node_->GetIf<Scalar>();
-      !is_compatible) {
-    PROTOVM_ABORT("Attempted \"set scalar\" operation but node has type %s",
-                  node_->GetTypeName());
-  }
+#include <stddef.h>

-  node_->value = scalar;
-  return StatusOr<void>::Ok();
-}
+#include <optional>

-StatusOr<void> RwProtoCursor::Merge(protozero::ConstBytes data,
-                                    uint32_t flags) {
-  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());
+// gen_amalgamated expanded: #include "perfetto/ext/base/small_vector.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/slice.h"

-  if (bool is_compatible = node_->GetIf<Node::Empty>() ||
-                           node_->GetIf<Node::Message>() ||
-                           node_->GetIf<Node::Bytes>();
-      !is_compatible) {
-    PROTOVM_ABORT("Attempted MERGE operation but node has type %s",
-                  node_->GetTypeName());
-  }
+namespace perfetto::tracing_v2 {
+
+enum class RewriteResult {
+  // The output slice owns the rewritten packet.
+  kRewritten,
+  // The packet has no groups. The caller must keep the input slices.
+  kUnchanged,
+  // The input has an invalid field or unmatched group boundary.
+  // This result also applies above kMaxNestingDepth.
+  kMalformedInput,
+  // A rewritten group exceeds proto_utils::kMaxMessageLength bytes.
+  kGroupTooLarge,
+};

-  if (data.size == 0) {
-    return StatusOr<void>::Ok();
-  }
+// Converts an SMB v2 packet to length-delimited protobuf during readback.
+// The rewriter replaces group boundaries with length-delimited field headers.
+// It keeps the field ID, tag byte count, and all other input bytes unchanged.
+//
+//   Input (hex):  0b       10 07  04    field 1 { field 2: 7 }
+//   Output:       0a 02    10 07
+//                    ^ length
+//
+// Only the bare byte 0x04 closes a group. The packet has no root wrapper.
+// The rewriter checks each field but does not parse length-delimited contents.
+// See proto_utils::kProtoGroupEndByte for the input format.
+//
+// Group lengths follow the v1 encoding: one byte for contents up to 127 bytes,
+// or a 4-byte padded varint for larger contents. The maximum is 256 MiB - 1.
+// A packet can have at most kMaxNestingDepth open groups, excluding the root.
+//
+// Each rewritten packet owns its output buffer. Packets without groups need
+// no copy or output allocation. The rewriter never returns partial output.
+class ProtoGroupRewriter {
+ public:
+  // Maximum number of groups open at once.
+  static constexpr size_t kMaxNestingDepth = 1024;

-  auto status_convert_to_message = ConvertToMessageIfNeeded(node_);
-  PROTOVM_RETURN_IF_NOT_OK(status_convert_to_message);
-  auto* message = node_->GetIf<Node::Message>();
+  // Rewrite() converts the packet in |input| to standard protobuf.
+  // Each slice must be nonempty. An empty input list returns kUnchanged.
+  // - On kRewritten, |*output| owns the rewritten packet in a new slice.
+  // - For any other result, Rewrite() keeps |*output| unchanged.
+  RewriteResult Rewrite(const Slices& input, std::optional<Slice>* output);

-  protozero::ProtoDecoder decoder(data);
+ private:
+  // Each entry holds the output offset of an open group's length slot.
+  base::SmallVector<size_t, 128> open_group_length_offsets_;
+};

-  for (auto field = decoder.ReadField(); field.valid();
-       field = decoder.ReadField()) {
-    auto it = message->field_id_to_node.Find(field.id());
+}  // namespace perfetto::tracing_v2

-    bool del_if_src_empty = (flags & kDelIfSrcEmpty) != 0;
-    if (it && del_if_src_empty &&
-        field.type() ==
-            protozero::proto_utils::ProtoWireType::kLengthDelimited &&
-        field.size() == 0) {
-      // Implements remove submessage semantics: empty src field means delete
-      // the dst field
-      message->field_id_to_node.Remove(*it);
-      allocator_->Delete(&*it);
-      continue;
-    }
+#endif  // SRC_TRACING_SERVICE_PROTO_GROUP_REWRITER_H_
+/*
+ * Copyright (C) 2026 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.
+ */

-    bool skip_submessages = (flags & kSkipSubmessages) != 0;
-    if (skip_submessages && it && it->value->GetIf<Node::Message>()) {
-      // Implements deep merge semantics: skip this message that was already
-      // merged by a previous operation
-      continue;
-    }
+// gen_amalgamated expanded: #include "src/tracing/service/proto_group_rewriter.h"

-    auto status_or_map_value = CreateNodeFromField(field);
-    PROTOVM_RETURN_IF_NOT_OK(status_or_map_value);
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>

-    if (!it) {
-      auto status_or_it = MapInsert(&message->field_id_to_node, field.id(),
-                                    std::move(*status_or_map_value));
-      PROTOVM_RETURN_IF_NOT_OK(status_or_it);
-      (*status_or_it)->value->has_been_merged = true;
-      continue;
-    }
+#include <algorithm>
+#include <memory>
+#include <utility>

-    if (it->value->GetIf<Node::MappedRepeatedField>()) {
-      PROTOVM_ABORT(
-          "Merge operation of mapped repeated field is not supported (field id "
-          "= %u)",
-          field.id());
-    }
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+// gen_amalgamated expanded: #include "perfetto/public/pb_utils.h"

-    // Promote field as repeated if needed:
-    // If this is the second time that the same field ID is being merged, then
-    // they are a repeated fields and they should live within an
-    // IndexedRepeatedField node.
-    if (it->value->has_been_merged) {
-      auto status_convert_to_indexed =
-          ConvertToIndexedRepeatedFieldIfNeeded(it->value.get());
-      PROTOVM_RETURN_IF_NOT_OK(status_convert_to_indexed);
-    }
+namespace perfetto::tracing_v2 {
+namespace {

-    if (auto* indexed_fields = it->value->GetIf<Node::IndexedRepeatedField>()) {
-      // Implements merge semantics for repeated fields: all existing fields are
-      // removed and replaced with the newly received fields.
-      if (!it->value->has_been_merged) {
-        // Optimization opportunity: reuse the existing nodes to avoid N
-        // allocation-deallocation pairs, where N is the number of newly
-        // received repeated fields.
-        allocator_->DeleteReferencedData(it->value.get());
-        it->value->has_been_merged = true;
-      }
-      MapInsert(&indexed_fields->index_to_node,
-                indexed_fields->index_to_node.Size(),
-                std::move(*status_or_map_value));
-      continue;
-    }
+namespace pu = ::protozero::proto_utils;
+
+// Reads nonempty slices as one byte stream. At the packet end,
+// read_ptr_ equals current_slice_end_.
+class SliceCursor {
+ public:
+  explicit SliceCursor(const Slices& slices)
+      : next_slice_(slices.data()),
+        slice_list_end_(slices.data() + slices.size()) {
+    for (const Slice& slice : slices)
+      input_size_ += slice.size;

-    // Optimization oppurtunity: reuse the existing node to avoid one
-    // allocation-deallocation pair
-    allocator_->Delete(it->value.release());
-    it->value = std::move(*status_or_map_value);
-    it->value->has_been_merged = true;
+    LoadNextSlice();
   }

-  // Reset the merge flags
-  for (auto& field : message->field_id_to_node) {
-    field.value->has_been_merged = false;
-  }
+  // Total input bytes across all slices.
+  size_t input_size() const { return input_size_; }
+  size_t offset() const { return offset_; }
+  bool at_end() const { return read_ptr_ == current_slice_end_; }

-  return StatusOr<void>::Ok();
-}
+  // Before this call, at_end() must be false.
+  uint8_t PeekByte() const { return *read_ptr_; }

-StatusOr<void> RwProtoCursor::Delete() {
-  PROTOVM_RETURN_IF_NOT_OK(CheckIsValid());
+  // Read one varint across slice boundaries. Failure can advance the cursor.
+  bool ReadVarInt(uint64_t* value) {
+    const uint8_t* next_ptr =
+        pu::ParseVarIntFast(read_ptr_, current_slice_end_, value);
+    if (PERFETTO_LIKELY(next_ptr)) {
+      AdvanceTo(next_ptr);
+      return true;
+    }

-  PROTOVM_ASSIGN_OR_RETURN(bool is_root, IsRoot());
-  if (is_root) {
-    node_->value = Node::Empty{};
-    return StatusOr<void>::Ok();
-  }
+    // No complete varint was found in this slice. If at least 10 bytes
+    // remain, reject it: a 64-bit varint must end within 10 bytes.
+    if (PERFETTO_UNLIKELY(slice_bytes_left() >=
+                          PERFETTO_PB_VARINT_MAX_SIZE_64)) {
+      return false;
+    }

-  PERFETTO_DCHECK(parent_link_.map);
-  PERFETTO_DCHECK(parent_link_.map_node);
+    // Read one byte at a time across slices.
+    // As in ParseVarInt(), only the low bit of the tenth byte contributes.
+    uint64_t decoded_value = 0;
+    for (uint32_t shift = 0; shift < 64 && !at_end(); shift += 7) {
+      const uint8_t byte = *read_ptr_;
+      AdvanceTo(read_ptr_ + 1);

-  parent_link_.map->Remove(*parent_link_.map_node);
-  allocator_->Delete(parent_link_.map_node);
+      // Place the seven data bits at this byte's bit offset in the value.
+      decoded_value |= static_cast<uint64_t>(byte & 0b0111'1111) << shift;

-  // Delete operation invalidates cursor
-  node_ = nullptr;
-  parent_link_ = {};
+      // A clear high bit marks the last byte of the varint.
+      if ((byte & 0b1000'0000) == 0) {
+        *value = decoded_value;
+        return true;
+      }
+    }
+    return false;
+  }

-  return StatusOr<void>::Ok();
-}
+  // Skip bytes across slices. Return false without advancing on truncated
+  // input.
+  bool Skip(uint64_t num_bytes) {
+    if (PERFETTO_LIKELY(num_bytes <= slice_bytes_left())) {
+      AdvanceTo(read_ptr_ + num_bytes);
+      return true;
+    }
+    if (num_bytes > input_bytes_left())
+      return false;

-StatusOr<void> RwProtoCursor::ConvertToMessageIfNeeded(Node* node) {
-  if (node->GetIf<Node::Message>()) {
-    return StatusOr<void>::Ok();
+    CopyOrSkipAcrossSlices(/*dst=*/nullptr, static_cast<size_t>(num_bytes));
+    return true;
   }

-  if (node->GetIf<Node::Empty>()) {
-    node->value = Node::Message{};
-    return StatusOr<void>::Ok();
+  // Copy bytes across slices. The input and |dst| must have enough space.
+  void CopyTo(uint8_t* dst, size_t num_bytes) {
+    PERFETTO_DCHECK(num_bytes <= input_bytes_left());
+    if (PERFETTO_LIKELY(num_bytes <= slice_bytes_left())) {
+      memcpy(dst, read_ptr_, num_bytes);
+      AdvanceTo(read_ptr_ + num_bytes);
+      return;
+    }
+    CopyOrSkipAcrossSlices(dst, num_bytes);
   }

-  auto* bytes = node->GetIf<Node::Bytes>();
-  if (!bytes) {
-    PROTOVM_ABORT("Attempted conversion to message but node has type %s",
-                  node->GetTypeName());
+ private:
+  size_t input_bytes_left() const { return input_size_ - offset(); }
+
+  size_t slice_bytes_left() const {
+    return static_cast<size_t>(current_slice_end_ - read_ptr_);
   }

-  Node::Message message;
+  // |next_ptr| must be in [read_ptr_, current_slice_end_].
+  // At the slice end, select the next slice, if available.
+  PERFETTO_ALWAYS_INLINE void AdvanceTo(const uint8_t* next_ptr) {
+    PERFETTO_DCHECK(next_ptr >= read_ptr_ && next_ptr <= current_slice_end_);
+    offset_ += static_cast<size_t>(next_ptr - read_ptr_);
+    read_ptr_ = next_ptr;

-  protozero::ProtoDecoder decoder(protozero::ConstBytes{
-      static_cast<const uint8_t*>(bytes->data.get()), bytes->size});
+    if (PERFETTO_UNLIKELY(read_ptr_ == current_slice_end_))
+      LoadNextSlice();
+  }

-  for (auto field = decoder.ReadField(); field.valid();
-       field = decoder.ReadField()) {
-    auto status_or_map_value = CreateNodeFromField(field);
-    if (!status_or_map_value.IsOk()) {
-      allocator_->DeleteReferencedData(&message);
-      PROTOVM_RETURN(status_or_map_value);
-    }
+  void LoadNextSlice() {
+    if (next_slice_ == slice_list_end_)
+      return;

-    auto it = message.field_id_to_node.Find(field.id());
+    PERFETTO_DCHECK(next_slice_->size > 0);
+    read_ptr_ = static_cast<const uint8_t*>(next_slice_->start);
+    current_slice_end_ = read_ptr_ + next_slice_->size;
+    ++next_slice_;
+  }

-    // First occurrence of this field id. Just insert a new field into the
-    // map.
-    if (!it) {
-      auto status_or_it = MapInsert(&message.field_id_to_node, field.id(),
-                                    std::move(*status_or_map_value));
-      if (!status_or_it.IsOk()) {
-        allocator_->DeleteReferencedData(&message);
-        PROTOVM_RETURN(status_or_it, "Insert message field (id = %u)",
-                       field.id());
+  // The caller checks the input size. A null |dst| skips bytes without a copy.
+  void CopyOrSkipAcrossSlices(uint8_t* dst, size_t num_bytes) {
+    while (num_bytes > 0) {
+      const size_t chunk_size = std::min(num_bytes, slice_bytes_left());
+      if (dst) {
+        memcpy(dst, read_ptr_, chunk_size);
+        dst += chunk_size;
       }
-      continue;
+      num_bytes -= chunk_size;
+      AdvanceTo(read_ptr_ + chunk_size);
     }
+  }

-    // Nth occurrence of this field id:
-    // 1. Make sure we have an IndexedRepeatedField node
-    // 2. Append into the IndexedRepeatedField's map
-    auto status_conversion =
-        ConvertToIndexedRepeatedFieldIfNeeded(it->value.get());
-    if (!status_conversion.IsOk()) {
-      allocator_->DeleteReferencedData(&message);
-      allocator_->Delete(status_or_map_value->release());
-      PROTOVM_RETURN(status_conversion);
-    }
+  // The next slice to load. read_ptr_ already points into the current slice.
+  const Slice* next_slice_;
+  const Slice* const slice_list_end_;
+  const uint8_t* current_slice_end_ = nullptr;
+  const uint8_t* read_ptr_ = nullptr;
+  size_t offset_ = 0;
+  size_t input_size_ = 0;
+};

-    auto& index_to_node =
-        it->value->GetIf<Node::IndexedRepeatedField>()->index_to_node;
-    auto status_or_it = MapInsert(&index_to_node, index_to_node.Size(),
-                                  std::move(*status_or_map_value));
-    if (!status_or_it.IsOk()) {
-      allocator_->DeleteReferencedData(&message);
-      PROTOVM_RETURN(status_or_it,
-                     "Insert repeated field (id = %u, index = %d)", field.id(),
-                     static_cast<int>(index_to_node.Size()));
+// Converts group boundaries and copies the input between them.
+// BeginGroup() allocates output on the first group and reserves a length slot.
+// EndGroup() writes the length and removes 3 slot bytes for short messages.
+//
+//   Open group:      ... | tag | _ _ _ _ | contents ... |
+//   Contents <= 127: ... | tag | length  | contents ... |
+//   Contents > 127:  ... | tag | L L L L | contents ... |
+//
+// Group stack offsets stay valid if the output buffer moves.
+class OutputWriter {
+ public:
+  using GroupLengthOffsets = base::SmallVector<size_t, 128>;
+
+  OutputWriter(const SliceCursor& input_cursor,
+               GroupLengthOffsets& group_length_offsets)
+      : copy_cursor_(input_cursor),
+        open_group_length_offsets_(group_length_offsets) {}
+
+  // Convert the start tag. Return false if the group exceeds the nesting limit.
+  bool BeginGroup(size_t tag_start_offset, size_t tag_size) {
+    if (PERFETTO_UNLIKELY(open_group_length_offsets_.size() >=
+                          ProtoGroupRewriter::kMaxNestingDepth)) {
+      return false;
     }
-  }

-  allocator_->DeleteReferencedData(node);
-  node->value = message;
+    // Copy the input fields before this group's start tag.
+    CopyInputUntil(tag_start_offset);

-  return StatusOr<void>::Ok();
-}
+    // Reserve space for the tag and the group's length slot.
+    EnsureAdditionalCapacity(tag_size + pu::kMessageLengthFieldSize);

-StatusOr<OwnedPtr<Node>> RwProtoCursor::CreateNodeFromField(
-    protozero::Field field) {
-  if (field.type() == protozero::proto_utils::ProtoWireType::kLengthDelimited) {
-    auto status_or_bytes = allocator_->AllocateAndCopyBytes(field.as_bytes());
-    PROTOVM_RETURN_IF_NOT_OK(status_or_bytes);
-    auto status_or_node =
-        allocator_->CreateNode<Node::Bytes>(std::move(*status_or_bytes));
-    if (!status_or_node.IsOk()) {
-      allocator_->DeleteReferencedData(&status_or_bytes.value());
-      PROTOVM_RETURN(status_or_node);
-    }
+    // Copy the start tag and change its wire type to length-delimited.
+    uint8_t* const tag_ptr = output_buffer_.get() + output_size_;
+    copy_cursor_.CopyTo(tag_ptr, tag_size);
+    // Clear bit 0 to change wire type 3 to 2 while preserving the field ID
+    // and continuation bit in the same byte.
+    PERFETTO_DCHECK(pu::GetTagFieldType(*tag_ptr) == pu::kWireTypeStartGroup);
+    *tag_ptr &= 0b1111'1110;
+    output_size_ += tag_size;

-    return std::move(*status_or_node);
+    // Save the length slot offset for EndGroup() and leave space for its value.
+    open_group_length_offsets_.emplace_back(output_size_);
+    output_size_ += pu::kMessageLengthFieldSize;
+    return true;
   }

-  auto status_or_node =
-      allocator_->CreateNode<Scalar>(field.type(), field.as_uint64());
-  if (!status_or_node.IsOk()) {
-    PROTOVM_RETURN(status_or_node);
+  // Close the innermost group and fill its length slot.
+  RewriteResult EndGroup(size_t end_byte_offset) {
+    if (PERFETTO_UNLIKELY(!HasOpenGroups()))
+      return RewriteResult::kMalformedInput;
+
+    // The group contains output already written and input fields not yet
+    // copied. Measure both parts to calculate the final group length.
+    const size_t group_length_offset = open_group_length_offsets_.back();
+    const size_t group_content_offset =
+        group_length_offset + pu::kMessageLengthFieldSize;
+    const size_t content_bytes_written = output_size_ - group_content_offset;
+    PERFETTO_DCHECK(end_byte_offset >= copy_cursor_.offset());
+    const size_t pending_input_size = end_byte_offset - copy_cursor_.offset();
+
+    // Check both sizes before adding them to prevent overflow.
+    if (PERFETTO_UNLIKELY(content_bytes_written > pu::kMaxMessageLength))
+      return RewriteResult::kGroupTooLarge;
+    const size_t max_pending_bytes =
+        pu::kMaxMessageLength - content_bytes_written;
+    if (PERFETTO_UNLIKELY(pending_input_size > max_pending_bytes)) {
+      return RewriteResult::kGroupTooLarge;
+    }
+    const size_t content_size = content_bytes_written + pending_input_size;
+
+    // Copy the remaining contents and skip the group's end byte.
+    CopyInputUntil(end_byte_offset);
+    copy_cursor_.Skip(1);
+
+    // CopyInputUntil() can allocate a larger buffer and invalidate old
+    // pointers. Use the saved offset to get a pointer into the current buffer.
+    uint8_t* const length_ptr = output_buffer_.get() + group_length_offset;
+
+    // Write the final length into the reserved slot.
+    if (content_size > pu::kMaxOneByteMessageLength) {
+      pu::WriteRedundantVarInt(static_cast<uint32_t>(content_size), length_ptr);
+    } else {
+      // The length needs only one of the four reserved bytes.
+      // Move the contents 3 bytes earlier to close the gap after the length.
+      uint8_t* const content_ptr = output_buffer_.get() + group_content_offset;
+      memmove(length_ptr + 1, content_ptr, content_size);
+      *length_ptr = static_cast<uint8_t>(content_size);
+      output_size_ -= (pu::kMessageLengthFieldSize - 1);
+    }
+
+    // Remove the closed group from the stack.
+    open_group_length_offsets_.pop_back();
+    return RewriteResult::kRewritten;
   }

-  return std::move(*status_or_node);
-}
+  // Transfer the output after the parser validates and closes all groups.
+  Slice Finish() {
+    PERFETTO_DCHECK(HasRewrittenGroups() && !HasOpenGroups());

-StatusOr<void> RwProtoCursor::ConvertToMappedRepeatedFieldIfNeeded(
-    Node* node,
-    uint32_t map_key_field_id) {
-  if (node->GetIf<Node::MappedRepeatedField>()) {
-    return StatusOr<void>::Ok();
+    // Copy any fields after the last group. Input offsets start at zero.
+    CopyInputUntil(copy_cursor_.input_size());
+    return Slice::TakeOwnership(std::move(output_buffer_), output_size_);
   }

-  if (node->GetIf<Node::Empty>()) {
-    node->value = Node::MappedRepeatedField{};
-    return StatusOr<void>::Ok();
-  }
+  bool HasOpenGroups() const { return !open_group_length_offsets_.empty(); }

-  // If the current node contains a message (either raw bytes or a decomposed
-  // Node::Message), convert it into a Node::MappedRepeatedField.
-  //
-  // The process is:
-  // 1. Extract the key (identified by 'map_key_field_id') from the message data
-  // within the current 'node'.
-  // 2. Allocate a new node. The original message content from the current node
-  // is moved into this new node.
-  // 3. Repurpose the current node, replacing it with an empty
-  // Node::MappedRepeatedField
-  //    (intrusive tree that stores message fields ordered by key).
-  // 4. Insert the new node (containing Node::Message) into the repurposed
-  // current node (Node::MappedRepeatedField) using the key extracted in step 1.
-  if (node->GetIf<Node::Bytes>() || node->GetIf<Node::Message>()) {
-    auto status_or_key = ReadScalarField(*node, map_key_field_id);
-    PROTOVM_RETURN_IF_NOT_OK(status_or_key);
+  // True after the first BeginGroup(). Only BeginGroup() allocates output.
+  bool HasRewrittenGroups() const { return output_buffer_ != nullptr; }

-    auto status_or_map_value = allocator_->CreateNode<Node::Empty>();
-    PROTOVM_RETURN_IF_NOT_OK(status_or_map_value);
+ private:
+  // Append input bytes before |input_end_offset| to the output.
+  // The parser has validated these bytes.
+  void CopyInputUntil(size_t input_end_offset) {
+    PERFETTO_DCHECK(input_end_offset >= copy_cursor_.offset());
+    const size_t bytes_to_copy = input_end_offset - copy_cursor_.offset();
+    if (bytes_to_copy == 0)
+      return;

-    auto map_value = std::move(*status_or_map_value);
-    map_value->value = std::move(node->value);
-    node->value = Node::MappedRepeatedField{};
+    // Reserve space before taking a pointer into the output buffer.
+    EnsureAdditionalCapacity(bytes_to_copy);

-    auto status_or_it =
-        MapInsert(&node->GetIf<Node::MappedRepeatedField>()->key_to_node,
-                  *status_or_key, std::move(map_value));
-    PROTOVM_RETURN(status_or_it);
+    // Append the bytes. CopyTo() advances the input cursor.
+    copy_cursor_.CopyTo(output_buffer_.get() + output_size_, bytes_to_copy);
+    output_size_ += bytes_to_copy;
   }

-  // If the current node is a Node::IndexedRepeatedField (intrusive tree with
-  // message fields ordered by index), this block converts it into a
-  // Node::MappedRepeatedField (intrusive tree with message fields ordered by
-  // key).
-  //
-  // The process is:
-  // 1. Initialize an empty Node::MappedRepeatedField
-  // 2. For each field in the current node (Node::IndexedRepeatedField):
-  //    2.1. Extract the key (identified by 'map_key_field_id'). Note that the
-  //         iterated fields must be messages.
-  //    2.2. Remove the field from the current node
-  //         (Node::IndexedRepeatedField).
-  //    2.3. Insert the field into the Node::MappedRepeatedField using the key
-  //         extracted in step 2.1.
-  // 3. Assign the Node::MappedRepeatedField to the current node, i.e. repurpose
-  //    current node from Node::IndexedRepeatedField to
-  //    Node::MappedRepeatedField)
-  if (auto* indexed = node->GetIf<Node::IndexedRepeatedField>()) {
-    Node::MappedRepeatedField mapped_repeated_field;
+  // Ensure room for |additional_bytes| after the current output.
+  // Buffer growth invalidates pointers into the old buffer.
+  void EnsureAdditionalCapacity(size_t additional_bytes) {
+    const size_t required_capacity = output_size_ + additional_bytes;
+    if (PERFETTO_LIKELY(required_capacity <= output_capacity_))
+      return;

-    for (auto it = indexed->index_to_node.begin(); it;) {
-      auto& map_entry = *it;
-      auto& value_node = *it->value;
+    size_t new_capacity;
+    if (output_capacity_ == 0) {
+      // Heuristic for the first allocation.
+      // - input_size: space for the original packet bytes.
+      // - input_size / 16: spare for groups that grow by 3 bytes.
+      // - 64: spare for length slots reserved before their groups close.
+      const size_t input_size = copy_cursor_.input_size();
+      new_capacity = input_size + input_size / 16 + 64;
+    } else {
+      // Double the existing capacity to limit repeated allocations and copies.
+      new_capacity = output_capacity_ * 2;
+    }
+    new_capacity = std::max(new_capacity, required_capacity);

-      it = indexed->index_to_node.Remove(it);
+    // Allocate a larger buffer and copy the output already written.
+    std::unique_ptr<uint8_t[]> larger_buffer(new uint8_t[new_capacity]);
+    if (output_size_)
+      memcpy(larger_buffer.get(), output_buffer_.get(), output_size_);

-      auto status_or_key = ReadScalarField(value_node, map_key_field_id);
-      PROTOVM_RETURN_IF_NOT_OK(status_or_key);
+    // Replace the old buffer after the copy completes.
+    output_buffer_ = std::move(larger_buffer);
+    output_capacity_ = new_capacity;
+  }

-      map_entry.key = *status_or_key;
-      mapped_repeated_field.key_to_node.Insert(map_entry);
+  // Next input byte to copy. The parser can be ahead of this cursor.
+  SliceCursor copy_cursor_;
+  GroupLengthOffsets& open_group_length_offsets_;
+  std::unique_ptr<uint8_t[]> output_buffer_;
+  // Used output bytes, including reserved length slots.
+  size_t output_size_ = 0;
+  // Allocated bytes in output_buffer_.
+  size_t output_capacity_ = 0;
+};
+
+// Parse field boundaries. A 0x04 inside a field value is ordinary data.
+RewriteResult RewriteFields(SliceCursor input_cursor, OutputWriter* writer) {
+  // A tag value has 32 bits: 29 for the field ID and 3 for the wire type.
+  constexpr uint64_t kMaxFieldId = UINT32_MAX >> pu::kFieldTypeNumBits;
+
+  while (!input_cursor.at_end()) {
+    // The bare end byte has no field ID. Handle it before tag validation.
+    if (input_cursor.PeekByte() == pu::kProtoGroupEndByte) {
+      const RewriteResult result = writer->EndGroup(input_cursor.offset());
+      PERFETTO_DCHECK(result != RewriteResult::kUnchanged);
+      if (PERFETTO_UNLIKELY(result != RewriteResult::kRewritten))
+        return result;
+      input_cursor.Skip(1);
+      continue;
     }

-    node->value = mapped_repeated_field;
+    // Save the start before ReadVarInt() advances past the tag.
+    const size_t tag_start_offset = input_cursor.offset();
+    uint64_t tag = 0;
+    if (PERFETTO_UNLIKELY(!input_cursor.ReadVarInt(&tag)))
+      return RewriteResult::kMalformedInput;

-    return StatusOr<void>::Ok();
-  }
+    const uint64_t field_id = pu::GetTagFieldId(tag);
+    if (PERFETTO_UNLIKELY(field_id == 0 || field_id > kMaxFieldId))
+      return RewriteResult::kMalformedInput;

-  PROTOVM_ABORT(
-      "Attempted to access field as MappedRepeatedField but node has type "
-      "%s",
-      node->GetTypeName());
-}
+    uint64_t value = 0;
+    bool valid = false;
+    switch (pu::GetTagFieldType(tag)) {
+      case pu::kWireTypeStartGroup:
+        valid = writer->BeginGroup(tag_start_offset,
+                                   input_cursor.offset() - tag_start_offset);
+        break;
+      case static_cast<uint32_t>(pu::ProtoWireType::kVarInt):
+        valid = input_cursor.ReadVarInt(&value);
+        break;
+      case static_cast<uint32_t>(pu::ProtoWireType::kFixed64):
+        valid = input_cursor.Skip(sizeof(uint64_t));
+        break;
+      case static_cast<uint32_t>(pu::ProtoWireType::kFixed32):
+        valid = input_cursor.Skip(sizeof(uint32_t));
+        break;
+      case static_cast<uint32_t>(pu::ProtoWireType::kLengthDelimited):
+        valid = input_cursor.ReadVarInt(&value) && input_cursor.Skip(value);
+        break;
+      default:
+        // Only the bare byte 0x04 closes a group.
+        // Reject standard end-group tags. Wire types 6 and 7 are undefined.
+        return RewriteResult::kMalformedInput;
+    }

-StatusOr<void> RwProtoCursor::ConvertToIndexedRepeatedFieldIfNeeded(
-    Node* node) {
-  if (node->GetIf<Node::IndexedRepeatedField>()) {
-    return StatusOr<void>::Ok();
+    if (PERFETTO_UNLIKELY(!valid))
+      return RewriteResult::kMalformedInput;
   }

-  if (node->GetIf<Node::MappedRepeatedField>()) {
-    PROTOVM_ABORT(
-        "Attempted \"convert to indexed repeated field\" operation but "
-        "node has type %s",
-        node->GetTypeName());
-  }
+  if (writer->HasOpenGroups())
+    return RewriteResult::kMalformedInput;

-  if (node->GetIf<Node::Empty>()) {
-    node->value = Node::IndexedRepeatedField{};
-    return StatusOr<void>::Ok();
-  }
+  return writer->HasRewrittenGroups() ? RewriteResult::kRewritten
+                                      : RewriteResult::kUnchanged;
+}

-  auto status_or_map_value = allocator_->CreateNode<Node::Empty>();
-  PROTOVM_RETURN_IF_NOT_OK(status_or_map_value);
+}  // namespace

-  auto map_value = std::move(*status_or_map_value);
-  map_value->value = std::move(node->value);
-  node->value = Node::IndexedRepeatedField{};
+RewriteResult ProtoGroupRewriter::Rewrite(const Slices& input,
+                                          std::optional<Slice>* output) {
+  open_group_length_offsets_.clear();

-  auto status_or_it =
-      MapInsert(&node->GetIf<Node::IndexedRepeatedField>()->index_to_node, 0,
-                std::move(map_value));
-  PROTOVM_RETURN(status_or_it);
+  // Cursor over the input for RewriteFields() to read and validate fields.
+  const SliceCursor initial_cursor(input);
+  // Writes validated fields to the output buffer and replaces group boundaries.
+  OutputWriter writer(initial_cursor, open_group_length_offsets_);
+
+  const RewriteResult result = RewriteFields(initial_cursor, &writer);
+  if (result != RewriteResult::kRewritten)
+    return result;
+
+  output->emplace(writer.Finish());
+  return RewriteResult::kRewritten;
 }

-StatusOr<IntrusiveMap::Iterator> RwProtoCursor::FindOrCreateMessageField(
-    Node* node,
-    uint32_t field_id) {
-  auto status = ConvertToMessageIfNeeded(node);
-  PROTOVM_RETURN_IF_NOT_OK(status);
+}  // namespace perfetto::tracing_v2
+// gen_amalgamated begin source: src/tracing/service/zlib_compressor.cc
+// gen_amalgamated begin header: src/tracing/service/zlib_compressor.h
+/*
+ * 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.
+ */

-  auto* message = node->GetIf<Node::Message>();
-  auto it = message->field_id_to_node.Find(field_id);
-  if (it) {
-    return it;
-  }
+#ifndef SRC_TRACING_SERVICE_ZLIB_COMPRESSOR_H_
+#define SRC_TRACING_SERVICE_ZLIB_COMPRESSOR_H_

-  auto status_or_map_value = allocator_->CreateNode<Node::Empty>();
-  PROTOVM_RETURN_IF_NOT_OK(status_or_map_value);
+#include <vector>

-  auto status_or_it = MapInsert(&message->field_id_to_node, field_id,
-                                std::move(*status_or_map_value));
-  PROTOVM_RETURN(status_or_it);
-}
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_packet.h"

-StatusOr<IntrusiveMap::Iterator>
-RwProtoCursor::FindOrCreateIndexedRepeatedField(Node* node, uint32_t index) {
-  auto& index_to_node =
-      node->GetIf<Node::IndexedRepeatedField>()->index_to_node;
+namespace perfetto {

-  auto it = index_to_node.Find(index);
-  if (it) {
-    return it;
-  }
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+void ZlibCompressFn(std::vector<TracePacket>*);
+#endif

-  bool requires_creation_and_is_not_simple_append =
-      index > index_to_node.Size();
-  if (requires_creation_and_is_not_simple_append) {
-    PROTOVM_ABORT(
-        "Attempted to insert repeated field at arbitrary position (only "
-        "append operation is supported)");
-  }
+}  // namespace perfetto

-  auto status_or_map_value = allocator_->CreateNode<Node::Empty>();
-  PROTOVM_RETURN_IF_NOT_OK(status_or_map_value);
+#endif  // SRC_TRACING_SERVICE_ZLIB_COMPRESSOR_H_
+/*
+ * 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.
+ */

-  return MapInsert(&node->GetIf<Node::IndexedRepeatedField>()->index_to_node,
-                   index, std::move(*status_or_map_value));
-}
+// gen_amalgamated expanded: #include "src/tracing/service/zlib_compressor.h"

-StatusOr<IntrusiveMap::Iterator> RwProtoCursor::FindOrCreateMappedRepeatedField(
-    Node* node,
-    uint64_t key) {
-  auto it = node->GetIf<Node::MappedRepeatedField>()->key_to_node.Find(key);
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-  if (it) {
-    return it;
-  }
+// File compiles to nothing when the buildflag is off (e.g. SDK opt-out).
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)

-  auto status_or_map_value = allocator_->CreateNode<Node::Empty>();
-  PROTOVM_RETURN_IF_NOT_OK(status_or_map_value);
+#include <zlib.h>

-  return MapInsert(&node->GetIf<Node::MappedRepeatedField>()->key_to_node, key,
-                   std::move(*status_or_map_value));
-}
+// gen_amalgamated expanded: #include "protos/perfetto/trace/trace.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/trace_packet.pbzero.h"
+// gen_amalgamated expanded: #include "src/tracing/service/packet_compressor_common.h"

-StatusOr<IntrusiveMap::Iterator> RwProtoCursor::MapInsert(
-    IntrusiveMap* map,
-    uint64_t key,
-    OwnedPtr<Node> map_value) {
-  auto status_or_map_node =
-      allocator_->CreateMapNode(key, std::move(map_value));
-  PROTOVM_RETURN_IF_NOT_OK(status_or_map_node, "Failed to allocate node");
+namespace perfetto {

-  auto [it, inserted] = map->Insert(*status_or_map_node->get());
-  if (!inserted) {
-    allocator_->Delete(status_or_map_node->release());
-    PROTOVM_ABORT(
-        "Failed to insert intrusive map entry (key = %d). Duplicated key?",
-        static_cast<int>(key));
-  }
+namespace {

-  status_or_map_node->release();
-  return it;
-}
+using packet_compressor::GetPreamble;
+using packet_compressor::kCompressSliceSize;
+using packet_compressor::Preamble;
+using packet_compressor::PreambleToSlice;

-StatusOr<uint64_t> RwProtoCursor::ReadScalarField(const Node& node,
-                                                  uint32_t field_id) {
-  if (auto* bytes = node.GetIf<Node::Bytes>()) {
-    protozero::ProtoDecoder decoder(protozero::ConstBytes{
-        static_cast<const uint8_t*>(bytes->data.get()), bytes->size});
+// A compressor for `TracePacket`s that uses zlib. The class is exposed for
+// testing.
+class ZlibPacketCompressor {
+ public:
+  ZlibPacketCompressor();
+  ~ZlibPacketCompressor();

-    auto field = decoder.ReadField();
-    while (field.valid() && field.id() != field_id) {
-      field = decoder.ReadField();
-    }
+  // Can be called multiple times, before Finish() is called.
+  void PushPacket(const TracePacket& packet);

-    if (!field.valid()) {
-      PROTOVM_ABORT(
-          "Attempted to read scalar field (id=%u) but it is not present",
-          field_id);
-    }
+  // Returned the compressed data. Can be called at most once. After this call,
+  // the object is unusable (PushPacket should not be called) and must be
+  // destroyed.
+  TracePacket Finish();

-    if (field.type() ==
-        protozero::proto_utils::ProtoWireType::kLengthDelimited) {
-      PROTOVM_ABORT("Attempted to length-delimited field (id=%u) as scalar",
-                    field_id);
-    }
+ private:
+  void PushData(const void* data, uint32_t size);
+  void NewOutputSlice();
+  void PushCurSlice();

-    return field.as_uint64();
+  z_stream stream_;
+  size_t total_new_slices_size_ = 0;
+  std::vector<Slice> new_slices_;
+  std::unique_ptr<uint8_t[]> cur_slice_;
+};
+
+ZlibPacketCompressor::ZlibPacketCompressor() {
+  memset(&stream_, 0, sizeof(stream_));
+  int status = deflateInit(&stream_, 6);
+  PERFETTO_CHECK(status == Z_OK);
+}
+
+ZlibPacketCompressor::~ZlibPacketCompressor() {
+  int status = deflateEnd(&stream_);
+  PERFETTO_CHECK(status == Z_OK);
+}
+
+void ZlibPacketCompressor::PushPacket(const TracePacket& packet) {
+  // We need to be able to tokenize packets in the compressed stream, so we
+  // prefix a proto preamble to each packet. The compressed stream looks like a
+  // valid Trace proto.
+  Preamble preamble =
+      GetPreamble<protos::pbzero::Trace::kPacketFieldNumber>(packet.size());
+  PushData(preamble.buf.data(), preamble.size);
+  for (const Slice& slice : packet.slices()) {
+    PushData(slice.start, static_cast<uint32_t>(slice.size));
   }
+}

-  if (auto* message = node.GetIf<Node::Message>()) {
-    auto it = message->field_id_to_node.Find(field_id);
-    if (!it) {
-      PROTOVM_ABORT(
-          "Attempted to read scalar field (id=%u) but it is not present",
-          field_id);
+void ZlibPacketCompressor::PushData(const void* data, uint32_t size) {
+  stream_.next_in = const_cast<Bytef*>(static_cast<const Bytef*>(data));
+  stream_.avail_in = static_cast<uInt>(size);
+  while (stream_.avail_in != 0) {
+    if (stream_.avail_out == 0) {
+      NewOutputSlice();
     }
+    int status = deflate(&stream_, Z_NO_FLUSH);
+    PERFETTO_CHECK(status == Z_OK);
+  }
+}

-    auto* scalar = it->value->GetIf<Scalar>();
-    if (!scalar) {
-      PROTOVM_ABORT(
-          "Attempted to read scalar field (id=%u) from node with type %s",
-          field_id, it->value->GetTypeName());
-    }
+TracePacket ZlibPacketCompressor::Finish() {
+  for (;;) {
+    int status = deflate(&stream_, Z_FINISH);
+    if (status == Z_STREAM_END)
+      break;
+    PERFETTO_CHECK(status == Z_OK || status == Z_BUF_ERROR);
+    NewOutputSlice();
+  }

-    return scalar->value;
+  PushCurSlice();
+
+  TracePacket packet;
+  packet.AddSlice(PreambleToSlice(
+      GetPreamble<protos::pbzero::TracePacket::kCompressedPacketsFieldNumber>(
+          total_new_slices_size_)));
+  for (auto& slice : new_slices_) {
+    packet.AddSlice(std::move(slice));
   }
+  return packet;
+}

-  PROTOVM_ABORT(
-      "Attempted to read scalar field (id=%u) but parent node has type %s",
-      field_id, node.GetTypeName());
+void ZlibPacketCompressor::NewOutputSlice() {
+  PushCurSlice();
+  cur_slice_ = std::make_unique<uint8_t[]>(kCompressSliceSize);
+  stream_.next_out = reinterpret_cast<Bytef*>(cur_slice_.get());
+  stream_.avail_out = kCompressSliceSize;
 }

-StatusOr<void> RwProtoCursor::CheckIsValid() const {
-  if (!node_) {
-    PROTOVM_ABORT("Attempted to access invalid cursor");
+void ZlibPacketCompressor::PushCurSlice() {
+  if (cur_slice_) {
+    total_new_slices_size_ += kCompressSliceSize - stream_.avail_out;
+    new_slices_.push_back(Slice::TakeOwnership(
+        std::move(cur_slice_), kCompressSliceSize - stream_.avail_out));
   }
-  return StatusOr<void>::Ok();
 }

-}  // namespace protovm
+}  // namespace
+
+void ZlibCompressFn(std::vector<TracePacket>* packets) {
+  if (packets->empty()) {
+    return;
+  }
+
+  ZlibPacketCompressor stream;
+
+  for (const TracePacket& packet : *packets) {
+    stream.PushPacket(packet);
+  }
+
+  TracePacket packet = stream.Finish();
+
+  packets->clear();
+  packets->push_back(std::move(packet));
+}
+
 }  // namespace perfetto
-// gen_amalgamated begin source: src/protovm/vm.cc
-// gen_amalgamated begin header: src/protovm/vm.h
+
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+// gen_amalgamated begin source: src/tracing/service/zstd_compressor.cc
+// gen_amalgamated begin header: src/tracing/service/zstd_compressor.h
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -57257,152 +62516,26 @@ StatusOr<void> RwProtoCursor::CheckIsValid() const {
  * limitations under the License.
  */

-#ifndef SRC_PROTOVM_VM_H_
-#define SRC_PROTOVM_VM_H_
-
-#include <memory>
+#ifndef SRC_TRACING_SERVICE_ZSTD_COMPRESSOR_H_
+#define SRC_TRACING_SERVICE_ZSTD_COMPRESSOR_H_

-// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
+#include <vector>

-// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
-// gen_amalgamated expanded: #include "src/protovm/executor.h"
-// gen_amalgamated expanded: #include "src/protovm/parser.h"
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_packet.h"

 namespace perfetto {
-namespace protovm {
-
-// A VM that executes programs defined by data sources at registration time.
-// Used by traced to apply patches (packets overwritten in the ring buffer) to
-// an incremental state packet, thus allowing efficient incremental tracing of
-// Layers/Windows/Views without requiring periodic invalidation and achieve
-// perfect interning.
-//
-// Overview of the VM's architecture and interactions:
-//
-//
-//         ***********
-//         *         *
-//         * Program *
-//         *         *
-//         ***********
-//              │
-//              │
-//              │
-//          ┌───┴──┐            ┌────────┐
-//          │      │            │        │
-//          │Parser├────────────┤Executor│
-//          │      │            │        │
-//          └──────┘            └─┬────┬─┘
-//                                │    │
-//                          ┌─────┘    └─────┐
-//                          │                │
-//                          │                │
-//                      ┌───┴────┐       ┌───┴────┐
-//                      │        │       │        │
-//                      │RoCursor│       │RwProto │
-//                      │        │       │::Cursor│
-//                      └────┬───┘       │        │
-//                           │           └───┬────┘
-//                           │               │
-//                           │               │
-//                           │               │
-//                      *********      ***************
-//                      *       *      *             *
-//                      * Patch *      * Incremental *
-//                      *       *      *    state    *
-//                      *********      *             *
-//                                     ***************
-//
-//  ┌─┐
-//  │ │  VM's component
-//  └─┘
-//
-//  ***
-//  * *  Data
-//  ***
-//
-//
-// Parser: Understands the instructions semantic and controls the program flow.
-//         It delegates to the Executor operations like navigating through the
-//         patch and incremental state data, reading values, and manipulating
-//         fields.
-//
-//
-// Executor: Thin glue layer that mainly forwards data back and forth between
-//           the Parser and cursors. Mainly useful for testing, as it can
-//           be easily mocked and allows to test the Parser in isolation.
-//
-//
-// RoCursor: Provides read-only access to the incoming data (the patch) to be
-//           applied. It allows to traverse the proto message structure of the
-//           patch, iterating over fields and extracting field values.
-//
-//
-// RwProto::Cursor: Provides read-write access to the incremental state. It
-//                  allows traversing the proto message structure of the
-//                  incremental state, as well as deleting/inserting/merging
-//                  fields.
-class Vm {
- public:
-  Vm(protozero::ConstBytes program,
-     size_t memory_limit_bytes,
-     protozero::ConstBytes initial_incremental_state = {nullptr, 0});
-  StatusOr<void> ApplyPatch(protozero::ConstBytes packet);
-  void SerializeIncrementalState(protozero::Message*) const;
-  std::string SerializeProgram() const;
-  std::unique_ptr<Vm> CloneReadOnly() const;
-  uint64_t GetMemoryUsageBytes() const;
-
- private:
-  struct ReadWriteState {
-    ReadWriteState(std::string& program,
-                   size_t memory_limit_bytes,
-                   protozero::ConstBytes initial_incremental_state)
-        : executor{},
-          parser{protozero::ConstBytes{
-                     reinterpret_cast<const uint8_t*>(program.data()),
-                     program.size()},
-                 &executor},
-          allocator{memory_limit_bytes},
-          incremental_state{&allocator} {
-      if (initial_incremental_state.data) {
-        incremental_state.GetRoot().SetBytes(initial_incremental_state);
-      }
-    }
-
-    Executor executor;
-    Parser parser;
-    Allocator allocator;
-    RwProto incremental_state;
-  };
-
-  struct ReadOnlyState {
-    explicit ReadOnlyState(std::string incremental_state)
-        : serialized_incremental_state(std::move(incremental_state)) {}

-    std::string serialized_incremental_state;
-  };
-
-  // Constructor used to produce read-only copies of the ProtoVm.
-  // Private to avoid unintended copies. It should be used only for
-  // CloneReadOnly().
-  Vm(const Vm&);
-
-  // Constructor used only for cloning
-  explicit Vm(std::string incremental_state);
-
-  std::string SerializeIncrementalStateAsString() const;
-
-  std::string owned_program_;
-  std::variant<ReadWriteState, ReadOnlyState> state_;
-};
+#if PERFETTO_BUILDFLAG(PERFETTO_ZSTD)
+// Compresses `packets` with zstd at the given compression `level`.
+void ZstdCompressFn(std::vector<TracePacket>*, int level);
+#endif

-}  // namespace protovm
 }  // namespace perfetto

-#endif  // SRC_PROTOVM_VM_H_
+#endif  // SRC_TRACING_SERVICE_ZSTD_COMPRESSOR_H_
 /*
- * Copyright (C) 2025 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -57417,81 +62550,179 @@ class Vm {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/protovm/vm.h"
+// gen_amalgamated expanded: #include "src/tracing/service/zstd_compressor.h"

-// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/scattered_heap_buffer.h"
-// gen_amalgamated expanded: #include "src/protovm/error_handling.h"
-// gen_amalgamated expanded: #include "src/protovm/ro_cursor.h"
-// gen_amalgamated expanded: #include "src/protovm/rw_proto.h"
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_ZSTD)
+
+#include <memory>
+
+#include <zstd.h>
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/trace.pbzero.h"
+// gen_amalgamated expanded: #include "protos/perfetto/trace/trace_packet.pbzero.h"
+// gen_amalgamated expanded: #include "src/tracing/service/packet_compressor_common.h"

 namespace perfetto {
-namespace protovm {

-Vm::Vm(protozero::ConstBytes program,
-       size_t memory_limit_bytes,
-       protozero::ConstBytes initial_incremental_state)
-    : owned_program_(program.ToStdString()),
-      state_(std::in_place_type_t<ReadWriteState>{},
-             owned_program_,
-             memory_limit_bytes,
-             initial_incremental_state) {}
+namespace {

-Vm::Vm(const Vm& other)
-    : owned_program_(other.SerializeProgram()),
-      state_(std::in_place_type_t<ReadOnlyState>{},
-             other.SerializeIncrementalStateAsString()) {}
+using packet_compressor::GetPreamble;
+using packet_compressor::kCompressSliceSize;
+using packet_compressor::Preamble;
+using packet_compressor::PreambleToSlice;

-StatusOr<void> Vm::ApplyPatch(protozero::ConstBytes packet) {
-  ReadWriteState* rw_state = std::get_if<ReadWriteState>(&state_);
-  if (!rw_state) {
-    return StatusOr<void>::Abort();
-  }
-  auto src = RoCursor(packet);
-  auto dst = rw_state->incremental_state.GetRoot();
-  return rw_state->parser.Run(src, dst);
+// ScopedResource's close function must return int; ZSTD_freeCStream returns
+// size_t, so wrap it. It only fails for a static-allocated stream, not the
+// heap one ZSTD_createCStream gives us, so returning 0 is safe.
+inline int ZstdFreeCStream(ZSTD_CStream* cstream) {
+  ZSTD_freeCStream(cstream);
+  return 0;
 }
+using ScopedZstdCStream = base::ScopedResource<ZSTD_CStream*,
+                                               ZstdFreeCStream,
+                                               /*InvalidValue=*/nullptr,
+                                               /*CheckClose=*/false>;

-void Vm::SerializeIncrementalState(protozero::Message* proto) const {
-  if (const ReadOnlyState* state = std::get_if<ReadOnlyState>(&state_); state) {
-    proto->AppendRawProtoBytes(state->serialized_incremental_state.data(),
-                               state->serialized_incremental_state.size());
-    return;
+// A compressor for `TracePacket`s that uses zstd's streaming API: data is fed
+// in with ZSTD_compressStream2(ZSTD_e_continue) and the frame is finalized with
+// ZSTD_e_end, emitting output in fixed-size slices (kCompressSliceSize).
+// zstd API reference: https://facebook.github.io/zstd/zstd_manual.html
+class ZstdPacketCompressor {
+ public:
+  explicit ZstdPacketCompressor(int level);
+
+  // Can be called multiple times, before Finish() is called.
+  void PushPacket(const TracePacket& packet);
+
+  // Returns the compressed data. Can be called at most once. After this call,
+  // the object is unusable (PushPacket should not be called) and must be
+  // destroyed.
+  TracePacket Finish();
+
+ private:
+  void PushData(const void* data, uint32_t size);
+  void NewOutputSlice();
+  void PushCurSlice();
+
+  ScopedZstdCStream cstream_;
+  // Points into `cur_slice_`. Zero-initialized so that the first compression
+  // call observes a full output buffer and allocates the initial slice.
+  ZSTD_outBuffer out_{/*dst=*/nullptr, /*size=*/0, /*pos=*/0};
+  size_t total_new_slices_size_ = 0;
+  std::vector<Slice> new_slices_;
+  std::unique_ptr<uint8_t[]> cur_slice_;
+};
+
+ZstdPacketCompressor::ZstdPacketCompressor(int level) {
+  cstream_.reset(ZSTD_createCStream());
+  PERFETTO_CHECK(cstream_);
+  // zstd maps 0 to its default level, clamps levels above its max, and treats
+  // negatives as fast modes, so any int is safe to pass through here.
+  size_t rc =
+      ZSTD_CCtx_setParameter(cstream_.get(), ZSTD_c_compressionLevel, level);
+  PERFETTO_CHECK(!ZSTD_isError(rc));
+}
+
+void ZstdPacketCompressor::PushPacket(const TracePacket& packet) {
+  // Prefix each packet with its proto preamble so the compressed stream itself
+  // parses as a valid Trace proto, and its packets can be tokenized back out.
+  Preamble preamble =
+      GetPreamble<protos::pbzero::Trace::kPacketFieldNumber>(packet.size());
+  PushData(preamble.buf.data(), preamble.size);
+  for (const Slice& slice : packet.slices()) {
+    PushData(slice.start, static_cast<uint32_t>(slice.size));
   }
+}

-  const ReadWriteState* state = std::get_if<ReadWriteState>(&state_);
-  state->incremental_state.Serialize(proto);
+void ZstdPacketCompressor::PushData(const void* data, uint32_t size) {
+  // ZSTD_e_continue hands data to the encoder, which buffers and emits at its
+  // own discretion. It may not consume all input in one call (e.g. when the
+  // output slice fills), so loop until `in` is drained, giving it a fresh slice
+  // whenever `out_` is full.
+  ZSTD_inBuffer in = {/*src=*/data, /*size=*/size, /*pos=*/0};
+  while (in.pos < in.size) {
+    if (out_.pos == out_.size) {
+      NewOutputSlice();
+    }
+    size_t rc =
+        ZSTD_compressStream2(cstream_.get(), &out_, &in, ZSTD_e_continue);
+    PERFETTO_CHECK(!ZSTD_isError(rc));
+  }
 }

-std::string Vm::SerializeIncrementalStateAsString() const {
-  protozero::HeapBuffered<protozero::Message> proto;
-  SerializeIncrementalState(proto.get());
-  return proto.SerializeAsString();
+TracePacket ZstdPacketCompressor::Finish() {
+  // ZSTD_e_end flushes buffered data and writes the frame epilogue. Per the
+  // contract, keep calling (draining `out_` into new slices) until it reports 0
+  // bytes remaining.
+  size_t remaining;
+  do {
+    if (out_.pos == out_.size) {
+      NewOutputSlice();
+    }
+    ZSTD_inBuffer in = {/*src=*/nullptr, /*size=*/0, /*pos=*/0};
+    remaining = ZSTD_compressStream2(cstream_.get(), &out_, &in, ZSTD_e_end);
+    PERFETTO_CHECK(!ZSTD_isError(remaining));
+  } while (remaining != 0);
+
+  PushCurSlice();
+
+  TracePacket packet;
+  packet.AddSlice(PreambleToSlice(
+      GetPreamble<
+          protos::pbzero::TracePacket::kZstdCompressedPacketsFieldNumber>(
+          total_new_slices_size_)));
+  for (auto& slice : new_slices_) {
+    packet.AddSlice(std::move(slice));
+  }
+  return packet;
 }

-std::string Vm::SerializeProgram() const {
-  return owned_program_;
+void ZstdPacketCompressor::NewOutputSlice() {
+  PushCurSlice();
+  cur_slice_ = std::make_unique<uint8_t[]>(kCompressSliceSize);
+  out_.dst = cur_slice_.get();
+  out_.size = kCompressSliceSize;
+  out_.pos = 0;
 }

-std::unique_ptr<Vm> Vm::CloneReadOnly() const {
-  return std::unique_ptr<Vm>(new Vm(*this));
+void ZstdPacketCompressor::PushCurSlice() {
+  if (!cur_slice_) {
+    return;
+  }
+  total_new_slices_size_ += out_.pos;
+  new_slices_.push_back(Slice::TakeOwnership(std::move(cur_slice_), out_.pos));
 }

-uint64_t Vm::GetMemoryUsageBytes() const {
-  if (const ReadOnlyState* state = std::get_if<ReadOnlyState>(&state_); state) {
-    return owned_program_.size() + state->serialized_incremental_state.size();
+}  // namespace
+
+void ZstdCompressFn(std::vector<TracePacket>* packets, int level) {
+  if (packets->empty()) {
+    return;
   }

-  const ReadWriteState* state = std::get_if<ReadWriteState>(&state_);
-  return owned_program_.size() + state->allocator.GetMemoryUsageBytes();
+  ZstdPacketCompressor stream(level);
+
+  for (const TracePacket& packet : *packets) {
+    stream.PushPacket(packet);
+  }
+
+  TracePacket packet = stream.Finish();
+
+  packets->clear();
+  packets->push_back(std::move(packet));
 }

-}  // namespace protovm
 }  // namespace perfetto
-// gen_amalgamated begin source: src/protozero/filtering/filter_bytecode_parser.cc
-// gen_amalgamated begin header: src/protozero/filtering/filter_bytecode_parser.h
+
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_ZSTD)
+// gen_amalgamated begin source: src/tracing/ipc/default_socket.cc
+// gen_amalgamated begin header: include/perfetto/ext/ipc/basic_types.h
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -57506,166 +62737,177 @@ uint64_t Vm::GetMemoryUsageBytes() const {
  * limitations under the License.
  */

-#ifndef SRC_PROTOZERO_FILTERING_FILTER_BYTECODE_PARSER_H_
-#define SRC_PROTOZERO_FILTERING_FILTER_BYTECODE_PARSER_H_
+#ifndef INCLUDE_PERFETTO_EXT_IPC_BASIC_TYPES_H_
+#define INCLUDE_PERFETTO_EXT_IPC_BASIC_TYPES_H_

 #include <stddef.h>
 #include <stdint.h>
+#include <sys/types.h>

-#include <vector>
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/cpp_message_obj.h"

-namespace protozero {
+namespace perfetto {
+namespace ipc {

-// Loads the proto-encoded bytecode in memory and allows fast lookups for tuples
-// (msg_index, field_id) to tell if a given field should be allowed or not and,
-// in the case of nested fields, what is the next message index to recurse into.
-// This class does two things:
-// 1. Expands the array of varint from the proto into a vector<uint32_t>. This
-//    is to avoid performing varint decoding on every lookup, at the cost of
-//    some extra memory (2KB-4KB). Note that the expanded vector is not just a
-//    1:1 copy of the proto one (more below). This is to avoid O(Fields) linear
-//    lookup complexity.
-// 2. Creates an index of offsets to remember the start word for each message.
-//    This is so we can jump to O(1) to the N-th message when recursing into a
-//    nested fields, without having to scan and find the (N-1)-th END_OF_MESSAGE
-//    marker.
-// Overall lookups are O(1) for field ids < 128 (kDirectlyIndexLimit) and O(N),
-// with N being the number of allowed field ranges for other fields.
-// See comments around |word_| below for the structure of the word vector.
-class FilterBytecodeParser {
- public:
-  static constexpr uint32_t kSimpleField = 0x7fffffff;
-  static constexpr uint32_t kFilterStringField = 0x7ffffffe;
-  // FilterStringFieldWithType uses a range: 0x7fff0000 to 0x7ffffffd.
-  // The semantic type is encoded in the lower 16 bits.
-  static constexpr uint32_t kFilterStringFieldWithType = 0x7fff0000;
-  static constexpr uint32_t kFilterStringFieldWithTypeMask = 0x7fff0000;
-  static constexpr uint32_t kSemanticTypeMask = 0xffff;
+using ProtoMessage = ::protozero::CppMessageObj;
+using ServiceID = uint32_t;
+using MethodID = uint32_t;
+using ClientID = uint64_t;
+using RequestID = uint64_t;

-  // Result of a Query() operation
-  struct QueryResult {
-    bool allowed;  // Whether the field is allowed at all or no.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+// AF_UNIX on Windows is supported only on Windows 10 from build 17063.
+// Also it doesn't bring major advantages compared to a TCP socket.
+// See go/perfetto-win .
+constexpr bool kUseTCPSocket = true;
+#else
+// Android, Linux, Mac, Fuchsia use local sockets.
+constexpr bool kUseTCPSocket = false;
+#endif

-    // If |allowed|==true && nested_msg_field() == true, this tells the message
-    // index of the nested field that should be used when recursing in the
-    // parser.
-    uint32_t nested_msg_index;
+// This determines the maximum size allowed for an IPC message. Trying to send
+// or receive a larger message will hit DCHECK(s) and auto-disconnect.
+constexpr size_t kIPCBufferSize = 128 * 1024;

-    // The semantic type of the field (only meaningful when
-    // filter_string_field() returns true). A value of 0 means unspecified.
-    uint32_t semantic_type;
+constexpr uid_t kInvalidUid = static_cast<uid_t>(-1);

-    // If |allowed|==true, specifies if the field is of a simple type (varint,
-    // fixed32/64, string or byte).
-    bool simple_field() const { return nested_msg_index == kSimpleField; }
+}  // namespace ipc
+}  // namespace perfetto

-    // If |allowed|==true, specifies if this field is a string field that needs
-    // to be filtered.
-    bool filter_string_field() const {
-      // Range: [kFilterStringFieldWithType, kFilterStringField] i.e.
-      // [0x7fff0000, 0x7ffffffe]. This excludes kSimpleField (0x7fffffff).
-      return nested_msg_index >= kFilterStringFieldWithType &&
-             nested_msg_index <= kFilterStringField;
-    }
+#endif  // INCLUDE_PERFETTO_EXT_IPC_BASIC_TYPES_H_
+/*
+ * 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.
+ */

-    // If |allowed|==true, specifies if the field is a nested field that needs
-    // recursion. The caller is expected to use |nested_msg_index| for the next
-    // Query() calls.
-    bool nested_msg_field() const {
-      static_assert(kFilterStringFieldWithType < kFilterStringField,
-                    "kFilterStringFieldWithType < kFilterStringField");
-      static_assert(kFilterStringField < kSimpleField,
-                    "kFilterStringField < kSimpleField");
-      return nested_msg_index < kFilterStringFieldWithType;
-    }
-  };
+// gen_amalgamated expanded: #include "perfetto/tracing/default_socket.h"

-  // Loads a filter. The filter data consists of a sequence of varints which
-  // contains the filter opcodes and a final checksum.
-  // If a filter is already loaded, it is replaced.
-  //
-  // Optionally accepts an overlay (pass nullptr/0 if not needed). The overlay
-  // can add new fields or upgrade existing fields (e.g., to FilterString).
-  // The overlay uses the same opcode format as the base bytecode, but with an
-  // explicit message index prefix:
-  //   [msg_index, (field_id << 3) | opcode, argument] ... [checksum]
-  // Entry sizes depend on the opcode (same as base bytecode).
-  // Entries must be sorted by (msg_index, field_id). The base bytecode and
-  // overlay are merged during loading, maintaining sorted field order.
-  // See /rfcs/0011-subset-string-filter-rules.md for design details.
-  bool Load(const void* filter_data,
-            size_t len,
-            const void* overlay_data = nullptr,
-            size_t overlay_len = 0);
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/android_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/ipc/basic_types.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/basic_types.h"

-  // Checks wheter a given field is allowed or not.
-  // msg_index = 0 is the index of the root message, where all queries should
-  // start from (typically perfetto.protos.Trace).
-  QueryResult Query(uint32_t msg_index, uint32_t field_id) const;
+#include <stdlib.h>

-  void Reset();
-  void set_suppress_logs_for_fuzzer(bool x) { suppress_logs_for_fuzzer_ = x; }
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <unistd.h>
+#endif

- private:
-  static constexpr uint32_t kDirectlyIndexLimit = 128;
-  static constexpr uint32_t kAllowed = 1u << 31u;
+namespace perfetto {
+namespace {
+
+const char* kRunPerfettoBaseDir = "/run/perfetto/";
+
+// On Linux and CrOS, check /run/perfetto/ before using /tmp/ as the socket
+// base directory.
+bool UseRunPerfettoBaseDir() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX)
+  // Note that the trailing / in |kRunPerfettoBaseDir| ensures we are checking
+  // against a directory, not a file.
+  int res = PERFETTO_EINTR(access(kRunPerfettoBaseDir, X_OK));
+  if (!res)
+    return true;

-  bool LoadInternal(const uint8_t* filter_data,
-                    size_t len,
-                    const uint8_t* overlay_data,
-                    size_t overlay_len);
+  // If the path doesn't exist (ENOENT), fail silently to the caller. Otherwise,
+  // fail with an explicit error message.
+  if (errno != ENOENT
+#if PERFETTO_BUILDFLAG(PERFETTO_CHROMIUM_BUILD)
+      // access(2) won't return EPERM, but Chromium sandbox returns EPERM if the
+      // sandbox doesn't allow the call (e.g. in the child processes).
+      && errno != EPERM
+#endif
+  ) {
+    PERFETTO_PLOG("%s exists but cannot be accessed. Falling back on /tmp/ ",
+                  kRunPerfettoBaseDir);
+  }
+  return false;
+#else
+  base::ignore_result(kRunPerfettoBaseDir);
+  return false;
+#endif
+}

-  // The state of all fields for all messages is stored in one contiguous array.
-  // This is to avoid memory fragmentation and allocator overhead.
-  // We expect a high number of messages (hundreds), but each message is small.
-  // For each message we store two sets of uint32:
-  // 1. A set of "directly indexed" fields, for field ids < 128.
-  // 2. The remainder is a set of ranges.
-  // So each message descriptor consists of a sequence of words as follows:
-  //
-  // [0] -> how many directly indexed fields are stored next (up to 128)
-  //
-  // [1..N] -> One word per field id (See "field state" below).
-  //
-  // [N + 1] -> Start of field id range 1
-  // [N + 2] -> End of field id range 1 (exclusive, STL-style).
-  // [N + 3] -> Field state for fields in range 1 (below)
-  //
-  // [N + 4] -> Start of field id range 2
-  // [N + 5] -> End of field id range 2 (exclusive, STL-style).
-  // [N + 6] -> Field state for fields in range 2 (below)
+}  // anonymous namespace

-  // The "field state" word is as follows:
-  // Bit 31: 1 if the field is allowed, 0 if disallowed.
-  //         Only directly indexed fields can be 0 (it doesn't make sense to add
-  //         a range and then say "btw it's NOT allowed".. don't add it then.
-  //         0 is only used for filling gaps in the directly indexed bucket.
-  // Bits [30..0] (only when MSB == allowed):
-  //  0x7fffffff: The field is "simple" (varint, fixed32/64, string, bytes) and
-  //      can be directly passed through in output. No recursion is needed.
-  //  0x7ffffffe: The field is string field which needs to be filtered.
-  //  0x7fff0000-0x7ffffffd: The field is a string field with a semantic type.
-  //      The lower 16 bits encode the semantic type.
-  //  [0, 0x7ffeffff]: The field is a nested submessage. The value is the index
-  //     that must be passed as first argument to the next Query() calls.
-  //     Note that the message index is purely a monotonic counter in the
-  std::vector<uint32_t> words_;
+const char* GetProducerSocket() {
+  const char* name = getenv("PERFETTO_PRODUCER_SOCK_NAME");
+  if (name == nullptr) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    name = "127.0.0.1:32278";
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+    name = "/dev/socket/traced_producer";
+#else
+    // Use /run/perfetto if it exists. Then fallback to /tmp.
+    static const char* producer_socket =
+        UseRunPerfettoBaseDir() ? "/run/perfetto/traced-producer.sock"
+                                : "/tmp/perfetto-producer";
+    name = producer_socket;
+#endif
+  }
+  base::ignore_result(UseRunPerfettoBaseDir);  // Silence unused func warnings.
+  return name;
+}

-  // One entry for each message index stored in the filter plus a sentinel at
-  // the end. Maps each message index to the offset in |words_| where the
-  // Nth message start.
-  // message_offset_.size() - 2 == the max message id that can be parsed.
-  std::vector<uint32_t> message_offset_;
+std::string GetRelaySocket() {
+  // The relay socket is optional and is connected only when the env var is set.
+  // In Android, if the env var isn't set then we check the
+  // |traced_relay.relay_port| system property.
+  const char* name = getenv("PERFETTO_RELAY_SOCK_NAME");
+  if (name != nullptr)
+    return std::string(name);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  return base::GetAndroidProp("traced_relay.relay_port");
+#else
+  return std::string();
+#endif
+}

-  bool suppress_logs_for_fuzzer_ = false;
-};
+std::vector<std::string> TokenizeProducerSockets(
+    const char* producer_socket_names) {
+  return base::SplitString(producer_socket_names, ",");
+}

-}  // namespace protozero
+const char* GetConsumerSocket() {
+  const char* name = getenv("PERFETTO_CONSUMER_SOCK_NAME");
+  if (name == nullptr) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    name = "127.0.0.1:32279";
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+    name = "/dev/socket/traced_consumer";
+#else
+    // Use /run/perfetto if it exists. Then fallback to /tmp.
+    static const char* consumer_socket =
+        UseRunPerfettoBaseDir() ? "/run/perfetto/traced-consumer.sock"
+                                : "/tmp/perfetto-consumer";
+    name = consumer_socket;
+#endif
+  }
+  return name;
+}

-#endif  // SRC_PROTOZERO_FILTERING_FILTER_BYTECODE_PARSER_H_
-// gen_amalgamated begin header: src/protozero/filtering/filter_bytecode_common.h
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/ipc/memfd.cc
+// gen_amalgamated begin header: src/tracing/ipc/memfd.h
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -57680,52 +62922,52 @@ class FilterBytecodeParser {
  * limitations under the License.
  */

-#ifndef SRC_PROTOZERO_FILTERING_FILTER_BYTECODE_COMMON_H_
-#define SRC_PROTOZERO_FILTERING_FILTER_BYTECODE_COMMON_H_
+#ifndef SRC_TRACING_IPC_MEMFD_H_
+#define SRC_TRACING_IPC_MEMFD_H_

-#include <stdint.h>
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-namespace protozero {
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"

-enum FilterOpcode : uint32_t {
-  // The immediate value is 0 in this case.
-  kFilterOpcode_EndOfMessage = 0,
+// The build has the memfd code. HasMemfdSupport() also checks the kernel.
+#define PERFETTO_MEMFD_ENABLED()              \
+  (PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+   PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX))

-  // The immediate value is the id of the allowed field.
-  kFilterOpcode_SimpleField = 1,
+// Some android build bots use a sysroot that doesn't support memfd when
+// compiling for the host, so we define the flags we need ourselves.

-  // The immediate value is the start of the range. The next word (without
-  // any shifting) is the length of the range.
-  kFilterOpcode_SimpleFieldRange = 2,
+// from memfd.h
+#ifndef MFD_CLOEXEC
+#define MFD_CLOEXEC 0x0001U
+#define MFD_ALLOW_SEALING 0x0002U
+#endif

-  // The immediate value is the id of the allowed field. The next word
-  // (without any shifting) is the index of the filter that should be used to
-  // recurse into the nested message.
-  kFilterOpcode_NestedField = 3,
+// from fcntl.h
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS 1033
+#define F_GET_SEALS 1034
+#define F_SEAL_SEAL 0x0001
+#define F_SEAL_SHRINK 0x0002
+#define F_SEAL_GROW 0x0004
+#define F_SEAL_WRITE 0x0008
+#endif

-  // The immediate value is the id of the allowed field. The behaviour of this
-  // opcode is the same as kFilterOpcode_SimpleField, with the further semantic
-  // that the field is a string and needs to be processed using the string
-  // filtering rules.
-  kFilterOpcode_FilterString = 4,
+namespace perfetto {

-  // The immediate value is the id of the allowed field. The next word (without
-  // any shifting) is the semantic type of the string field. The behaviour is
-  // similar to kFilterOpcode_FilterString, but carries semantic type info so
-  // that the string filter can apply type-specific rules.
-  kFilterOpcode_FilterStringWithType = 5,
-};
+// Whether the operating system supports memfd.
+bool HasMemfdSupport();

-// Constants for extracting opcode and field_id from bytecode words.
-// Bytecode words are encoded as: (field_id << kOpcodeShift) | opcode
-constexpr uint32_t kOpcodeMask = 0x7u;
-constexpr uint32_t kOpcodeShift = 3;
+// Call memfd(2) if available on platform and return the fd as result. This call
+// also makes a kernel version check for safety on older kernels (b/116769556).
+// Returns an invalid ScopedFile on failure.
+base::ScopedFile CreateMemfd(const char* name, unsigned int flags);

-}  // namespace protozero
+}  // namespace perfetto

-#endif  // SRC_PROTOZERO_FILTERING_FILTER_BYTECODE_COMMON_H_
+#endif  // SRC_TRACING_IPC_MEMFD_H_
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -57740,463 +62982,341 @@ constexpr uint32_t kOpcodeShift = 3;
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/protozero/filtering/filter_bytecode_parser.h"
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <limits>
-#include <vector>
-
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/fnv_hash.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
-// gen_amalgamated expanded: #include "perfetto/public/compiler.h"
-// gen_amalgamated expanded: #include "src/protozero/filtering/filter_bytecode_common.h"
+// gen_amalgamated expanded: #include "src/tracing/ipc/memfd.h"

-namespace protozero {
+#include <errno.h>

-namespace {
+#if PERFETTO_MEMFD_ENABLED()

-// Parses varint-encoded bytecode and verifies checksum.
-// Returns true on success, with the checksum removed from |words|.
-bool ParseAndVerifyChecksum(const uint8_t* data,
-                            size_t len,
-                            std::vector<uint32_t>* words,
-                            bool suppress_logs) {
-  if (len == 0) {
-    return false;
-  }
-  words->reserve(len);  // An overestimation, but avoids reallocations.
+#include <stdio.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <sys/utsname.h>
+#include <unistd.h>

-  // Parse varints while computing checksum in a single pass.
-  // The last word is the checksum itself and should not be hashed.
-  perfetto::base::FnvHasher hasher;
-  uint32_t actual_csum = 0;
+// Some android build bots use a sysroot that doesn't support memfd when
+// compiling for the host, so we redefine it if necessary.
+#if !defined(__NR_memfd_create)
+#if defined(__x86_64__)
+#define __NR_memfd_create 319
+#elif defined(__i386__)
+#define __NR_memfd_create 356
+#elif defined(__aarch64__)
+#define __NR_memfd_create 279
+#elif defined(__arm__)
+#define __NR_memfd_create 385
+#else
+#error "unsupported sysroot without memfd support"
+#endif
+#endif  // !defined(__NR_memfd_create)

-  using BytecodeDecoder =
-      PackedRepeatedFieldIterator<proto_utils::ProtoWireType::kVarInt,
-                                  uint32_t>;
-  bool has_checksum = false;
-  bool packed_parse_err = false;
-  for (BytecodeDecoder it(data, len, &packed_parse_err); it;) {
-    uint32_t word = *it++;
-    if (bool is_last_word = !it; is_last_word) {
-      actual_csum = word;
-      has_checksum = true;
-      break;
-    }
-    words->emplace_back(word);
-    hasher.Update(word);
-  }
-  if (packed_parse_err || !has_checksum) {
-    words->clear();
-    return false;
-  }
-  auto expected_csum = static_cast<uint32_t>(hasher.digest());
-  if (expected_csum != actual_csum) {
-    if (!suppress_logs) {
-      PERFETTO_ELOG("Filter bytecode checksum failed. Expected: %x, actual: %x",
-                    expected_csum, actual_csum);
+namespace perfetto {
+bool HasMemfdSupport() {
+  static bool kSupportsMemfd = [] {
+    // Check kernel version supports memfd_create(). Some older kernels segfault
+    // executing memfd_create() rather than returning ENOSYS (b/116769556).
+    static constexpr int kRequiredMajor = 3;
+    static constexpr int kRequiredMinor = 17;
+    struct utsname uts;
+    int major, minor;
+    if (uname(&uts) == 0 && strcmp(uts.sysname, "Linux") == 0 &&
+        sscanf(uts.release, "%d.%d", &major, &minor) == 2 &&
+        ((major < kRequiredMajor ||
+          (major == kRequiredMajor && minor < kRequiredMinor)))) {
+      return false;
     }
-    words->clear();
-    return false;
-  }
-  return true;
-}

-struct OverlayEntry {
-  uint32_t msg_index;
-  uint32_t field_id;
-  uint32_t message_id;
-};
+    base::ScopedFile fd;
+    fd.reset(static_cast<int>(syscall(__NR_memfd_create, "perfetto_shmem",
+                                      MFD_CLOEXEC | MFD_ALLOW_SEALING)));
+    return !!fd;
+  }();
+  return kSupportsMemfd;
+}

-// Returns the message_id for an overlay entry based on opcode and argument.
-// Returns 0 if the opcode is invalid.
-uint32_t GetMessageIdForOverlay(uint32_t opcode, uint32_t argument) {
-  switch (opcode) {
-    case kFilterOpcode_SimpleField:
-      return FilterBytecodeParser::kSimpleField;
-    case kFilterOpcode_FilterString:
-      return FilterBytecodeParser::kFilterStringField;
-    case kFilterOpcode_FilterStringWithType:
-      // For FilterStringWithType, incorporate semantic type into message_id.
-      return FilterBytecodeParser::kFilterStringFieldWithType |
-             (argument & FilterBytecodeParser::kSemanticTypeMask);
-    default:
-      return 0;  // Invalid opcode
+base::ScopedFile CreateMemfd(const char* name, unsigned int flags) {
+  if (!HasMemfdSupport()) {
+    errno = ENOSYS;
+    return base::ScopedFile();
   }
+  return base::ScopedFile(
+      static_cast<int>(syscall(__NR_memfd_create, name, flags)));
 }
+}  // namespace perfetto

-}  // namespace
+#else  // PERFETTO_MEMFD_ENABLED()

-void FilterBytecodeParser::Reset() {
-  bool suppress = suppress_logs_for_fuzzer_;
-  *this = FilterBytecodeParser();
-  suppress_logs_for_fuzzer_ = suppress;
+namespace perfetto {
+bool HasMemfdSupport() {
+  return false;
 }
-
-bool FilterBytecodeParser::Load(const void* filter_data,
-                                size_t len,
-                                const void* overlay_data,
-                                size_t overlay_len) {
-  Reset();
-  bool res =
-      LoadInternal(static_cast<const uint8_t*>(filter_data), len,
-                   static_cast<const uint8_t*>(overlay_data), overlay_len);
-  // If load fails, don't leave the parser in a half broken state.
-  if (!res)
-    Reset();
-  return res;
+base::ScopedFile CreateMemfd(const char*, unsigned int) {
+  errno = ENOSYS;
+  return base::ScopedFile();
 }
+}  // namespace perfetto

-bool FilterBytecodeParser::LoadInternal(const uint8_t* filter_data,
-                                        size_t len,
-                                        const uint8_t* overlay_data,
-                                        size_t overlay_len) {
-  // First unpack the varints into a plain uint32 vector, so it's easy to
-  // iterate through them and look ahead.
-  std::vector<uint32_t> words;
-  if (!ParseAndVerifyChecksum(filter_data, len, &words,
-                              suppress_logs_for_fuzzer_))
-    return false;
+#endif  // PERFETTO_MEMFD_ENABLED()
+// gen_amalgamated begin source: src/tracing/ipc/posix_shared_memory.cc
+// gen_amalgamated begin header: src/tracing/ipc/posix_shared_memory.h
+/*
+ * 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.
+ */

-  // Parse the overlay (if provided).
-  std::vector<OverlayEntry> overlay;
-  if (overlay_data && overlay_len > 0) {
-    std::vector<uint32_t> overlay_words;
-    if (!ParseAndVerifyChecksum(overlay_data, overlay_len, &overlay_words,
-                                suppress_logs_for_fuzzer_)) {
-      return false;
-    }
+#ifndef SRC_TRACING_IPC_POSIX_SHARED_MEMORY_H_
+#define SRC_TRACING_IPC_POSIX_SHARED_MEMORY_H_

-    // Each entry is exactly 3 words: [msg_index, field_word, argument]
-    // where field_id = field_word >> 3. The argument is 0 when not needed.
-    if (overlay_words.size() % 3 != 0) {
-      PERFETTO_DLOG("overlay error: size %zu not multiple of 3",
-                    overlay_words.size());
-      return false;
-    }
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"

-    for (size_t i = 0; i < overlay_words.size(); i += 3) {
-      overlay.emplace_back();
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)

-      uint32_t opcode = overlay_words[i + 1] & kOpcodeMask;
-      uint32_t message_id =
-          GetMessageIdForOverlay(opcode, overlay_words[i + 2]);
-      if (message_id == 0) {
-        PERFETTO_DLOG("overlay error: invalid opcode %u at index %zu", opcode,
-                      i + 1);
-        return false;
-      }
+#include <stddef.h>

-      OverlayEntry& entry = overlay.back();
-      entry.msg_index = overlay_words[i];
-      entry.field_id = overlay_words[i + 1] >> kOpcodeShift;
-      entry.message_id = message_id;
+#include <memory>

-      if (overlay.size() == 1) {
-        continue;
-      }
-      // Validate that overlay entries are sorted by (msg_index, field_id).
-      const OverlayEntry& prev_entry = overlay[overlay.size() - 2];
-      if (entry.msg_index < prev_entry.msg_index ||
-          (entry.msg_index == prev_entry.msg_index &&
-           entry.field_id <= prev_entry.field_id)) {
-        PERFETTO_DLOG(
-            "overlay error: entries not sorted at index %zu (msg %u, "
-            "field %u) after (msg %u, field %u)",
-            i, entry.msg_index, entry.field_id, prev_entry.msg_index,
-            prev_entry.field_id);
-        return false;
-      }
-    }
-  }
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory.h"

-  // Temporary storage for each message. Cleared on every END_OF_MESSAGE.
-  std::vector<uint32_t> direct_indexed_fields;
-  std::vector<uint32_t> ranges;
-  uint32_t max_msg_index = 0;
-  uint32_t current_msg_index = 0;
-  size_t overlay_idx = 0;
+namespace perfetto {

-  auto add_directly_indexed_field = [&](uint32_t field_id, uint32_t msg_id) {
-    PERFETTO_DCHECK(field_id > 0 && field_id < kDirectlyIndexLimit);
-    direct_indexed_fields.resize(std::max(direct_indexed_fields.size(),
-                                          static_cast<size_t>(field_id) + 1));
-    direct_indexed_fields[field_id] = kAllowed | msg_id;
+// Implements the SharedMemory and its factory for the posix-based transport.
+class PosixSharedMemory : public SharedMemory {
+ public:
+  class Factory : public SharedMemory::Factory {
+   public:
+    ~Factory() override;
+    std::unique_ptr<SharedMemory> CreateSharedMemory(size_t) override;
   };

-  auto add_range = [&](uint32_t id_start, uint32_t id_end, uint32_t msg_id) {
-    PERFETTO_DCHECK(id_end > id_start);
-    PERFETTO_DCHECK(id_start >= kDirectlyIndexLimit);
-    ranges.emplace_back(id_start);
-    ranges.emplace_back(id_end);
-    ranges.emplace_back(kAllowed | msg_id);
-  };
+  // Creates and maps a shared memory region of |size| bytes.
+  // Uses a memfd with seals that prevent resizing and adding further seals.
+  // In-tree Android builds require a sealed memfd.
+  // Other builds fall back to an unlinked temporary file if memfd creation
+  // fails.
+  // Returns an error status on failure.
+  static base::StatusOr<std::unique_ptr<PosixSharedMemory>> Create(size_t size);
+
+  // Mmaps an existing SHM region for shared read/write access.
+  // - Takes ownership of the descriptor, also on failure.
+  // - Returns nullptr if descriptor validation fails.
+  // - Also returns nullptr if mmap() fails (e.g. OOM).
+  // - Rejects an empty file, and a file larger than |max_size|, before mmap().
+  // - If |require_seals_if_supported| is true and the system supports
+  //   memfd_create, requires F_SEAL_SEAL, F_SEAL_GROW and F_SEAL_SHRINK
+  //   before it reads the size.
+  static std::unique_ptr<PosixSharedMemory> AttachToFd(
+      base::ScopedFile,
+      bool require_seals_if_supported,
+      size_t max_size);

-  // Merges overlay entries into the current message being built.
-  //
-  // This function processes overlay entries for the current message up to (and
-  // including) the given |field_id|. Since both base bytecode and overlay are
-  // sorted by (msg_index, field_id), we can use a two-pointer merge approach:
-  // - Entries with field_id < the given field_id are ADDed as new fields
-  // - An entry with field_id == the given field_id is an UPGRADE (returned)
-  // - Entries with field_id > the given field_id are left for later
-  //
-  // Pass std::numeric_limits<uint32_t>::max() as field_id to drain all
-  // remaining entries for the current message (called at EndOfMessage).
-  //
-  // Returns:
-  // - The msg_id to use if there's an exact match (upgrade case)
-  // - 0 if no match (use the base bytecode's msg_id)
-  // - std::numeric_limits<uint32_t>::max() on error
-  constexpr uint32_t kOverlayError = std::numeric_limits<uint32_t>::max();
-  auto process_overlay = [&](uint32_t field_id) -> uint32_t {
-    while (overlay_idx < overlay.size()) {
-      const OverlayEntry& entry = overlay[overlay_idx];
+  ~PosixSharedMemory() override;
+
+  int fd() const { return fd_.get(); }
+
+  // SharedMemory implementation.
+  using SharedMemory::start;  // Equal priority to const and non-const versions
+  const void* start() const override { return start_; }
+  size_t size() const override { return size_; }
+
+ private:
+  // Returns an error status if mmap() fails.
+  static base::StatusOr<std::unique_ptr<PosixSharedMemory>> MapFD(
+      base::ScopedFile,
+      size_t);
+
+  PosixSharedMemory(void* start, size_t size, base::ScopedFile);
+  PosixSharedMemory(const PosixSharedMemory&) = delete;
+  PosixSharedMemory& operator=(const PosixSharedMemory&) = delete;
+
+  void* const start_;
+  const size_t size_;
+  base::ScopedFile fd_;
+};
+
+}  // namespace perfetto
+
+#endif  // OS_LINUX || OS_ANDROID || OS_APPLE
+#endif  // SRC_TRACING_IPC_POSIX_SHARED_MEMORY_H_
+/*
+ * 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.
+ */

-      // Stop if this entry is for a later message or a later field.
-      if (entry.msg_index > current_msg_index ||
-          (entry.msg_index == current_msg_index && entry.field_id > field_id)) {
-        break;
-      }
+// gen_amalgamated expanded: #include "src/tracing/ipc/posix_shared_memory.h"

-      // Message indexes are dense and we verified above that we are sorted
-      // so this must be for the current message.
-      PERFETTO_DCHECK(entry.msg_index == current_msg_index);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)

-      // Exact match - this is an upgrade. Return the msg_id for the caller.
-      if (entry.field_id == field_id) {
-        ++overlay_idx;
-        return entry.message_id;
-      }
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>

-      // entry_field < field_id: This is a new field to ADD.
-      if (entry.field_id < kDirectlyIndexLimit) {
-        add_directly_indexed_field(entry.field_id, entry.message_id);
-      } else {
-        add_range(entry.field_id, entry.field_id + 1, entry.message_id);
-      }
-      ++overlay_idx;
-    }
-    // No match found.
-    return 0;
-  };
+#include <cinttypes>
+#include <memory>
+#include <utility>

-  bool is_eom = true;
-  for (size_t i = 0; i < words.size(); ++i) {
-    const uint32_t word = words[i];
-    const bool has_next_word = i < words.size() - 1;
-    const uint32_t opcode = word & kOpcodeMask;
-    const uint32_t field_id = word >> kOpcodeShift;
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_macros.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/temp_file.h"
+// gen_amalgamated expanded: #include "src/tracing/ipc/memfd.h"

-    is_eom = opcode == kFilterOpcode_EndOfMessage;
-    if (field_id == 0 && opcode != kFilterOpcode_EndOfMessage) {
-      PERFETTO_DLOG("bytecode error @ word %zu, invalid field id (0)", i);
-      return false;
-    }
+namespace perfetto {

-    if (opcode == kFilterOpcode_SimpleField ||
-        opcode == kFilterOpcode_NestedField ||
-        opcode == kFilterOpcode_FilterString ||
-        opcode == kFilterOpcode_FilterStringWithType) {
-      // Field words are organized as follow:
-      // MSB: 1 if allowed, 0 if not allowed.
-      // Remaining bits:
-      //   Message index in the case of nested (non-simple) messages.
-      //   0x7fff0000-0x7ffffffe for string fields with semantic type.
-      //   0x7ffffffe in the case of string fields which need filtering.
-      //   0x7fffffff in the case of simple fields.
-      uint32_t msg_id;
-      if (opcode == kFilterOpcode_SimpleField) {
-        msg_id = kSimpleField;
-      } else if (opcode == kFilterOpcode_FilterString) {
-        msg_id = kFilterStringField;
-      } else if (opcode == kFilterOpcode_FilterStringWithType) {
-        // The next word in the bytecode contains the semantic type.
-        if (!has_next_word) {
-          PERFETTO_DLOG(
-              "bytecode error @ word %zu: unterminated filter string with type",
-              i);
-          return false;
-        }
-        uint32_t semantic_type = words[++i];
-        msg_id =
-            kFilterStringFieldWithType | (semantic_type & kSemanticTypeMask);
-      } else {  // FILTER_OPCODE_NESTED_FIELD
-        // The next word in the bytecode contains the message index.
-        if (!has_next_word) {
-          PERFETTO_DLOG("bytecode error @ word %zu: unterminated nested field",
-                        i);
-          return false;
-        }
-        msg_id = words[++i];
-        max_msg_index = std::max(max_msg_index, msg_id);
-      }
+namespace {
+int kFileSeals = F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL;
+}  // namespace

-      // Process overlay: add any fields that come before this one, and check
-      // if this field should be upgraded.
-      uint32_t overlay_msg_id = process_overlay(field_id);
-      if (overlay_msg_id == kOverlayError) {
-        return false;
-      }
-      if (overlay_msg_id != 0) {
-        msg_id = overlay_msg_id;
-      }
+// static
+base::StatusOr<std::unique_ptr<PosixSharedMemory>> PosixSharedMemory::Create(
+    size_t size) {
+  if (size == 0)
+    return base::ErrStatus("Invalid shmem size 0");

-      if (field_id < kDirectlyIndexLimit) {
-        add_directly_indexed_field(field_id, msg_id);
-      } else {
-        // In the case of a large field id (rare) we waste an extra word and
-        // represent it as a range. Doesn't make sense to introduce extra
-        // complexity to deal with rare cases like this.
-        add_range(field_id, field_id + 1, msg_id);
-      }
-    } else if (opcode == kFilterOpcode_SimpleFieldRange) {
-      if (!has_next_word) {
-        PERFETTO_DLOG("bytecode error @ word %zu: unterminated range", i);
-        return false;
-      }
-      const uint32_t range_len = words[++i];
-      const uint32_t range_end = field_id + range_len;  // STL-style, excl.
-      uint32_t id = field_id;
+  base::ScopedFile fd =
+      CreateMemfd("perfetto_shmem", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+  const bool is_memfd = !!fd;
+  if (!is_memfd) {
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+    // In-tree builds only allow memfd, so the service can verify the seals.
+    // TODO: if this fails on Android we should fall back on ashmem.
+    return base::ErrStatus("memfd_create() failed: %s", strerror(errno));
+#else
+    ASSIGN_OR_RETURN(
+        base::TempFile temp_file,
+        base::TempFile::Create(base::TempFile::LinkMode::kUnlinked));
+    fd = temp_file.ReleaseFD();
+#endif
+  }

-      // Here's the subtle complexity: at the bytecode level, we don't know
-      // anything about the kDirectlyIndexLimit. It is legit to define a range
-      // that spans across the direct-indexing threshold (e.g. 126-132). In that
-      // case we want to add all the elements < the indexing to the O(1) bucket
-      // and add only the remaining range as a non-indexed range.
-      for (; id < range_end && id < kDirectlyIndexLimit; ++id)
-        add_directly_indexed_field(id, kAllowed | kSimpleField);
-      PERFETTO_DCHECK(id >= kDirectlyIndexLimit || id == range_end);
-      if (id < range_end)
-        add_range(id, range_end, kSimpleField);
-    } else if (opcode == kFilterOpcode_EndOfMessage) {
-      // Drain any remaining overlay entries for this message.
-      if (process_overlay(std::numeric_limits<uint32_t>::max()) ==
-          kOverlayError) {
-        return false;
-      }
+  if (ftruncate(fd.get(), static_cast<off_t>(size)) != 0)
+    return base::ErrStatus("Couldn't resize shmem FD: %s", strerror(errno));

-      // Verify that the ranges are non-overlapping. Assumes that the ranges
-      // are sorted (they are, because the bytecode is sorted).
-      for (size_t r = 0; r + 3 < ranges.size(); r += 3) {
-        const uint32_t prev_range_end = ranges[r + 1];
-        const uint32_t curr_range_start = ranges[r + 3];
-        if (curr_range_start < prev_range_end) {
-          PERFETTO_DLOG(
-              "bytecode error @ message %u: overlapping ranges [%u, %u) "
-              "and [%u, ...)",
-              current_msg_index, ranges[r], prev_range_end, curr_range_start);
-          return false;
-        }
-      }
+  if (is_memfd && fcntl(fd.get(), F_ADD_SEALS, kFileSeals) != 0)
+    return base::ErrStatus("Couldn't seal shmem FD: %s", strerror(errno));

-      // For each message append:
-      // 1. The "header" word telling how many directly indexed fields there
-      //    are.
-      // 2. The words for the directly indexed fields (id < 128).
-      // 3. The rest of the fields, encoded as ranges.
-      // Also update the |message_offset_| index to remember the word offset for
-      // the current message.
-      message_offset_.emplace_back(static_cast<uint32_t>(words_.size()));
-      words_.emplace_back(static_cast<uint32_t>(direct_indexed_fields.size()));
-      words_.insert(words_.end(), direct_indexed_fields.begin(),
-                    direct_indexed_fields.end());
-      words_.insert(words_.end(), ranges.begin(), ranges.end());
-      direct_indexed_fields.clear();
-      ranges.clear();
-      ++current_msg_index;
-    } else {
-      PERFETTO_DLOG("bytecode error @ word %zu: invalid opcode (%x)", i, word);
-      return false;
-    }
-  }  // (for word in bytecode).
+  return MapFD(std::move(fd), size);
+}

-  if (!is_eom) {
-    PERFETTO_DLOG(
-        "bytecode error: end of message not the last word in the bytecode");
-    return false;
-  }
+// static
+std::unique_ptr<PosixSharedMemory> PosixSharedMemory::AttachToFd(
+    base::ScopedFile fd,
+    bool require_seals_if_supported,
+    size_t max_size) {
+  if (!fd)
+    return nullptr;
+  bool requires_seals = require_seals_if_supported;

-  if (overlay_idx != overlay.size()) {
-    PERFETTO_DLOG("bytecode error: overlay contains %zu unconsumed entries",
-                  overlay.size() - overlay_idx);
-    return false;
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+  // In-tree kernels all support memfd.
+  PERFETTO_CHECK(HasMemfdSupport());
+#else
+  // In out-of-tree builds, we only require seals if the kernel supports memfd.
+  if (requires_seals)
+    requires_seals = HasMemfdSupport();
+#endif
+
+  if (requires_seals) {
+    // If the system supports memfd, we require a sealed memfd.
+    int res = fcntl(*fd, F_GET_SEALS);
+    if (res == -1 || (res & kFileSeals) != kFileSeals) {
+      PERFETTO_PLOG("Couldn't verify file seals on shmem FD");
+      return nullptr;
+    }
   }

-  if (max_msg_index > 0 && max_msg_index >= message_offset_.size()) {
-    PERFETTO_DLOG(
-        "bytecode error: a message index (%u) is out of range "
-        "(num_messages=%zu)",
-        max_msg_index, message_offset_.size());
-    return false;
+  struct stat stat_buf = {};
+  if (fstat(fd.get(), &stat_buf) != 0) {
+    PERFETTO_PLOG("Couldn't stat shmem FD");
+    return nullptr;
+  }
+  if (stat_buf.st_size <= 0 ||
+      static_cast<uint64_t>(stat_buf.st_size) > max_size) {
+    PERFETTO_ELOG("Invalid shmem FD size %" PRId64 " (max %zu)",
+                  static_cast<int64_t>(stat_buf.st_size), max_size);
+    return nullptr;
+  }
+  auto memory = MapFD(std::move(fd), static_cast<size_t>(stat_buf.st_size));
+  if (!memory.ok()) {
+    PERFETTO_ELOG("%s", memory.status().c_message());
+    return nullptr;
   }
+  return std::move(*memory);
+}

-  // Add a final entry to |message_offset_| so we can tell where the last
-  // message ends without an extra branch in the Query() hotpath.
-  message_offset_.emplace_back(static_cast<uint32_t>(words_.size()));
+// static
+base::StatusOr<std::unique_ptr<PosixSharedMemory>> PosixSharedMemory::MapFD(
+    base::ScopedFile fd,
+    size_t size) {
+  PERFETTO_DCHECK(fd);
+  PERFETTO_DCHECK(size > 0);
+  void* start =
+      mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0);
+  if (start == MAP_FAILED)
+    return base::ErrStatus("Couldn't map shmem FD: %s", strerror(errno));
+  return std::unique_ptr<PosixSharedMemory>(
+      new PosixSharedMemory(start, size, std::move(fd)));
+}

-  return true;
+PosixSharedMemory::PosixSharedMemory(void* start,
+                                     size_t size,
+                                     base::ScopedFile fd)
+    : start_(start), size_(size), fd_(std::move(fd)) {}
+
+PosixSharedMemory::~PosixSharedMemory() {
+  munmap(start(), size());
 }

-FilterBytecodeParser::QueryResult FilterBytecodeParser::Query(
-    uint32_t msg_index,
-    uint32_t field_id) const {
-  FilterBytecodeParser::QueryResult res{false, 0u, 0u};
-  if (static_cast<uint64_t>(msg_index) + 1 >=
-      static_cast<uint64_t>(message_offset_.size())) {
-    return res;
-  }
-  const uint32_t start_offset = message_offset_[msg_index];
-  // These are DCHECKs and not just CHECKS because the |words_| is populated
-  // by the LoadInternal call above. These cannot be violated with a malformed
-  // bytecode.
-  PERFETTO_DCHECK(start_offset < words_.size());
-  const uint32_t* word = &words_[start_offset];
-  const uint32_t end_off = message_offset_[msg_index + 1];
-  const uint32_t* const end = words_.data() + end_off;
-  PERFETTO_DCHECK(end > word && end <= words_.data() + words_.size());
-  const uint32_t num_directly_indexed = *(word++);
-  PERFETTO_DCHECK(num_directly_indexed <= kDirectlyIndexLimit);
-  PERFETTO_DCHECK(word + num_directly_indexed <= end);
-  uint32_t field_state = 0;
-  if (PERFETTO_LIKELY(field_id < num_directly_indexed)) {
-    PERFETTO_DCHECK(&word[field_id] < end);
-    field_state = word[field_id];
-  } else {
-    for (word = word + num_directly_indexed; word + 2 < end;) {
-      const uint32_t range_start = *(word++);
-      const uint32_t range_end = *(word++);
-      const uint32_t range_state = *(word++);
-      if (field_id >= range_start && field_id < range_end) {
-        field_state = range_state;
-        break;
-      }
-    }  // for (word in ranges)
-  }  // if (field_id >= num_directly_indexed)
+PosixSharedMemory::Factory::~Factory() {}

-  res.allowed = (field_state & kAllowed) != 0;
-  res.nested_msg_index = field_state & ~kAllowed;
-  // Extract semantic type if this is a FilterStringFieldWithType.
-  // Exclude kFilterStringField (0x7ffffffe) which doesn't have semantic type.
-  if ((res.nested_msg_index & kFilterStringFieldWithTypeMask) ==
-          kFilterStringFieldWithType &&
-      res.nested_msg_index != kFilterStringField) {
-    res.semantic_type = res.nested_msg_index & kSemanticTypeMask;
-  }
-  PERFETTO_DCHECK(!res.nested_msg_field() ||
-                  res.nested_msg_index < message_offset_.size() - 1);
-  return res;
+std::unique_ptr<SharedMemory> PosixSharedMemory::Factory::CreateSharedMemory(
+    size_t size) {
+  return PosixSharedMemory::Create(size).ValueOrDie();
 }

-}  // namespace protozero
-// gen_amalgamated begin source: src/protozero/filtering/string_filter.cc
-// gen_amalgamated begin header: src/protozero/filtering/string_filter.h
+}  // namespace perfetto
+
+#endif  // OS_LINUX || OS_ANDROID || OS_APPLE
+// gen_amalgamated begin source: src/tracing/ipc/shared_memory_windows.cc
+// gen_amalgamated begin header: src/tracing/ipc/shared_memory_windows.h
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -58211,136 +63331,217 @@ FilterBytecodeParser::QueryResult FilterBytecodeParser::Query(
  * limitations under the License.
  */

-#ifndef SRC_PROTOZERO_FILTERING_STRING_FILTER_H_
-#define SRC_PROTOZERO_FILTERING_STRING_FILTER_H_
+#ifndef SRC_TRACING_IPC_SHARED_MEMORY_WINDOWS_H_
+#define SRC_TRACING_IPC_SHARED_MEMORY_WINDOWS_H_
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)

-#include <array>
-#include <cstddef>
-#include <cstdint>
-#include <optional>
 #include <string>
-#include <string_view>
-#include <vector>

-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/regex.h"
-// gen_amalgamated expanded: #include "perfetto/public/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory.h"

-namespace protozero {
+namespace perfetto {

-// Performs filtering of strings in an "iptables" style. See the comments in
-// |TraceConfig.TraceFilter| for information on how this class works.
-class StringFilter {
+// Implements the SharedMemory and its factory for the Windows IPC transport.
+// This used only for standalone builds and NOT in chromium, which instead uses
+// a custom Mojo wrapper (MojoSharedMemory in chromium's //services/tracing/).
+class SharedMemoryWindows : public SharedMemory {
  public:
-  // Bitmask for semantic types. Supports up to 128 semantic types.
-  class SemanticTypeMask {
+  class Factory : public SharedMemory::Factory {
    public:
-    using Word = uint64_t;
-    static constexpr size_t kBitsPerWord = sizeof(Word) * 8;
-    static constexpr size_t kLimit = 128;
+    ~Factory() override;
+    std::unique_ptr<SharedMemory> CreateSharedMemory(size_t) override;
+  };

-    // Returns a SemanticTypeMask with only bit 0 set (UNSPECIFIED only).
-    // UNSPECIFIED is its own category and only matches if bit 0 is set.
-    static constexpr SemanticTypeMask Unspecified() {
-      return SemanticTypeMask(Word(1), Word(0));
-    }
+  // Create a brand new SHM region.
+  enum Flags { kNone = 0, kInheritableHandles };
+  static std::unique_ptr<SharedMemoryWindows> Create(
+      size_t size,
+      Flags flags = Flags::kNone);
+  static std::unique_ptr<SharedMemoryWindows> Attach(const std::string& key);
+  static std::unique_ptr<SharedMemoryWindows> AttachToHandleWithKey(
+      base::ScopedPlatformHandle fd,
+      const std::string& key);
+  ~SharedMemoryWindows() override;
+  const std::string& key() const { return key_; }
+  const base::ScopedPlatformHandle& handle() const { return handle_; }

-    // Creates a SemanticTypeMask from raw word values (for testing).
-    static constexpr SemanticTypeMask FromWords(Word w0, Word w1) {
-      return SemanticTypeMask(w0, w1);
-    }
+  // SharedMemory implementation.
+  using SharedMemory::start;  // Equal priority to const and non-const versions
+  const void* start() const override { return start_; }
+  size_t size() const override { return size_; }

-    // Default constructor: no bits set.
-    constexpr SemanticTypeMask() = default;
+ private:
+  SharedMemoryWindows(void* start,
+                      size_t size,
+                      std::string,
+                      base::ScopedPlatformHandle);
+  SharedMemoryWindows(const SharedMemoryWindows&) = delete;
+  SharedMemoryWindows& operator=(const SharedMemoryWindows&) = delete;

-    // Sets the bit for |semantic_type|.
-    PERFETTO_ALWAYS_INLINE void Set(uint32_t semantic_type) {
-      PERFETTO_DCHECK(semantic_type < kLimit);
-      uint32_t word_index = semantic_type / kBitsPerWord;
-      uint32_t bit_index = semantic_type % kBitsPerWord;
-      words_[word_index] |= Word(1) << bit_index;
-    }
+  void* const start_;
+  const size_t size_;
+  std::string key_;
+  base::ScopedPlatformHandle handle_;
+};

-    // Returns true if the bit for |semantic_type| is set.
-    PERFETTO_ALWAYS_INLINE bool IsSet(uint32_t semantic_type) const {
-      // If beyond supported range, return true (safe default: apply rule).
-      if (PERFETTO_UNLIKELY(semantic_type >= kLimit)) {
-        return true;
-      }
-      uint32_t word_index = semantic_type / kBitsPerWord;
-      uint32_t bit_index = semantic_type % kBitsPerWord;
-      return (words_[word_index] & (Word(1) << bit_index)) != 0;
-    }
+}  // namespace perfetto

-   private:
-    constexpr SemanticTypeMask(Word w0, Word w1) : words_{w0, w1} {}
+#endif  // OS_WIN

-    std::array<Word, 2> words_ = {};
-  };
+#endif  // SRC_TRACING_IPC_SHARED_MEMORY_WINDOWS_H_
+/*
+ * Copyright (C) 2021 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.
+ */

-  enum class Policy : uint8_t {
-    kMatchRedactGroups = 1,
-    kAtraceMatchRedactGroups = 2,
-    kMatchBreak = 3,
-    kAtraceMatchBreak = 4,
-    kAtraceRepeatedSearchRedactGroups = 5,
-  };
+// gen_amalgamated expanded: #include "src/tracing/ipc/shared_memory_windows.h"

-  // Adds a new rule for filtering strings.
-  //
-  // If `name` is non-empty and a rule with the same name already exists, it
-  // will be replaced; otherwise the rule is appended.
-  //
-  // `semantic_type_mask` is a bitmask indicating which semantic types this rule
-  // applies to. UNSPECIFIED (0) is its own category and only matches if bit 0
-  // is explicitly set. Defaults to matching only UNSPECIFIED.
-  void AddRule(
-      Policy policy,
-      std::string_view pattern,
-      std::string atrace_payload_starts_with,
-      std::string name = {},
-      SemanticTypeMask semantic_type_mask = SemanticTypeMask::Unspecified());
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+#include <memory>
+#include <random>
+
+#include <windows.h>
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+
+namespace perfetto {
+
+// static
+std::unique_ptr<SharedMemoryWindows> SharedMemoryWindows::Create(size_t size,
+                                                                 Flags flags) {
+  base::ScopedPlatformHandle shmem_handle;
+  std::random_device rnd_dev;
+  uint64_t rnd_key = (static_cast<uint64_t>(rnd_dev()) << 32) | rnd_dev();
+  std::string key = "perfetto_shm_" + base::Uint64ToHexStringNoPrefix(rnd_key);
+
+  SECURITY_ATTRIBUTES security_attributes = {};
+  security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+  if (flags & Flags::kInheritableHandles)
+    security_attributes.bInheritHandle = TRUE;
+
+  shmem_handle.reset(CreateFileMappingA(
+      INVALID_HANDLE_VALUE,  // Use paging file.
+      &security_attributes, PAGE_READWRITE,
+      static_cast<DWORD>(size >> 32),  // maximum object size (high-order DWORD)
+      static_cast<DWORD>(size),        // maximum object size (low-order DWORD)
+      key.c_str()));
+
+  if (!shmem_handle) {
+    PERFETTO_PLOG("CreateFileMapping() call failed");
+    return nullptr;
+  }
+  void* start =
+      MapViewOfFile(*shmem_handle, FILE_MAP_ALL_ACCESS, /*offsetHigh=*/0,
+                    /*offsetLow=*/0, size);
+  if (!start) {
+    PERFETTO_PLOG("MapViewOfFile() failed");
+    return nullptr;
+  }
+
+  return std::unique_ptr<SharedMemoryWindows>(new SharedMemoryWindows(
+      start, size, std::move(key), std::move(shmem_handle)));
+}
+
+// static
+std::unique_ptr<SharedMemoryWindows> SharedMemoryWindows::Attach(
+    const std::string& key) {
+  base::ScopedPlatformHandle shmem_handle;
+  shmem_handle.reset(
+      OpenFileMappingA(FILE_MAP_ALL_ACCESS, /*inherit=*/false, key.c_str()));
+  if (!shmem_handle) {
+    PERFETTO_PLOG("Failed to OpenFileMapping()");
+    return nullptr;
+  }
+
+  void* start =
+      MapViewOfFile(*shmem_handle, FILE_MAP_ALL_ACCESS, /*offsetHigh=*/0,
+                    /*offsetLow=*/0, /*dwNumberOfBytesToMap=*/0);
+  if (!start) {
+    PERFETTO_PLOG("MapViewOfFile() failed");
+    return nullptr;
+  }

-  // Tries to filter the given string. Returns true if the string was modified
-  // in any way, false otherwise. Uses semantic_type=0 (unspecified).
-  bool MaybeFilter(char* ptr, size_t len) const {
-    return MaybeFilter(ptr, len, /*semantic_type=*/0);
+  MEMORY_BASIC_INFORMATION info{};
+  if (!VirtualQuery(start, &info, sizeof(info))) {
+    PERFETTO_PLOG("VirtualQuery() failed");
+    return nullptr;
   }
+  size_t size = info.RegionSize;
+  return std::unique_ptr<SharedMemoryWindows>(
+      new SharedMemoryWindows(start, size, key, std::move(shmem_handle)));
+}

-  // Tries to filter the given string with a specific semantic type.
-  // Only rules that match the semantic type (or have no type restriction)
-  // are applied.
-  bool MaybeFilter(char* ptr, size_t len, uint32_t semantic_type) const {
-    if (len == 0 || rules_.empty()) {
-      return false;
-    }
-    return MaybeFilterInternal(ptr, len, semantic_type);
+// static
+std::unique_ptr<SharedMemoryWindows> SharedMemoryWindows::AttachToHandleWithKey(
+    base::ScopedPlatformHandle shmem_handle,
+    const std::string& key) {
+  void* start =
+      MapViewOfFile(*shmem_handle, FILE_MAP_ALL_ACCESS, /*offsetHigh=*/0,
+                    /*offsetLow=*/0, /*dwNumberOfBytesToMap=*/0);
+  if (!start) {
+    PERFETTO_PLOG("MapViewOfFile() failed");
+    return nullptr;
   }

- private:
-  struct Rule {
-    Policy policy;
-    std::optional<perfetto::base::CopyableRegex> pattern;
-    std::string atrace_payload_starts_with;
-    std::string name;
-    SemanticTypeMask semantic_type_mask = SemanticTypeMask::Unspecified();
-  };
+  MEMORY_BASIC_INFORMATION info{};
+  if (!VirtualQuery(start, &info, sizeof(info))) {
+    PERFETTO_PLOG("VirtualQuery() failed");
+    return nullptr;
+  }
+  size_t size = info.RegionSize;

-  bool MaybeFilterInternal(char* ptr, size_t len, uint32_t semantic_type) const;
+  return std::unique_ptr<SharedMemoryWindows>(
+      new SharedMemoryWindows(start, size, key, std::move(shmem_handle)));
+}

-  // All rules, in the order they were added.
-  std::vector<Rule> rules_;
+SharedMemoryWindows::SharedMemoryWindows(void* start,
+                                         size_t size,
+                                         std::string key,
+                                         base::ScopedPlatformHandle handle)
+    : start_(start),
+      size_(size),
+      key_(std::move(key)),
+      handle_(std::move(handle)) {}

-  // Reusable buffer for regex match groups, avoiding heap allocations on each
-  // filter call.
-  mutable std::vector<std::string_view> matches_;
-};
+SharedMemoryWindows::~SharedMemoryWindows() {
+  if (start_)
+    UnmapViewOfFile(start_);
+}

-}  // namespace protozero
+SharedMemoryWindows::Factory::~Factory() = default;

-#endif  // SRC_PROTOZERO_FILTERING_STRING_FILTER_H_
+std::unique_ptr<SharedMemory> SharedMemoryWindows::Factory::CreateSharedMemory(
+    size_t size) {
+  return SharedMemoryWindows::Create(size);
+}
+
+}  // namespace perfetto
+
+#endif  // !OS_WIN
+// gen_amalgamated begin source: src/tracing/v2/producer_ring_buffer_arbiter.cc
+// gen_amalgamated begin header: src/tracing/v2/producer_ring_buffer_arbiter.h
+// gen_amalgamated begin header: src/tracing/v2/shared_ring_buffer.h
+// gen_amalgamated begin header: src/tracing/v2/shared_ring_buffer_abi.h
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -58355,398 +63556,870 @@ class StringFilter {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/protozero/filtering/string_filter.h"
+#ifndef SRC_TRACING_V2_SHARED_RING_BUFFER_ABI_H_
+#define SRC_TRACING_V2_SHARED_RING_BUFFER_ABI_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>

 #include <algorithm>
-#include <cstdint>
-#include <cstring>
-#include <string>
-#include <string_view>
-#include <utility>
-#include <vector>
+#include <atomic>
+#include <optional>

-// gen_amalgamated expanded: #include "perfetto/public/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/status.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/bits.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/basic_types.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+// gen_amalgamated expanded: #include "src/tracing/ipc/memfd.h"

-namespace protozero {
-namespace {
+namespace perfetto::tracing_v2 {

-constexpr std::string_view kRedacted = "P60REDACTED";
-constexpr char kRedactedDash = '-';
+// Shared-memory ABI for a tracing-v2 producer ring buffer.
+// Protocol: RFC 0046,
+// https://github.com/google/perfetto/discussions/7120.
+// Parent design: RFC 0014, https://github.com/google/perfetto/discussions/4508.
+//
+// Several trace writers can write at once. One reader drains their data in
+// reservation order. A relocated fragment gets a later reservation.
+//
+// The ring buffer has one header followed by fixed-size chunks:
+// - The header holds the read and write positions.
+// - Each chunk starts with an atomic state word. It records ownership and the
+//   number of published fragments while the chunk belongs to a writer.
+//
+// The ABI assumes little-endian producer and service processes.

-// Returns a pointer to the first character after the tgid pipe character in
-// the atrace string given by [ptr, end). Returns null if no such character
-// exists.
+// Sharing over IPC
+// ----------------
 //
-// Examples:
-// E|1024 -> nullptr
-// foobarbaz -> nullptr
-// B|1024|x -> pointer to x
-PERFETTO_ALWAYS_INLINE const char* FindAtracePayloadPtr(const char* ptr,
-                                                        const char* end) {
-  // Don't even bother checking any strings which are so short that they could
-  // not contain a post-tgid section. This filters out strings like "E|" which
-  // emitted by Bionic.
-  //
-  // Also filter out any other strings starting with "E" as they never contain
-  // anything past the tgid: this removes >half of the strings for ~zero cost.
-  static constexpr size_t kEarliestSecondPipeIndex = 2;
-  const char* search_start = ptr + kEarliestSecondPipeIndex;
-  if (PERFETTO_UNLIKELY(search_start >= end || *ptr == 'E')) {
-    return nullptr;
-  }
+// A producer in another process shares the ring buffer with the service as a
+// sealed memfd. Without memfd, IPC producers and services use only v1.
+//
+// IPC code uses the two checks below, not memfd directly. If tracing v2 ever
+// needs more from the platform, only these checks change.

-  // We skipped past the first '|' character by starting at the character at
-  // index 2. Just find the next pipe character (i.e. the one after tgid) using
-  // memchr.
-  const char* pipe = static_cast<const char*>(
-      memchr(search_start, '|', size_t(end - search_start)));
-  return pipe ? pipe + 1 : nullptr;
+// Build-time check. True if the build has the memfd code.
+// It guards the code that shares the memfd. Part of that code, such as
+// PosixSharedMemory, does not build on Windows.
+#define PERFETTO_TRACING_V2_IPC() PERFETTO_MEMFD_ENABLED()
+
+// Runtime check. True if the kernel supports memfd.
+// Always false when PERFETTO_TRACING_V2_IPC() is false.
+//
+// Only IPC code may call this. HasMemfdSupport() is in src/tracing/ipc:common,
+// which only IPC builds have.
+inline bool IpcSupportsTracingV2() {
+  return HasMemfdSupport();
 }

-PERFETTO_ALWAYS_INLINE bool StartsWith(const char* ptr,
-                                       const char* end,
-                                       const std::string& starts_with) {
-  // Verify that the atrace string has enough characters to match against all
-  // the characters in the "starts with" string.
-  size_t len = starts_with.size();
-  if (PERFETTO_UNLIKELY(ptr + len > end))
-    return false;
+// Shared-memory layout
+// --------------------
+//
+//   +------------------------+---------+---------+---------+-----+
+//   | RingBufferHeader, 64 B | chunk 0 | chunk 1 | chunk 2 | ... |
+//   +------------------------+---------+---------+---------+-----+
+//   0                        64

-  // Empty string matches everything.
-  if (PERFETTO_UNLIKELY(len == 0))
-    return true;
+// Chunks must hold several small fragments to amortize their header overhead.
+constexpr uint32_t kMinChunkSize = 256;

-  // Quick rejection: check first character before expensive memcmp.
-  // This is very effective since most strings don't match.
-  if (PERFETTO_LIKELY(*ptr != *starts_with.data()))
-    return false;
+// Maximum chunk size, including the header. The service copies a chunk
+// without its header (at least 4 bytes) into one TBChunk, which holds at most
+// 64 KiB - 1 bytes.
+constexpr uint32_t kMaxChunkSize = 64 * 1024;

-  // If first char matches, do full memcmp for remaining characters.
-  return memcmp(ptr + 1, starts_with.data() + 1, len - 1) == 0;
-}
+// Each chunk's atomic<uint32_t> requires four-byte alignment.
+constexpr uint32_t kChunkAlignmentBytes = 4;

-void RedactMatches(const std::vector<std::string_view>& matches) {
-  // Go through every group in the matches.
-  for (size_t i = 1; i < matches.size(); ++i) {
-    const auto& match = matches[i];
-    // Skip unmatched optional groups: empty string_view may have nullptr
-    // data(), and passing nullptr to memcpy/memset is UB even with size 0.
-    if (match.empty())
-      continue;
+// Minimum supported chunk count.
+constexpr uint32_t kMinChunksPerRing = 2;

-    // Overwrite the match with characters from |kRedacted|. If match is
-    // smaller, we will not use all of |kRedacted| but that's fine (i.e. we
-    // will overwrite with a truncated |kRedacted|).
-    size_t match_len = match.size();
-    size_t redacted_len = std::min(match_len, kRedacted.size());
-    memcpy(const_cast<char*>(match.data()), kRedacted.data(), redacted_len);
+// Chunk area size (excluding the header) used when the SMB size hint is 0.
+constexpr size_t kDefaultChunkBudgetBytes = 128 * 1024;

-    // Overwrite any characters after |kRedacted| with |kRedactedDash|.
-    memset(const_cast<char*>(match.data()) + redacted_len, kRedactedDash,
-           match_len - redacted_len);
+// Ring buffer header
+// ------------------
+//
+//   byte offset
+//   0              4              8              12               64
+//   +--------------+--------------+--------------+----------------+
+//   |   read_pos   |  write_pos   | num_writers_ |    reserved    |
+//   |              |              |   waiting    |                |
+//   +--------------+--------------+--------------+----------------+
+//   |<------- rw_positions ------>|<- atomic32 ->|
+//          atomic<uint64_t>
+//
+// Writers check capacity by loading rw_positions once. In memory:
+// - The first four bytes hold read_pos.
+// - The next four bytes hold write_pos.
+// Keeping both in one atomic avoids combining counters read at different times.
+//
+// The reader is the only one that moves read_pos. It publishes a new value once
+// per drain pass and then wakes any writer parked on a full ring buffer.
+// read_pos is also the first four bytes of rw_positions, which is the address
+// the futex waits on.
+//
+// num_writers_waiting lets the reader skip a futex wake when nobody is waiting
+// for space. It is only an optimization and never decides whether the ring
+// buffer is full or who owns a chunk.
+//
+// Bytes 12..63 pad the header to one cache line.
+//
+static_assert(sizeof(std::atomic<uint32_t>) == 4 &&
+                  alignof(std::atomic<uint32_t>) <= 4,
+              "Chunk state must fit in a 4-byte-aligned ABI word");
+static_assert(sizeof(std::atomic<uint64_t>) == 8,
+              "The packed positions must occupy 8 bytes");
+static_assert(std::atomic<uint32_t>::is_always_lock_free &&
+                  std::atomic<uint64_t>::is_always_lock_free,
+              "Shared-memory atomics must be lock-free");
+
+struct alignas(64) RingBufferHeader {
+  std::atomic<uint64_t> rw_positions;
+  std::atomic<uint32_t> num_writers_waiting;
+  uint8_t reserved[52];
+};
+
+static_assert(offsetof(RingBufferHeader, num_writers_waiting) == 8 &&
+                  offsetof(RingBufferHeader, reserved) == 12 &&
+                  sizeof(RingBufferHeader) == 64,
+              "RingBufferHeader does not match the shared-memory ABI");
+
+// Logical positions and chunk indexing
+// ------------------------------------
+//
+// write_pos is the next position a writer can reserve. read_pos is the next
+// position the reader must consume. These are logical positions:
+// - They keep advancing past num_chunks across successive traversals.
+// - As uint32_t counters, they roll over from UINT32_MAX to zero.
+// - Both reader and writer map a position to a physical chunk before access.
+//   ChunkIndex::FromPosition() performs that mapping modulo num_chunks.
+//
+// A writer reserves a position by advancing write_pos, then tries to claim
+// its physical chunk.
+//
+// The number of reserved positions not yet handled by the reader is:
+//
+//   outstanding = uint32_t(write_pos - read_pos) <= num_chunks < 2^31
+//
+// - If outstanding is zero, write_pos == read_pos and the ring buffer is empty.
+// - If outstanding equals num_chunks, the ring buffer is full:
+//   write_pos == uint32_t(read_pos + num_chunks). Writers cannot advance
+//   write_pos until the reader advances read_pos to make space.
+//
+// Unsigned subtraction also works when write_pos wraps back to zero. For
+// example:
+//
+//   read_pos  = UINT32_MAX - 3
+//   write_pos = 2
+//   uint32_t(write_pos - read_pos) = 6
+//
+// A legal result is at most num_chunks. A larger result means that the two
+// positions do not describe a valid ring buffer state.
+//
+// At most one outstanding reservation maps to each physical chunk. An exact
+// comparison of the chunk's state word therefore determines ownership.
+// A delayed claim can match a later traversal after the wrap count repeats.
+// See WrapCountForPosition() for this limit.
+
+// The unsigned subtraction above is unambiguous only while fewer than 2^31
+// positions are outstanding. num_chunks is a power of two, so 2^30 is the
+// largest legal chunk count.
+constexpr uint32_t kMaxChunksPerRing = 1u << 30;
+
+// True if |chunk_size| is in [kMinChunkSize, kMaxChunkSize] and a multiple of
+// kChunkAlignmentBytes.
+constexpr bool IsValidChunkSize(uint32_t chunk_size) {
+  return chunk_size >= kMinChunkSize && chunk_size <= kMaxChunkSize &&
+         chunk_size % kChunkAlignmentBytes == 0;
+}
+
+// Validates the layout of an untrusted ring buffer and returns its chunk
+// count, or an error describing why the layout is invalid.
+// The service calls it on the ring buffer that a producer attaches.
+//
+// - Any thread can call it. It reads no shared bytes.
+// - The transport limits the mapping size separately.
+// - The SharedRingBuffer constructor treats validation errors as fatal.
+// - RingBufferSizeForShmSizeHint() works the other way, it picks a size that
+//   passes this check.
+inline base::StatusOr<uint32_t> NumChunksForRingBufferLayout(
+    const void* start,
+    size_t size,
+    uint32_t chunk_size) {
+  if (!start ||
+      reinterpret_cast<uintptr_t>(start) % alignof(RingBufferHeader) != 0) {
+    return base::ErrStatus("ring buffer start is null or misaligned");
+  }
+  if (!IsValidChunkSize(chunk_size))
+    return base::ErrStatus("invalid chunk size %u", chunk_size);
+  // Subtract the header after this check to avoid overflow on 32-bit builds.
+  if (size < sizeof(RingBufferHeader))
+    return base::ErrStatus("ring buffer size %zu is below the header", size);
+  const size_t chunks_size = size - sizeof(RingBufferHeader);
+  if (chunks_size % chunk_size != 0) {
+    return base::ErrStatus("ring buffer size %zu is not header + N * %u", size,
+                           chunk_size);
+  }
+  const size_t count = chunks_size / chunk_size;
+  // Two chunks form the minimum useful configuration. One chunk satisfies
+  // the ABI: write_pos - read_pos is 0 when empty and 1 when full.
+  // The Free wrap count distinguishes successive uses of that chunk.
+  //
+  // The power-of-two rule permits chunk indexing with a mask. The maximum
+  // keeps outstanding positions below 2^31 for unambiguous unsigned
+  // subtraction.
+  if (count < kMinChunksPerRing || count > kMaxChunksPerRing ||
+      !base::IsPowerOfTwo(count)) {
+    return base::ErrStatus("invalid chunk count %zu", count);
   }
+  return static_cast<uint32_t>(count);
 }

-}  // namespace
+// Picks the size of a new ring buffer.
+// The producer calls this before it allocates its ring buffer, to turn a byte
+// size hint into a size that NumChunksForRingBufferLayout() accepts.
+//
+// - Returns the size, header included, of the largest valid ring buffer
+//   whose chunks fit in |shmem_size_hint_bytes| bytes.
+// - Uses kDefaultChunkBudgetBytes if |shmem_size_hint_bytes| is zero.
+// - The header comes on top of the hint.
+//   The returned size is capped at |max_shmem_size_bytes|.
+// - Returns an error status if fewer than kMinChunksPerRing chunks fit, or if
+//   |chunk_size| is invalid.
+// - Any thread can call it, because it reads no shared memory.
+inline base::StatusOr<size_t> RingBufferSizeForShmSizeHint(
+    size_t shmem_size_hint_bytes,
+    size_t max_shmem_size_bytes,
+    uint32_t chunk_size) {
+  if (!IsValidChunkSize(chunk_size))
+    return base::ErrStatus("invalid chunk size %u", chunk_size);
+  if (max_shmem_size_bytes < sizeof(RingBufferHeader)) {
+    return base::ErrStatus("maximum size %zu is below the header",
+                           max_shmem_size_bytes);
+  }
+
+  const size_t available_chunk_bytes = std::min(
+      shmem_size_hint_bytes ? shmem_size_hint_bytes : kDefaultChunkBudgetBytes,
+      max_shmem_size_bytes - sizeof(RingBufferHeader));
+  const size_t max_count = available_chunk_bytes / chunk_size;
+
+  // Find the largest power of two that is at most |max_count|: start at
+  // kMaxChunksPerRing, a power of two, and halve it until it fits.
+  // Every value on the way is a power of two and at most the maximum, so only
+  // the minimum chunk count is left to check.
+  static_assert(base::IsPowerOfTwo(kMaxChunksPerRing));
+  size_t count = kMaxChunksPerRing;
+  while (count > max_count)
+    count /= 2;
+
+  if (count < kMinChunksPerRing) {
+    return base::ErrStatus("no ring buffer of %u-byte chunks fits in %zu bytes",
+                           chunk_size, available_chunk_bytes);
+  }
+  const size_t size = sizeof(RingBufferHeader) + count * chunk_size;
+  PERFETTO_CHECK(size <= max_shmem_size_bytes);
+  return size;
+}

-void StringFilter::AddRule(Policy policy,
-                           std::string_view pattern_str,
-                           std::string atrace_payload_starts_with,
-                           std::string name,
-                           SemanticTypeMask semantic_type_mask) {
-  perfetto::base::CopyableRegex re(
-      perfetto::base::Regex::CreateOrCheck(std::string(pattern_str)));
-  Rule new_rule{policy, std::move(re), std::move(atrace_payload_starts_with),
-                std::move(name), semantic_type_mask};
-  // If name is non-empty, look for existing rule with same name and replace.
-  if (!new_rule.name.empty()) {
-    for (Rule& existing : rules_) {
-      if (existing.name == new_rule.name) {
-        existing = std::move(new_rule);
-        return;
-      }
-    }
-  }
-  rules_.push_back(std::move(new_rule));
+constexpr uint64_t PackRwPositions(uint32_t write_pos, uint32_t read_pos) {
+  return (static_cast<uint64_t>(write_pos) << 32) | read_pos;
 }

-bool StringFilter::MaybeFilterInternal(char* ptr,
-                                       size_t len,
-                                       uint32_t semantic_type) const {
-  bool atrace_find_tried = false;
-  const char* atrace_payload_ptr = nullptr;
-  std::string_view input(ptr, len);
+constexpr uint32_t WritePosOf(uint64_t rw_positions) {
+  return static_cast<uint32_t>(rw_positions >> 32);
+}

-  for (const Rule& rule : rules_) {
-    if (!rule.semantic_type_mask.IsSet(semantic_type)) {
-      continue;
-    }
-    switch (rule.policy) {
-      case Policy::kMatchBreak:
-        if (PERFETTO_UNLIKELY(rule.pattern->FullMatch(input)))
-          return false;
-        break;
-      case Policy::kMatchRedactGroups:
-        if (PERFETTO_UNLIKELY(
-                rule.pattern->FullMatchWithGroups(input, matches_))) {
-          RedactMatches(matches_);
-          return true;
-        }
-        break;
-      case Policy::kAtraceMatchBreak:
-        atrace_payload_ptr = atrace_find_tried
-                                 ? atrace_payload_ptr
-                                 : FindAtracePayloadPtr(ptr, ptr + len);
-        atrace_find_tried = true;
-        if (atrace_payload_ptr &&
-            StartsWith(atrace_payload_ptr, ptr + len,
-                       rule.atrace_payload_starts_with) &&
-            rule.pattern->FullMatch(input)) {
-          return false;
-        }
-        break;
-      case Policy::kAtraceMatchRedactGroups:
-        atrace_payload_ptr = atrace_find_tried
-                                 ? atrace_payload_ptr
-                                 : FindAtracePayloadPtr(ptr, ptr + len);
-        atrace_find_tried = true;
-        if (atrace_payload_ptr &&
-            StartsWith(atrace_payload_ptr, ptr + len,
-                       rule.atrace_payload_starts_with) &&
-            PERFETTO_UNLIKELY(
-                rule.pattern->FullMatchWithGroups(input, matches_))) {
-          RedactMatches(matches_);
-          return true;
-        }
-        break;
-      case Policy::kAtraceRepeatedSearchRedactGroups:
-        atrace_payload_ptr = atrace_find_tried
-                                 ? atrace_payload_ptr
-                                 : FindAtracePayloadPtr(ptr, ptr + len);
-        atrace_find_tried = true;
-        if (atrace_payload_ptr && StartsWith(atrace_payload_ptr, ptr + len,
-                                             rule.atrace_payload_starts_with)) {
-          bool has_any_matches = false;
-          for (auto iter = rule.pattern->PartialMatchAll(input);
-               iter.NextWithGroups(matches_);) {
-            RedactMatches(matches_);
-            has_any_matches = true;
-          }
-          if (has_any_matches) {
-            return true;
-          }
-        }
-        break;
-    }
-  }
-  return false;
+constexpr uint32_t ReadPosOf(uint64_t rw_positions) {
+  return static_cast<uint32_t>(rw_positions);
 }

-}  // namespace protozero
-// gen_amalgamated begin source: src/protozero/filtering/message_filter.cc
-// gen_amalgamated begin header: src/protozero/filtering/message_filter.h
-// gen_amalgamated begin header: src/protozero/filtering/message_tokenizer.h
-/*
- * Copyright (C) 2021 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.
- */
+// Replaces the read half of a previously loaded rw_positions value, leaving
+// write_pos unchanged. The reader publishes read_pos this way.
+constexpr uint64_t ReplaceReadPos(uint64_t rw_positions, uint32_t read_pos) {
+  return PackRwPositions(WritePosOf(rw_positions), read_pos);
+}

-#ifndef SRC_PROTOZERO_FILTERING_MESSAGE_TOKENIZER_H_
-#define SRC_PROTOZERO_FILTERING_MESSAGE_TOKENIZER_H_
+constexpr uint32_t NumOutstandingPositions(uint32_t write_pos,
+                                           uint32_t read_pos) {
+  return write_pos - read_pos;
+}

-#include <stdint.h>
+// A physical chunk index. Variable names distinguish indices from positions:
+// - *_idx selects a chunk in memory, from 0 to num_chunks - 1.
+//   Successive positions cycle through these indices, then return to zero.
+// - *_pos identifies a reservation in logical order.
+//   It keeps advancing across traversals, past num_chunks.
+//   It rolls over from UINT32_MAX to zero.
+//
+// For a ring buffer with four chunks:
+//
+//   chunk_pos: 0 1 2 3 4 5 6 7 8
+//   chunk_idx: 0 1 2 3 0 1 2 3 0
+class ChunkIndex {
+ public:
+  // Maps a logical position to its physical chunk in a validated ring buffer.
+  //
+  // num_chunks = 2^k, so the low k bits of a position select the physical chunk
+  // and the remaining bits count completed traversals:
+  //
+  //             bits 0..k-1                  bits k..31
+  //   +----------------------------+----------------------------+
+  //   |    physical chunk index    |      traversal number      |
+  //   +----------------------------+----------------------------+
+  //               k bits                     32 - k bits
+  //
+  // For example, an eight-chunk ring buffer has three chunk-index bits. The low
+  // three bits select chunks 0 through 7. The remaining 29 bits count completed
+  // traversals.
+  static constexpr ChunkIndex FromPosition(uint32_t chunk_pos,
+                                           uint32_t num_chunks) {
+    return ChunkIndex{chunk_pos & (num_chunks - 1)};
+  }

-// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+  // Builds from an existing physical chunk index.
+  // Uses |chunk_idx| unchanged, without applying modulo num_chunks.
+  static constexpr ChunkIndex FromIndex(uint32_t chunk_idx) {
+    return ChunkIndex{chunk_idx};
+  }

-namespace protozero {
+  constexpr uint32_t value() const { return chunk_idx_; }

-// A helper class for schema-less tokenizing of protobuf messages.
-// This class takes a stream of proto-encoded bytes, pushed one by one in input
-// via Push(octet), and returns a stream of tokens (each Push() call can return
-// 0 or 1 token).
-// A "token" contains metadata about a field, specifically: its ID, its wire
-// type and:
-//  - For varint and fixed32/64 fields: its payload.
-//  - For string and bytes fields: the length of its payload.
-//    In this case the caller is supposed to "eat" those N bytes before calling
-//    Push() again.
-// Note that this class cannot differentiate between a string/bytes field or
-// a submessage, because they are encoded in the same way. The caller is
-// supposed to know whether a field can be recursed into by just keep calling
-// Push() or is a string that should be skipped.
-// This is inline to allow the compiler to see through the Push method and
-// avoid a function call for each byte.
-class MessageTokenizer {
- public:
-  struct Token {
-    uint32_t field_id;  // 0 == not valid.
-    proto_utils::ProtoWireType type;
+ private:
+  explicit constexpr ChunkIndex(uint32_t chunk_idx) : chunk_idx_(chunk_idx) {}

-    // For kLengthDelimited, |value| represent the length of the payload.
-    uint64_t value;
+  uint32_t chunk_idx_;
+};

-    inline bool valid() const { return field_id != 0; }
-    bool operator==(const Token& o) const {
-      return field_id == o.field_id && type == o.type && value == o.value;
-    }
-  };
+// Chunk state word
+// ----------------
+//
+// Each chunk starts with a 32-bit atomic state word (std::atomic<uint32_t>).
+//
+// There are five states:
+// 1. kFree
+// 2. kBeingWritten
+// 3. kComplete
+// 4. kRewriteRequested
+// 5. kRewriteAcknowledged
+//
+// The low control byte (8 bits) determines how to interpret the upper 24 bits:
+// - Free has 8 unused bits that must be zero, followed by wrap_count (16 bits).
+// - BeingWritten, Complete and RewriteRequested carry num_fragments (8 bits)
+//   and WriterID (16 bits).
+// - RewriteAcknowledged has no other fields so its upper bits are all zero.
+//
+// A Free word contains:
+//
+//   +------------------+------------------+------------------+
+//   |      byte 0      |      byte 1      |    bytes 2-3     |
+//   +------------------+------------------+------------------+
+//   |   control = 0    | num_fragments = 0|    wrap_count    |
+//   | (Free, format 0, |                  |                  |
+//   |    no flags)     |                  |                  |
+//   +------------------+------------------+------------------+
+//
+// On the first traversal, wrap_count is zero and every chunk's Free word is
+// zero. A zero-filled ring buffer therefore starts with all chunks Free and
+// read_pos == write_pos == 0. Later traversals use wrap_count << 16 as the
+// Free word.
+//
+// BeingWritten, Complete and RewriteRequested contain:
+//
+//   +------------------+------------------+------------------+
+//   |      byte 0      |      byte 1      |    bytes 2-3     |
+//   +------------------+------------------+------------------+
+//   |   control byte   |  num_fragments   |     WriterID     |
+//   +------------------+------------------+------------------+
+//
+// The control byte is:
+//
+//   +---------+---------+---------+---------+---------+
+//   |bits 0-2 |bits 3-4 |  bit 5  |  bit 6  |  bit 7  |
+//   +---------+---------+---------+---------+---------+
+//   |  state  | format  |  data   |continues|continues|
+//   |         |         |  loss   | on next |from prev|
+//   +---------+---------+---------+---------+---------+
+//
+// RewriteAcknowledged carries no other fields so every other bit is zero.
+
+enum class ChunkState : uint32_t {
+  // No writer owns this chunk:
+  // - A writer whose reserved position selects this chunk and has the same
+  //   wrap count can change Free to BeingWritten before writing.
+  // - To consume an unclaimed reservation, the reader must first advance the
+  //   wrap count atomically. The delayed writer's claim then fails.
+  kFree = 0,
+
+  // One writer owns the chunk:
+  // - The first N = num_fragments fragments are published and can be read.
+  // - The writer may be appending fragment N + 1 after those published bytes.
+  //
+  // The writer publishes by changing BeingWritten to Complete. The reader
+  // can instead copy the first N fragments and request a rewrite, without
+  // waiting for the writer to finish.
+  kBeingWritten = 1,
+
+  // The writer has published all num_fragments fragments and stopped writing.
+  // The reader and writer can now race to change the state:
+  // - The reader consumes the fragments and changes Complete to Free.
+  // - The same writer changes Complete back to BeingWritten to append more.
+  // If the reader wins, the writer drops its cached handle to the chunk.
+  kComplete = 2,
+
+  // A partial chunk read won the race against the writer's publication:
+  // - The reader consumed N = num_fragments and can move on.
+  // - The writer still owns the chunk and may be writing fragment N + 1.
+  //
+  // The writer notices the request when it tries to publish. It must save
+  // any unpublished fragment before acknowledging. The reader cannot free
+  // the chunk until that acknowledgement.
+  kRewriteRequested = 3,
+
+  // The writer notices RewriteRequested when it next tries to publish:
+  // - It saves any unpublished fragment in private memory.
+  // - It changes the old chunk to RewriteAcknowledged and stops touching it.
+  //
+  // Acknowledgement happens before waiting for a replacement chunk.
+  // Only the reader changes RewriteAcknowledged to Free, when it next visits
+  // this chunk on a later traversal.
+  kRewriteAcknowledged = 4,
+
+  // A reader that does not know a state cannot tell who owns the chunk. It
+  // stops rather than reclaiming it.
+  kReserved5 = 5,
+  kReserved6 = 6,
+  kReserved7 = 7,
+};

-  // Pushes a byte in input and returns a token, only when getting to the last
-  // byte of each field. Specifically:
-  // - For varint and fixed32 fields, the Token is returned after the last byte
-  //   of the numeric payload is pushed.
-  // - For length-delimited fields, this returns after the last byte of the
-  //   length is pushed (i.e. right before the payload starts). The caller is
-  //   expected to either skip the next |value| bytes (in the case of a string
-  //   or bytes fields) or keep calling Push, in the case of a submessage.
-  inline Token Push(uint8_t octet) {
-    using protozero::proto_utils::ProtoWireType;
+enum class ChunkFormat : uint32_t {
+  // Bytes 4..5 contain a little-endian target BufferID. Payload starts at 6.
+  kTargetBuffer = 0,

-    // Parsing a fixed32/64 field is the only case where we don't have to do
-    // any varint decoding. This is why this block is before the remaining
-    // switch statement below (all the rest is a varint).
-    if (PERFETTO_UNLIKELY(state_ == kFixedIntValue)) {
-      PERFETTO_DCHECK(fixed_int_bits_ == 32 || fixed_int_bits_ == 64);
-      fixed_int_value_ |= static_cast<uint64_t>(octet) << fixed_int_shift_;
-      fixed_int_shift_ += 8;
-      if (fixed_int_shift_ < fixed_int_bits_)
-        return Token{};  // Intermediate byte of a fixed32/64.
-      auto wire_type = fixed_int_bits_ == 32 ? ProtoWireType::kFixed32
-                                             : ProtoWireType::kFixed64;
-      uint64_t fixed_int_value = fixed_int_value_;
-      fixed_int_value_ = fixed_int_shift_ = fixed_int_bits_ = 0;
-      state_ = kFieldPreamble;
-      return Token{field_id_, wire_type, fixed_int_value};
-    }
+  // Reserved for a format carrying per-packet routing information.
+  kReservedRouting = 1,
+  kReserved2 = 2,
+  kReserved3 = 3,
+};

-    // At this point either we are: (i) parsing a field preamble; (ii) parsing a
-    // varint field paylod; (iii) parsing the length of a length-delimited
-    // field. In all cases, we need to decode a varint before proceeding.
-    varint_ |= static_cast<uint64_t>(octet & 0x7F) << varint_shift_;
-    if (octet & 0x80) {
-      varint_shift_ += 7;
-      if (PERFETTO_UNLIKELY(varint_shift_ >= 64)) {
-        varint_shift_ = 0;
-        state_ = kInvalidVarInt;
-      }
-      return Token{};  // Still parsing a varint.
-    }
+// Field widths and positions in the 32-bit word. The low byte is the control
+// byte, followed by the fragment count and WriterID.
+constexpr uint32_t kPayloadFlagsBits = 3;
+constexpr uint32_t kChunkFormatBits = 2;
+constexpr uint32_t kChunkStateBits = 3;
+constexpr uint32_t kNumFragmentsBits = 8;
+constexpr uint32_t kWriterIDBits = 16;
+
+constexpr uint32_t kChunkStateShift = 0;
+constexpr uint32_t kChunkFormatShift = kChunkStateShift + kChunkStateBits;
+constexpr uint32_t kPayloadFlagsShift = kChunkFormatShift + kChunkFormatBits;
+constexpr uint32_t kNumFragmentsShift = kPayloadFlagsShift + kPayloadFlagsBits;
+constexpr uint32_t kWriterIDShift = kNumFragmentsShift + kNumFragmentsBits;
+
+constexpr uint32_t kChunkStateMask = (1u << kChunkStateBits) - 1;
+constexpr uint32_t kChunkFormatMask = ((1u << kChunkFormatBits) - 1)
+                                      << kChunkFormatShift;
+constexpr uint32_t kPayloadFlagsMask = ((1u << kPayloadFlagsBits) - 1)
+                                       << kPayloadFlagsShift;
+constexpr uint32_t kNumFragmentsMask = ((1u << kNumFragmentsBits) - 1)
+                                       << kNumFragmentsShift;
+constexpr uint32_t kWriterIDMask = ((1u << kWriterIDBits) - 1)
+                                   << kWriterIDShift;
+
+constexpr uint32_t kMaxFragmentsPerChunk = (1u << kNumFragmentsBits) - 1;
+
+// Free uses the WriterID field for the wrap count. Its other data bits must be
+// zero.
+constexpr uint32_t kWrapCountShift = kWriterIDShift;
+
+enum PayloadFlags : uint32_t {
+  // The writer lost data before or while filling this chunk.
+  // - The flag does not identify where data was lost within the chunk.
+  // - The reader discards all published fragments and reports loss.
+  //   This includes complete, good packets before or after the gap.
+  // - A writer may keep appending. The flag stays set for that reservation.
+  //   Fragments appended to this chunk are also discarded once published.
+  // This allows cached reuse after loss without forcing a new reservation.
+  //
+  // TODO(sashwinbalaji): Carry the cause of the loss. TBv2 reports this flag
+  // as DATA_LOSS_READ_GAP. v1 reports a full buffer as DATA_LOSS_SMB_FULL.
+  kFlagDataLoss = 1u << kPayloadFlagsShift,
+
+  // The last fragment is not the end of its packet. The packet continues in
+  // this writer's next chunk. Only Complete may carry this flag, and the
+  // writer must not reuse a chunk carrying it.
+  kFlagContinuesOnNextChunk = 1u << (kPayloadFlagsShift + 1),
+
+  // The first fragment contains the next part of a packet that started in this
+  // writer's previous chunk.
+  kFlagContinuesFromPrevChunk = 1u << (kPayloadFlagsShift + 2),
+};

-    uint64_t varint = varint_;
-    varint_ = 0;
-    varint_shift_ = 0;
+// Chunk ownership and wrap identity
+// ---------------------------------
+//
+// Only the reader writes Free. The writer publishes each closed fragment and
+// may reuse its Complete chunk to append another.
+//
+// The reader can make progress even when a writer is descheduled:
+//
+// 1. The writer pauses before claiming the chunk.
+//    - The position is reserved. The chunk has not been claimed.
+//    - The reader atomically changes Free(wrap) to Free(next wrap).
+//      Only the wrap count changes. The state stays Free.
+//    - The reader advances read_pos without delivering fragments.
+//    - The chunk is ready for chunk_pos + num_chunks. That position's writer
+//      can claim it by changing the state to BeingWritten.
+//    - The original writer resumes with the old wrap count. Its claim fails
+//      without changing the chunk. It tries a new reservation.
+//    - If the writer claims first, the reader's update fails instead.
+//      The reader leaves read_pos unchanged and retries the position.
+//
+// 2. The writer pauses after claiming the chunk.
+//    - The reader copies the published fragments.
+//    - The reader changes BeingWritten to RewriteRequested, then advances
+//      read_pos. The writer still owns the chunk.
+//    - Later reservations cannot claim this chunk while it awaits
+//      acknowledgement. The reader advances past those reservations without
+//      changing the chunk's state word or delivering fragments.
+//    - The owning writer resumes and saves any unpublished fragment.
+//      Only that writer can change RewriteRequested to RewriteAcknowledged.
+//    - On a later traversal, the reader changes RewriteAcknowledged to
+//      Free(next wrap). It then advances read_pos without delivering fragments.
+//    - The chunk is now available for a subsequent reservation.
+//
+// An unclaimed reservation that the reader skips is called a hole. Skipping
+// means advancing read_pos without delivering fragments for that reservation.
+// It can also require a state word update, as in the Free and
+// RewriteAcknowledged cases above.
+//
+//                 writer claims                writer publishes
+//     Free(wrap)  ------------> BeingWritten(N)  -----------> Complete(M)
+//          |                           |                           |
+//          | reader                    | reader                    | reader
+//          v                           v                           v
+//   Free(next wrap)           RewriteRequested(N)           Free(next wrap)
+//                                      |
+//                                      | writer finishes with old chunk
+//                                      v
+//                             RewriteAcknowledged
+//                                      |
+//                                      | reader, on a later traversal
+//                                      v
+//                               Free(next wrap)
+//
+// N and M count published fragments. A claim starts at N = 0. Reuse takes
+// Complete(M) back to BeingWritten(M).
+//
+// The writer's publication and the reader's rewrite request compare the entire
+// BeingWritten word, including format, flags, num_fragments and WriterID.
+// Only one CAS can succeed against that word. No second atomic is needed.
+// If scraping wins, the writer relocates only the unpublished fragment after
+// the N published fragments.
+//
+// A reservation allows one claim against Free(wrap_count(chunk_pos)).
+// On failure, the writer must reserve a new position. The returned word can
+// belong to another writer or a position beyond the one the reader consumed.
+// A retry against that word can overwrite another reservation's chunk.
+//
+//   Actor   From                   Action                   To
+//   ------  ---------------------  -----------------------  -------------------
+//   writer  Free(wrap)             claim                    BeingWritten(0)
+//   writer  Free(wrap) gone        reserve a new position   unchanged
+//   reader  Free(wrap)             consume unclaimed        Free(next wrap)
+//   reader  Free(other wrap)       protocol error, stop     unchanged
+//   writer  BeingWritten(N)        publish                  Complete(M)
+//   reader  BeingWritten(N)        read N fragments         RewriteRequested(N)
+//   writer  Complete(N)            reuse                    BeingWritten(N)
+//   writer  Complete gone          drop cached handle       unchanged
+//   reader  Complete               consume                  Free(next wrap)
+//   writer  RewriteRequested       copy fragment, release   RewriteAcknowledged
+//   reader  RewriteRequested       advance read_pos         unchanged
+//   reader  RewriteAcknowledged    reclaim                  Free(next wrap)
+//   reader  reserved state 5..7    unknown owner, stop      unchanged
+//   reader  Free CAS lost          retry immediately        unchanged
+//   reader  BeingWritten CAS lost  discard copy, retry      unchanged
+//   reader  Complete CAS lost      discard copy, retry      unchanged
+//   reader  acknowledgement lost   protocol error, stop     unchanged
+//   writer  publication CAS lost   handle rewrite or abort  unchanged
+//   writer  any other lost CAS     bug, abort               unchanged
+//
+// Lost-CAS rows describe a failed attempt. It does not change the shared word.
+// The reader retries within Drain(), subject to its per-position attempt limit.
+// A failed publication must find RewriteRequested(N), or the writer aborts.
+// The reader also stops on reserved bits in Free or RewriteAcknowledged.
+//
+// Free stores the low 16 bits of the traversal number:
+//
+//   wrap_count = uint16_t(chunk_pos / num_chunks)
+//
+// For the same chunk, that value repeats after:
+//
+//   min(num_chunks * 65536, 2^32) reservations
+//
+// A writer delayed between reservation and claim for that long can match a
+// later Free word. Its data is delivered at that later position, whose writer
+// loses the claim. Chunk ownership holds, but reservation order is lost for
+// the delayed writer. This is the limit of the 16-bit wrap count.
+//
+// Computes the wrap from a position. It does not inspect the chunk's state.
+inline uint16_t WrapCountForPosition(uint32_t chunk_pos, uint32_t num_chunks) {
+  PERFETTO_DCHECK(base::IsPowerOfTwo(num_chunks));
+  return static_cast<uint16_t>(chunk_pos >> base::CountTrailZeros(num_chunks));
+}

-    switch (state_) {
-      case kFieldPreamble: {
-        auto field_type = static_cast<uint32_t>(varint & 7u);  // 7 = 0..0111
-        field_id_ = static_cast<uint32_t>(varint >> 3);
+constexpr ChunkState ChunkStateOf(uint32_t state_word) {
+  return static_cast<ChunkState>((state_word & kChunkStateMask) >>
+                                 kChunkStateShift);
+}

-        // The field type is legit, now check it's well formed and within
-        // boundaries.
-        if (field_type == static_cast<uint32_t>(ProtoWireType::kVarInt)) {
-          state_ = kVarIntValue;
-        } else if (field_type ==
-                       static_cast<uint32_t>(ProtoWireType::kFixed32) ||
-                   field_type ==
-                       static_cast<uint32_t>(ProtoWireType::kFixed64)) {
-          state_ = kFixedIntValue;
-          fixed_int_shift_ = 0;
-          fixed_int_value_ = 0;
-          fixed_int_bits_ =
-              field_type == static_cast<uint32_t>(ProtoWireType::kFixed32) ? 32
-                                                                           : 64;
-        } else if (field_type ==
-                   static_cast<uint32_t>(ProtoWireType::kLengthDelimited)) {
-          state_ = kLenDelimited;
-        } else {
-          state_ = kInvalidFieldType;
-        }
-        return Token{};
-      }
+constexpr bool HasDataFields(ChunkState state) {
+  return state == ChunkState::kBeingWritten || state == ChunkState::kComplete ||
+         state == ChunkState::kRewriteRequested;
+}

-      case kVarIntValue: {
-        // Return the varint field payload and go back to the next field.
-        state_ = kFieldPreamble;
-        return Token{field_id_, ProtoWireType::kVarInt, varint};
-      }
+constexpr uint32_t MakeFreeStateWord(uint16_t wrap_count) {
+  return static_cast<uint32_t>(wrap_count) << kWrapCountShift;
+}

-      case kLenDelimited: {
-        const auto payload_len = varint;
-        if (payload_len > protozero::proto_utils::kMaxMessageLength) {
-          state_ = kMessageTooBig;
-          return Token{};
-        }
-        state_ = kFieldPreamble;
-        // At this point the caller is expected to consume the next
-        // |payload_len| bytes.
-        return Token{field_id_, ProtoWireType::kLengthDelimited, payload_len};
-      }
+// The Free word a reservation at |chunk_pos| must find, and the word the
+// reader leaves for the next traversal when called with chunk_pos + num_chunks.
+inline uint32_t MakeFreeStateWordForPosition(uint32_t chunk_pos,
+                                             uint32_t num_chunks) {
+  return MakeFreeStateWord(WrapCountForPosition(chunk_pos, num_chunks));
+}

-      case kFixedIntValue:
-        // Unreachable because of the if before the switch.
-        PERFETTO_DCHECK(false);
-        break;
+// These accessors apply to BeingWritten, Complete and RewriteRequested.
+// PayloadFlagsOf() leaves the flags in their encoded bit positions, so its
+// result can be passed to MakeDataStateWord().
+constexpr ChunkFormat ChunkFormatOf(uint32_t state_word) {
+  return static_cast<ChunkFormat>((state_word & kChunkFormatMask) >>
+                                  kChunkFormatShift);
+}

-      // Unrecoverable error states.
-      case kInvalidFieldType:
-      case kMessageTooBig:
-      case kInvalidVarInt:
-        break;
-    }  // switch(state_)
+constexpr uint32_t PayloadFlagsOf(uint32_t state_word) {
+  return state_word & kPayloadFlagsMask;
+}
+
+constexpr uint32_t NumFragmentsOf(uint32_t state_word) {
+  return (state_word & kNumFragmentsMask) >> kNumFragmentsShift;
+}
+
+constexpr WriterID WriterIDOf(uint32_t state_word) {
+  return static_cast<WriterID>((state_word & kWriterIDMask) >> kWriterIDShift);
+}
+
+inline uint32_t MakeDataStateWord(ChunkState state,
+                                  ChunkFormat format,
+                                  uint32_t payload_flags,
+                                  uint32_t num_fragments,
+                                  WriterID writer_id) {
+  PERFETTO_DCHECK(HasDataFields(state));
+  PERFETTO_DCHECK(static_cast<uint32_t>(format) < (1u << kChunkFormatBits));
+  PERFETTO_DCHECK((payload_flags & ~kPayloadFlagsMask) == 0);
+  PERFETTO_DCHECK(num_fragments <= kMaxFragmentsPerChunk);
+  return (static_cast<uint32_t>(state) << kChunkStateShift) |
+         (static_cast<uint32_t>(format) << kChunkFormatShift) |
+         (payload_flags & kPayloadFlagsMask) |
+         ((num_fragments << kNumFragmentsShift) & kNumFragmentsMask) |
+         (static_cast<uint32_t>(writer_id) << kWriterIDShift);
+}
+
+constexpr uint32_t kRewriteAcknowledgedStateWord =
+    static_cast<uint32_t>(ChunkState::kRewriteAcknowledged) << kChunkStateShift;
+
+// Replaces only the state bits, preserving format, flags, count and WriterID.
+// The reader uses this to request a rewrite without understanding the chunk
+// format. The writer uses it to take a Complete chunk back to BeingWritten
+// while the word keeps describing the fragments it already published.
+constexpr uint32_t ReplaceChunkState(uint32_t state_word, ChunkState state) {
+  return (state_word & ~kChunkStateMask) |
+         (static_cast<uint32_t>(state) << kChunkStateShift);
+}
+
+// Target-buffer chunk format
+// --------------------------
+//
+// A data-bearing format-0 chunk begins with this six-byte header. The remaining
+// chunk_size - 6 bytes hold payload and size entries:
+//
+//   +---------+---------+-------------------+-------------------+
+//   | byte 0  | byte 1  |     bytes 2-3     |     bytes 4-5     |
+//   +---------+---------+-------------------+-------------------+
+//   | control |  num    |     WriterID      |  target BufferID  |
+//   |  byte   |fragments|                   |                   |
+//   +---------+---------+-------------------+-------------------+
+//   \_____________ atomic state word _______/
+//
+// The rest of a format-0 chunk is laid out as follows:
+//
+//   low address                                         high address
+//   0                 4          6                                 chunk_size
+//   +-----------------+----------+-----------+------+--------------+
+//   | atomic state    | BufferID | payloads  | free | size varints |
+//   | word            |          | grow ---> |      | <--- grow    |
+//   +-----------------+----------+-----------+------+--------------+
+//                                                     ... N-1  1  0
+//
+// Fragment 0's size is at the end of the chunk. num_fragments specifies the
+// number of published payload fragments and size varints.
+//
+// Publication and access:
+// - The writer fills each fragment and its size entry before the release
+//   transition from BeingWritten to Complete.
+// - The reader acquire-loads the state word. It decodes and validates each size
+//   before it copies the payload.
+//
+// BufferID becomes visible with the first fragment publication.
+// If num_fragments is zero, the reader must not load BufferID.
+//
+// If kFlagDataLoss is set, the reader discards the chunk's published fragments
+// without access to their payload.

-    return Token{};  // Keep GCC happy.
-  }
+constexpr uint32_t kTargetBufferIdOffset = 4;
+constexpr uint32_t kTargetBufferPayloadOffset = 6;

-  // Returns true if the tokenizer FSM has reached quiescence (i.e. if we are
-  // NOT in the middle of parsing a field).
-  bool idle() const {
-    return state_ == kFieldPreamble && varint_shift_ == 0 &&
-           fixed_int_shift_ == 0;
-  }
+inline void StoreTargetBufferId(uint8_t* chunk, BufferID buffer_id) {
+  memcpy(&chunk[kTargetBufferIdOffset], &buffer_id, sizeof(buffer_id));
+}

-  // Only for reporting parser errors in the trace.
-  uint32_t state() const { return static_cast<uint32_t>(state_); }
+inline BufferID LoadTargetBufferId(const uint8_t* chunk) {
+  BufferID buffer_id;
+  memcpy(&buffer_id, &chunk[kTargetBufferIdOffset], sizeof(buffer_id));
+  return buffer_id;
+}

- private:
-  enum State {
-    kFieldPreamble = 0,  // Parsing the varint for the field preamble.
-    kVarIntValue = 1,    // Parsing the payload of a varint field.
-    kFixedIntValue = 2,  // Parsing the payload of a fixed32/64 field.
-    kLenDelimited = 3,   // Parsing the length of a length-delimited field.
+// Fragment size directory
+// -----------------------
+//
+// The fragment sizes are reversed varints at the end of the chunk.
+// - Fragment 0's size ends at chunk_size.
+// - Each later size is prepended at a lower address.
+//
+//   low address                                 high address
+//   +----------+----------+----------+----------+----------+
+//   | size N-1 |   ...    |  size 2  |  size 1  |  size 0  |
+//   +----------+----------+----------+----------+----------+
+//
+// Within each size entry:
+// - Each byte carries seven value bits and a continuation bit (0x80).
+// - The least significant group is at the highest address.
+// - The reader moves towards lower addresses until the continuation bit is
+//   clear. It does not read the next, unpublished entry.
+
+// Fragment sizes share Protozero's message-length limit: four varint bytes.
+constexpr uint32_t kMaxFragmentSizeVarIntBytes = 4;
+
+// Returns the largest supported n with n + varint_size(n) <= available_bytes.
+// Returns zero if no payload byte fits.
+// |available_bytes| includes both the payload and its size entry. Each size
+// byte carries seven bits, so the limits below include the corresponding
+// number of size bytes.
+inline uint32_t MaxFragmentSizeForAvailableBytes(uint32_t available_bytes) {
+  if (available_bytes <= 1)
+    return 0;

-    // Unrecoverable error states:
-    kInvalidFieldType = 4,  // Encountered an invalid field type.
-    kMessageTooBig = 5,     // Size of the length delimited message was too big.
-    kInvalidVarInt = 6,     // Varint larger than 64 bits.
-  };
+  // One size byte: (2^7 - 1) + 1 = 2^7 total. Subtract one size byte.
+  if (available_bytes <= (1u << 7))
+    return available_bytes - 1;
+  // Two size bytes: (2^14 - 1) + 2 = 2^14 + 1 total. Subtract two.
+  if (available_bytes <= (1u << 14) + 1)
+    return available_bytes - 2;
+  // Three size bytes: (2^21 - 1) + 3 = 2^21 + 2 total. Subtract three.
+  if (available_bytes <= (1u << 21) + 2)
+    return available_bytes - 3;
+  return std::min(
+      available_bytes - 4,
+      static_cast<uint32_t>(protozero::proto_utils::kMaxMessageLength));
+}
+
+inline uint32_t MaxFragmentSizeForEmptyChunk(uint32_t chunk_size) {
+  // Callers use a validated ring buffer, so a size below 256 is a caller error.
+  PERFETTO_CHECK(chunk_size >= kMinChunkSize);
+  const uint32_t available_bytes = chunk_size - kTargetBufferPayloadOffset;
+  return MaxFragmentSizeForAvailableBytes(available_bytes);
+}
+
+// Writes the size into the directory immediately before |sizes_begin| and
+// returns the new directory start.
+// Requires varint_size(size) writable bytes before |sizes_begin|.
+// |size| must be at most protozero::proto_utils::kMaxMessageLength.
+inline uint8_t* WriteFragmentSizeReversed(uint8_t* sizes_begin, uint32_t size) {
+  PERFETTO_DCHECK(size <= protozero::proto_utils::kMaxMessageLength);
+  // TODO(sashwinbalaji): Add proto_utils::WriteVarIntReversed to write directly
+  // into the directory. This would avoid the temporary buffer and copy below.
+  uint8_t encoded[kMaxFragmentSizeVarIntBytes];
+  const uint8_t* const encoded_end =
+      protozero::proto_utils::WriteVarInt(size, encoded);
+  const size_t encoded_size = static_cast<size_t>(encoded_end - encoded);
+  // Put the first varint byte at the highest address: the reader starts
+  // there and reads towards lower addresses (see ReadFragmentSizeReversed()).
+  for (size_t i = 0; i < encoded_size; ++i) {
+    --sizes_begin;
+    *sizes_begin = encoded[i];
+  }
+  return sizes_begin;
+}
+
+// Decodes one reversed size varint, starting just below |*sizes_cursor|.
+//
+// For example, 300 is stored as 02 AC from low to high address.
+// The reader visits AC first, then 02:
+//
+//        ... | 02 | AC |  <- chunk_size
+//              ^    ^
+//              |    first byte read: AC, continuation bit set
+//              second byte read: 02, no continuation bit, stop
+//
+// The reader validates the chunk in two steps:
+// 1. Decode the sizes with |lower_bound| set to the payload start.
+//    This keeps reads out of the header. The payload end is not yet known.
+// 2. Add up the decoded sizes. The payload must end at or before the directory
+//    start. Reject the chunk on overlap, without copying payload.
+//
+// Rejects truncated entries and entries needing more than four bytes.
+// Four bytes limit the decoded size to Protozero's kMaxMessageLength.
+//
+// - Success returns the size, which may be zero. |*sizes_cursor| moves to the
+//   last byte read, just above the next entry.
+// - Failure returns std::nullopt and leaves |*sizes_cursor| unchanged.
+inline std::optional<uint32_t> ReadFragmentSizeReversed(
+    const uint8_t* lower_bound,
+    const uint8_t** sizes_cursor) {
+  PERFETTO_DCHECK(lower_bound <= *sizes_cursor);
+  const uint8_t* cursor = *sizes_cursor;
+  uint32_t value = 0;
+  uint32_t num_bytes = 0;
+  // TODO(sashwinbalaji): Benchmark alternatives:
+  // - Increment a running shift by seven instead of multiplying each time.
+  // - Find the terminator first, then decode forwards with shift-and-or.
+  for (;;) {
+    if (cursor == lower_bound || num_bytes == kMaxFragmentSizeVarIntBytes)
+      return std::nullopt;
+    --cursor;
+    const uint8_t cur = *cursor;
+    // Reading down the directory yields the least significant group first.
+    const uint32_t data_bits = cur & 0x7f;
+    const uint32_t shift = 7 * num_bytes;
+    value |= data_bits << shift;
+    ++num_bytes;
+    if ((cur & 0x80) == 0)
+      break;
+  }

-  State state_ = kFieldPreamble;
-  uint32_t field_id_ = 0;
-  uint64_t varint_ = 0;
-  uint32_t varint_shift_ = 0;
-  uint32_t fixed_int_shift_ = 0;
-  uint32_t fixed_int_bits_ = 0;
-  uint64_t fixed_int_value_ = 0;
-};
+  *sizes_cursor = cursor;
+  return value;
+}

-}  // namespace protozero
+}  // namespace perfetto::tracing_v2

-#endif  // SRC_PROTOZERO_FILTERING_MESSAGE_TOKENIZER_H_
+#endif  // SRC_TRACING_V2_SHARED_RING_BUFFER_ABI_H_
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -58761,249 +64434,294 @@ class MessageTokenizer {
  * limitations under the License.
  */

-#ifndef SRC_PROTOZERO_FILTERING_MESSAGE_FILTER_H_
-#define SRC_PROTOZERO_FILTERING_MESSAGE_FILTER_H_
+#ifndef SRC_TRACING_V2_SHARED_RING_BUFFER_H_
+#define SRC_TRACING_V2_SHARED_RING_BUFFER_H_

+#include <stddef.h>
 #include <stdint.h>

-#include <memory>
-#include <string>
-#include <unordered_map>
+#include <atomic>

-// gen_amalgamated expanded: #include "src/protozero/filtering/filter_bytecode_parser.h"
-// gen_amalgamated expanded: #include "src/protozero/filtering/message_tokenizer.h"
-// gen_amalgamated expanded: #include "src/protozero/filtering/string_filter.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/futex.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_abi.h"

-namespace protozero {
+namespace perfetto::tracing_v2 {

-// A class to filter binary-encoded proto messages using an allow-list of field
-// ids, also known as "filter bytecode". The filter determines which fields are
-// allowed to be passed through in output and strips all the other fields.
-// See go/trace-filtering for full design.
-// This class takes in input:
-// 1) The filter bytecode, loaded once via the LoadFilterBytecode() method.
-// 2) A proto-encoded binary message. The message doesn't have to be contiguous,
-//    it can be passed as an array of arbitrarily chunked fragments.
-// The FilterMessage*() method returns in output a proto message, stripping out
-// all unknown fields. If the input is malformed (e.g., unknown proto field wire
-// types, lengths out of bound) the whole filtering failed and the |error| flag
-// of the FilteredMessage object is set to true.
-// The filtering operation is based on rewriting a copy of the message into a
-// self-allocated buffer, which is then returned in the output. The input buffer
-// is NOT altered.
-// Note also that the process of rewriting the protos gets rid of most redundant
-// varint encoding (if present). So even if all fields are allow-listed, the
-// output might NOT be bitwise identical to the input (but it will be
-// semantically equivalent).
-// Furthermore the enable_field_usage_tracking() method allows to keep track of
-// a histogram of allowed / denied fields. It slows down filtering and is
-// intended only on host tools.
-class MessageFilter {
- public:
-  class Config {
-   public:
-    // Loads filter bytecode. If a filter is already loaded, it is replaced.
-    // Optionally accepts an overlay (pass nullptr/0 if not needed).
-    // See FilterBytecodeParser::Load() for overlay format details.
-    bool LoadFilterBytecode(const void* filter_data,
-                            size_t len,
-                            const void* overlay_data = nullptr,
-                            size_t overlay_len = 0);
+namespace test {
+class SharedRingBufferInternalsForTest;
+}

-    bool SetFilterRoot(std::initializer_list<uint32_t> field_ids);
+// Provides the atomic operations on a shared ring buffer.
+//
+// Like SharedMemoryABI in v1, this class interprets memory from its owner.
+// The owner must keep the region mapped until this view, its reader and its
+// writers are destroyed.
+//
+// The ring buffer is lock-free, with multiple writers and a single reader:
+// - Writers reserve positions concurrently. When the ring buffer is full,
+//   each writer applies its BufferExhaustedPolicy and records any data it
+//   drops.
+// - The reader consumes positions in order and is the only actor that makes a
+//   chunk Free again.
+// - All updates to rw_positions and the chunk state words go through this
+//   class.
+//
+// Chunk transitions use compare-and-exchange (CAS):
+// - Each CAS compares the entire state word, including all fields.
+// - For BeingWritten, Complete and RewriteRequested, those fields are format,
+//   flags, num_fragments and WriterID. Free contains the wrap count instead.
+// - In transition comments, N and M are published fragment counts.
+//
+// TODO(sashwinbalaji): CAS operations currently use the default seq_cst order.
+// Their implementation comments propose weaker orders for a later optimization
+// pass. Audit the synchronization guarantees and measure the benefit before
+// replacing seq_cst.
+class SharedRingBuffer {
+ public:
+  // Attaches to a ring buffer at |start|. Its layout is:
+  //
+  //   size = sizeof(RingBufferHeader) + num_chunks * chunk_size
+  //
+  // - sizeof(RingBufferHeader) is 64 bytes. |start| must be 64-byte aligned.
+  // - num_chunks must be a power of two, from 2 to 2^30.
+  // - |chunk_size| must be in [256 B, 64 KiB] and a multiple of four.
+  //   It does not need to be a power of two.
+  // - |size| must match the equation exactly, with no trailing bytes.
+  //   It does not need to be a power of two.
+  //
+  // A newly created ring buffer must be zero-filled.
+  SharedRingBuffer(uint8_t* start, size_t size, uint32_t chunk_size);
+  ~SharedRingBuffer() = default;

-    const FilterBytecodeParser& filter() const { return filter_; }
-    const StringFilter& string_filter() const { return string_filter_; }
-    StringFilter& string_filter() { return string_filter_; }
-    uint32_t root_msg_index() const { return root_msg_index_; }
+  SharedRingBuffer(const SharedRingBuffer&) = delete;
+  SharedRingBuffer& operator=(const SharedRingBuffer&) = delete;
+  SharedRingBuffer(SharedRingBuffer&&) = delete;
+  SharedRingBuffer& operator=(SharedRingBuffer&&) = delete;

-   private:
-    FilterBytecodeParser filter_;
-    StringFilter string_filter_;
-    uint32_t root_msg_index_ = 0;
-  };
+  // Immutable for the life of the ring buffer.
+  uint32_t num_chunks() const { return num_chunks_; }
+  uint32_t chunk_size() const { return chunk_size_; }

-  MessageFilter();
-  explicit MessageFilter(Config);
-  ~MessageFilter();
+  uint8_t* chunk_at(ChunkIndex chunk_idx) {
+    PERFETTO_DCHECK(chunk_idx.value() < num_chunks_);
+    return start_ + sizeof(RingBufferHeader) +
+           static_cast<size_t>(chunk_idx.value()) * chunk_size_;
+  }
+  const uint8_t* chunk_at(ChunkIndex chunk_idx) const {
+    PERFETTO_DCHECK(chunk_idx.value() < num_chunks_);
+    return start_ + sizeof(RingBufferHeader) +
+           static_cast<size_t>(chunk_idx.value()) * chunk_size_;
+  }

-  struct InputSlice {
-    const void* data;
-    size_t len;
+  // Writer-side reservation.
+  //
+  // A writer reserves a position and acquires its physical chunk in separate
+  // operations:
+  // 1. TryReserveWritePos() advances write_pos.
+  // 2. TryAcquireChunkForWriting() changes Free(wrap_count(chunk_pos)) to
+  //    BeingWritten.
+  //
+  // If the reader reaches the reservation before step 2, it must atomically
+  // advance the chunk's wrap count before it advances read_pos.
+  // The delayed writer's claim then fails because its expected wrap count no
+  // longer matches. This prevents publication at a consumed position.
+
+  enum class ReserveResult {
+    kReserved,
+    // num_chunks positions are already outstanding. Nothing was reserved,
+    // so a stalling policy may wait and try again.
+    kFull,
   };

-  struct FilteredMessage {
-    FilteredMessage(std::unique_ptr<uint8_t[]> d, size_t s)
-        : data(std::move(d)), size(s) {}
-    std::unique_ptr<uint8_t[]> data;
-    size_t size;  // The used bytes in |data|. This is <= sizeof(data).
-    bool error = false;
+  struct Reservation {
+    ReserveResult result = ReserveResult::kFull;
+    // Valid only for kReserved. This is a position in the reservation order,
+    // not a physical chunk index.
+    uint32_t chunk_pos = 0;
+    // The read_pos sampled by the last reservation attempt. If the writer has
+    // to wait, the futex sleeps only while read_pos still has this value.
+    uint32_t read_pos_for_wait = 0;
   };

-  // Loads the filter bytecode that will be used to filter any subsequent
-  // message. Must be called before the first call to FilterMessage*().
-  // |filter_data| must point to a byte buffer for a proto-encoded ProtoFilter
-  // message (see proto_filter.proto).
-  // Optionally accepts an overlay (pass nullptr/0 if not needed).
-  bool LoadFilterBytecode(const void* filter_data,
-                          size_t len,
-                          const void* overlay_data = nullptr,
-                          size_t overlay_len = 0) {
-    return config_.LoadFilterBytecode(filter_data, len, overlay_data,
-                                      overlay_len);
-  }
-
-  // This affects the filter starting point of the subsequent FilterMessage*()
-  // calls. By default the filtering process starts from the message @ index 0,
-  // the root message passed to proto_filter when generating the bytecode
-  // (in typical tracing use-cases, this is perfetto.protos.Trace). However, the
-  // caller (TracingServiceImpl) might want to filter packets from the 2nd level
-  // (perfetto.protos.TracePacket) because the root level is prepended after
-  // the fact. This call allows to change the root message for the filter.
-  // The argument |field_ids| is an array of proto field ids and determines the
-  // path to the new root. For instance, in the case of [1,2,3] SetFilterRoot
-  // will identify the sub-message for the field "root.1.2.3" and use that.
-  // In order for this to succeed all the fields in the path must be allowed
-  // in the filter and must be a nested message type.
-  bool SetFilterRoot(std::initializer_list<uint32_t> field_ids) {
-    return config_.SetFilterRoot(field_ids);
-  }
-
-  // Takes an input message, fragmented in arbitrary slices, and returns a
-  // filtered message in output.
-  FilteredMessage FilterMessageFragments(const InputSlice*, size_t num_slices);
-
-  // Helper for tests, where the input is a contiguous buffer.
-  FilteredMessage FilterMessage(const void* data, size_t len) {
-    InputSlice slice{data, len};
-    return FilterMessageFragments(&slice, 1);
-  }
+  // Reserves the next position if the ring buffer has room.
+  // If another writer or the reader updates rw_positions first, it rechecks
+  // capacity with the updated positions before another reservation attempt.
+  Reservation TryReserveWritePos();

-  // When enabled returns a map of "field path" to "usage counter".
-  // The key (std::string) is a binary buffer (i.e. NOT an ASCII/UTF-8 string)
-  // which contains a varint for each field. Consider the following:
-  // message Root { Sub1 f1 = 1; };
-  // message Sub1 { Sub2 f2 = 7;}
-  // message Sub2 { string f3 = 5; }
-  // The field .f1.f2.f3 will be encoded as \x01\0x07\x05.
-  // The value is the number of times that field has been encountered. If the
-  // field is not allow-listed in the bytecode (the field is stripped in output)
-  // the count will be negative.
-  void enable_field_usage_tracking(bool x) { track_field_usage_ = x; }
-  const std::unordered_map<std::string, int32_t>& field_usage() const {
-    return field_usage_;
-  }
+  // Writer-side chunk transitions.

-  const Config& config() const { return config_; }
-  Config& config() { return config_; }
+  // Free(wrap_count(chunk_pos)) -> BeingWritten(0).
+  // |being_written_word| supplies this writer's WriterID, format and initial
+  // flags. Its fragment count must be zero because the chunk has no published
+  // payload for this reservation.
+  //
+  // On failure, reserve a later position. The reader can consume this position
+  // before the claim, or a writer from an earlier reservation can still own
+  // the chunk. A retry against the returned word can overwrite that writer's
+  // chunk or claim a position the reader already consumed.
+  bool TryAcquireChunkForWriting(uint32_t chunk_pos,
+                                 uint32_t being_written_word);
+
+  // BeingWritten(N) -> Complete(M). |*expected| is the last BeingWritten word.
+  //
+  // On failure, |*expected| receives the reader's RewriteRequested(N) word,
+  // which must preserve the format, flags, fragment count and WriterID.
+  // Only the reader's rewrite request can change the word while this writer
+  // owns the chunk. The caller must relocate any unpublished fragment.
+  bool TryReleaseChunkAsComplete(ChunkIndex chunk_idx,
+                                 uint32_t complete_word,
+                                 uint32_t* expected);
+
+  // Complete(N) -> BeingWritten(N), so the writer can append fragments to its
+  // cached chunk. |observed| is the Complete word this writer published.
+  //
+  // On failure, the reader reclaimed the chunk first. The writer must discard
+  // its cached handle and acquire another chunk.
+  bool TryReacquireChunkForWriting(ChunkIndex chunk_idx, uint32_t observed);

-  // Returns the helper class used to perform string filtering.
-  StringFilter& string_filter() { return config_.string_filter(); }
+  // RewriteRequested -> RewriteAcknowledged after the writer saves any
+  // unpublished fragment and stops access to the old chunk.
+  //
+  // On failure, report a protocol error. Only this writer can change
+  // RewriteRequested, so |observed| must still match the shared word.
+  bool TryAcknowledgeRewrite(ChunkIndex chunk_idx, uint32_t observed);
+
+  // Reader side.
+
+  // Returns the current chunk state word with acquire ordering. For a data
+  // state, the reader can access the published fragments and their sizes.
+  // BufferID is visible only when the published fragment count is nonzero.
+  uint32_t LoadChunkStateWordAcquire(ChunkIndex chunk_idx) const;
+
+  // Returns the next logical position a writer can reserve.
+  // - This bounds the reservations the reader can consume.
+  // - It does not say which chunks have published fragments.
+  //   The reader checks each chunk's state word for that.
+  // - An older position can shorten a drain pass. A later pass can consume
+  //   the remaining reservations.
+  uint32_t LoadWritePosRelaxed() const;
+
+  // Returns write_pos - read_pos from one relaxed load of both positions.
+  // - Includes chunks that writers have not published yet.
+  // - Includes positions reserved but left unclaimed when a chunk claim failed.
+  //   They still count until the reader advances past them.
+  //
+  // Concurrent updates can make this snapshot out of date, but that only
+  // affects when a drain is requested. Space reservation and chunk access use
+  // separate checks.
+  uint32_t LoadNumOutstandingPositionsRelaxed() const;

- private:
-  // This is called by FilterMessageFragments().
-  // Inlining allows the compiler turn the per-byte call/return into a for loop,
-  // while, at the same time, keeping the code easy to read and reason about.
-  // It gives a 20-25% speedup (265ms vs 215ms for a 25MB trace).
-  void FilterOneByte(uint8_t octet) PERFETTO_ALWAYS_INLINE;
+  // BeingWritten(N) -> RewriteRequested(N), with all other fields unchanged.
+  // |*expected| must be the word the reader used to copy the fragments.
+  //
+  // On failure, the writer published first and |*expected| receives the current
+  // word. The reader must discard its copy and retry this position in Drain().
+  bool TryRequestRewrite(ChunkIndex chunk_idx, uint32_t* expected);

-  // No-inline because this is a slowpath (only when usage tracking is enabled).
-  void IncrementCurrentFieldUsage(uint32_t field_id,
-                                  bool allowed) PERFETTO_NO_INLINE;
+  // The following transitions are the only ones that expose a chunk to the
+  // next pass around the ring buffer. The new wrap count comes from
+  // |chunk_pos|, not from the old state word.

-  // Gets into an error state which swallows all the input and emits no output.
-  void SetUnrecoverableErrorState();
+  // Free(wrap_count(chunk_pos)) -> Free(next_wrap(chunk_pos)).
+  // Consumes an unclaimed position and prepares the chunk for
+  // chunk_pos + num_chunks. |*expected| must be this position's Free word.
+  //
+  // On failure, the delayed writer claimed the chunk first and |*expected|
+  // receives the current word. The reader must retry this position in Drain().
+  bool TryMoveFreeChunkToNextWrap(uint32_t chunk_pos, uint32_t* expected);

-  // We keep track of the nest of messages in a stack. Each StackState
-  // object corresponds to a level of nesting in the proto message structure.
-  // Every time a new field of type len-delimited that has a corresponding
-  // sub-message in the bytecode is encountered, a new StackState is pushed in
-  // |stack_|. stack_[0] is a sentinel to prevent over-popping without adding
-  // extra branches in the fastpath.
-  // |stack_|. stack_[1] is the state of the root message.
-  struct StackState {
-    uint32_t in_bytes = 0;  // Number of input bytes processed.
+  // Complete(N) -> Free(next_wrap(chunk_pos)).
+  // |*expected| must be the word the reader used to copy the fragments.
+  //
+  // On failure, the writer reused the chunk first and |*expected| receives the
+  // current word. The reader must discard its copy and retry this position in
+  // Drain(). If the chunk is now BeingWritten, that retry requests a rewrite.
+  bool TryReleaseCompleteChunkAsFree(uint32_t chunk_pos, uint32_t* expected);

-    // When |in_bytes| reaches this value, the current state should be popped.
-    // This is set when recursing into nested submessages. This is 0 only for
-    // stack_[0] (we don't know the size of the root message upfront).
-    uint32_t in_bytes_limit = 0;
+  // RewriteAcknowledged -> Free(next_wrap(chunk_pos)).
+  //
+  // On failure, |*observed| receives the unexpected word. This is a protocol
+  // error because only this reader can change an acknowledged chunk.
+  bool TryReleaseRewriteAcknowledgedChunkAsFree(uint32_t chunk_pos,
+                                                uint32_t* observed);

-    // This is set when a len-delimited message is encountered, either a string
-    // or a nested submessage that is NOT allow-listed in the bytecode.
-    // This causes input bytes to be consumed without being parsed from the
-    // input stream. If |passthrough_eaten_bytes| == true, they will be copied
-    // as-is in output (e.g. in the case of an allowed string/bytes field).
-    uint32_t eat_next_bytes = 0;
+  // Backpressure: the writer's path when the ring buffer is full.
+  //
+  // Writers wait on read_pos. The reader publishes a new value and wakes them.
+  // The waiter count only avoids an unnecessary wake syscall. Capacity is
+  // always decided from rw_positions.
+
+  enum class WriterWaitResult {
+    // The writer must recheck capacity after a wake, timeout, interruption or
+    // spurious return. The writer's deadline determines when it stops retries.
+    kRetry,
+    // This build or kernel cannot provide the wait. Do not retry the syscall.
+    // The writer can sleep and retry acquisition instead.
+    kUnavailable,
+  };

-    // Keeps tracks of the stream_writer output counter (out_.written()) then
-    // the StackState is pushed. This is used to work out, when popping, how
-    // many bytes have been written for the current submessage.
-    uint32_t out_bytes_written_at_start = 0;
+  // Whether WaitForReadPosChange() is implemented on this platform.
+  static constexpr bool SupportsWriterWait() { return PERFETTO_HAS_FUTEX(); }

-    uint32_t field_id = 0;   // The proto field id for the current message.
-    uint32_t msg_index = 0;  // The index of the message filter in the bytecode.
+  // Blocks until read_pos differs from |read_pos_for_wait| or |timeout_ms|
+  // elapses. |read_pos_for_wait| comes from the last Reservation, whether it
+  // reported kFull or a position whose claim failed.
+  WriterWaitResult WaitForReadPosChange(uint32_t read_pos_for_wait,
+                                        uint32_t timeout_ms);

-    // This is a pointer to the proto preamble for the current submessage
-    // (it's nullptr for stack_[0] and non-null elsewhere). This will be filled
-    // with the actual size of the message (out_.written() -
-    // |out_bytes_written_at_start|) when finishing (popping) the message.
-    // This must be filled using WriteRedundantVarint(). Note that the
-    // |size_field_len| is variable and depends on the actual length of the
-    // input message. If the output message has roughly the same size of the
-    // input message, the length will not be redundant.
-    // In other words: the length of the field is reserved when the submessage
-    // starts. At that point we know the upper-bound for the output message
-    // (a filtered submessage can be <= the original one, but not >). So we
-    // reserve as many bytes it takes to write the input length in varint.
-    // Then, when the message is finalized and we know the actual output size
-    // we backfill the field.
-    // Consider the example of a submessage where the input size = 130 (>127,
-    // 2 varint bytes) and the output is 120 bytes. The length will be 2 bytes
-    // wide even though could have been encoded with just one byte.
-    uint8_t* size_field = nullptr;
-    uint32_t size_field_len = 0;
+  // Publishes read_pos without overwriting a concurrent write_pos update, then
+  // wakes waiting writers. Called once per drain pass, so the shared read_pos
+  // can lag the reader's local value. The lag only under-reports free capacity.
+  void PublishReadPos(uint32_t read_pos);

-    // The pointer to the start of the string to update the string if it is
-    // filtered.
-    uint8_t* filter_string_ptr = nullptr;
+ private:
+  friend class test::SharedRingBufferInternalsForTest;

-    // The semantic type of the string field being filtered (0 = unspecified).
-    uint32_t filter_string_semantic_type = 0;
+  // The production entry points load the initial expected value themselves.
+  // Tests supply an older value to exercise the CAS retry path
+  // deterministically.
+  Reservation TryReserveWritePosFromSnapshot(uint64_t rw_positions);
+  void PublishReadPosFromSnapshot(uint64_t rw_positions, uint32_t read_pos);

-    // How |eat_next_bytes| should be handled. It seems that keeping this field
-    // at the end rather than next to |eat_next_bytes| makes the filter a little
-    // (but measurably) faster. (likely something related with struct layout vs
-    // cache sizes).
-    enum FilterAction {
-      kDrop,
-      kPassthrough,
-      kFilterString,
-    };
-    FilterAction action = FilterAction::kDrop;
-  };
+  // Shared-memory address and wrap-count helpers.

-  uint32_t out_written() { return static_cast<uint32_t>(out_ - &out_buf_[0]); }
+  std::atomic<uint32_t>* chunk_state_word_at(ChunkIndex chunk_idx) {
+    return reinterpret_cast<std::atomic<uint32_t>*>(chunk_at(chunk_idx));
+  }
+  const std::atomic<uint32_t>* chunk_state_word_at(ChunkIndex chunk_idx) const {
+    return reinterpret_cast<const std::atomic<uint32_t>*>(chunk_at(chunk_idx));
+  }

-  Config config_;
+  std::atomic<uint32_t>* chunk_state_word_for_position(uint32_t chunk_pos) {
+    return chunk_state_word_at(
+        ChunkIndex::FromPosition(chunk_pos, num_chunks_));
+  }

-  std::unique_ptr<uint8_t[]> out_buf_;
-  uint8_t* out_ = nullptr;
-  uint8_t* out_end_ = nullptr;
+  // Returns the Free word for the next position that uses the same chunk.
+  uint32_t MakeFreeWordForNextWrap(uint32_t chunk_pos) const {
+    // Deriving the value from the next position also handles uint32_t rollover.
+    const uint32_t next_pos = chunk_pos + num_chunks_;
+    return MakeFreeStateWordForPosition(next_pos, num_chunks_);
+  }

-  MessageTokenizer tokenizer_;
-  std::vector<StackState> stack_;
+  RingBufferHeader* header() {
+    return reinterpret_cast<RingBufferHeader*>(start_);
+  }
+  const RingBufferHeader* header() const {
+    return reinterpret_cast<const RingBufferHeader*>(start_);
+  }

-  bool error_ = false;
-  bool track_field_usage_ = false;
-  std::unordered_map<std::string, int32_t> field_usage_;
+  uint8_t* const start_;
+  const uint32_t num_chunks_;
+  const uint32_t chunk_size_;
 };

-}  // namespace protozero
+}  // namespace perfetto::tracing_v2

-#endif  // SRC_PROTOZERO_FILTERING_MESSAGE_FILTER_H_
+#endif  // SRC_TRACING_V2_SHARED_RING_BUFFER_H_
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -59018,325 +64736,353 @@ class MessageFilter {
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/protozero/filtering/message_filter.h"
-
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
-// gen_amalgamated expanded: #include "src/protozero/filtering/string_filter.h"
-
-namespace protozero {
-
-namespace {
-
-// Inline helpers to append proto fields in output. They are the equivalent of
-// the protozero::Message::AppendXXX() fields but don't require building and
-// maintaining a full protozero::Message object or dealing with scattered
-// output slices.
-// All these functions assume there is enough space in the output buffer, which
-// should be always the case assuming that we don't end up generating more
-// output than input.
-
-inline void AppendVarInt(uint32_t field_id, uint64_t value, uint8_t** out) {
-  *out = proto_utils::WriteVarInt(proto_utils::MakeTagVarInt(field_id), *out);
-  *out = proto_utils::WriteVarInt(value, *out);
-}
-
-// For fixed32 / fixed64.
-template <typename INT_T /* uint32_t | uint64_t*/>
-inline void AppendFixed(uint32_t field_id, INT_T value, uint8_t** out) {
-  *out = proto_utils::WriteVarInt(proto_utils::MakeTagFixed<INT_T>(field_id),
-                                  *out);
-  memcpy(*out, &value, sizeof(value));
-  *out += sizeof(value);
-}
-
-// For length-delimited (string, bytes) fields. Note: this function appends only
-// the proto preamble and the varint field that states the length of the payload
-// not the payload itself.
-// In the case of submessages, the caller needs to re-write the length at the
-// end in the in the returned memory area.
-// The problem here is that, because of filtering, the length of a submessage
-// might be < original length (the original length is still an upper-bound).
-// Returns a pair with: (1) the pointer where the final length should be written
-// into, (2) the length of the size field.
-// The caller must write a redundant varint to match the original size (i.e.
-// needs to use WriteRedundantVarInt()).
-inline std::pair<uint8_t*, uint32_t> AppendLenDelim(uint32_t field_id,
-                                                    uint32_t len,
-                                                    uint8_t** out) {
-  *out = proto_utils::WriteVarInt(proto_utils::MakeTagLengthDelimited(field_id),
-                                  *out);
-  uint8_t* size_field_start = *out;
-  *out = proto_utils::WriteVarInt(len, *out);
-  const size_t size_field_len = static_cast<size_t>(*out - size_field_start);
-  return std::make_pair(size_field_start, size_field_len);
-}
-}  // namespace
-
-MessageFilter::MessageFilter(Config config) : config_(std::move(config)) {
-  // Push a state on the stack for the implicit root message.
-  stack_.emplace_back();
-}
+#ifndef SRC_TRACING_V2_PRODUCER_RING_BUFFER_ARBITER_H_
+#define SRC_TRACING_V2_PRODUCER_RING_BUFFER_ARBITER_H_

-MessageFilter::MessageFilter() : MessageFilter(Config()) {}
+#include <stddef.h>
+#include <stdint.h>

-MessageFilter::~MessageFilter() = default;
+#include <atomic>
+#include <functional>
+#include <memory>

-bool MessageFilter::Config::LoadFilterBytecode(const void* filter_data,
-                                               size_t len,
-                                               const void* overlay_data,
-                                               size_t overlay_len) {
-  return filter_.Load(filter_data, len, overlay_data, overlay_len);
-}
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/weak_ptr.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/basic_types.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/buffer_exhausted_policy.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/core/forward_decls.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer.h"

-bool MessageFilter::Config::SetFilterRoot(
-    std::initializer_list<uint32_t> field_ids) {
-  uint32_t root_msg_idx = 0;
-  for (uint32_t field_id : field_ids) {
-    auto res = filter_.Query(root_msg_idx, field_id);
-    if (!res.allowed || !res.nested_msg_field())
-      return false;
-    root_msg_idx = res.nested_msg_index;
-  }
-  root_msg_index_ = root_msg_idx;
-  return true;
-}
+namespace perfetto {

-MessageFilter::FilteredMessage MessageFilter::FilterMessageFragments(
-    const InputSlice* slices,
-    size_t num_slices) {
-  // First compute the upper bound for the output. The filtered message cannot
-  // be > the original message.
-  uint32_t total_len = 0;
-  for (size_t i = 0; i < num_slices; ++i)
-    total_len += slices[i].len;
-  out_buf_.reset(new uint8_t[total_len]);
-  out_ = out_buf_.get();
-  out_end_ = out_ + total_len;
+namespace base {
+class TaskRunner;
+}  // namespace base

-  // Reset the parser state.
-  tokenizer_ = MessageTokenizer();
-  error_ = false;
-  stack_.clear();
-  stack_.resize(2);
-  // stack_[0] is a sentinel and should never be hit in nominal cases. If we
-  // end up there we will just keep consuming the input stream and detecting
-  // at the end, without hurting the fastpath.
-  stack_[0].in_bytes_limit = UINT32_MAX;
-  stack_[0].eat_next_bytes = UINT32_MAX;
-  // stack_[1] is the actual root message.
-  stack_[1].in_bytes_limit = total_len;
-  stack_[1].msg_index = config_.root_msg_index();
+class ProducerEndpoint;
+class SharedMemory;
+class SharedMemoryArbiter;
+class TraceWriter;

-  // Process the input data and write the output.
-  for (size_t slice_idx = 0; slice_idx < num_slices; ++slice_idx) {
-    const InputSlice& slice = slices[slice_idx];
-    const uint8_t* data = static_cast<const uint8_t*>(slice.data);
-    for (size_t i = 0; i < slice.len; ++i)
-      FilterOneByte(data[i]);
-  }
+namespace tracing_v2 {

-  // Construct the output object.
-  PERFETTO_CHECK(out_ >= out_buf_.get() && out_ <= out_end_);
-  auto used_size = static_cast<size_t>(out_ - out_buf_.get());
-  FilteredMessage res{std::move(out_buf_), used_size};
-  res.error = error_;
-  if (stack_.size() != 1 || !tokenizer_.idle() ||
-      stack_[0].in_bytes != total_len) {
-    res.error = true;
-  }
-  return res;
-}
+namespace test {
+class ProducerRingBufferArbiterTestPeer;
+}  // namespace test

-void MessageFilter::FilterOneByte(uint8_t octet) {
-  PERFETTO_DCHECK(!stack_.empty());
+// The producer side of one tracing v2 ring buffer.
+//
+// The producer's ProducerEndpoint (for example ProducerIPCClientImpl) creates
+// one arbiter for each ring buffer that it shares with the service. In this
+// file:
+// - "endpoint" means that ProducerEndpoint.
+// - "SMB arbiter" means the SharedMemoryArbiter used for v1 tracing.
+// - "writer" means TraceWriterV2Impl.
+//
+// This class:
+// - Keeps the ring buffer mapping alive and owns the SharedRingBuffer view.
+// - Creates writers. Their WriterIDs come from the SMB arbiter, because v1
+//   and v2 writers of one producer share one ID pool.
+// - Tracks the service reader. See ReaderState.
+// - Delivers drain requests to the endpoint thread, which sends
+//   DrainV2RingBuffer to the service. See DrainUrgency.
+// - Runs each Flush() callback after its drain request.
+//
+// Threads:
+// - Writers call ring_buffer(), RequestDrain(), IsReaderAttached(), Flush()
+//   and OnWriterDestroyed() from any thread. CreateTraceWriter() can also run
+//   on any thread.
+// - Creation, state changes, posted tasks, flush callbacks and service
+//   requests run on the endpoint thread.
+//
+// The caller selects v2, then calls InitializeV2RingBuffer() on the endpoint
+// thread before it lets writers use the ring buffer.
+// Time goes down.
+// "--->" is a direct call, and "===>" is an IPC message.
+//
+// endpoint                   this class                   service
+//    |                           |                           |
+//    | Create()                  |                           |
+//    |-------------------------->| kPending                  |
+//    |== AttachV2RingBuffer, with the memfd ================>| maps, checks,
+//    |<== reply =============================================| attaches
+//    | OnReaderAttachReply()     |                           |
+//    |-------------------------->| kAttached, or             |
+//    |                           | kDetached if rejected     |
+//
+// - Later instances reuse the ring buffer.
+// - If Create() fails, the endpoint has no arbiter and returns
+//   NullTraceWriters for v2 requests.
+//
+// Flush(callback) posts its own drain task, then the callback. Time goes
+// down. "===>" is a posted task, or an IPC request to the service.
+//
+//  writer thread          endpoint thread             service
+//        |                       |                       |
+//        | Flush(callback)       |                       |
+//        |==== drain task ======>|                       |
+//        |==== callback ========>|                       |
+//        |                       |== DrainV2RingBuffer =>| copies chunks
+//        |                       | callback()            |
+//
+// A message that the callback sends reaches the service after the drain
+// request. The service handles them in order.
+//
+// Ownership:
+//
+// - "owns": the owner destroys it, as a member or through a unique_ptr.
+// - "shares": a shared_ptr keeps it alive while any owner holds it.
+// - "uses": a raw pointer. The object must outlive the user.
+// - "holds": an allocated ID. The holder must release it.
+//
+//   endpoint (for example ProducerIPCClientImpl)
+//     |-- owns --> SharedMemoryArbiterImpl: the WriterID pool
+//     |-- owns --> ProducerRingBufferArbiter (this class)
+//                    |-- shares-> SharedMemory: the ring buffer mapping
+//                    |-- owns --> SharedRingBuffer: the view
+//                    |-- uses --> endpoint, SharedMemoryArbiterImpl
+//
+//   data source (SDK)
+//     |-- owns --> TraceWriterV2Impl
+//                    |-- uses --> ProducerRingBufferArbiter
+//                    |-- holds -> a WriterID from SharedMemoryArbiterImpl
+//                    |-- owns --> SharedRingBufferWriter
+//                                   |-- uses --> SharedRingBuffer
+//
+// Writers use raw pointers into objects that the endpoint owns. The WriterID
+// keeps these pointers valid:
+// - While a writer holds a WriterID, TryShutdown() of the SMB arbiter fails.
+// - So the endpoint keeps the SMB arbiter, this object and the mapping.
+// - The writer releases its WriterID last, in OnWriterDestroyed().
+class ProducerRingBufferArbiter {
+ public:
+  // Whether writers can rely on a service reader to free space.
+  // - Only the endpoint thread changes the state, but writers can read it
+  //   from any thread.
+  // - This class sends drain requests in every state. The service handles
+  //   attach and drain requests in order. It ignores a drain for a ring
+  //   buffer that it did not accept.
+  //
+  //     Create()                OnReaderAttachReply(true)
+  //   ----------> [ kPending ] --------------------------> [ kAttached ]
+  //                     |                                        |
+  //                     +-------------------+--------------------+
+  //                                         | Disconnect() or
+  //                                         | OnReaderAttachReply(false)
+  //                                         v
+  //                                   [ kDetached ]
+  //                                    (terminal)
+  enum class ReaderState {
+    // The endpoint has not received an attach reply yet.
+    // - Writers can already publish.
+    // - The service can still reject the ring buffer.
+    // - A writer does not wait for space in a full ring buffer. It drops the
+    //   packet instead, also under kStall.
+    kPending,
+
+    // The service accepted the ring buffer. Its reader drains it.
+    // - A writer can wait for space in a full ring buffer.
+    kAttached,
+
+    // Terminal. No reader now or later. Disconnect() enters it after
+    // rejection, disconnect or destruction of this class.
+    // - New writers are NullTraceWriters.
+    // - Existing writers keep the mapping. They drop packets when the ring
+    //   buffer is full.
+    kDetached,
+  };

-  auto* state = &stack_.back();
-  StackState next_state{};
-  bool push_next_state = false;
+  // How soon RequestDrain() must send the drain request.
+  //
+  // Only the endpoint thread sends requests. Normally RequestDrain() posts a
+  // task there, and all requests made before the task runs share it. This
+  // saves IPC traffic. But if the caller is on the endpoint thread and blocks
+  // it right after RequestDrain(), the task cannot run until the caller
+  // unblocks, and the caller waits for that drain. The urgency tells
+  // RequestDrain() whether the request can wait for a task.
+  //
+  // It changes only when the request is sent. The service handles every
+  // request the same way, and RequestDrain() never waits for the drain.
+  enum class DrainUrgency {
+    // Set it when the caller does not block after RequestDrain().
+    // A task on the endpoint thread sends the request. Requests made before
+    // that task runs share it, so a burst of calls costs one request.
+    kRoutine,
+
+    // Set it when the caller blocks right after RequestDrain(), until the
+    // service reads the ring buffer.
+    // - On the endpoint thread, sends the request at once, before
+    //   RequestDrain() returns.
+    // - On other threads, the same as kRoutine. The endpoint thread is free
+    //   to run the task.
+    kUrgent,
+  };

-  if (state->eat_next_bytes > 0) {
-    // This is the case where the previous tokenizer_.Push() call returned a
-    // length delimited message which is NOT a submessage (a string or a bytes
-    // field). We just want to consume it, and pass it through/filter strings
-    // if the field was allowed.
-    --state->eat_next_bytes;
-    if (state->action == StackState::kPassthrough) {
-      *(out_++) = octet;
-    } else if (state->action == StackState::kFilterString) {
-      *(out_++) = octet;
-      if (state->eat_next_bytes == 0) {
-        config_.string_filter().MaybeFilter(
-            reinterpret_cast<char*>(state->filter_string_ptr),
-            static_cast<size_t>(out_ - state->filter_string_ptr),
-            state->filter_string_semantic_type);
-      }
-    }
-  } else {
-    MessageTokenizer::Token token = tokenizer_.Push(octet);
-    // |token| will not be valid() in most cases and this is WAI. When pushing
-    // a varint field, only the last byte yields a token, all the other bytes
-    // return an invalid token, they just update the internal tokenizer state.
-    if (token.valid()) {
-      auto filter = config_.filter().Query(state->msg_index, token.field_id);
-      switch (token.type) {
-        case proto_utils::ProtoWireType::kVarInt:
-          if (filter.allowed && filter.simple_field())
-            AppendVarInt(token.field_id, token.value, &out_);
-          break;
-        case proto_utils::ProtoWireType::kFixed32:
-          if (filter.allowed && filter.simple_field())
-            AppendFixed(token.field_id, static_cast<uint32_t>(token.value),
-                        &out_);
-          break;
-        case proto_utils::ProtoWireType::kFixed64:
-          if (filter.allowed && filter.simple_field())
-            AppendFixed(token.field_id, static_cast<uint64_t>(token.value),
-                        &out_);
-          break;
-        case proto_utils::ProtoWireType::kLengthDelimited:
-          // Here we have two cases:
-          // A. A simple string/bytes field: we just want to consume the next
-          //    bytes (the string payload), optionally passing them through in
-          //    output if the field is allowed.
-          // B. This is a nested submessage. In this case we want to recurse and
-          //    push a new state on the stack.
-          // Note that we can't tell the difference between a
-          // "non-allowed string" and a "non-allowed submessage". But it doesn't
-          // matter because in both cases we just want to skip the next N bytes.
-          const auto submessage_len = static_cast<uint32_t>(token.value);
-          auto in_bytes_left = state->in_bytes_limit - state->in_bytes - 1;
-          if (PERFETTO_UNLIKELY(submessage_len > in_bytes_left)) {
-            // This is a malicious / malformed string/bytes/submessage that
-            // claims to be larger than the outer message that contains it.
-            return SetUnrecoverableErrorState();
-          }
+  // Allocates the shared memory for a ring buffer of |size| bytes, header
+  // included, or returns an error status on failure.
+  using AllocateSharedMemoryFn =
+      base::StatusOr<std::shared_ptr<SharedMemory>> (*)(size_t size);
+
+  // Sizes and allocates a ring buffer from |config|, then creates its arbiter
+  // in kPending.
+  // - The chunk size and the drain occupancy threshold come from |config|.
+  // - |task_runner| and |endpoint| must outlive the arbiter.
+  // - |allocate_shared_memory| must return a mapping of the size it is given.
+  // - Returns an error status if sizing or allocation fails.
+  // - The endpoint then attaches memory() to the service, and passes the reply
+  //   to OnReaderAttachReply().
+  static base::StatusOr<std::unique_ptr<ProducerRingBufferArbiter>> Create(
+      base::TaskRunner* task_runner,
+      ProducerEndpoint* endpoint,
+      const DataSourceConfig& config,
+      size_t shmem_size_hint_bytes,
+      AllocateSharedMemoryFn allocate_shared_memory);
+
+  // The endpoint destroys this on its thread after all writers release their
+  // IDs. Calls Disconnect().
+  ~ProducerRingBufferArbiter();
+
+  // Writers keep a pointer to this object.
+  ProducerRingBufferArbiter(const ProducerRingBufferArbiter&) = delete;
+  ProducerRingBufferArbiter& operator=(const ProducerRingBufferArbiter&) =
+      delete;

-          if (filter.allowed && filter.nested_msg_field() &&
-              submessage_len > 0) {
-            // submessage_len == 0 is the edge case of a message with a 0-len
-            // (but present) submessage. In this case, if allowed, we don't want
-            // to push any further state (doing so would desync the FSM) but we
-            // still want to emit it.
-            // At this point |submessage_len| is only an upper bound. The
-            // final message written in output can be <= the one in input,
-            // only some of its fields might be allowed (also remember that
-            // this class implicitly removes redundancy varint encoding of
-            // len-delimited field lengths). The final length varint (the
-            // return value of AppendLenDelim()) will be filled when popping
-            // from |stack_|.
-            auto size_field =
-                AppendLenDelim(token.field_id, submessage_len, &out_);
-            push_next_state = true;
-            next_state.field_id = token.field_id;
-            next_state.msg_index = filter.nested_msg_index;
-            next_state.in_bytes_limit = submessage_len;
-            next_state.size_field = size_field.first;
-            next_state.size_field_len = size_field.second;
-            next_state.out_bytes_written_at_start = out_written();
-          } else {
-            // A string or bytes field, or a 0 length submessage.
-            state->eat_next_bytes = submessage_len;
-            if (filter.allowed && filter.filter_string_field()) {
-              state->action = StackState::kFilterString;
-              AppendLenDelim(token.field_id, submessage_len, &out_);
-              state->filter_string_ptr = out_;
-              state->filter_string_semantic_type = filter.semantic_type;
-            } else if (filter.allowed) {
-              state->action = StackState::kPassthrough;
-              AppendLenDelim(token.field_id, submessage_len, &out_);
-            } else {
-              state->action = StackState::kDrop;
-            }
-          }
-          break;
-      }  // switch(type)
+  // The mapping that holds the ring buffer, which the endpoint attaches to the
+  // service.
+  // Fixed for the life of this object.
+  const std::shared_ptr<SharedMemory>& memory() const { return memory_; }

-      if (PERFETTO_UNLIKELY(track_field_usage_)) {
-        IncrementCurrentFieldUsage(token.field_id, filter.allowed);
-      }
-    }  // if (token.valid)
-  }  // if (eat_next_bytes == 0)
+  // Reader state:

-  ++state->in_bytes;
-  while (state->in_bytes >= state->in_bytes_limit) {
-    PERFETTO_DCHECK(state->in_bytes == state->in_bytes_limit);
-    push_next_state = false;
+  // Called by the endpoint with the attach reply, on the endpoint thread.
+  // Enters kAttached on acceptance or kDetached on rejection. Does nothing
+  // after Disconnect(), because the reply can arrive after it.
+  void OnReaderAttachReply(bool reader_attached);

-    // We can't possibly write more than we read.
-    const uint32_t msg_bytes_written = static_cast<uint32_t>(
-        out_written() - state->out_bytes_written_at_start);
-    PERFETTO_DCHECK(msg_bytes_written <= state->in_bytes_limit);
+  // True in kAttached only. If false, nothing frees space in a full ring
+  // buffer. Writers call it on their own thread.
+  bool IsReaderAttached() const;

-    // Backfill the length field of the
-    proto_utils::WriteRedundantVarInt(msg_bytes_written, state->size_field,
-                                      state->size_field_len);
+  // Enters kDetached after rejection or disconnect, on the endpoint thread.
+  // Repeated calls are safe.
+  void Disconnect();

-    const uint32_t in_bytes_processes_for_last_msg = state->in_bytes;
-    stack_.pop_back();
-    PERFETTO_CHECK(!stack_.empty());
-    state = &stack_.back();
-    state->in_bytes += in_bytes_processes_for_last_msg;
-    if (PERFETTO_UNLIKELY(!tokenizer_.idle())) {
-      // If we hit this case, it means that we got to the end of a submessage
-      // while decoding a field. We can't recover from this and we don't want to
-      // propagate a broken sub-message.
-      return SetUnrecoverableErrorState();
-    }
+  // Writers:
+
+  // Creates a writer for |target_buffer|, on any thread. The caller owns the
+  // writer.
+  //
+  // Never returns null. Returns a NullTraceWriter, which discards all
+  // packets, if:
+  // - the state is kDetached, or
+  // - the SMB arbiter has no free WriterID (exhaustion or shutdown).
+  std::unique_ptr<TraceWriter> CreateTraceWriter(BufferID target_buffer,
+                                                 BufferExhaustedPolicy);
+
+  // The writer calls this on its thread after its final publication.
+  // - Releases its WriterID in the SMB arbiter.
+  // - The release can let the endpoint destroy this object. So the call must
+  //   be the writer's last access to it.
+  void OnWriterDestroyed(WriterID);
+
+  // The view that writers borrow. Fixed for the life of this object.
+  SharedRingBuffer* ring_buffer() { return &ring_buffer_; }
+
+  uint32_t drain_occupancy_threshold() const {
+    return drain_occupancy_threshold_;
   }

-  if (push_next_state) {
-    PERFETTO_DCHECK(tokenizer_.idle());
-    stack_.emplace_back(std::move(next_state));
-    state = &stack_.back();
+  // Drain requests, from writers on their own thread:
+
+  // Asks the service to read the ring buffer. See DrainUrgency.
+  void RequestDrain(DrainUrgency);
+
+  // Requests a drain after the writer publishes its data. Then runs |callback|
+  // on the endpoint thread. See the class comment for the order.
+  // - Does not wait for a service ack.
+  // - If the endpoint destroys this object before the drain task runs, the
+  //   task sends nothing. The endpoint thread still runs |callback|.
+  void Flush(std::function<void()>);
+
+  // The returned pointer can only be dereferenced on the endpoint thread.
+  base::WeakPtr<ProducerRingBufferArbiter> GetWeakPtr() const {
+    return weak_factory_.GetWeakPtr();
   }
-}

-void MessageFilter::SetUnrecoverableErrorState() {
-  error_ = true;
-  stack_.clear();
-  stack_.resize(1);
-  auto& state = stack_[0];
-  state.eat_next_bytes = UINT32_MAX;
-  state.in_bytes_limit = UINT32_MAX;
-  state.action = StackState::kDrop;
-  out_ = out_buf_.get();  // Reset the write pointer.
-}
+ private:
+  friend class test::ProducerRingBufferArbiterTestPeer;
+
+  // Only Create() calls this.
+  // |memory| must be non-null and satisfy NumChunksForRingBufferLayout().
+  // |drain_occupancy_threshold| is the number of outstanding positions at
+  // which a publication requests a drain, from 1 to the ring's chunk count.
+  ProducerRingBufferArbiter(base::TaskRunner* task_runner,
+                            ProducerEndpoint* endpoint,
+                            std::shared_ptr<SharedMemory> memory,
+                            uint32_t chunk_size,
+                            uint32_t drain_occupancy_threshold);
+
+  // Runs on the endpoint thread. CHECKs that the transition is valid.
+  void SetReaderState(ReaderState);
+
+  // Posts a drain task to the endpoint thread. Callable from any thread.
+  // - Without |force|, requests share a pending task.
+  // - |force| posts a new task so Flush() can place its callback after it.
+  // - The task clears |drain_task_pending_|, then sends DrainV2RingBuffer.
+  void PostDrainTask(bool force);
+
+  // --- Borrowed. They must outlive this object. ---
+
+  // Runs tasks on the endpoint thread. Writers post drain and flush
+  // requests here.
+  base::TaskRunner* const task_runner_;
+  // Sends drain requests to the service, on the endpoint thread only.
+  ProducerEndpoint* const endpoint_;
+  // The SMB arbiter. Any thread allocates and releases WriterIDs here.
+  SharedMemoryArbiter* const shared_memory_arbiter_;

-void MessageFilter::IncrementCurrentFieldUsage(uint32_t field_id,
-                                               bool allowed) {
-  // Slowpath. Used mainly in offline tools and tests to workout used fields in
-  // a proto.
-  PERFETTO_DCHECK(track_field_usage_);
+  // --- Ring buffer mapping and view. Fixed after construction. ---

-  // Field path contains a concatenation of varints, one for each nesting level.
-  // e.g. y in message Root { Sub x = 2; }; message Sub { SubSub y = 7; }
-  // is encoded as [varint(2) + varint(7)].
-  // We use varint to take the most out of SSO (small string opt). In most cases
-  // the path will fit in the on-stack 22 bytes, requiring no heap.
-  std::string field_path;
+  // The shared memory that holds the ring buffer. Declared before
+  // |ring_buffer_|, which points into it.
+  //
+  // Mapping ownership depends on the transport:
+  // - In-process, producer and service share this pointer. The mapping stays
+  //   alive until both release it.
+  // - Over IPC, the service maps the fd separately and owns that mapping.
+  const std::shared_ptr<SharedMemory> memory_;
+  // The view that writers borrow.
+  SharedRingBuffer ring_buffer_;
+  // A publication asks for a drain when the ring buffer has at least this many
+  // outstanding positions.
+  // - The count covers the positions of all writers.
+  // - It includes reservations whose chunks are not published yet.
+  // - Fixed at construction and shared by all writers.
+  const uint32_t drain_occupancy_threshold_;
+
+  // --- Shared with writer threads. ---
+
+  // Writers read it from any thread. Only SetReaderState() writes it.
+  std::atomic<ReaderState> reader_state_{ReaderState::kPending};
+  // Nonzero while a shared drain task is pending. A request without |force|
+  // sets it before it posts that task. The task clears it before it sends
+  // DrainV2RingBuffer.
+  std::atomic<uint32_t> drain_task_pending_{0};

-  auto append_field_id = [&field_path](uint32_t id) {
-    uint8_t buf[10];
-    uint8_t* end = proto_utils::WriteVarInt(id, buf);
-    field_path.append(reinterpret_cast<char*>(buf),
-                      static_cast<size_t>(end - buf));
-  };
+  PERFETTO_THREAD_CHECKER(thread_checker_)

-  // Append all the ancestors IDs from the state stack.
-  // The first entry of the stack has always ID 0 and we skip it (we don't know
-  // the ID of the root message itself).
-  PERFETTO_DCHECK(stack_.size() >= 2 && stack_[1].field_id == 0);
-  for (size_t i = 2; i < stack_.size(); ++i)
-    append_field_id(stack_[i].field_id);
-  // Append the id of the field in the current message.
-  append_field_id(field_id);
-  field_usage_[field_path] += allowed ? 1 : -1;
-}
+  // Any thread can call GetWeakPtr(). Only the endpoint thread dereferences
+  // the weak pointers.
+  base::WeakPtrFactory<ProducerRingBufferArbiter> weak_factory_{this};
+};

-}  // namespace protozero
-// gen_amalgamated begin source: src/protozero/filtering/message_filter_config.cc
-// gen_amalgamated begin header: src/protozero/filtering/message_filter_config.h
+}  // namespace tracing_v2
+}  // namespace perfetto
+
+#endif  // SRC_TRACING_V2_PRODUCER_RING_BUFFER_ARBITER_H_
+// gen_amalgamated begin header: src/tracing/v2/trace_writer_v2_impl.h
+// gen_amalgamated begin header: src/tracing/v2/shared_ring_buffer_writer.h
 /*
  * Copyright (C) 2026 The Android Open Source Project
  *
@@ -59353,28 +65099,347 @@ void MessageFilter::IncrementCurrentFieldUsage(uint32_t field_id,
  * limitations under the License.
  */

-#ifndef SRC_PROTOZERO_FILTERING_MESSAGE_FILTER_CONFIG_H_
-#define SRC_PROTOZERO_FILTERING_MESSAGE_FILTER_CONFIG_H_
+#ifndef SRC_TRACING_V2_SHARED_RING_BUFFER_WRITER_H_
+#define SRC_TRACING_V2_SHARED_RING_BUFFER_WRITER_H_

-// gen_amalgamated expanded: #include "perfetto/base/status.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/trace_config.gen.h"
-// gen_amalgamated expanded: #include "src/protozero/filtering/message_filter.h"
+#include <stdint.h>

-namespace protozero {
+#include <functional>
+#include <optional>
+#include <vector>

-// Loads a MessageFilter from the trace_filter field of a TraceConfig.
-// This sets up the string filter rules (both base and v54 chains) and loads
-// the bytecode (v1/v2 + v54 overlay).
-// On failure, returns a non-ok status with a descriptive error message.
-// On success, the caller may optionally call SetFilterRoot() to adjust the
-// root message (e.g. for per-packet filtering in the tracing service).
-perfetto::base::Status LoadMessageFilterConfig(
-    const perfetto::protos::gen::TraceConfig::TraceFilter& filt,
-    MessageFilter* filter);
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/basic_types.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_abi.h"

-}  // namespace protozero
+namespace perfetto::tracing_v2 {

-#endif  // SRC_PROTOZERO_FILTERING_MESSAGE_FILTER_CONFIG_H_
+// Writes the packet fragments produced by one TraceWriter into a
+// SharedRingBuffer.
+//
+// - A fragment is one packet, or part of a packet that crosses a chunk
+//   boundary. A chunk can hold several fragments. The writer handles their
+//   byte ranges and sizes without interpreting their contents.
+// - Use each instance from one thread at a time. Several instances can write
+//   to the same ring buffer concurrently.
+// - Keep the ring buffer's memory and SharedRingBuffer view alive until
+//   every writer is destroyed. The destructor still publishes its chunk.
+// - The writer gets a chunk in two steps:
+//   1. Reserve a write position. This moves write_pos.
+//   2. Claim the chunk at that position: change its state from Free to
+//      BeingWritten.
+//   Step 2 can fail, for example if an older writer still holds the chunk.
+//   The reserved position then stays unused, and only the reader can move
+//   past it.
+// - The writer only accesses the ring buffer. It does not contact the reader,
+//   and it does not apply a BufferExhaustedPolicy. TraceWriterV2Impl does
+//   both.
+// - A call that needs a new chunk makes at most num_chunks attempts to
+//   reserve and claim one. If none succeeds, the call returns at once with
+//   kFull or kClaimFailed. Only WaitForReadPosChange() blocks.
+//
+// The writer is in one of three states:
+// - Ready: no fragment is open. The writer can still hold the chunk that it
+//   published last. It appends the next fragment there if the fragment fits,
+//   without a new reservation.
+// - Fragment open: BeginFragment() returned a range. The caller writes the
+//   fragment's bytes into it.
+// - Relocation pending: the reader took the chunk before EndFragment()
+//   published the fragment, and the writer could not claim a replacement
+//   chunk. The writer keeps a copy of the fragment. See EndFragment().
+//
+// The calls and the states that allow them:
+// - BeginFragment(): Ready. On success, opens a fragment.
+// - EndFragment(): Fragment open. Publishes the fragment and returns to
+//   Ready, or enters Relocation pending.
+// - RetryRelocation(): Relocation pending. Publishes the saved copy and
+//   returns to Ready, or stays in Relocation pending.
+// - DropRelocation(): Relocation pending. Drops the saved copy and returns to
+//   Ready.
+// - FinishCurrentChunk(): Ready or Fragment open. Releases the held chunk, so
+//   the next fragment needs a new one. TraceWriterV2Impl::Flush() uses it.
+//   - In Fragment open, it abandons the open fragment and publishes the
+//     earlier fragments of the chunk.
+// - WaitForReadPosChange(): after a call returned kFull or kClaimFailed.
+// - RecordDataLoss(), has_pending_data_loss(), accessors and GetStats(): any
+//   state.
+class SharedRingBufferWriter {
+ public:
+  // Result of BeginFragment(), which may need a new chunk.
+  enum class BeginFragmentResult {
+    kSuccess,
+    // The ring buffer is structurally full: num_chunks positions are
+    // outstanding and the reader is behind. The call reserved nothing.
+    kFull,
+    // The call reserved a position, but could not claim its chunk, for example
+    // because an older writer still holds it.
+    //
+    // The reserved position stays unused until the reader moves past it.
+    // Chunks pinned by a stalled writer produce this without the ring buffer
+    // being full.
+    kClaimFailed,
+    // The request is larger than a freshly claimed chunk could hold. This is a
+    // caller bug, not backpressure.
+    kTooLarge,
+  };
+
+  // Result of EndFragment(), RetryRelocation() and FinishCurrentChunk(), which
+  // publish.
+  enum class EndFragmentResult {
+    kSuccess,
+    // The reader took the chunk before the publication, and the writer could
+    // not claim a replacement chunk. The reasons are the same as in
+    // BeginFragmentResult. The writer is then in Relocation pending.
+    kFull,
+    kClaimFailed,
+  };
+
+  // Result of RetryRelocation().
+  struct RetryRelocationResult {
+    EndFragmentResult result = EndFragmentResult::kFull;
+    // True if the call claimed at least one replacement chunk.
+    // - A successful claim ends one acquisition. A caller that limits each
+    //   acquisition, such as TraceWriterV2Impl, then starts a new limit.
+    // - With kFull or kClaimFailed, the reader also took the last replacement
+    //   before its publication, and the next claim attempts failed.
+    bool acquired_replacement = false;
+  };
+
+  // A contiguous range for the caller to fill. Valid until the next call on
+  // this SharedRingBufferWriter.
+  struct FragmentRange {
+    BeginFragmentResult result = BeginFragmentResult::kFull;
+    uint8_t* begin = nullptr;
+    uint8_t* end = nullptr;
+  };
+
+  SharedRingBufferWriter(SharedRingBuffer* ring,
+                         WriterID writer_id,
+                         BufferID target_buffer);
+  ~SharedRingBufferWriter();
+
+  SharedRingBufferWriter(const SharedRingBufferWriter&) = delete;
+  SharedRingBufferWriter& operator=(const SharedRingBufferWriter&) = delete;
+  SharedRingBufferWriter(SharedRingBufferWriter&&) = delete;
+  SharedRingBufferWriter& operator=(SharedRingBufferWriter&&) = delete;
+
+  // Starts a fragment for the caller to fill.
+  // - On success, returns a writable range of at least |min_size| bytes.
+  // - Reuses space in the current chunk when possible. Otherwise, tries to
+  //   claim a new chunk. See the class comment.
+  // - Call EndFragment() after writing the bytes.
+  // - Only one fragment can be open at a time.
+  //
+  // |continues_from_prev| describes the packet this fragment belongs to:
+  // - true: continues the packet from this writer's previous chunk.
+  // - false: starts a new packet.
+  // The flag describes the first fragment of a chunk.
+  //
+  // If BeginFragment(..., true) cannot acquire space, the caller can abandon
+  // the rest of the packet. The next packet must start with
+  // BeginFragment(..., false), even though the previous chunk still marks a
+  // continuation. Only the caller knows that a new packet starts. The writer
+  // cannot infer |continues_from_prev| from the previous chunk's flag.
+  FragmentRange BeginFragment(uint32_t min_size, bool continues_from_prev);
+
+  // Ends the open fragment at |size| bytes and publishes it, so the reader
+  // can copy it. |size| must fit the range from BeginFragment().
+  //
+  // Set |continues_on_next| if the packet continues in the next chunk, which
+  // then starts with BeginFragment(..., true). The flag marks the chunk's last
+  // fragment, so the writer appends nothing more to this chunk.
+  //
+  // The reader can take a chunk while its last fragment is still open: it
+  // copies the published fragments and asks the writer to move the open one.
+  // EndFragment() then relocates the fragment:
+  // 1. It saves a copy and acknowledges the request, so the reader can reuse
+  //    the old chunk.
+  // 2. It claims a new chunk, copies the fragment there and publishes it. If
+  //    the reader takes this chunk too, it repeats.
+  // If step 2 cannot claim a chunk, returns kFull or kClaimFailed. The
+  // relocation is then pending: call RetryRelocation() or DropRelocation().
+  EndFragmentResult EndFragment(uint32_t size, bool continues_on_next);
+
+  // Tries again to claim a chunk for the saved fragment, and publishes it
+  // there, as EndFragment() does. Only in Relocation pending.
+  RetryRelocationResult RetryRelocation();
+
+  // Drops the saved fragment. The next publication sets kFlagDataLoss. Only in
+  // Relocation pending.
+  void DropRelocation();
+
+  // Blocks the calling thread. The wait requests at most |timeout_ms|.
+  // Scheduling can make the elapsed time longer.
+  //
+  // Call it after kFull or kClaimFailed. A return does not reserve space. Call
+  // again to try for a chunk.
+  // - With a futex: returns when read_pos differs from the value that the
+  //   last reservation attempt saw, at the timeout, or on a spurious wake. So
+  //   if the reader moved read_pos after that attempt, it returns at once.
+  // - Without a futex: sleeps for the next step of v1's backoff, 0, 8, 72, ...
+  //   microseconds, at most 100 ms per step. No sleep requests more than
+  //   |timeout_ms|. It does not check read_pos.
+  void WaitForReadPosChange(uint32_t timeout_ms);
+
+  // Publishes whatever is held and lets go of the chunk. Any open fragment is
+  // abandoned: its bytes were never counted, so nothing is published for it.
+  // Safe to call with nothing held. The destructor calls it.
+  EndFragmentResult FinishCurrentChunk();
+
+  // Records loss for the next chunk publication.
+  // - A cached chunk can still be reused, even when the ring buffer is full.
+  // - The publication sets kFlagDataLoss. The reader discards all published
+  //   fragments in that chunk. Even complete packets before or after the gap
+  //   are discarded. The flag cannot identify which packets are safe.
+  // - Data already delivered by the reader cannot be retracted.
+  void RecordDataLoss() { data_loss_pending_ = true; }
+
+  // True until a claimed or published chunk carries the recorded loss. This
+  // includes a loss flag that a pending relocation inherited from the
+  // rewritten chunk.
+  //
+  // TraceWriterV2Impl uses it so that kStallThenDrop does not stall again for
+  // each dropped packet.
+  bool has_pending_data_loss() const {
+    return data_loss_pending_ ||
+           (pending_relocation_ &&
+            (pending_relocation_->chunk_flags & kFlagDataLoss));
+  }
+
+  WriterID writer_id() const { return writer_id_; }
+  // Largest range that BeginFragment() can return.
+  uint32_t max_fragment_size() const { return max_fragment_size_; }
+
+  // For diagnostics only. The protocol never reads these counters.
+  // TODO(sashwinbalaji): Wire these counters into service statistics.
+  struct Stats {
+    uint64_t failed_claims = 0;
+    uint64_t fragments_dropped = 0;
+    uint64_t relocations = 0;
+  };
+  Stats GetStats() const { return stats_; }
+
+ private:
+  friend class test::SharedRingBufferInternalsForTest;
+
+  static constexpr uint32_t kNoFragmentOpen = UINT32_MAX;
+
+  // A saved fragment that waits for a replacement chunk. Its bytes are in
+  // |relocation_payload_|.
+  struct PendingRelocation {
+    // Flags for the replacement chunk's BeingWritten word. Only
+    // kFlagContinuesFromPrevChunk and kFlagDataLoss, inherited from the
+    // rewritten chunk.
+    uint32_t chunk_flags = 0;
+    // Sets kFlagContinuesOnNextChunk on the replacement's publication.
+    bool continues_on_next = false;
+  };
+
+  // Maximum payload available to one more fragment in the cached chunk, which
+  // must exist.
+  uint32_t MaxFragmentSizeInCurrentChunk() const;
+  ChunkState cur_chunk_state() const {
+    return ChunkStateOf(expected_state_word_);
+  }
+  bool has_open_fragment() const {
+    return cur_fragment_begin_ != kNoFragmentOpen;
+  }
+
+  FragmentRange BeginFragmentInCurrentChunk(uint32_t available);
+  BeginFragmentResult AcquireNewChunk(uint32_t continuation_flags);
+  // Only the just-ended fragment can be complete but unpublished:
+  // - A size of zero is a valid empty fragment.
+  // - nullopt means FinishCurrentChunk() has no fragment to publish.
+  EndFragmentResult ReleaseCurrentChunkAsComplete(
+      std::optional<uint32_t> fragment_size,
+      bool continues_on_next);
+  // Writes the saved fragment into the newly claimed chunk, as its only
+  // fragment.
+  void CopyRelocatedFragment(uint32_t fragment_size);
+  // Clears only this writer's cached chunk state. It does not modify the ring
+  // buffer.
+  void ResetCurrentChunk();
+
+  SharedRingBuffer* const ring_;
+  const WriterID writer_id_;
+  const BufferID target_buffer_;
+  const uint32_t chunk_size_;
+  // MaxFragmentSizeForEmptyChunk(chunk_size_), computed once.
+  const uint32_t max_fragment_size_;
+
+  // State cached for the chunk this writer currently owns.
+  uint8_t* cur_chunk_ = nullptr;
+  ChunkIndex cur_chunk_idx_ = ChunkIndex::FromIndex(0);
+  // The exact word this writer's next compare-and-swap expects. It can differ
+  // from the shared word once the reader has requested a rewrite.
+  uint32_t expected_state_word_ = 0;
+  // Payload grows from the header. Size entries grow backwards from the end.
+  // - payload_end_ is measured from the chunk start.
+  // - size_directory_bytes_ counts bytes used from the chunk end.
+  //
+  // Example: a 256-byte chunk with 200 payload bytes and 4 directory bytes.
+  //
+  //   0        6                     206                252         256
+  //   +--------+---------------------+------------------+-----------+
+  //   | header |  payload grows ---> |    free space    | <-- sizes |
+  //   +--------+---------------------+------------------+-----------+
+  //
+  // Here: chunk_size_ = 256, payload_end_ = 206, size_directory_bytes_ = 4.
+  // Directory start = chunk_size_ - size_directory_bytes_ = 252.
+  // Free bytes = chunk_size_ - payload_end_ - size_directory_bytes_ = 46.
+  //
+  // Byte offset from the chunk start to the end of finalized payload.
+  uint32_t payload_end_ = 0;
+  // Size-directory bytes used from the chunk end. Zero means no entries.
+  uint32_t size_directory_bytes_ = 0;
+  // Finalized fragments, published or not. expected_state_word_ contains the
+  // count already visible to the reader.
+  uint32_t num_fragments_ = 0;
+  // Byte offset from the chunk start to the open fragment, or kNoFragmentOpen.
+  uint32_t cur_fragment_begin_ = kNoFragmentOpen;
+
+  // Whether kFlagDataLoss still needs to be set in a chunk's state word.
+  // - Cleared after successfully claiming or publishing a chunk with the flag.
+  // - Kept pending if the reader requests a rewrite before publication.
+  //   AcquireNewChunk() then sets the flag in the replacement chunk.
+  // - A rewrite of BeingWritten(0) can move the chunk's loss flag into
+  //   |pending_relocation_|, not back into this flag. has_pending_data_loss()
+  //   checks both.
+  bool data_loss_pending_ = false;
+
+  // The read_pos that the last reservation attempt saw.
+  // WaitForReadPosChange() waits while read_pos keeps this value.
+  uint32_t read_pos_for_wait_ = 0;
+
+  // Stop trying the syscall if the build or kernel cannot provide it.
+  // Once false, this writer uses sleep backoff for later waits too.
+  bool use_futex_ = SharedRingBuffer::SupportsWriterWait();
+
+  // The next sleep of the backoff without futex. It grows with each sleep, up
+  // to 100 ms, across calls. It resets when the writer claims a chunk.
+  uint32_t fallback_sleep_us_ = 0;
+
+  // Set when a relocation cannot claim a replacement chunk. RetryRelocation()
+  // clears it after a claim. DropRelocation() also clears it.
+  std::optional<PendingRelocation> pending_relocation_;
+
+  // Unpublished fragment saved while changing chunks. Allocated on demand and
+  // reused. Writers that never relocate need no payload storage here.
+  std::vector<uint8_t> relocation_payload_;
+
+  // Test callback, called after each replacement claim, before the
+  // publication.
+  // - The callback requests a rewrite of the replacement, as the reader can.
+  //   The publication then fails, and the writer relocates again.
+  // Empty in production. Tests force the race without scheduling threads.
+  std::function<void()> after_replacement_claim_for_testing_;
+
+  Stats stats_;
+};
+
+}  // namespace perfetto::tracing_v2
+
+#endif  // SRC_TRACING_V2_SHARED_RING_BUFFER_WRITER_H_
 /*
  * Copyright (C) 2026 The Android Open Source Project
  *
@@ -59391,118 +65456,537 @@ perfetto::base::Status LoadMessageFilterConfig(
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/protozero/filtering/message_filter_config.h"
+#ifndef SRC_TRACING_V2_TRACE_WRITER_V2_IMPL_H_
+#define SRC_TRACING_V2_TRACE_WRITER_V2_IMPL_H_

 #include <cstdint>
+#include <functional>
+#include <memory>
 #include <optional>
-#include <string>

-// gen_amalgamated expanded: #include "perfetto/base/status.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/trace_config.gen.h"
-// gen_amalgamated expanded: #include "src/protozero/filtering/message_filter.h"
-// gen_amalgamated expanded: #include "src/protozero/filtering/string_filter.h"
+// gen_amalgamated expanded: #include "perfetto/base/proc_utils.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/basic_types.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_writer.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message_handle.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/root_message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/scattered_stream_writer.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/buffer_exhausted_policy.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_writer.h"

-namespace protozero {
+namespace perfetto::tracing_v2 {

-namespace {
+class ProducerRingBufferArbiter;

-using TraceFilter = perfetto::protos::gen::TraceConfig::TraceFilter;
-using StringFilterRule = TraceFilter::StringFilterRule;
+namespace test {
+class TraceWriterV2ImplTestPeer;
+}  // namespace test

-std::optional<StringFilter::Policy> ConvertPolicy(
-    TraceFilter::StringFilterPolicy policy) {
-  switch (policy) {
-    case TraceFilter::SFP_UNSPECIFIED:
-      return std::nullopt;
-    case TraceFilter::SFP_MATCH_REDACT_GROUPS:
-      return StringFilter::Policy::kMatchRedactGroups;
-    case TraceFilter::SFP_ATRACE_MATCH_REDACT_GROUPS:
-      return StringFilter::Policy::kAtraceMatchRedactGroups;
-    case TraceFilter::SFP_MATCH_BREAK:
-      return StringFilter::Policy::kMatchBreak;
-    case TraceFilter::SFP_ATRACE_MATCH_BREAK:
-      return StringFilter::Policy::kAtraceMatchBreak;
-    case TraceFilter::SFP_ATRACE_REPEATED_SEARCH_REDACT_GROUPS:
-      return StringFilter::Policy::kAtraceRepeatedSearchRedactGroups;
+// TraceWriter implementation backed by a tracing v2 shared ring buffer.
+//
+// Data path from the SDK to the service. "v" is a call or a write. "^" is a
+// read by the service. "<--" is an IPC request.
+//
+//          v1: SMB                           v2: ring buffer
+//
+//   data source (SDK)                 data source (SDK)
+//     | NewTracePacket()                | NewTracePacket()
+//     v                                 v
+//   TraceWriterImpl                   TraceWriterV2Impl (this class)
+//     |                                 |
+//     | GetNewChunk()                   | BeginFragment(), EndFragment()
+//     v                                 v
+//   SharedMemoryArbiterImpl           SharedRingBufferWriter
+//     | picks a free chunk,             | claims and publishes chunks
+//     | batches completed chunks        | lock-free. The bytes do not
+//     |                                 | go through
+//     |                                 | ProducerRingBufferArbiter.
+//     v                                 v
+//   [ SMB ]                           [ ring buffer ]
+//     ^                                 ^
+//     | copies the listed chunks        | copies all published chunks
+//     |                                 |
+//   service <-- CommitData IPC        service <-- DrainV2RingBuffer IPC
+//               from the arbiter:                 from the ring buffer arbiter:
+//               "chunks N, M are                  "read what is
+//               complete"                         published"
+//
+// The ring buffer arbiter is ProducerRingBufferArbiter. It sends the drain
+// requests of all writers of the ring buffer.
+//
+// Only this class calls both SharedRingBufferWriter and
+// ProducerRingBufferArbiter:
+// - SharedRingBufferWriter claims chunks, and publishes and relocates
+//   fragments.
+// - The arbiter delivers drain requests to the endpoint thread.
+//
+// This class decides:
+// - When to ask for a drain:
+//   - After a publication, if the ring buffer's outstanding positions reach
+//     the arbiter's drain occupancy threshold.
+//   - After failed claims, if the writer drops the data. Only the reader can
+//     move past those positions.
+//   - Before each wait for space.
+// - What to do when no chunk is free: wait for the reader and try again, or
+//   drop the packet. See TryStallForSpace().
+//
+// A nested message can span chunks, and the reader can copy an earlier
+// fragment while the message is still open. So this writer cannot patch
+// length fields later, as v1 does. It uses proto group encoding instead:
+// - The format: proto_utils::kProtoGroupEndByte.
+// - The service converts each packet back to length-delimited protobuf: see
+//   ProtoGroupRewriter.
+//
+// If the writer drops any part of a packet:
+// - Its remaining bytes go to the drop buffer.
+// - The next publication sets kFlagDataLoss. The reader discards all
+//   published fragments in that chunk and reports the gap.
+// - The service discards orphan continuations. It still delivers later
+//   complete packets from chunks without the flag.
+//
+// Threads:
+// - As with TraceWriterImpl, use each instance from one thread at a time.
+class TraceWriterV2Impl : public TraceWriter,
+                          public protozero::MessageFinalizationListener,
+                          public protozero::ScatteredStreamWriter::Delegate {
+ public:
+  // ProducerRingBufferArbiter::CreateTraceWriter() creates each writer.
+  //
+  // |ring_buffer_arbiter| supplies the ring buffer. Packet bytes go directly
+  // into it. The arbiter also receives drain requests and flushes. The
+  // destructor releases |id| through it.
+  //
+  // - |id|: the arbiter allocates it from the shared WriterID pool. See the
+  //   ownership diagram in ProducerRingBufferArbiter.
+  // - |target_buffer|: the service buffer for the packets. Each chunk header
+  //   stores it.
+  // - |policy|: what the writer does when the ring buffer is full. See
+  //   BufferExhaustedPolicy.
+  TraceWriterV2Impl(ProducerRingBufferArbiter* ring_buffer_arbiter,
+                    WriterID id,
+                    BufferID target_buffer,
+                    BufferExhaustedPolicy policy);
+  ~TraceWriterV2Impl() override;
+
+  TraceWriterV2Impl(const TraceWriterV2Impl&) = delete;
+  TraceWriterV2Impl& operator=(const TraceWriterV2Impl&) = delete;
+  TraceWriterV2Impl(TraceWriterV2Impl&&) = delete;
+  TraceWriterV2Impl& operator=(TraceWriterV2Impl&&) = delete;
+
+  // TraceWriter:
+  TracePacketHandle NewTracePacket() override;
+  void FinishTracePacket() override;
+  // Unlike v1, |callback| does not wait for a service ack. See
+  // ProducerRingBufferArbiter::Flush().
+  void Flush(std::function<void()> callback = {}) override;
+  WriterID writer_id() const override {
+    return ring_buffer_writer_.writer_id();
   }
-  return std::nullopt;
-}
+  uint64_t written() const override { return stream_writer_.written(); }
+  uint64_t drop_count() const override { return drop_count_; }

-std::optional<StringFilter::SemanticTypeMask> ConvertSemanticTypeMask(
-    const StringFilterRule& rule) {
-  // UNSPECIFIED (0) is treated as its own category - it only matches rules
-  // that explicitly include bit 0 in their mask. If no semantic types are
-  // specified, default to matching only UNSPECIFIED (bit 0).
-  if (rule.semantic_type().empty()) {
-    return StringFilter::SemanticTypeMask::Unspecified();
-  }
-  StringFilter::SemanticTypeMask mask;
-  for (const auto& type : rule.semantic_type()) {
-    auto semantic_type = static_cast<uint32_t>(type);
-    if (semantic_type >= StringFilter::SemanticTypeMask::kLimit) {
-      return std::nullopt;
-    }
-    mask.Set(semantic_type);
-  }
-  return mask;
+ private:
+  friend class test::TraceWriterV2ImplTestPeer;
+
+  using ClockFunction = base::TimeMillis (*)();
+
+  // protozero::MessageFinalizationListener:
+  void OnMessageFinalized(protozero::Message*) override;
+
+  // protozero::ScatteredStreamWriter::Delegate:
+  protozero::ContiguousMemoryRange GetNewBuffer() override;
+  uint8_t* AnnotatePatch(uint8_t*) override;
+
+  // Checks that no packet is open, before a new packet or a flush.
+  // - A caller can call Message::Finalize() directly, without the handle.
+  //   The message is then closed, but its last fragment stays unpublished.
+  //   This publishes it.
+  // - Raw stream callers skip finalization. They must call
+  //   FinishTracePacket() first, or the CHECK fails.
+  void EnsurePacketClosed();
+
+  // Obtains storage for the next fragment of the current packet: a new
+  // fragment in the ring buffer, or the drop buffer. NewTracePacket() calls it
+  // for the first fragment, GetNewBuffer() for each continuation.
+  // - On success, returns the fragment's range and ends drop mode.
+  // - If no chunk is free, applies the policy through TryStallForSpace(). If
+  //   the policy drops the packet, enters drop mode and returns the drop
+  //   buffer.
+  protozero::ContiguousMemoryRange BeginPacketFragment(
+      bool continues_from_prev);
+
+  // Publishes the open fragment, if any.
+  // - |continues_on_next| sets kFlagContinuesOnNextChunk.
+  // - After the publication, asks for a drain at the threshold.
+  // - If a relocation needs a new chunk, applies the policy through
+  //   TryStallForSpace(), with one stall deadline for each replacement
+  //   acquisition.
+  // - If the policy drops the fragment, enters drop mode for the rest of the
+  //   packet.
+  // - Returns with no relocation pending in |ring_buffer_writer_|.
+  void EndPacketFragment(bool continues_on_next);
+
+  // Decides what to do after |ring_buffer_writer_| found no free chunk: wait
+  // for the reader, or drop the data.
+  //
+  // Returns true after a wait. The caller then tries again to get a chunk.
+  // Returns false, without a wait, if the writer must drop the data:
+  // - The policy is kDrop.
+  // - The policy is kStallThenDrop, and an earlier loss is still unreported.
+  // - No reader is attached, so nothing frees space.
+  // - The stall deadline passed. Under kStall, this aborts instead.
+  //
+  // |claim_failed|: the failed call returned kClaimFailed. Its reserved
+  // positions stay unused until the reader moves past them. So a drop also
+  // asks for a drain.
+  //
+  // |stall_deadline|: the deadline of the current chunk acquisition. Pass an
+  // unset value on the first call for an acquisition, and the same value on
+  // each retry. The first wait sets it.
+  bool TryStallForSpace(bool claim_failed,
+                        std::optional<base::TimeMillis>* stall_deadline);
+
+  // Drops the rest of the open packet: its bytes go to GetDropBuffer() from now
+  // on.
+  // - If drop mode is not active yet, counts one drop and records the loss
+  //   for the next publication.
+  // - Drop mode lasts until a later packet gets a fragment in the ring buffer.
+  void EnterDropMode();
+
+  // Returns the process-wide drop buffer as a writable range. Only in drop
+  // mode.
+  protozero::ContiguousMemoryRange GetDropBuffer();
+
+  // Receives drain requests, Flush() and OnWriterDestroyed(). See the
+  // constructor.
+  ProducerRingBufferArbiter* const ring_buffer_arbiter_;
+
+  // Claims chunks and publishes fragments in the ring buffer.
+  SharedRingBufferWriter ring_buffer_writer_;
+
+  // What this writer does when the ring buffer has no free chunk.
+  const BufferExhaustedPolicy buffer_exhausted_policy_;
+
+  // Time source for stall deadlines. Tests replace it to advance time
+  // deterministically across acquisition attempts.
+  ClockFunction get_time_ms_ = &base::GetWallTimeMs;
+
+  // PID of the process that created this writer. A DCHECK uses it to detect
+  // a process fork during tracing, which this writer does not support.
+  const base::PlatformProcessId process_id_;
+
+  // Protozero writes packet bytes through this. It points into the open
+  // fragment, or into the drop buffer in drop mode.
+  protozero::ScatteredStreamWriter stream_writer_;
+
+  // A pointer, so this header does not need the generated TracePacket header.
+  // NewTracePacket() resets and reuses the same root message for every packet.
+  //
+  // TODO(sashwinbalaji): Consider std::optional<RootMessage<TracePacket>>.
+  // - It saves one heap allocation per writer, but needs the generated
+  //   TracePacket header here.
+  // - It waits because TraceWriterImpl makes the same choice. One decision
+  //   must cover both writers.
+  // - Revisit if a profile shows this allocation at writer creation.
+  std::unique_ptr<protozero::RootMessage<protos::pbzero::TracePacket>>
+      cur_packet_;
+
+  // Start of the open fragment in shared memory.
+  // Null after the fragment closes, and in drop mode.
+  uint8_t* fragment_begin_ = nullptr;
+
+  // True from NewTracePacket() until FinishTracePacket().
+  bool packet_open_ = false;
+
+  // True while writes go to the drop buffer. Stays true until a later
+  // NewTracePacket() gets a fragment in the ring buffer.
+  bool in_drop_mode_ = false;
+
+  // Sets first_packet_on_sequence on the first packet of this writer.
+  bool first_packet_on_sequence_ = true;
+
+  // Number of times this writer entered drop mode. Consecutive dropped
+  // packets count once, as in TraceWriterImpl.
+  uint64_t drop_count_ = 0;
+};
+
+}  // namespace perfetto::tracing_v2
+
+#endif  // SRC_TRACING_V2_TRACE_WRITER_V2_IMPL_H_
+/*
+ * Copyright (C) 2026 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.
+ */
+
+// gen_amalgamated expanded: #include "src/tracing/v2/producer_ring_buffer_arbiter.h"
+
+#include <algorithm>
+#include <utility>
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_macros.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/uuid.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory_arbiter.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_writer.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/tracing_service.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/core/data_source_config.h"
+// gen_amalgamated expanded: #include "src/tracing/core/null_trace_writer.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_abi.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/trace_writer_v2_impl.h"
+
+namespace perfetto::tracing_v2 {
+namespace {
+
+// Chooses a chunk size by weight, defaulting to kMinChunkSize.
+uint32_t ChooseChunkSize(const DataSourceConfig& config) {
+  using ChunkSizeOption =
+      DataSourceConfig::ExperimentalTracingV2Config::ChunkSizeOption;
+
+  const auto& options = config.experimental_tracing_v2().chunk_size_options();
+  auto weight_of = [](const ChunkSizeOption& option) -> uint64_t {
+    if (!IsValidChunkSize(option.size_bytes()))
+      return 0;
+    return option.has_weight() ? option.weight() : 1;
+  };
+
+  uint64_t total_weight = 0;
+  for (const ChunkSizeOption& option : options)
+    total_weight += weight_of(option);
+
+  if (total_weight == 0)
+    return kMinChunkSize;
+
+  // Walk the options until their cumulative weight passes |target|.
+  const uint64_t target =
+      static_cast<uint64_t>(base::Uuidv4().lsb()) % total_weight;
+  uint64_t cumulative_weight = 0;
+  for (const ChunkSizeOption& option : options) {
+    cumulative_weight += weight_of(option);
+    if (target < cumulative_weight)
+      return option.size_bytes();
+  }
+
+  // Not reached: the last cumulative weight is the total, which is above
+  // |target|, so the loop returns.
+  PERFETTO_DFATAL("ChooseChunkSize: no option for the target");
+  return kMinChunkSize;
+}
+
+// Returns the drain threshold, in positions, for a ring buffer of
+// |num_chunks| chunks.
+// |drain_occupancy_percent| must be -1 to 100, as validated by the service:
+// - -1: 1, so a writer asks for a drain after every publication.
+// - 0: |kDefaultDrainOccupancyPercent|% of |num_chunks|.
+// - 1 to 100: that percent of |num_chunks|.
+// The result is at least 1.
+uint32_t ComputeDrainOccupancyThreshold(uint32_t num_chunks,
+                                        int32_t drain_occupancy_percent) {
+  constexpr uint32_t kDefaultDrainOccupancyPercent = 25;
+
+  PERFETTO_DCHECK(drain_occupancy_percent >= -1 &&
+                  drain_occupancy_percent <= 100);
+  if (drain_occupancy_percent == -1)
+    return 1;
+  const uint32_t percent = drain_occupancy_percent == 0
+                               ? kDefaultDrainOccupancyPercent
+                               : static_cast<uint32_t>(drain_occupancy_percent);
+  const uint64_t threshold = uint64_t{num_chunks} * percent / 100;
+  return std::max(1u, static_cast<uint32_t>(threshold));
 }

 }  // namespace

-perfetto::base::Status LoadMessageFilterConfig(const TraceFilter& filt,
-                                               MessageFilter* filter) {
-  StringFilter& string_filter = filter->string_filter();
+// static
+base::StatusOr<std::unique_ptr<ProducerRingBufferArbiter>>
+ProducerRingBufferArbiter::Create(
+    base::TaskRunner* task_runner,
+    ProducerEndpoint* endpoint,
+    const DataSourceConfig& config,
+    size_t shmem_size_hint_bytes,
+    AllocateSharedMemoryFn allocate_shared_memory) {
+  const uint32_t chunk_size_bytes = ChooseChunkSize(config);

-  auto add_rule = [&](const auto& rule) -> perfetto::base::Status {
-    auto policy = ConvertPolicy(rule.policy());
-    if (!policy.has_value()) {
-      return perfetto::base::ErrStatus(
-          "Trace filter has invalid string filtering rules");
-    }
-    auto semantic_type = ConvertSemanticTypeMask(rule);
-    if (!semantic_type.has_value()) {
-      return perfetto::base::ErrStatus(
-          "Trace filter has invalid semantic types in string filtering rules");
-    }
-    string_filter.AddRule(*policy, rule.regex_pattern(),
-                          rule.atrace_payload_starts_with(), rule.name(),
-                          *semantic_type);
-    return perfetto::base::OkStatus();
-  };
+  ASSIGN_OR_RETURN(const size_t shmem_size_bytes,
+                   RingBufferSizeForShmSizeHint(shmem_size_hint_bytes,
+                                                TracingService::kMaxShmSize,
+                                                chunk_size_bytes));

-  // Load base string filter chain.
-  for (const auto& rule : filt.string_filter_chain().rules()) {
-    auto status = add_rule(rule);
-    if (!status.ok())
-      return status;
-  }
+  ASSIGN_OR_RETURN(std::shared_ptr<SharedMemory> memory,
+                   allocate_shared_memory(shmem_size_bytes));

-  // Load v54 string filter chain. Rules with matching names will replace
-  // existing rules; others will be appended.
-  for (const auto& rule : filt.string_filter_chain_v54().rules()) {
-    auto status = add_rule(rule);
-    if (!status.ok())
-      return status;
+  const uint32_t num_chunks = static_cast<uint32_t>(
+      (shmem_size_bytes - sizeof(RingBufferHeader)) / chunk_size_bytes);
+  const uint32_t drain_occupancy_threshold = ComputeDrainOccupancyThreshold(
+      num_chunks, config.experimental_tracing_v2().drain_occupancy_percent());
+
+  return std::unique_ptr<ProducerRingBufferArbiter>(
+      new ProducerRingBufferArbiter(task_runner, endpoint, std::move(memory),
+                                    chunk_size_bytes,
+                                    drain_occupancy_threshold));
+}
+
+ProducerRingBufferArbiter::ProducerRingBufferArbiter(
+    base::TaskRunner* task_runner,
+    ProducerEndpoint* endpoint,
+    std::shared_ptr<SharedMemory> memory,
+    uint32_t chunk_size,
+    uint32_t drain_occupancy_threshold)
+    : task_runner_(task_runner),
+      endpoint_(endpoint),
+      shared_memory_arbiter_(endpoint->MaybeSharedMemoryArbiter()),
+      memory_(std::move(memory)),
+      ring_buffer_(static_cast<uint8_t*>(memory_->start()),
+                   memory_->size(),
+                   chunk_size),
+      drain_occupancy_threshold_(drain_occupancy_threshold) {
+  PERFETTO_CHECK(shared_memory_arbiter_);
+}
+
+ProducerRingBufferArbiter::~ProducerRingBufferArbiter() {
+  Disconnect();
+}
+
+void ProducerRingBufferArbiter::OnReaderAttachReply(bool reader_attached) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  // The attach reply can arrive after Disconnect().
+  if (reader_state_.load() == ReaderState::kDetached)
+    return;
+  if (!reader_attached) {
+    PERFETTO_ELOG("tracing v2: ring buffer attachment failed");
+    Disconnect();
+    return;
   }
+  SetReaderState(ReaderState::kAttached);
+}

-  const std::string& bytecode_v1 = filt.bytecode();
-  const std::string& bytecode_v2 = filt.bytecode_v2();
-  const std::string& bytecode = bytecode_v2.empty() ? bytecode_v1 : bytecode_v2;
-  const std::string& overlay = filt.bytecode_overlay_v54();
-  if (!filter->LoadFilterBytecode(bytecode.data(), bytecode.size(),
-                                  overlay.empty() ? nullptr : overlay.data(),
-                                  overlay.size())) {
-    return perfetto::base::ErrStatus("Trace filter bytecode invalid");
+bool ProducerRingBufferArbiter::IsReaderAttached() const {
+  return reader_state_.load() == ReaderState::kAttached;
+}
+
+void ProducerRingBufferArbiter::Disconnect() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  SetReaderState(ReaderState::kDetached);
+}
+
+void ProducerRingBufferArbiter::SetReaderState(ReaderState next) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  const ReaderState current = reader_state_.load();
+  PERFETTO_CHECK(
+      next == ReaderState::kDetached ||
+      (current == ReaderState::kPending && next == ReaderState::kAttached));
+  reader_state_.store(next);
+}
+
+std::unique_ptr<TraceWriter> ProducerRingBufferArbiter::CreateTraceWriter(
+    BufferID target_buffer,
+    BufferExhaustedPolicy policy) {
+  if (PERFETTO_UNLIKELY(reader_state_.load() == ReaderState::kDetached))
+    return std::make_unique<NullTraceWriter>();
+
+  const WriterID writer_id =
+      shared_memory_arbiter_->AllocateTracingV2WriterID();
+  if (PERFETTO_UNLIKELY(!writer_id))
+    return std::make_unique<NullTraceWriter>();
+
+  return std::make_unique<TraceWriterV2Impl>(this, writer_id, target_buffer,
+                                             policy);
+}
+
+void ProducerRingBufferArbiter::OnWriterDestroyed(WriterID id) {
+  // TODO(sashwinbalaji): Make WriterID reuse safe between v2 and v1 writers.
+  // The problem:
+  // - The SMB arbiter can give this ID to a v1 writer at once.
+  // - The last ring buffer chunk of this writer can still wait for a drain.
+  // - TraceBufferV2 accepts one input format for each (producer, writer)
+  //   sequence. While it keeps the sequence, it rejects chunks of the other
+  //   format as ABI violations. So one of the two writers loses data.
+
+  // After this release, the endpoint can destroy this object. Do not access
+  // members after it.
+  shared_memory_arbiter_->ReleaseTracingV2WriterID(id);
+}
+
+void ProducerRingBufferArbiter::RequestDrain(DrainUrgency urgency) {
+  // See DrainUrgency::kUrgent.
+  if (urgency == DrainUrgency::kUrgent &&
+      task_runner_->RunsTasksOnCurrentThread()) {
+    endpoint_->DrainV2RingBuffer();
+    return;
   }
+  PostDrainTask(/*force=*/false);
+}

-  return perfetto::base::OkStatus();
+void ProducerRingBufferArbiter::Flush(std::function<void()> callback) {
+  // Set force=true to queue our own drain task before the callback. The
+  // endpoint thread then sends the drain request before it runs the callback.
+  //
+  // A shared drain task would leave a race:
+  // 1. Another writer sets the flag, then pauses before PostTask().
+  // 2. Flush() sees the flag and queues only its callback.
+  // 3. The callback runs before the other writer queues the drain task.
+  PostDrainTask(/*force=*/true);
+
+  if (callback)
+    task_runner_->PostTask(std::move(callback));
 }

-}  // namespace protozero
-// gen_amalgamated begin source: src/tracing/service/zlib_compressor.cc
-// gen_amalgamated begin header: src/tracing/service/zlib_compressor.h
+void ProducerRingBufferArbiter::PostDrainTask(bool force) {
+  // When |force| is false, requests share a pending drain task. The writer
+  // sets |drain_task_pending_| after it changes the ring buffer, so that task
+  // covers the change. The atomic operations below act on
+  // |drain_task_pending_|:
+  //
+  //   writer thread               endpoint thread (the pending task)
+  //   -------------               ----------------------------------
+  //   W1. publish a chunk, or     E1. exchange(0)
+  //       leave a failed claim    E2. send DrainV2RingBuffer
+  //   W2. fetch_or(1)
+  //       - was set: post nothing
+  //       - was clear: post a task
+  //
+  // If W2 finds |drain_task_pending_| already set, a drain is still due after
+  // W1. That request covers this change too, so this writer needs no extra
+  // task. E1 must be a read-modify-write for this:
+  // - Only W2 and E1 write the flag, and both are read-modify-writes.
+  // - If W2 reads 1, an earlier W2 posted the pending task. In the
+  //   modification order of the flag, no E1 comes between those two W2s.
+  // - So the next E1 comes after this W2. It reads the value of this W2 or
+  //   of a later W2.
+  // - So this W2 synchronizes with that E1, and W1 happens before the E2
+  //   after it.
+  //
+  // Flush() sets |force|. See Flush() for why it cannot share a pending task.
+  if (!force && drain_task_pending_.fetch_or(1))
+    return;
+
+  task_runner_->PostTask([weak_this = weak_factory_.GetWeakPtr()] {
+    if (!weak_this)
+      return;
+    PERFETTO_DCHECK_THREAD(weak_this->thread_checker_);
+    // E1, then E2. Clearing the flag first lets a later request post another
+    // task. E1 must be a read-modify-write: see above.
+    weak_this->drain_task_pending_.exchange(0);
+    weak_this->endpoint_->DrainV2RingBuffer();
+  });
+}
+
+}  // namespace perfetto::tracing_v2
+// gen_amalgamated begin source: src/tracing/v2/shared_ring_buffer.cc
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -59517,25 +66001,411 @@ perfetto::base::Status LoadMessageFilterConfig(const TraceFilter& filt,
  * limitations under the License.
  */

-#ifndef SRC_TRACING_SERVICE_ZLIB_COMPRESSOR_H_
-#define SRC_TRACING_SERVICE_ZLIB_COMPRESSOR_H_
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer.h"

-#include <vector>
+#include <limits.h>
+#include <stdint.h>

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_packet.h"
+#include <atomic>

-namespace perfetto {
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/futex.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_abi.h"

-#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
-void ZlibCompressFn(std::vector<TracePacket>*);
-#endif
+namespace perfetto::tracing_v2 {
+namespace {
+
+static_assert(kMaxFragmentSizeVarIntBytes ==
+                  protozero::proto_utils::kMessageLengthFieldSize,
+              "Ring buffer fragment sizes must use the same varint byte limit "
+              "as Protozero message lengths");
+
+// The buffer stores read_pos in the low 32 bits of rw_positions. Pass its
+// address to the futex API, but keep all C++ accesses on the containing
+// atomic<uint64_t>.
+//
+// The supported CPU/ABI set provides atomic observation of that 32-bit half
+// during the 64-bit CAS.
+const uint32_t* ReadPosFutexAddress(const std::atomic<uint64_t>* rw_positions) {
+  static_assert(PERFETTO_IS_LITTLE_ENDIAN(),
+                "The low-word futex requires read_pos to be the first four "
+                "bytes of rw_positions");
+  return reinterpret_cast<const uint32_t*>(rw_positions);
+}
+
+// Callers validate untrusted layouts with NumChunksForRingBufferLayout()
+// first. An invalid layout here is a bug.
+uint32_t CheckedNumChunks(const void* start, size_t size, uint32_t chunk_size) {
+  base::StatusOr<uint32_t> num_chunks =
+      NumChunksForRingBufferLayout(start, size, chunk_size);
+  if (!num_chunks.ok())
+    PERFETTO_FATAL("tracing v2: %s", num_chunks.status().c_message());
+  return *num_chunks;
+}
+
+}  // namespace
+
+// --- Construction. ---
+
+SharedRingBuffer::SharedRingBuffer(uint8_t* start,
+                                   size_t size,
+                                   uint32_t chunk_size)
+    : start_(start),
+      num_chunks_(CheckedNumChunks(start, size, chunk_size)),
+      chunk_size_(chunk_size) {}
+
+// --- Writer-side reservation. ---
+
+SharedRingBuffer::Reservation SharedRingBuffer::TryReserveWritePos() {
+  // The reader marks chunks Free before publishing read_pos.
+  // A writer that sees the new read_pos also sees those Free state words.
+  return TryReserveWritePosFromSnapshot(
+      header()->rw_positions.load(std::memory_order_acquire));
+}
+
+SharedRingBuffer::Reservation SharedRingBuffer::TryReserveWritePosFromSnapshot(
+    uint64_t rw_positions) {
+  RingBufferHeader* ring_header = header();
+  Reservation reservation{};
+  for (;;) {
+    const uint32_t write_pos = WritePosOf(rw_positions);
+    const uint32_t read_pos = ReadPosOf(rw_positions);
+    reservation.read_pos_for_wait = read_pos;
+
+    if (NumOutstandingPositions(write_pos, read_pos) >= num_chunks_) {
+      reservation.result = ReserveResult::kFull;
+      return reservation;
+    }
+
+    // (write_pos, read_pos) -> (write_pos + 1, read_pos).
+    //
+    // On failure, no position is reserved. The CAS stores the current write_pos
+    // and read_pos in the local rw_positions value. The loop rechecks capacity
+    // before another attempt. A competing update can come from:
+    // - Another writer that advances write_pos.
+    // - The reader that publishes read_pos.
+    //
+    // compare_exchange_weak can also fail spuriously.
+    //
+    // Proposed memory ordering:
+    // - Success: acquire to observe the Free words published with read_pos.
+    // - Failure: acquire for the same reason. The refreshed read_pos feeds
+    //   the next capacity check.
+    if (ring_header->rw_positions.compare_exchange_weak(
+            rw_positions, PackRwPositions(write_pos + 1, read_pos))) {
+      reservation.result = ReserveResult::kReserved;
+      reservation.chunk_pos = write_pos;
+      return reservation;
+    }
+  }
+}
+
+// --- Writer-side chunk transitions. ---
+
+bool SharedRingBuffer::TryAcquireChunkForWriting(uint32_t chunk_pos,
+                                                 uint32_t being_written_word) {
+  PERFETTO_DCHECK(ChunkStateOf(being_written_word) ==
+                  ChunkState::kBeingWritten);
+  PERFETTO_DCHECK(NumFragmentsOf(being_written_word) == 0);
+
+  // Free(wrap_count(chunk_pos)) -> BeingWritten(0).
+  //
+  // On failure, the chunk does not match this reservation's Free word:
+  // - The reader advanced the wrap count before this writer claimed the chunk.
+  // - A writer from an earlier reservation still owns the physical chunk, or
+  //   the reader has not reclaimed its RewriteAcknowledged state yet.
+  //
+  // The caller must reserve a new position. A retry against the returned word
+  // can claim a consumed position or overwrite another writer's chunk.
+  //
+  // Proposed memory ordering:
+  // - Success: acquire to observe the reader's release of Free.
+  //   The reader must finish copying before this writer overwrites the chunk.
+  // - Failure: relaxed because the returned word is ignored.
+  uint32_t expected = MakeFreeStateWordForPosition(chunk_pos, num_chunks_);
+  std::atomic<uint32_t>* state_word = chunk_state_word_for_position(chunk_pos);
+  return state_word->compare_exchange_strong(expected, being_written_word);
+}
+
+bool SharedRingBuffer::TryReleaseChunkAsComplete(ChunkIndex chunk_idx,
+                                                 uint32_t complete_word,
+                                                 uint32_t* expected) {
+  PERFETTO_DCHECK(ChunkStateOf(*expected) == ChunkState::kBeingWritten);
+  PERFETTO_DCHECK(ChunkStateOf(complete_word) == ChunkState::kComplete);
+
+  // BeingWritten(N) -> Complete(M).
+  //
+  // On failure, the reader requested a rewrite first. |*expected| receives
+  // RewriteRequested(N), and the caller relocates any unpublished fragment.
+  //
+  // Proposed memory ordering:
+  // - Success: release to publish M fragments and their sizes.
+  //   The first publication also makes BufferID visible.
+  // - Failure: acquire to observe the reader's RewriteRequested(N).
+  //   The reader must finish copying N fragments before the writer relocates.
+  std::atomic<uint32_t>* state_word = chunk_state_word_at(chunk_idx);
+  return state_word->compare_exchange_strong(*expected, complete_word);
+}
+
+bool SharedRingBuffer::TryReacquireChunkForWriting(ChunkIndex chunk_idx,
+                                                   uint32_t observed) {
+  PERFETTO_DCHECK(ChunkStateOf(observed) == ChunkState::kComplete);
+
+  // Complete(N) -> BeingWritten(N).
+  //
+  // On failure, the reader reclaimed the chunk first. The writer discards its
+  // cached handle and acquires another chunk.
+  //
+  // Proposed memory ordering:
+  // - Success: relaxed because reuse publishes no new bytes.
+  //   The read-modify-write extends Complete(N)'s release sequence.
+  //   The reader's acquire load still sees the N published fragments.
+  // - Failure: relaxed because the returned word is ignored.
+  uint32_t expected = observed;
+  std::atomic<uint32_t>* state_word = chunk_state_word_at(chunk_idx);
+  return state_word->compare_exchange_strong(
+      expected, ReplaceChunkState(observed, ChunkState::kBeingWritten));
+}
+
+bool SharedRingBuffer::TryAcknowledgeRewrite(ChunkIndex chunk_idx,
+                                             uint32_t observed) {
+  PERFETTO_DCHECK(ChunkStateOf(observed) == ChunkState::kRewriteRequested);
+
+  // RewriteRequested -> RewriteAcknowledged, with all other bits zero.
+  //
+  // On failure, report a protocol error. Only this writer can change
+  // RewriteRequested, so the observed word must still match.
+  //
+  // Proposed memory ordering:
+  // - Success: release to save the unpublished fragment before
+  //   acknowledgement. The reader can then reclaim the chunk.
+  // - Failure: relaxed because it only reports a protocol error.
+  uint32_t expected = observed;
+  std::atomic<uint32_t>* state_word = chunk_state_word_at(chunk_idx);
+  return state_word->compare_exchange_strong(expected,
+                                             kRewriteAcknowledgedStateWord);
+}
+
+// --- Reader-side chunk transitions. ---
+
+uint32_t SharedRingBuffer::LoadChunkStateWordAcquire(
+    ChunkIndex chunk_idx) const {
+  // The acquire load pairs with the writer's release publication of fragments
+  // and their sizes. The first publication also makes BufferID visible.
+  //
+  // If the writer reuses Complete(N) as BeingWritten(N), its CAS preserves the
+  // fragment count and extends the release sequence. This load still makes
+  // those N fragments, their sizes and BufferID visible when N is nonzero.
+  return chunk_state_word_at(chunk_idx)->load(std::memory_order_acquire);
+}
+
+uint32_t SharedRingBuffer::LoadWritePosRelaxed() const {
+  // This bounds the reader's pass. It does not show whether each reservation
+  // has published data. LoadChunkStateWordAcquire() provides that information.
+  return WritePosOf(header()->rw_positions.load(std::memory_order_relaxed));
+}
+
+uint32_t SharedRingBuffer::LoadNumOutstandingPositionsRelaxed() const {
+  const uint64_t rw_positions =
+      header()->rw_positions.load(std::memory_order_relaxed);
+  return NumOutstandingPositions(WritePosOf(rw_positions),
+                                 ReadPosOf(rw_positions));
+}
+
+bool SharedRingBuffer::TryRequestRewrite(ChunkIndex chunk_idx,
+                                         uint32_t* expected) {
+  PERFETTO_DCHECK(ChunkStateOf(*expected) == ChunkState::kBeingWritten);
+
+  // BeingWritten(N) -> RewriteRequested(N).
+  // |*expected| is the word the reader used to copy the N fragments.
+  //
+  // On failure, the writer published first. The reader discards its copy and
+  // retries this position immediately in Drain() to include the new fragments.
+  //
+  // Proposed memory ordering:
+  // - Success: release to finish that copy before the writer observes
+  //   RewriteRequested and relocates its unpublished fragment.
+  // - Failure: relaxed because the copy is discarded. The next attempt
+  //   acquire-loads the state word again before reading any payload.
+  std::atomic<uint32_t>* state_word = chunk_state_word_at(chunk_idx);
+  return state_word->compare_exchange_strong(
+      *expected, ReplaceChunkState(*expected, ChunkState::kRewriteRequested));
+}
+
+bool SharedRingBuffer::TryMoveFreeChunkToNextWrap(uint32_t chunk_pos,
+                                                  uint32_t* expected) {
+  PERFETTO_DCHECK(ChunkStateOf(*expected) == ChunkState::kFree);
+
+  // Free(wrap_count(chunk_pos)) -> Free(next wrap).
+  //
+  // On failure, the delayed writer claimed the chunk first. The reader retries
+  // this position immediately in Drain() to consume BeingWritten or Complete.
+  //
+  // Proposed memory ordering:
+  // - Success: release to publish the next Free wrap to a claiming writer.
+  // - Failure: relaxed because the next attempt acquire-loads the state
+  //   word again before reading any payload.
+  std::atomic<uint32_t>* state_word = chunk_state_word_for_position(chunk_pos);
+  return state_word->compare_exchange_strong(
+      *expected, MakeFreeWordForNextWrap(chunk_pos));
+}
+
+bool SharedRingBuffer::TryReleaseCompleteChunkAsFree(uint32_t chunk_pos,
+                                                     uint32_t* expected) {
+  PERFETTO_DCHECK(ChunkStateOf(*expected) == ChunkState::kComplete);
+
+  // Complete(N) -> Free(next wrap).
+  // |*expected| is the word the reader used to copy the N fragments.
+  //
+  // On failure, the writer reused the chunk first. It can also publish more
+  // fragments before the reader retries, so the N-fragment copy can be stale.
+  // Drain() reloads the state and copies the current published fragments.
+  // If the state is BeingWritten, the reader then requests a rewrite.
+  // Otherwise, it tries to release Complete again. The reader delivers the
+  // copy only after a successful transition, so retries cannot duplicate data.
+  //
+  // Proposed memory ordering:
+  // - Success: release to finish the copy before another writer acquires
+  //   Free and overwrites the chunk.
+  // - Failure: relaxed because the copy is discarded. The next attempt
+  //   acquire-loads the state word again before reading any payload.
+  std::atomic<uint32_t>* state_word = chunk_state_word_for_position(chunk_pos);
+  return state_word->compare_exchange_strong(
+      *expected, MakeFreeWordForNextWrap(chunk_pos));
+}
+
+bool SharedRingBuffer::TryReleaseRewriteAcknowledgedChunkAsFree(
+    uint32_t chunk_pos,
+    uint32_t* observed) {
+  // RewriteAcknowledged -> Free(next wrap).
+  // The expected word contains only the RewriteAcknowledged state bits.
+  //
+  // On failure, |*observed| receives the unexpected word. This is a protocol
+  // error because only this reader can change an acknowledged chunk.
+  //
+  // Proposed memory ordering:
+  // - Success: acq_rel for two handoffs:
+  //   - Acquire observes the writer's acknowledgement after it saves its
+  //     unpublished fragment.
+  //   - Release passes the chunk to the next writer after that copy.
+  // - Failure: relaxed because the returned word only diagnoses the
+  //   protocol error.
+  uint32_t expected = kRewriteAcknowledgedStateWord;
+  std::atomic<uint32_t>* state_word = chunk_state_word_for_position(chunk_pos);
+  const bool reclaimed = state_word->compare_exchange_strong(
+      expected, MakeFreeWordForNextWrap(chunk_pos));
+  if (PERFETTO_UNLIKELY(!reclaimed))
+    *observed = expected;
+  return reclaimed;
+}
+
+// --- Backpressure. ---
+
+SharedRingBuffer::WriterWaitResult SharedRingBuffer::WaitForReadPosChange(
+    uint32_t read_pos_for_wait,
+    uint32_t timeout_ms) {
+  PERFETTO_DCHECK(timeout_ms > 0);
+  if (!SupportsWriterWait())
+    return WriterWaitResult::kUnavailable;
+
+  RingBufferHeader* ring_header = header();
+
+  // To avoid a missed wake:
+  // - the writer counts itself as waiting before checking read_pos.
+  // - the reader publishes read_pos before checking the waiter count.
+  //
+  // The fences order each update before the other side's check. Together
+  // they prevent both checks from seeing an older value:
+  //
+  //   writer                         reader
+  //   ------                         ------
+  //   num_writers_waiting += 1       publish read_pos
+  //   seq_cst fence                  seq_cst fence
+  //   load read_pos                  load num_writers_waiting
+  //   FutexWait if unchanged         FutexWake if nonzero
+  //
+  // - Writer's fence first: the reader sees the waiter and wakes it.
+  // - Reader's fence first: the writer sees the new read_pos.
+  //   It does not sleep.
+  //
+  // num_writers_waiting is only a wake hint, so its accesses use relaxed order.
+  ring_header->num_writers_waiting.fetch_add(1, std::memory_order_relaxed);
+  std::atomic_thread_fence(std::memory_order_seq_cst);
+
+  WriterWaitResult result = WriterWaitResult::kRetry;
+  const uint32_t read_pos =
+      ReadPosOf(ring_header->rw_positions.load(std::memory_order_relaxed));
+  if (read_pos == read_pos_for_wait) {
+    // FutexWait checks the value again before sleeping. If read_pos changed
+    // after the load above, the call returns kValueMismatch and no wake is
+    // lost.
+    //
+    // Every result other than kError means "recheck capacity".
+    const base::FutexWaitResult wait_result =
+        base::FutexWait(ReadPosFutexAddress(&ring_header->rw_positions),
+                        read_pos_for_wait, timeout_ms);
+    result = wait_result == base::FutexWaitResult::kError
+                 ? WriterWaitResult::kUnavailable
+                 : WriterWaitResult::kRetry;
+  }
+
+  ring_header->num_writers_waiting.fetch_sub(1, std::memory_order_relaxed);
+  return result;
+}
+
+void SharedRingBuffer::PublishReadPos(uint32_t read_pos) {
+  // This load only supplies the initial expected value for the CAS below.
+  PublishReadPosFromSnapshot(
+      header()->rw_positions.load(std::memory_order_relaxed), read_pos);
+}
+
+void SharedRingBuffer::PublishReadPosFromSnapshot(uint64_t rw_positions,
+                                                  uint32_t read_pos) {
+  RingBufferHeader* ring_header = header();
+
+  // (write_pos, old read_pos) -> (write_pos, read_pos).
+  //
+  // On failure, the CAS stores the current positions in the local rw_positions.
+  // A writer can advance write_pos first, or compare_exchange_weak can fail
+  // spuriously. The loop preserves that write_pos and retries the read_pos
+  // update.
+  //
+  // Proposed memory ordering:
+  // - Success: release so writers that acquire the new read_pos also see
+  //   this pass's Free words.
+  // - Failure: relaxed because the returned word only supplies the next
+  //   CAS attempt. No payload access depends on it.
+  while (!ring_header->rw_positions.compare_exchange_weak(
+      rw_positions, ReplaceReadPos(rw_positions, read_pos))) {
+  }
+
+  if (!SupportsWriterWait())
+    return;
+
+  // See the missed-wake schedule in WaitForReadPosChange(). The fence
+  // orders read_pos publication before the waiter-count load.
+  std::atomic_thread_fence(std::memory_order_seq_cst);
+
+  if (ring_header->num_writers_waiting.load(std::memory_order_relaxed) == 0)
+    return;

-}  // namespace perfetto
+  // Wake everyone because one drain pass can free many chunks. Waking one
+  // writer could leave capacity unused.
+  //
+  // This runs once per pass rather than once per reclaimed chunk.
+  //
+  // Waits are bounded, so a failed wake delays writers but cannot strand them.
+  base::FutexWake(ReadPosFutexAddress(&ring_header->rw_positions), INT_MAX);
+}

-#endif  // SRC_TRACING_SERVICE_ZLIB_COMPRESSOR_H_
+}  // namespace perfetto::tracing_v2
+// gen_amalgamated begin source: src/tracing/v2/shared_ring_buffer_reader.cc
+// gen_amalgamated begin header: src/tracing/v2/shared_ring_buffer_reader.h
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2026 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.
@@ -59550,149 +66420,593 @@ void ZlibCompressFn(std::vector<TracePacket>*);
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/tracing/service/zlib_compressor.h"
+#ifndef SRC_TRACING_V2_SHARED_RING_BUFFER_READER_H_
+#define SRC_TRACING_V2_SHARED_RING_BUFFER_READER_H_

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+#include <stdint.h>

-// File compiles to nothing when the buildflag is off (e.g. SDK opt-out).
-#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+#include <functional>
+#include <vector>

-#include <zlib.h>
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/basic_types.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_abi.h"

-// gen_amalgamated expanded: #include "protos/perfetto/trace/trace.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/trace_packet.pbzero.h"
-// gen_amalgamated expanded: #include "src/tracing/service/packet_compressor_common.h"
+namespace perfetto::tracing_v2 {

-namespace perfetto {
+namespace test {
+class SharedRingBufferInternalsForTest;
+}

-namespace {
+// Reads packet fragments from one SharedRingBuffer.
+//
+// - Use each instance from one execution context. The ring buffer's storage and
+//   SharedRingBuffer view must outlive the reader.
+// - Reservations are consumed in order. A relocated fragment gets a later
+//   reservation.
+// - The reader never waits for a writer, but may retry when it loses a
+//   concurrent state transition.
+// - If a writer still owns a chunk, the reader copies its published fragments.
+//   This is a partial chunk read. The writer moves its unpublished fragment to
+//   a later reservation.
+// - A chunk marked kFlagDataLoss is consumed without delivering its fragments.
+//   The delegate receives a loss report instead.
+// - Fragment sizes are decoded once into reader-owned values, and the payload
+//   copy uses those values without rereading the shared size bytes. The
+//   delegate only ever sees reader-owned scratch, never a view of
+//   producer-controlled shared memory.
+class SharedRingBufferReader {
+ public:
+  // Result of consuming one position.
+  enum class ConsumeResult {
+    // No reservation at read_pos is visible yet.
+    // read_pos has caught up with the observed write_pos.
+    kNoData,
+    // A chunk was handed to the delegate and read_pos advanced.
+    kChunkRead,
+    // No payload was delivered. read_pos advanced and data can no longer be
+    // published for this position.
+    kPositionSkipped,
+    // A writer changed the chunk before the reader's CAS:
+    // - Claimed Free before the reader advanced its wrap count.
+    // - Published Complete before the reader requested a rewrite.
+    // - Reused Complete before the reader reclaimed it.
+    // read_pos stays unchanged. Drain() retries the same position immediately,
+    // within its per-position attempt limit.
+    kRetryImmediately,
+    // Invalid ring buffer state. This reader cannot continue.
+    // - The offending position is neither advanced nor reclaimed.
+    // - Earlier chunks in this drain may already have been delivered.
+    // - Drain() still publishes any progress made before the error.
+    kProtocolError,
+  };

-using packet_compressor::GetPreamble;
-using packet_compressor::kCompressSliceSize;
-using packet_compressor::Preamble;
-using packet_compressor::PreambleToSlice;
+  struct ChunkContents {
+    WriterID writer_id = 0;
+    BufferID target_buffer = 0;
+    // A bitwise OR of PayloadFlags.
+    uint32_t payload_flags = 0;
+    // A view into reader-owned scratch. Valid only for the duration of the
+    // Delegate call.
+    const protozero::ConstBytes* fragments = nullptr;
+    uint32_t num_fragments = 0;
+  };

-// A compressor for `TracePacket`s that uses zlib. The class is exposed for
-// testing.
-class ZlibPacketCompressor {
- public:
-  ZlibPacketCompressor();
-  ~ZlibPacketCompressor();
+  struct DrainResult {
+    uint32_t positions_consumed = 0;
+    ConsumeResult last_result = ConsumeResult::kNoData;

-  // Can be called multiple times, before Finish() is called.
-  void PushPacket(const TracePacket& packet);
+    // The caller should schedule another Drain() call.
+    bool needs_another_drain() const {
+      return last_result != ConsumeResult::kNoData &&
+             last_result != ConsumeResult::kProtocolError;
+    }
+  };

-  // Returned the compressed data. Can be called at most once. After this call,
-  // the object is unusable (PushPacket should not be called) and must be
-  // destroyed.
-  TracePacket Finish();
+  // Handles the drained fragments and loss reports. Must outlive the reader.
+  // Calls run synchronously from Drain() and must not reenter or destroy the
+  // reader.
+  class Delegate {
+   public:
+    virtual ~Delegate();
+
+    // Called after a chunk's published payload has been copied into
+    // reader-owned memory. The view is valid only for this call.
+    virtual void OnChunkRead(const ChunkContents&) = 0;
+
+    // Called when published data is discarded after validation fails or the
+    // chunk carries kFlagDataLoss. Also handles a flagged Complete chunk with
+    // no fragments. No OnChunkRead() call delivers those fragments.
+    //
+    // The delegate must handle packet recovery for this writer:
+    // - Discard any partial packet and remember the gap.
+    // - A later unflagged chunk may start with an orphan continuation.
+    //   Discard that fragment because its earlier bytes were lost.
+    // - Later fragments in that chunk can start complete, valid packets.
+    //   Deliver those packets and report the gap on the first one.
+    virtual void OnDataLoss(WriterID) = 0;
+  };
+
+  SharedRingBufferReader(SharedRingBuffer* ring, Delegate* delegate);
+  ~SharedRingBufferReader();
+
+  SharedRingBufferReader(const SharedRingBufferReader&) = delete;
+  SharedRingBufferReader& operator=(const SharedRingBufferReader&) = delete;
+  SharedRingBufferReader(SharedRingBufferReader&&) = delete;
+  SharedRingBufferReader& operator=(SharedRingBufferReader&&) = delete;
+
+  // Consumes up to |max_positions| logical reservations:
+  // - A position counts even when it delivers no fragments.
+  // - An unclaimed reservation uses the budget when the reader passes it.
+  // - A lost CAS retries the same position without using the position budget.
+  //   A separate attempt limit bounds retries at each position. Reaching it
+  //   leaves that position unchanged and requests another drain.
+  //
+  // Publishes read_pos once and wakes writers parked on a full ring buffer.
+  // The budget bounds copying and delegate callbacks in this pass.
+  // This lets other tasks run between passes over a large ring buffer.
+  DrainResult Drain(uint32_t max_positions);
+
+  bool has_protocol_error() const { return has_protocol_error_; }
+  uint32_t read_pos() const { return read_pos_; }
+
+  // For diagnostics only. The protocol never reads these counters.
+  // TODO(sashwinbalaji): Wire these counters into service statistics.
+  struct Stats {
+    uint64_t positions_skipped = 0;
+    // Successful BeingWritten -> RewriteRequested transitions.
+    uint64_t rewrite_requests = 0;
+    uint64_t chunks_read = 0;
+    uint64_t malformed_chunks = 0;
+    uint64_t unsupported_format_chunks = 0;
+  };
+  Stats GetStats() const { return stats_; }

  private:
-  void PushData(const void* data, uint32_t size);
-  void NewOutputSlice();
-  void PushCurSlice();
+  friend class test::SharedRingBufferInternalsForTest;

-  z_stream stream_;
-  size_t total_new_slices_size_ = 0;
-  std::vector<Slice> new_slices_;
-  std::unique_ptr<uint8_t[]> cur_slice_;
-};
+  // Consumes at most one position. Drain() publishes read_pos once per pass.
+  ConsumeResult ConsumeNextPosition();

-ZlibPacketCompressor::ZlibPacketCompressor() {
-  memset(&stream_, 0, sizeof(stream_));
-  int status = deflateInit(&stream_, 6);
-  PERFETTO_CHECK(status == Z_OK);
-}
+  enum class CopiedChunkStatus {
+    kNoFragments,
+    kReady,
+    kMalformed,
+    kUnsupportedFormat,
+    kDataLoss,
+  };

-ZlibPacketCompressor::~ZlibPacketCompressor() {
-  int status = deflateEnd(&stream_);
-  PERFETTO_CHECK(status == Z_OK);
-}
+  // Validates and copies the published fragments.
+  // Skips payload access if kFlagDataLoss is set. Otherwise validates sizes
+  // and format before copying.
+  // The caller handles ownership before reporting any data loss.
+  CopiedChunkStatus CopyPublishedFragments(ChunkIndex chunk_idx,
+                                           uint32_t state_word);
+
+  // Called only after winning the position's compare-and-swap.
+  // Delivers valid, unflagged fragments to the delegate. Reports discarded
+  // chunks as loss.
+  ConsumeResult HandleCopiedChunk(CopiedChunkStatus);
+
+  // Latches the error and logs |reason| once, together with the position and
+  // the offending word. read_pos is not advanced.
+  ConsumeResult StopOnProtocolError(const char* reason, uint32_t state_word);
+
+  SharedRingBuffer* const ring_;
+  Delegate* const delegate_;
+  const uint32_t num_chunks_;
+  const uint32_t chunk_size_;
+
+  // The reader owns this value and publishes it once per Drain().
+  uint32_t read_pos_ = 0;
+  bool has_protocol_error_ = false;
+
+  // Test callback, called just before the reader's chunk-state CAS.
+  // - The reader has loaded the state and copied any readable fragments.
+  // - The callback makes a writer claim, publish or reuse the chunk.
+  //   The reader's CAS then fails because it expects the earlier state.
+  // - Tests verify that Drain() retries without advancing read_pos or
+  //   delivering fragments from the failed attempt.
+  // Empty in production. Tests force the race without scheduling threads.
+  std::function<void()> before_state_transition_for_testing_;
+
+  // TODO(sashwinbalaji): Revisit both scratch vectors:
+  // - Check heap/stack allocation in production and tests before enlarging
+  //   the reader with inline std::array members.
+  // - copied_payload_: establish a common maximum chunk size, then use a byte
+  //   array of that size.
+  // - copied_fragments_: use kMaxFragmentsPerChunk inline ConstBytes entries.
+  // - Measure whether this + offset access improves the read/copy path over
+  //   following pointers to separate vector allocations.
+
+  // Published fragment bytes copied out of shared memory.
+  std::vector<uint8_t> copied_payload_;
+
+  // Each fragment's decoded size and pointer into copied_payload_.
+  std::vector<protozero::ConstBytes> copied_fragments_;
+
+  // Chunk metadata and a view of copied_fragments_ passed to OnChunkRead().
+  ChunkContents copied_chunk_;
+
+  Stats stats_;
+};

-void ZlibPacketCompressor::PushPacket(const TracePacket& packet) {
-  // We need to be able to tokenize packets in the compressed stream, so we
-  // prefix a proto preamble to each packet. The compressed stream looks like a
-  // valid Trace proto.
-  Preamble preamble =
-      GetPreamble<protos::pbzero::Trace::kPacketFieldNumber>(packet.size());
-  PushData(preamble.buf.data(), preamble.size);
-  for (const Slice& slice : packet.slices()) {
-    PushData(slice.start, static_cast<uint32_t>(slice.size));
-  }
-}
+}  // namespace perfetto::tracing_v2

-void ZlibPacketCompressor::PushData(const void* data, uint32_t size) {
-  stream_.next_in = const_cast<Bytef*>(static_cast<const Bytef*>(data));
-  stream_.avail_in = static_cast<uInt>(size);
-  while (stream_.avail_in != 0) {
-    if (stream_.avail_out == 0) {
-      NewOutputSlice();
-    }
-    int status = deflate(&stream_, Z_NO_FLUSH);
-    PERFETTO_CHECK(status == Z_OK);
-  }
-}
+#endif  // SRC_TRACING_V2_SHARED_RING_BUFFER_READER_H_
+/*
+ * Copyright (C) 2026 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.
+ */

-TracePacket ZlibPacketCompressor::Finish() {
-  for (;;) {
-    int status = deflate(&stream_, Z_FINISH);
-    if (status == Z_STREAM_END)
-      break;
-    PERFETTO_CHECK(status == Z_OK || status == Z_BUF_ERROR);
-    NewOutputSlice();
-  }
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_reader.h"

-  PushCurSlice();
+#include <stdint.h>

-  TracePacket packet;
-  packet.AddSlice(PreambleToSlice(
-      GetPreamble<protos::pbzero::TracePacket::kCompressedPacketsFieldNumber>(
-          total_new_slices_size_)));
-  for (auto& slice : new_slices_) {
-    packet.AddSlice(std::move(slice));
-  }
-  return packet;
-}
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_abi.h"

-void ZlibPacketCompressor::NewOutputSlice() {
-  PushCurSlice();
-  cur_slice_ = std::make_unique<uint8_t[]>(kCompressSliceSize);
-  stream_.next_out = reinterpret_cast<Bytef*>(cur_slice_.get());
-  stream_.avail_out = kCompressSliceSize;
-}
+namespace perfetto::tracing_v2 {
+namespace {

-void ZlibPacketCompressor::PushCurSlice() {
-  if (cur_slice_) {
-    total_new_slices_size_ += kCompressSliceSize - stream_.avail_out;
-    new_slices_.push_back(Slice::TakeOwnership(
-        std::move(cur_slice_), kCompressSliceSize - stream_.avail_out));
-  }
+// Bound attempts at one position. For M = kMaxFragmentsPerChunk:
+// - Each fragment can defeat the reader's CAS twice:
+//   1. Publication: BeingWritten(N) -> Complete(N + 1).
+//   2. Reuse: Complete(N + 1) -> BeingWritten(N + 1).
+//   Allow M attempts for each, giving 2 * M.
+// - Add one for the initial Free -> BeingWritten claim.
+// - Add one for the reader's successful CAS, giving 2 * M + 2.
+//
+// This is conservative. SharedRingBufferWriter stops reusing at M fragments,
+// so there are at most M - 1 reuses. Zero-byte fragments still use the count.
+// FinishCurrentChunk() drops the cached handle even when abandoning a fragment.
+// The limit also bounds work if another producer keeps changing the state
+// without increasing the fragment count.
+constexpr uint32_t kMaxAttemptsPerPosition = 2 * kMaxFragmentsPerChunk + 2;
+
+// For the protocol-error log only.
+const char* ChunkStateName(ChunkState state) {
+  switch (state) {
+    case ChunkState::kFree:
+      return "Free";
+    case ChunkState::kBeingWritten:
+      return "BeingWritten";
+    case ChunkState::kComplete:
+      return "Complete";
+    case ChunkState::kRewriteRequested:
+      return "RewriteRequested";
+    case ChunkState::kRewriteAcknowledged:
+      return "RewriteAcknowledged";
+    case ChunkState::kReserved5:
+    case ChunkState::kReserved6:
+    case ChunkState::kReserved7:
+      return "reserved";
+  }
+  return "unknown";
 }

 }  // namespace

-void ZlibCompressFn(std::vector<TracePacket>* packets) {
-  if (packets->empty()) {
-    return;
+SharedRingBufferReader::Delegate::~Delegate() = default;
+
+SharedRingBufferReader::SharedRingBufferReader(SharedRingBuffer* ring,
+                                               Delegate* delegate)
+    : ring_(ring),
+      delegate_(delegate),
+      num_chunks_(ring->num_chunks()),
+      chunk_size_(ring->chunk_size()) {
+  // Reserve enough scratch for any chunk format so draining does not allocate.
+  copied_payload_.reserve(chunk_size_);
+  copied_fragments_.reserve(kMaxFragmentsPerChunk);
+}
+
+SharedRingBufferReader::~SharedRingBufferReader() = default;
+
+SharedRingBufferReader::DrainResult SharedRingBufferReader::Drain(
+    uint32_t max_positions) {
+  const uint32_t start_pos = read_pos_;
+  DrainResult result{};
+  for (uint32_t i = 0; i < max_positions; ++i) {
+    // A failed CAS leaves read_pos_ unchanged. Retry the same position with
+    // a fresh state and fragment copy before it counts towards max_positions.
+    // Limit attempts so repeated writer updates cannot monopolize this pass.
+    for (uint32_t attempt = 0; attempt < kMaxAttemptsPerPosition; ++attempt) {
+      result.last_result = ConsumeNextPosition();
+      if (result.last_result != ConsumeResult::kRetryImmediately)
+        break;
+    }
+    // If every attempt lost, yield with another drain requested. Contention
+    // alone is not a protocol error. Publish earlier progress below.
+    if (result.last_result != ConsumeResult::kChunkRead &&
+        result.last_result != ConsumeResult::kPositionSkipped) {
+      break;
+    }
   }

-  ZlibPacketCompressor stream;
-
-  for (const TracePacket& packet : *packets) {
-    stream.PushPacket(packet);
+  // Unsigned subtraction also counts positions across uint32_t rollover.
+  // Advancing from UINT32_MAX to zero consumes one position.
+  result.positions_consumed = read_pos_ - start_pos;
+  if (result.positions_consumed != 0) {
+    // One publication and at most one wake cover the whole pass. Until this
+    // point writers can only under-estimate free capacity.
+    //
+    // TODO(sashwinbalaji): benchmark useful publication batch sizes.
+    ring_->PublishReadPos(read_pos_);
   }
+  return result;
+}

-  TracePacket packet = stream.Finish();
+// The shared lifecycle is in shared_ring_buffer_abi.h.
+//
+// The races this function has to handle:
+//
+// 1. Free: advance to the next wrap, racing the delayed writer's claim.
+// 2. BeingWritten: copy N fragments, then request RewriteRequested(N), racing
+//    the writer's publication of Complete(M).
+// 3. Complete: copy the fragments, then reclaim, racing the writer's reuse.
+// 4. RewriteRequested: skip. The writer still owns the chunk. Once it becomes
+//    RewriteAcknowledged, only the reader may reclaim it on a later traversal.
+//
+// In the first three cases, a failed CAS leaves read_pos unchanged. Drain()
+// retries immediately. Deliver the copy only after a successful transition,
+// so a retry cannot deliver the same fragments twice.
+//
+// RewriteAcknowledged has no competing writer transition. On failure to reclaim
+// it, report a protocol error.
+SharedRingBufferReader::ConsumeResult
+SharedRingBufferReader::ConsumeNextPosition() {
+  if (PERFETTO_UNLIKELY(has_protocol_error_))
+    return ConsumeResult::kProtocolError;
+
+  // A relaxed load is enough here. An older write_pos only shortens this
+  // drain pass, which is still correct. A later pass can consume the rest.
+  const uint32_t write_pos = ring_->LoadWritePosRelaxed();
+  const uint32_t outstanding = NumOutstandingPositions(write_pos, read_pos_);
+  if (outstanding == 0)
+    return ConsumeResult::kNoData;
+  if (PERFETTO_UNLIKELY(outstanding > num_chunks_)) {
+    // A legal writer cannot reserve more than num_chunks outstanding
+    // positions.
+    has_protocol_error_ = true;
+    PERFETTO_ELOG(
+        "tracing v2: stopping ring buffer reader: write_pos %u is %u "
+        "positions ahead of read_pos %u, which is more than the %u chunks "
+        "in the ring buffer",
+        write_pos, outstanding, read_pos_, num_chunks_);
+    return ConsumeResult::kProtocolError;
+  }
+
+  // read_pos_ is the next logical position.
+  const uint32_t chunk_pos = read_pos_;
+  const ChunkIndex chunk_idx = ChunkIndex::FromPosition(chunk_pos, num_chunks_);
+
+  // A failed compare-and-swap replaces this with the word that won.
+  uint32_t state_word = ring_->LoadChunkStateWordAcquire(chunk_idx);
+
+  switch (ChunkStateOf(state_word)) {
+    case ChunkState::kFree: {
+      // Check reserved bits first. Reclaiming must not hide an invalid word.
+      if (PERFETTO_UNLIKELY((state_word & ~kWriterIDMask) != 0))
+        return StopOnProtocolError("Free word has reserved bits", state_word);
+      // Only this reader advances the wrap. A different wrap here is an error.
+      const uint32_t expected_free_word =
+          MakeFreeStateWordForPosition(chunk_pos, num_chunks_);
+      if (PERFETTO_UNLIKELY(state_word != expected_free_word)) {
+        return StopOnProtocolError(
+            "Free word carries another position's wrap count", state_word);
+      }
+      if (before_state_transition_for_testing_)
+        before_state_transition_for_testing_();
+      // Advance the wrap count to consume this unclaimed reservation.
+      // On failure, the delayed writer claimed first. Drain() retries this
+      // position with the writer's BeingWritten or Complete state.
+      if (!ring_->TryMoveFreeChunkToNextWrap(chunk_pos, &state_word))
+        return ConsumeResult::kRetryImmediately;
+      ++read_pos_;
+      ++stats_.positions_skipped;
+      return ConsumeResult::kPositionSkipped;
+    }
+
+    case ChunkState::kBeingWritten: {
+      const auto status = CopyPublishedFragments(chunk_idx, state_word);
+      if (before_state_transition_for_testing_)
+        before_state_transition_for_testing_();
+      // Request a rewrite before read_pos_ advances, even if validation failed.
+      // On failure, the writer published first. Drain() retries with a fresh
+      // copy before it delivers fragments or reports loss for this position.
+      if (!ring_->TryRequestRewrite(chunk_idx, &state_word))
+        return ConsumeResult::kRetryImmediately;
+      ++read_pos_;
+      ++stats_.rewrite_requests;
+      return HandleCopiedChunk(status);
+    }
+
+    case ChunkState::kComplete: {
+      const auto status = CopyPublishedFragments(chunk_idx, state_word);
+      if (before_state_transition_for_testing_)
+        before_state_transition_for_testing_();
+      // Reclaim the chunk before read_pos_ advances, even if validation failed.
+      // On failure, the writer reused the chunk and can publish more fragments.
+      // Drain() reloads the state and copies the current published fragments.
+      // If the state is BeingWritten, that attempt requests a rewrite.
+      // Deliver fragments or report loss only after a successful transition.
+      if (!ring_->TryReleaseCompleteChunkAsFree(chunk_pos, &state_word))
+        return ConsumeResult::kRetryImmediately;
+      ++read_pos_;
+      // Report loss even if the Complete chunk has no fragments. The writer
+      // cannot reuse the reclaimed chunk, so no later publication can report
+      // its flag.
+      //
+      // For BeingWritten(0), the writer still owns the chunk and transfers
+      // kFlagDataLoss to the relocated fragment instead.
+      if (status == CopiedChunkStatus::kNoFragments &&
+          (copied_chunk_.payload_flags & kFlagDataLoss)) {
+        delegate_->OnDataLoss(copied_chunk_.writer_id);
+      }
+      return HandleCopiedChunk(status);
+    }

-  packets->clear();
-  packets->push_back(std::move(packet));
+    case ChunkState::kRewriteRequested:
+      // This chunk is still awaiting rewrite acknowledgement from its owner.
+      // The current reservation could not claim it. Advance read_pos without
+      // changing the chunk's state word or delivering fragments.
+      ++read_pos_;
+      ++stats_.positions_skipped;
+      return ConsumeResult::kPositionSkipped;
+
+    case ChunkState::kRewriteAcknowledged:
+      // Reclaim the chunk before advancing past this unclaimed reservation.
+      // No fragments are delivered. The chunk becomes available for a later
+      // reservation.
+      //
+      // The expected word contains only the RewriteAcknowledged state bits.
+      // Only this reader can change it after the writer's acknowledgement.
+      // On failure, the word was invalid or another actor changed it.
+      if (!ring_->TryReleaseRewriteAcknowledgedChunkAsFree(chunk_pos,
+                                                           &state_word)) {
+        return StopOnProtocolError(
+            ChunkStateOf(state_word) == ChunkState::kRewriteAcknowledged
+                ? "RewriteAcknowledged word has payload bits set"
+                : "RewriteAcknowledged word changed under the reader",
+            state_word);
+      }
+      ++read_pos_;
+      ++stats_.positions_skipped;
+      return ConsumeResult::kPositionSkipped;
+
+    case ChunkState::kReserved5:
+    case ChunkState::kReserved6:
+    case ChunkState::kReserved7:
+      // The reader cannot safely reclaim an unknown state.
+      return StopOnProtocolError("reserved chunk state", state_word);
+  }
+  PERFETTO_FATAL("tracing v2: unhandled chunk state word 0x%08x", state_word);
+}
+
+SharedRingBufferReader::CopiedChunkStatus
+SharedRingBufferReader::CopyPublishedFragments(ChunkIndex chunk_idx,
+                                               uint32_t state_word) {
+  copied_fragments_.clear();
+  copied_chunk_ = ChunkContents{};
+  copied_chunk_.writer_id = WriterIDOf(state_word);
+  copied_chunk_.payload_flags = PayloadFlagsOf(state_word);
+
+  const uint32_t num_fragments = NumFragmentsOf(state_word);
+  if (num_fragments == 0) {
+    // A writer may still be storing BufferID after claiming BeingWritten(0).
+    // Only its first fragment publication makes that store visible. Do not
+    // touch any bytes beyond the state word when no fragment is published.
+    return CopiedChunkStatus::kNoFragments;
+  }
+
+  // The loss flag does not identify where data was lost within the chunk.
+  // - Drop every published fragment, even a complete packet after the gap.
+  // - Do not read an unpublished fragment. The writer may relocate it.
+  // The caller must win the state transition before reporting the gap or
+  // advancing read_pos. See Delegate::OnDataLoss() for packet recovery.
+  if (state_word & kFlagDataLoss)
+    return CopiedChunkStatus::kDataLoss;
+
+  if (ChunkFormatOf(state_word) != ChunkFormat::kTargetBuffer)
+    return CopiedChunkStatus::kUnsupportedFormat;
+
+  // Decode first to find the payload end and the directory start.
+  // Until then, payload_begin is the decoder's lower bound. It prevents size
+  // reads from reaching the header, but does not prove the payload fits.
+  const uint32_t capacity = chunk_size_ - kTargetBufferPayloadOffset;
+  const uint8_t* chunk = ring_->chunk_at(chunk_idx);
+  const uint8_t* const payload_begin = chunk + kTargetBufferPayloadOffset;
+  const uint8_t* sizes_cursor = chunk + chunk_size_;
+  uint32_t total = 0;
+  for (uint32_t i = 0; i < num_fragments; ++i) {
+    const auto fragment_size =
+        ReadFragmentSizeReversed(payload_begin, &sizes_cursor);
+    if (!fragment_size || *fragment_size > capacity - total) {
+      return CopiedChunkStatus::kMalformed;
+    }
+    total += *fragment_size;
+    copied_fragments_.push_back(protozero::ConstBytes{nullptr, *fragment_size});
+  }
+
+  // The sizes are valid varints. Their payloads may still overlap the
+  // directory:
+  // - Count the bytes actually read, including non-minimal size encodings.
+  // - On overlap, reject the chunk before copying or delivering any payload.
+  // - sizes_cursor is local to this read attempt. Returning discards it.
+  //   No cursor change needs to be undone.
+  const uint32_t sizes_bytes =
+      static_cast<uint32_t>(chunk + chunk_size_ - sizes_cursor);
+  if (total > capacity - sizes_bytes)
+    return CopiedChunkStatus::kMalformed;
+
+  // Copy the published payload out of shared memory.
+  copied_payload_.assign(payload_begin, payload_begin + total);
+
+  // Point each fragment at its copy and collect the chunk metadata.
+  size_t offset = 0;
+  for (protozero::ConstBytes& fragment : copied_fragments_) {
+    fragment.data = copied_payload_.data() + offset;
+    offset += fragment.size;
+  }
+  copied_chunk_.target_buffer = LoadTargetBufferId(chunk);
+  copied_chunk_.fragments = copied_fragments_.data();
+  copied_chunk_.num_fragments = num_fragments;
+  return CopiedChunkStatus::kReady;
+}
+
+SharedRingBufferReader::ConsumeResult SharedRingBufferReader::HandleCopiedChunk(
+    CopiedChunkStatus status) {
+  switch (status) {
+    case CopiedChunkStatus::kReady:
+      ++stats_.chunks_read;
+      delegate_->OnChunkRead(copied_chunk_);
+      return ConsumeResult::kChunkRead;
+    case CopiedChunkStatus::kMalformed:
+      ++stats_.malformed_chunks;
+      delegate_->OnDataLoss(copied_chunk_.writer_id);
+      break;
+    case CopiedChunkStatus::kUnsupportedFormat:
+      ++stats_.unsupported_format_chunks;
+      delegate_->OnDataLoss(copied_chunk_.writer_id);
+      break;
+    case CopiedChunkStatus::kDataLoss:
+      delegate_->OnDataLoss(copied_chunk_.writer_id);
+      break;
+    case CopiedChunkStatus::kNoFragments:
+      break;
+  }
+
+  ++stats_.positions_skipped;
+  return ConsumeResult::kPositionSkipped;
 }

-}  // namespace perfetto
+SharedRingBufferReader::ConsumeResult
+SharedRingBufferReader::StopOnProtocolError(const char* reason,
+                                            uint32_t state_word) {
+  // Stop rather than trusting a malformed word from a producer. Log once.
+  has_protocol_error_ = true;
+  PERFETTO_ELOG(
+      "tracing v2: stopping ring buffer reader at position %u: %s "
+      "(chunk state word 0x%08x, %s)",
+      read_pos_, reason, state_word, ChunkStateName(ChunkStateOf(state_word)));
+  return ConsumeResult::kProtocolError;
+}

-#endif  // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
-// gen_amalgamated begin source: src/tracing/service/zstd_compressor.cc
-// gen_amalgamated begin header: src/tracing/service/zstd_compressor.h
+}  // namespace perfetto::tracing_v2
+// gen_amalgamated begin source: src/tracing/v2/shared_ring_buffer_writer.cc
 /*
  * Copyright (C) 2026 The Android Open Source Project
  *
@@ -59709,24 +67023,495 @@ void ZlibCompressFn(std::vector<TracePacket>* packets) {
  * limitations under the License.
  */

-#ifndef SRC_TRACING_SERVICE_ZSTD_COMPRESSOR_H_
-#define SRC_TRACING_SERVICE_ZSTD_COMPRESSOR_H_
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_writer.h"

-#include <vector>
+#include <stdint.h>
+#include <string.h>

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_packet.h"
+#include <algorithm>
+#include <optional>

-namespace perfetto {
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/basic_types.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_abi.h"

-#if PERFETTO_BUILDFLAG(PERFETTO_ZSTD)
-// Compresses `packets` with zstd at the given compression `level`.
-void ZstdCompressFn(std::vector<TracePacket>*, int level);
-#endif
+namespace perfetto::tracing_v2 {
+namespace {

-}  // namespace perfetto
+// The 100 ms sleep limit matches SharedMemoryArbiterImpl::GetNewChunk().
+constexpr uint32_t kMaxFallbackSleepUs = 100000;

-#endif  // SRC_TRACING_SERVICE_ZSTD_COMPRESSOR_H_
+}  // namespace
+
+SharedRingBufferWriter::SharedRingBufferWriter(SharedRingBuffer* ring,
+                                               WriterID writer_id,
+                                               BufferID target_buffer)
+    : ring_(ring),
+      writer_id_(writer_id),
+      target_buffer_(target_buffer),
+      chunk_size_(ring->chunk_size()),
+      max_fragment_size_(MaxFragmentSizeForEmptyChunk(chunk_size_)) {
+  // The WriterID goes into every chunk that this writer claims. The caller
+  // controls its lifetime.
+  //
+  // TraceWriterV2Impl releases it after its final publication. The service
+  // can still read chunks under the ID after that. See the TODO in
+  // ProducerRingBufferArbiter::OnWriterDestroyed().
+  PERFETTO_DCHECK(writer_id_ != 0 && writer_id_ <= kMaxWriterID);
+}
+
+SharedRingBufferWriter::~SharedRingBufferWriter() {
+  FinishCurrentChunk();
+}
+
+SharedRingBufferWriter::FragmentRange SharedRingBufferWriter::BeginFragment(
+    uint32_t min_size,
+    bool continues_from_prev) {
+  PERFETTO_DCHECK(!has_open_fragment());
+  // A new chunk now would put the next packet ahead of the saved fragment.
+  PERFETTO_DCHECK(!pending_relocation_);
+  // EndFragment(..., /*continues_on_next=*/true) discards the cached handle
+  // when a packet continues into another chunk. A continuation must claim a
+  // new chunk because only AcquireNewChunk() sets kFlagContinuesFromPrevChunk.
+  PERFETTO_DCHECK(!continues_from_prev || !cur_chunk_);
+
+  if (min_size > max_fragment_size_)
+    return FragmentRange{BeginFragmentResult::kTooLarge};
+
+  // Try the cached Complete chunk before a new reservation.
+  if (cur_chunk_) {
+    PERFETTO_DCHECK(cur_chunk_state() == ChunkState::kComplete);
+    const uint32_t available = MaxFragmentSizeInCurrentChunk();
+    // Pending loss does not prevent reuse, which requires no new reservation.
+    // The next publication sets kFlagDataLoss. The reader then discards all
+    // published fragments in this chunk, including those before the loss.
+    if (available >= min_size && ring_->TryReacquireChunkForWriting(
+                                     cur_chunk_idx_, expected_state_word_)) {
+      expected_state_word_ =
+          ReplaceChunkState(expected_state_word_, ChunkState::kBeingWritten);
+      return BeginFragmentInCurrentChunk(available);
+    }
+    // The chunk has insufficient space or the reader reclaimed it first.
+    // Discard the handle before the next claim.
+    ResetCurrentChunk();
+  }
+
+  const uint32_t flags = continues_from_prev ? kFlagContinuesFromPrevChunk : 0u;
+  const BeginFragmentResult result = AcquireNewChunk(flags);
+  if (result != BeginFragmentResult::kSuccess)
+    return FragmentRange{result};
+  return BeginFragmentInCurrentChunk(max_fragment_size_);
+}
+
+SharedRingBufferWriter::FragmentRange
+SharedRingBufferWriter::BeginFragmentInCurrentChunk(uint32_t available) {
+  PERFETTO_DCHECK(cur_chunk_);
+  PERFETTO_DCHECK(cur_chunk_state() == ChunkState::kBeingWritten);
+  PERFETTO_DCHECK(available > 0);
+  cur_fragment_begin_ = payload_end_;
+  return FragmentRange{/*result=*/BeginFragmentResult::kSuccess,
+                       /*begin=*/cur_chunk_ + cur_fragment_begin_,
+                       /*end=*/cur_chunk_ + cur_fragment_begin_ + available};
+}
+
+SharedRingBufferWriter::EndFragmentResult SharedRingBufferWriter::EndFragment(
+    uint32_t size,
+    bool continues_on_next) {
+  PERFETTO_DCHECK(has_open_fragment());
+  PERFETTO_DCHECK(cur_chunk_);
+  PERFETTO_DCHECK(cur_chunk_state() == ChunkState::kBeingWritten);
+  PERFETTO_DCHECK(size_directory_bytes_ <= chunk_size_);
+  PERFETTO_DCHECK(cur_fragment_begin_ <= chunk_size_ - size_directory_bytes_);
+  // The fragment must leave space for its size entry before the directory.
+  PERFETTO_DCHECK(
+      size <= MaxFragmentSizeForAvailableBytes(
+                  chunk_size_ - size_directory_bytes_ - cur_fragment_begin_));
+
+  // The reader cannot access this fragment until publication updates the shared
+  // fragment count. Write the size entry before that publication.
+  uint8_t* const chunk_end = cur_chunk_ + chunk_size_;
+  uint8_t* const sizes_begin =
+      WriteFragmentSizeReversed(chunk_end - size_directory_bytes_, size);
+  // sizes_begin points to the new size entry, before the existing entries.
+  // Subtract it from chunk_end to count all directory bytes, including the new
+  // entry. The directory grows towards lower addresses.
+  size_directory_bytes_ = static_cast<uint32_t>(chunk_end - sizes_begin);
+  payload_end_ = cur_fragment_begin_ + size;
+  ++num_fragments_;
+  cur_fragment_begin_ = kNoFragmentOpen;
+
+  return ReleaseCurrentChunkAsComplete(size, continues_on_next);
+}
+
+SharedRingBufferWriter::EndFragmentResult
+SharedRingBufferWriter::FinishCurrentChunk() {
+  PERFETTO_DCHECK(!pending_relocation_);
+  // Abandon any open fragment. Its bytes are outside the published count, so
+  // this does not affect the fragments the reader can already access.
+  cur_fragment_begin_ = kNoFragmentOpen;
+
+  EndFragmentResult result = EndFragmentResult::kSuccess;
+  if (cur_chunk_ && cur_chunk_state() == ChunkState::kBeingWritten) {
+    result = ReleaseCurrentChunkAsComplete(std::nullopt,
+                                           /*continues_on_next=*/false);
+  }
+  ResetCurrentChunk();
+  return result;
+}
+
+uint32_t SharedRingBufferWriter::MaxFragmentSizeInCurrentChunk() const {
+  PERFETTO_DCHECK(cur_chunk_);
+  if (num_fragments_ >= kMaxFragmentsPerChunk)
+    return 0;
+  PERFETTO_DCHECK(payload_end_ <= chunk_size_);
+  PERFETTO_DCHECK(size_directory_bytes_ <= chunk_size_ - payload_end_);
+  const uint32_t available_bytes =
+      chunk_size_ - payload_end_ - size_directory_bytes_;
+  return MaxFragmentSizeForAvailableBytes(available_bytes);
+}
+
+SharedRingBufferWriter::BeginFragmentResult
+SharedRingBufferWriter::AcquireNewChunk(uint32_t continuation_flags) {
+  PERFETTO_DCHECK(!cur_chunk_);
+
+  // A new chunk must preserve loss from either source:
+  // - data_loss_pending_: this writer has a loss to report.
+  // - continuation_flags: a rewrite of BeingWritten(0) left a loss unreported.
+  //   The replacement inherits that flag even if data_loss_pending_ is false.
+  const uint32_t flags =
+      continuation_flags | (data_loss_pending_ ? kFlagDataLoss : 0u);
+  const uint32_t being_written_word =
+      MakeDataStateWord(ChunkState::kBeingWritten, ChunkFormat::kTargetBuffer,
+                        flags, 0, writer_id_);
+  const uint32_t num_chunks = ring_->num_chunks();
+
+  // After num_chunks failed claims, return without a chunk.
+  //
+  // Other writers can reserve intervening positions, so our attempts
+  // can select the same physical chunk on different traversals.
+  // This limit bounds attempts, but does not guarantee a visit to every chunk.
+  uint32_t num_failed_claims = 0;
+
+  // Each attempt reserves a position, then tries to claim its physical chunk:
+  //
+  // 1. TryReserveWritePos() finds the ring buffer full.
+  //    - write_pos stays unchanged. No position was reserved.
+  //    - Return without a chunk below.
+  //
+  // 2. Reservation and TryAcquireChunkForWriting() both succeed.
+  //    - This writer now owns the chunk. Return it to the caller.
+  //
+  // 3. Reservation succeeds, but TryAcquireChunkForWriting() fails.
+  //    - The reader advanced the wrap count before we claimed the chunk.
+  //      We may have been descheduled after advancing write_pos.
+  //    - Or the chunk still belongs to an older reservation:
+  //      - Its writer claimed the chunk, then got descheduled.
+  //      - The reader copied its published fragments and set RewriteRequested.
+  //      - The reader advanced read_pos. It did not release the chunk.
+  //      - Advancing read_pos made room for another reservation.
+  //        TryReserveWritePos() checks the positions, not the chunk state.
+  //      - Our new reservation selects that same physical chunk.
+  //        The claim fails because the older writer has not acknowledged yet.
+  //    - Leave the reservation unclaimed. Try a new reservation with continue.
+  //    - After num_chunks failed claims, return without a chunk instead.
+  for (;;) {
+    const auto reservation = ring_->TryReserveWritePos();
+    read_pos_for_wait_ = reservation.read_pos_for_wait;
+
+    if (reservation.result == SharedRingBuffer::ReserveResult::kReserved) {
+      // Claim the chunk only if its Free word matches this reservation's wrap.
+      if (ring_->TryAcquireChunkForWriting(reservation.chunk_pos,
+                                           being_written_word)) {
+        cur_chunk_idx_ =
+            ChunkIndex::FromPosition(reservation.chunk_pos, num_chunks);
+        cur_chunk_ = ring_->chunk_at(cur_chunk_idx_);
+        expected_state_word_ = being_written_word;
+        payload_end_ = kTargetBufferPayloadOffset;
+        size_directory_bytes_ = 0;
+        num_fragments_ = 0;
+        // The shared word now contains any pending loss. If a rewrite prevents
+        // publication, relocation preserves the flag from that word.
+        data_loss_pending_ = false;
+        fallback_sleep_us_ = 0;
+        StoreTargetBufferId(cur_chunk_, target_buffer_);
+        return BeginFragmentResult::kSuccess;
+      }
+
+      ++stats_.failed_claims;
+      // write_pos advanced, but the writer did not acquire the physical chunk.
+      // This leaves an unclaimed position for the reader to consume, unless it
+      // already consumed the position before our claim.
+      //
+      // Retry with a new reservation, which supplies both the index and wrap
+      // count. Other writers can reserve intervening positions, so this need
+      // not select the next physical chunk. A different Free wrap belongs to
+      // a position we did not reserve. Do not use it to retry the failed claim.
+      if (++num_failed_claims < num_chunks)
+        continue;
+    }
+
+    // If we reach this point, either:
+    // - TryReserveWritePos() found the ring buffer full and did not increment
+    //   write_pos.
+    // - TryAcquireChunkForWriting() failed num_chunks times in this round.
+    //   Earlier failures took continue above to try another reservation.
+    //   The last failure reached the attempt limit.
+
+    // The result tells why the call found no chunk:
+    // - kClaimFailed: a claim failed at least once in this call, even if a
+    //   later reservation found the ring buffer full.
+    // - kFull: every reservation found the ring buffer full.
+    //
+    // The caller applies its BufferExhaustedPolicy. To wait, it calls
+    // WaitForReadPosChange() and then calls again.
+    return num_failed_claims != 0 ? BeginFragmentResult::kClaimFailed
+                                  : BeginFragmentResult::kFull;
+  }
+}
+
+void SharedRingBufferWriter::WaitForReadPosChange(uint32_t timeout_ms) {
+  if (use_futex_) {
+    // Wait while read_pos equals the value from the last reservation attempt.
+    const SharedRingBuffer::WriterWaitResult result =
+        ring_->WaitForReadPosChange(read_pos_for_wait_, timeout_ms);
+    if (result == SharedRingBuffer::WriterWaitResult::kRetry)
+      return;
+    // If the futex is unavailable, use fallback sleeps for this writer's later
+    // waits. This avoids repeated calls to an unsupported syscall.
+    use_futex_ = false;
+  }
+
+  // No futex. Sleep with v1's backoff: 0, 8, 72, ... microseconds, up to
+  // kMaxFallbackSleepUs. No sleep requests more than |timeout_ms|.
+  const uint64_t timeout_us = uint64_t{timeout_ms} * 1000;
+  base::SleepMicroseconds(static_cast<unsigned>(
+      std::min<uint64_t>(fallback_sleep_us_, timeout_us)));
+  fallback_sleep_us_ =
+      std::min(kMaxFallbackSleepUs, (fallback_sleep_us_ + 1) * 8);
+}
+
+SharedRingBufferWriter::EndFragmentResult
+SharedRingBufferWriter::ReleaseCurrentChunkAsComplete(
+    std::optional<uint32_t> fragment_size,
+    bool continues_on_next) {
+  PERFETTO_DCHECK(cur_chunk_);
+  PERFETTO_DCHECK(cur_chunk_state() == ChunkState::kBeingWritten);
+  PERFETTO_DCHECK(!has_open_fragment());
+
+  // Publication competes with the reader's rewrite request. For N published
+  // fragments, the possible transitions are:
+  // - Writer: BeingWritten(N) -> Complete(num_fragments_).
+  // - Reader: BeingWritten(N) -> RewriteRequested(N).
+  //
+  // On failure, relocate any unpublished fragment and retry publication.
+  // The reader can also request a rewrite of the replacement chunk.
+  // Repeat until publication succeeds or acquisition of a replacement fails.
+  //
+  // A failed acquisition leaves the relocation pending.
+  for (;;) {
+    uint32_t flags = PayloadFlagsOf(expected_state_word_);
+    // Publish pending loss and the fragment count in the same state word.
+    // The reader must observe the loss flag before it can deliver fragments.
+    if (data_loss_pending_)
+      flags |= kFlagDataLoss;
+    if (continues_on_next)
+      flags |= kFlagContinuesOnNextChunk;
+    const uint32_t complete_word =
+        MakeDataStateWord(ChunkState::kComplete, ChunkFormat::kTargetBuffer,
+                          flags, num_fragments_, writer_id_);
+
+    uint32_t expected = expected_state_word_;
+    if (ring_->TryReleaseChunkAsComplete(cur_chunk_idx_, complete_word,
+                                         &expected)) {
+      expected_state_word_ = complete_word;
+      data_loss_pending_ = false;
+      PERFETTO_DCHECK(payload_end_ <= chunk_size_);
+      PERFETTO_DCHECK(size_directory_bytes_ <= chunk_size_ - payload_end_);
+
+      // Discard the cached handle if any of these limits prevents reuse:
+      // - continues_on_next: kFlagContinuesOnNextChunk describes the last
+      //   fragment. If another fragment follows, the flag describes the wrong
+      //   fragment and the reader cannot identify the split packet.
+      // - num_fragments_ reached kMaxFragmentsPerChunk.
+      // - At most one byte remains between the payload and the size directory.
+      //   BeginFragment() requires a non-empty range plus one size byte, even
+      //   if the caller later ends the fragment with a size of zero.
+      if (continues_on_next || num_fragments_ >= kMaxFragmentsPerChunk ||
+          chunk_size_ - payload_end_ - size_directory_bytes_ <= 1) {
+        ResetCurrentChunk();
+      }
+      return EndFragmentResult::kSuccess;
+    }
+
+    // On failure, expected must be RewriteRequested for this writer. Only the
+    // reader can change the word before publication, so any other value is a
+    // protocol error.
+    if (PERFETTO_UNLIKELY(ChunkStateOf(expected) !=
+                              ChunkState::kRewriteRequested ||
+                          WriterIDOf(expected) != writer_id_)) {
+      PERFETTO_FATAL(
+          "tracing v2: publication of chunk %u by writer %u lost to state word "
+          "0x%08x, which is not a rewrite request for this writer",
+          cur_chunk_idx_.value(), writer_id_, expected);
+    }
+
+    // expected is RewriteRequested: the reader did a partial chunk read while
+    // this writer prepared another fragment.
+    // - It consumed NumFragmentsOf(expected) published fragments. It discarded
+    //   them if kFlagDataLoss was set or validation failed.
+    // - It did not consume the unpublished fragment. The reader will not return
+    //   to this reservation for more fragments.
+    // - The writer must publish the remaining
+    //   num_fragments_ - NumFragmentsOf(expected) fragments in a new chunk.
+    const uint32_t num_fragments_read = NumFragmentsOf(expected);
+
+    // The rewrite request preserves the count from our last publication.
+    PERFETTO_DCHECK(num_fragments_read == NumFragmentsOf(expected_state_word_));
+
+    // At most one fragment needs relocation because EndFragment() publishes
+    // before another fragment can begin:
+    // - EndFragment() adds the finished fragment to num_fragments_.
+    // - FinishCurrentChunk() abandons the open fragment without increasing it.
+    PERFETTO_DCHECK(num_fragments_ ==
+                    num_fragments_read + (fragment_size.has_value() ? 1u : 0u));
+
+    // The unpublished fragment occupies the last |*fragment_size| bytes before
+    // payload_end_. The reader already consumed the earlier fragments.
+    // Save these bytes before acknowledgement. The reader can then reclaim
+    // the chunk.
+    if (fragment_size) {
+      // TODO(sashwinbalaji): Measure allocation latency on the first relocation
+      // and retained memory per writer. Use the results to decide when to
+      // allocate this scratch buffer and whether to retain it for later use.
+      relocation_payload_.assign(cur_chunk_ + payload_end_ - *fragment_size,
+                                 cur_chunk_ + payload_end_);
+    }
+
+    // Acknowledge before the next acquisition. The reader can then reclaim the
+    // old chunk even if this writer must wait for replacement space.
+    if (PERFETTO_UNLIKELY(
+            !ring_->TryAcknowledgeRewrite(cur_chunk_idx_, expected))) {
+      PERFETTO_FATAL(
+          "tracing v2: writer %u could not acknowledge chunk %u: only its "
+          "owner may leave RewriteRequested",
+          writer_id_, cur_chunk_idx_.value());
+    }
+    ++stats_.relocations;
+
+    // If num_fragments_read is zero, preserve both flags:
+    // - kFlagContinuesFromPrevChunk still describes the unpublished fragment.
+    // - kFlagDataLoss still needs a report because the reader ignores loss in
+    //   BeingWritten(0).
+    //
+    // Otherwise, clear both flags:
+    // - The reader consumed the first fragment, so its continuation flag no
+    //   longer applies. The unpublished fragment starts a new packet because
+    //   EndFragment(..., /*continues_on_next=*/true) prevents further fragments
+    //   in the old chunk.
+    // - The reader handles the old loss. The delegate tracks that gap for the
+    //   next complete packet. Repeating the flag would discard the replacement.
+    //
+    // kFlagContinuesOnNextChunk comes from this call's continues_on_next value.
+    const uint32_t inherited_flags =
+        PayloadFlagsOf(expected) &
+        (kFlagContinuesFromPrevChunk | kFlagDataLoss);
+    const uint32_t relocated_flags =
+        num_fragments_read == 0 ? inherited_flags : 0;
+
+    ResetCurrentChunk();
+
+    if (!fragment_size) {
+      PERFETTO_DCHECK(!continues_on_next);
+      // FinishCurrentChunk() has no fragment to relocate. If the reader did not
+      // report the old loss, the next acquired chunk must report it.
+      if (relocated_flags & kFlagDataLoss)
+        data_loss_pending_ = true;
+      return EndFragmentResult::kSuccess;
+    }
+
+    // AcquireNewChunk() also includes data_loss_pending_. A loss recorded after
+    // the old publication must survive even if the reader handled the old flag.
+    const BeginFragmentResult claim = AcquireNewChunk(relocated_flags);
+    if (claim != BeginFragmentResult::kSuccess) {
+      // Keep the saved fragment. The caller retries or drops it.
+      pending_relocation_ =
+          PendingRelocation{relocated_flags, continues_on_next};
+      return claim == BeginFragmentResult::kFull
+                 ? EndFragmentResult::kFull
+                 : EndFragmentResult::kClaimFailed;
+    }
+    if (PERFETTO_UNLIKELY(after_replacement_claim_for_testing_))
+      after_replacement_claim_for_testing_();
+    CopyRelocatedFragment(*fragment_size);
+  }
+}
+
+SharedRingBufferWriter::RetryRelocationResult
+SharedRingBufferWriter::RetryRelocation() {
+  PERFETTO_DCHECK(pending_relocation_);
+  PERFETTO_DCHECK(!cur_chunk_);
+
+  RetryRelocationResult retry;
+  const BeginFragmentResult claim =
+      AcquireNewChunk(pending_relocation_->chunk_flags);
+  if (claim != BeginFragmentResult::kSuccess) {
+    retry.result = claim == BeginFragmentResult::kFull
+                       ? EndFragmentResult::kFull
+                       : EndFragmentResult::kClaimFailed;
+    return retry;
+  }
+  retry.acquired_replacement = true;
+  if (PERFETTO_UNLIKELY(after_replacement_claim_for_testing_))
+    after_replacement_claim_for_testing_();
+
+  const auto fragment_size = static_cast<uint32_t>(relocation_payload_.size());
+  const bool continues_on_next = pending_relocation_->continues_on_next;
+  pending_relocation_.reset();
+  CopyRelocatedFragment(fragment_size);
+  // If the reader also takes this replacement, this relocates again.
+  retry.result =
+      ReleaseCurrentChunkAsComplete(fragment_size, continues_on_next);
+  return retry;
+}
+
+void SharedRingBufferWriter::DropRelocation() {
+  PERFETTO_DCHECK(pending_relocation_);
+  pending_relocation_.reset();
+  ++stats_.fragments_dropped;
+  // Also covers a loss flag that the relocation inherited.
+  data_loss_pending_ = true;
+}
+
+void SharedRingBufferWriter::CopyRelocatedFragment(uint32_t fragment_size) {
+  // All chunks have the same capacity, so the saved fragment and its size
+  // entry fit in the empty replacement. An empty payload still needs a size
+  // entry and contributes one to the fragment count.
+  if (!relocation_payload_.empty()) {
+    memcpy(cur_chunk_ + kTargetBufferPayloadOffset, relocation_payload_.data(),
+           relocation_payload_.size());
+  }
+  payload_end_ = kTargetBufferPayloadOffset + fragment_size;
+  uint8_t* const chunk_end = cur_chunk_ + chunk_size_;
+  uint8_t* const sizes_begin =
+      WriteFragmentSizeReversed(chunk_end, fragment_size);
+  size_directory_bytes_ = static_cast<uint32_t>(chunk_end - sizes_begin);
+  num_fragments_ = 1;
+}
+
+void SharedRingBufferWriter::ResetCurrentChunk() {
+  cur_chunk_ = nullptr;
+  cur_chunk_idx_ = ChunkIndex::FromIndex(0);
+  expected_state_word_ = 0;
+  payload_end_ = 0;
+  size_directory_bytes_ = 0;
+  num_fragments_ = 0;
+  cur_fragment_begin_ = kNoFragmentOpen;
+}
+
+}  // namespace perfetto::tracing_v2
+// gen_amalgamated begin source: src/tracing/v2/trace_writer_v2_impl.cc
 /*
  * Copyright (C) 2026 The Android Open Source Project
  *
@@ -59743,175 +67528,338 @@ void ZstdCompressFn(std::vector<TracePacket>*, int level);
  * limitations under the License.
  */

-// gen_amalgamated expanded: #include "src/tracing/service/zstd_compressor.h"
-
-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-
-#if PERFETTO_BUILDFLAG(PERFETTO_ZSTD)
-
-#include <memory>
+// gen_amalgamated expanded: #include "src/tracing/v2/trace_writer_v2_impl.h"

-#include <zstd.h>
+#include <algorithm>
+#include <cstdint>
+#include <functional>
+#include <optional>
+#include <utility>

 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/trace.pbzero.h"
-// gen_amalgamated expanded: #include "protos/perfetto/trace/trace_packet.pbzero.h"
-// gen_amalgamated expanded: #include "src/tracing/service/packet_compressor_common.h"
+// gen_amalgamated expanded: #include "perfetto/base/proc_utils.h"
+// gen_amalgamated expanded: #include "perfetto/base/time.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_annotations.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/producer_ring_buffer_arbiter.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_abi.h"

-namespace perfetto {
+// gen_amalgamated expanded: #include "protos/perfetto/trace/trace_packet.pbzero.h"

+namespace perfetto::tracing_v2 {
 namespace {

-using packet_compressor::GetPreamble;
-using packet_compressor::kCompressSliceSize;
-using packet_compressor::Preamble;
-using packet_compressor::PreambleToSlice;
+// A new fragment must fit Protozero's largest scalar field.
+constexpr uint32_t kMinFragmentPayloadSize =
+    static_cast<uint32_t>(protozero::proto_utils::kMaxSimpleFieldEncodedSize);

-// ScopedResource's close function must return int; ZSTD_freeCStream returns
-// size_t, so wrap it. It only fails for a static-allocated stream, not the
-// heap one ZSTD_createCStream gives us, so returning 0 is safe.
-inline int ZstdFreeCStream(ZSTD_CStream* cstream) {
-  ZSTD_freeCStream(cstream);
-  return 0;
+// Each wait for space requests at most this time. A wait can end early, for
+// example when the reader moves read_pos.
+// SharedRingBufferWriter::WaitForReadPosChange() states the exact guarantees.
+//
+// The limit makes the writer check again also without reader progress:
+// - If the reader detached during the wait, the writer drops the data. It
+//   does not wait until the stall deadline and crash.
+// - If the last drain did not free space, the writer asks for another one.
+//
+// The 100 ms limit matches the longest sleep in
+// SharedMemoryArbiterImpl::GetNewChunk().
+constexpr uint32_t kMaxWaitMs = 100;
+
+// After this time, kStall aborts and kStallThenDrop drops the data.
+// - The time starts when one chunk acquisition first needs to wait.
+// - It covers all failed claim rounds and waits of that acquisition.
+// - It does not limit the time to finish a fragment: a relocation can claim
+//   several replacements, each with its own deadline.
+//
+// The 30-second limit matches v1's approximate timeout from kAssertAtNStalls
+// in SharedMemoryArbiterImpl::GetNewChunk().
+constexpr uint32_t kStallTimeoutMs = 30000;
+
+// Drop mode writes go here, so the data source can finish the packet. Nobody
+// reads it.
+// - All writers of the process share it, as v1 writers share
+//   g_garbage_chunk. Their concurrent writes are a benign race.
+// - Sized to the largest chunk. A raw stream caller can then request a
+//   contiguous range as large as in a normal fragment.
+uint8_t g_drop_buffer[kMaxChunkSize];
+
+}  // namespace
+
+TraceWriterV2Impl::TraceWriterV2Impl(
+    ProducerRingBufferArbiter* ring_buffer_arbiter,
+    WriterID id,
+    BufferID target_buffer,
+    BufferExhaustedPolicy policy)
+    : ring_buffer_arbiter_(ring_buffer_arbiter),
+      ring_buffer_writer_(ring_buffer_arbiter->ring_buffer(),
+                          id,
+                          target_buffer),
+      buffer_exhausted_policy_(policy),
+      process_id_(base::GetProcessId()),
+      stream_writer_(this),
+      cur_packet_(std::make_unique<
+                  protozero::RootMessage<protos::pbzero::TracePacket>>()) {}
+
+TraceWriterV2Impl::~TraceWriterV2Impl() {
+  // Flush implicitly, as TraceWriterBase documents: publish the last packet
+  // and ask for a drain. A publication alone makes the bytes readable, but it
+  // does not make the service read them.
+  //
+  // Both happen before the arbiter releases this WriterID:
+  // - The release can let the endpoint destroy the arbiter.
+  // - Ring buffer reservations keep the order if another ring buffer writer
+  //   reuses the ID. Nothing handles reuse by a v1 writer yet. See the TODO
+  //   in ProducerRingBufferArbiter::OnWriterDestroyed().
+  FinishTracePacket();
+  Flush();
+  ring_buffer_arbiter_->OnWriterDestroyed(writer_id());
 }
-using ScopedZstdCStream = base::ScopedResource<ZSTD_CStream*,
-                                               ZstdFreeCStream,
-                                               /*InvalidValue=*/nullptr,
-                                               /*CheckClose=*/false>;

-// A compressor for `TracePacket`s that uses zstd's streaming API: data is fed
-// in with ZSTD_compressStream2(ZSTD_e_continue) and the frame is finalized with
-// ZSTD_e_end, emitting output in fixed-size slices (kCompressSliceSize).
-// zstd API reference: https://facebook.github.io/zstd/zstd_manual.html
-class ZstdPacketCompressor {
- public:
-  explicit ZstdPacketCompressor(int level);
+TraceWriter::TracePacketHandle TraceWriterV2Impl::NewTracePacket() {
+  PERFETTO_DCHECK(process_id_ == base::GetProcessId());
+  EnsurePacketClosed();

-  // Can be called multiple times, before Finish() is called.
-  void PushPacket(const TracePacket& packet);
+  stream_writer_.Reset(BeginPacketFragment(/*continues_from_prev=*/false));
+  cur_packet_->Reset(&stream_writer_,
+                     protozero::Message::Encoding::kProtoGroup);
+  packet_open_ = true;
+  if (PERFETTO_UNLIKELY(first_packet_on_sequence_)) {
+    cur_packet_->set_first_packet_on_sequence(true);
+    first_packet_on_sequence_ = false;
+  }

-  // Returns the compressed data. Can be called at most once. After this call,
-  // the object is unusable (PushPacket should not be called) and must be
-  // destroyed.
-  TracePacket Finish();
+  TracePacketHandle handle(cur_packet_.get());
+  handle.set_finalization_listener(this);
+  return handle;
+}

- private:
-  void PushData(const void* data, uint32_t size);
-  void NewOutputSlice();
-  void PushCurSlice();
+void TraceWriterV2Impl::FinishTracePacket() {
+  PERFETTO_DCHECK(process_id_ == base::GetProcessId());
+  if (!packet_open_)
+    return;

-  ScopedZstdCStream cstream_;
-  // Points into `cur_slice_`. Zero-initialized so that the first compression
-  // call observes a full output buffer and allocates the initial slice.
-  ZSTD_outBuffer out_{/*dst=*/nullptr, /*size=*/0, /*pos=*/0};
-  size_t total_new_slices_size_ = 0;
-  std::vector<Slice> new_slices_;
-  std::unique_ptr<uint8_t[]> cur_slice_;
-};
+  // Finalize() can append closing bytes that cross a chunk boundary. Keep
+  // packet_open_ true until it returns so GetNewBuffer() can obtain space for
+  // those bytes. Then publish the final fragment without a continuation flag.
+  cur_packet_->Finalize();
+  packet_open_ = false;
+  EndPacketFragment(/*continues_on_next=*/false);
+}

-ZstdPacketCompressor::ZstdPacketCompressor(int level) {
-  cstream_.reset(ZSTD_createCStream());
-  PERFETTO_CHECK(cstream_);
-  // zstd maps 0 to its default level, clamps levels above its max, and treats
-  // negatives as fast modes, so any int is safe to pass through here.
-  size_t rc =
-      ZSTD_CCtx_setParameter(cstream_.get(), ZSTD_c_compressionLevel, level);
-  PERFETTO_CHECK(!ZSTD_isError(rc));
+void TraceWriterV2Impl::Flush(std::function<void()> callback) {
+  EnsurePacketClosed();
+
+  // A completed fragment leaves its chunk cached for later packets. Release
+  // that chunk before ring_buffer_arbiter_->Flush() so later packets use new
+  // reservations.
+  ring_buffer_writer_.FinishCurrentChunk();
+  stream_writer_.Reset({nullptr, nullptr});
+  ring_buffer_arbiter_->Flush(std::move(callback));
 }

-void ZstdPacketCompressor::PushPacket(const TracePacket& packet) {
-  // Prefix each packet with its proto preamble so the compressed stream itself
-  // parses as a valid Trace proto, and its packets can be tokenized back out.
-  Preamble preamble =
-      GetPreamble<protos::pbzero::Trace::kPacketFieldNumber>(packet.size());
-  PushData(preamble.buf.data(), preamble.size);
-  for (const Slice& slice : packet.slices()) {
-    PushData(slice.start, static_cast<uint32_t>(slice.size));
-  }
+void TraceWriterV2Impl::OnMessageFinalized(protozero::Message*) {
+  FinishTracePacket();
 }

-void ZstdPacketCompressor::PushData(const void* data, uint32_t size) {
-  // ZSTD_e_continue hands data to the encoder, which buffers and emits at its
-  // own discretion. It may not consume all input in one call (e.g. when the
-  // output slice fills), so loop until `in` is drained, giving it a fresh slice
-  // whenever `out_` is full.
-  ZSTD_inBuffer in = {/*src=*/data, /*size=*/size, /*pos=*/0};
-  while (in.pos < in.size) {
-    if (out_.pos == out_.size) {
-      NewOutputSlice();
-    }
-    size_t rc =
-        ZSTD_compressStream2(cstream_.get(), &out_, &in, ZSTD_e_continue);
-    PERFETTO_CHECK(!ZSTD_isError(rc));
+protozero::ContiguousMemoryRange TraceWriterV2Impl::GetNewBuffer() {
+  if (!packet_open_) {
+    // A stale handle can request space without an open packet. This caller
+    // error violates the data-source API contract.
+    PERFETTO_DFATAL("TraceWriterV2Impl: write outside an open packet");
+    EnterDropMode();
+    return GetDropBuffer();
   }
-}

-TracePacket ZstdPacketCompressor::Finish() {
-  // ZSTD_e_end flushes buffered data and writes the frame epilogue. Per the
-  // contract, keep calling (draining `out_` into new slices) until it reports 0
-  // bytes remaining.
-  size_t remaining;
-  do {
-    if (out_.pos == out_.size) {
-      NewOutputSlice();
-    }
-    ZSTD_inBuffer in = {/*src=*/nullptr, /*size=*/0, /*pos=*/0};
-    remaining = ZSTD_compressStream2(cstream_.get(), &out_, &in, ZSTD_e_end);
-    PERFETTO_CHECK(!ZSTD_isError(remaining));
-  } while (remaining != 0);
+  // If the writer loses part of a packet, the remaining bytes are not useful.
+  // Send the rest to the drop buffer. Try the ring buffer for the next packet.
+  if (in_drop_mode_)
+    return GetDropBuffer();

-  PushCurSlice();
+  // The current range cannot satisfy the stream's request. Publish this
+  // fragment with kFlagContinuesOnNextChunk and continue in another chunk.
+  EndPacketFragment(/*continues_on_next=*/true);

-  TracePacket packet;
-  packet.AddSlice(PreambleToSlice(
-      GetPreamble<
-          protos::pbzero::TracePacket::kZstdCompressedPacketsFieldNumber>(
-          total_new_slices_size_)));
-  for (auto& slice : new_slices_) {
-    packet.AddSlice(std::move(slice));
-  }
-  return packet;
+  // EndPacketFragment() can fail to relocate after the reader requests a
+  // rewrite. Then the rest of the packet goes to the drop buffer, not to a
+  // continuation fragment.
+  if (in_drop_mode_)
+    return GetDropBuffer();
+
+  return BeginPacketFragment(/*continues_from_prev=*/true);
 }

-void ZstdPacketCompressor::NewOutputSlice() {
-  PushCurSlice();
-  cur_slice_ = std::make_unique<uint8_t[]>(kCompressSliceSize);
-  out_.dst = cur_slice_.get();
-  out_.size = kCompressSliceSize;
-  out_.pos = 0;
+uint8_t* TraceWriterV2Impl::AnnotatePatch(uint8_t*) {
+  PERFETTO_FATAL("TraceWriterV2Impl cannot patch previously written bytes");
 }

-void ZstdPacketCompressor::PushCurSlice() {
-  if (!cur_slice_) {
-    return;
-  }
-  total_new_slices_size_ += out_.pos;
-  new_slices_.push_back(Slice::TakeOwnership(std::move(cur_slice_), out_.pos));
+void TraceWriterV2Impl::EnsurePacketClosed() {
+  if (packet_open_ && cur_packet_->is_finalized())
+    FinishTracePacket();
+  PERFETTO_CHECK(!packet_open_);
 }

-}  // namespace
+protozero::ContiguousMemoryRange TraceWriterV2Impl::BeginPacketFragment(
+    bool continues_from_prev) {
+  std::optional<base::TimeMillis> stall_deadline;
+  for (;;) {
+    const auto range = ring_buffer_writer_.BeginFragment(
+        kMinFragmentPayloadSize, continues_from_prev);
+    if (range.result == SharedRingBufferWriter::BeginFragmentResult::kSuccess) {
+      fragment_begin_ = range.begin;
+      in_drop_mode_ = false;
+      return {range.begin, range.end};
+    }
+    // Every valid chunk size holds kMinFragmentPayloadSize.
+    PERFETTO_DCHECK(range.result !=
+                    SharedRingBufferWriter::BeginFragmentResult::kTooLarge);

-void ZstdCompressFn(std::vector<TracePacket>* packets, int level) {
-  if (packets->empty()) {
+    const bool claim_failed =
+        range.result ==
+        SharedRingBufferWriter::BeginFragmentResult::kClaimFailed;
+    if (!TryStallForSpace(claim_failed, &stall_deadline)) {
+      EnterDropMode();
+      return GetDropBuffer();
+    }
+  }
+}
+
+void TraceWriterV2Impl::EndPacketFragment(bool continues_on_next) {
+  if (!fragment_begin_) {
+    // The packet went to the drop buffer. The next publication carries
+    // kFlagDataLoss, even if it reuses a cached chunk.
     return;
   }

-  ZstdPacketCompressor stream(level);
+  const uint32_t used =
+      static_cast<uint32_t>(stream_writer_.write_ptr() - fragment_begin_);
+  fragment_begin_ = nullptr;

-  for (const TracePacket& packet : *packets) {
-    stream.PushPacket(packet);
+  // If the reader took the chunk, EndFragment() relocates the fragment and
+  // needs a replacement chunk. See SharedRingBufferWriter::EndFragment().
+  //
+  // If EndFragment() cannot claim a replacement, the relocation stays pending
+  // until the policy lets the writer try again or drop it.
+  SharedRingBufferWriter::EndFragmentResult result =
+      ring_buffer_writer_.EndFragment(used, continues_on_next);
+  // The stall deadline of the current replacement acquisition. It starts at
+  // the first wait after EndFragment() returns.
+  std::optional<base::TimeMillis> stall_deadline;
+  while (result != SharedRingBufferWriter::EndFragmentResult::kSuccess) {
+    const bool claim_failed =
+        result == SharedRingBufferWriter::EndFragmentResult::kClaimFailed;
+    if (!TryStallForSpace(claim_failed, &stall_deadline)) {
+      // Discard the saved fragment and the rest of the packet.
+      ring_buffer_writer_.DropRelocation();
+      EnterDropMode();
+      return;
+    }
+    const auto retry = ring_buffer_writer_.RetryRelocation();
+    // A claimed replacement ends the current acquisition, even if the reader
+    // then took it before publication. The next acquisition gets its own
+    // deadline at its first wait. Retries without a claim keep the deadline.
+    if (retry.acquired_replacement)
+      stall_deadline.reset();
+    result = retry.result;
+  }
+
+  // Batch drain requests until occupancy reaches the threshold:
+  // - This reduces task and IPC traffic when packets arrive separately.
+  // - It lets this writer reuse its cached Complete chunk for longer. A drain
+  //   frees that chunk, and the next fragment then needs a new one.
+  const uint32_t outstanding_positions =
+      ring_buffer_arbiter_->ring_buffer()->LoadNumOutstandingPositionsRelaxed();
+  if (outstanding_positions >=
+      ring_buffer_arbiter_->drain_occupancy_threshold()) {
+    ring_buffer_arbiter_->RequestDrain(
+        ProducerRingBufferArbiter::DrainUrgency::kRoutine);
+  }
+}
+
+bool TraceWriterV2Impl::TryStallForSpace(
+    bool claim_failed,
+    std::optional<base::TimeMillis>* stall_deadline) {
+  // While a loss waits for its report, kStallThenDrop acts as kDrop.
+  // Otherwise each dropped packet can cost another full timeout.
+  //
+  // A later
+  // acquisition can stall again after a chunk carries the loss. This matches
+  // v1's drop_packets_ behavior.
+  bool can_stall = buffer_exhausted_policy_ != BufferExhaustedPolicy::kDrop;
+  if (buffer_exhausted_policy_ == BufferExhaustedPolicy::kStallThenDrop &&
+      ring_buffer_writer_.has_pending_data_loss()) {
+    can_stall = false;
+  }
+
+  // Time left before the stall deadline. Zero means drop.
+  // Without an attached reader, nothing frees space. So drop, also under
+  // kStall. A wait would only end at the deadline, where kStall crashes.
+  uint32_t time_left_ms = 0;
+  if (can_stall && ring_buffer_arbiter_->IsReaderAttached()) {
+    const base::TimeMillis now = get_time_ms_();
+    if (!stall_deadline->has_value())
+      *stall_deadline = now + base::TimeMillis(kStallTimeoutMs);
+    const base::TimeMillis deadline = **stall_deadline;
+    if (now < deadline) {
+      time_left_ms = static_cast<uint32_t>((deadline - now).count());
+    } else if (buffer_exhausted_policy_ == BufferExhaustedPolicy::kStall) {
+      PERFETTO_FATAL(
+          "tracing v2: writer %u could not acquire a chunk for %u ms: "
+          "possible deadlock",
+          writer_id(), kStallTimeoutMs);
+    }
   }

-  TracePacket packet = stream.Finish();
+  // Every drop takes this exit.
+  if (time_left_ms == 0) {
+    // Failed claims left reserved positions that only the reader can move
+    // past. Until it does, they block the reservations of all writers. An
+    // earlier drain does not cover positions reserved after it.
+    if (claim_failed) {
+      ring_buffer_arbiter_->RequestDrain(
+          ProducerRingBufferArbiter::DrainUrgency::kRoutine);
+    }
+    PERFETTO_DLOG(
+        "tracing v2: writer %u: %s: dropping data", writer_id(),
+        claim_failed ? "no chunk could be claimed" : "ring buffer full");
+    return false;
+  }

-  packets->clear();
-  packets->push_back(std::move(packet));
+  // Make the reader run before the wait. Its drain frees space, and moves
+  // read_pos past the positions of failed claims.
+  ring_buffer_arbiter_->RequestDrain(
+      ProducerRingBufferArbiter::DrainUrgency::kUrgent);
+
+  // With a futex, the wait compares read_pos with the value that the last
+  // reservation attempt saw, before the request above. So it also sees reader
+  // progress that happens between the request and the wait.
+  //
+  // The requested wait time comes from the time left before the deadline,
+  // measured before the request above. So the wait can end slightly after the
+  // deadline. The next check then drops or aborts.
+  ring_buffer_writer_.WaitForReadPosChange(std::min(kMaxWaitMs, time_left_ms));
+  return true;
 }

-}  // namespace perfetto
+void TraceWriterV2Impl::EnterDropMode() {
+  fragment_begin_ = nullptr;
+  // Consecutive dropped packets count as one drop.
+  if (in_drop_mode_)
+    return;
+  in_drop_mode_ = true;
+  ++drop_count_;
+  ring_buffer_writer_.RecordDataLoss();
+}

-#endif  // PERFETTO_BUILDFLAG(PERFETTO_ZSTD)
+protozero::ContiguousMemoryRange TraceWriterV2Impl::GetDropBuffer() {
+  PERFETTO_DCHECK(in_drop_mode_);
+  PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(&g_drop_buffer, sizeof(g_drop_buffer),
+                                      "nobody reads the drop buffer")
+  return {&g_drop_buffer[0], &g_drop_buffer[0] + sizeof(g_drop_buffer)};
+}
+
+}  // namespace perfetto::tracing_v2
 // gen_amalgamated begin source: src/tracing/service/clock.cc
 // gen_amalgamated begin header: src/tracing/service/clock.h
 /*
@@ -60501,8 +68449,8 @@ double RandomImpl::GetValue() {
 }

 }  // namespace perfetto::tracing_service
-// gen_amalgamated begin source: src/tracing/service/trace_buffer_v1.cc
-// gen_amalgamated begin header: src/tracing/service/trace_buffer_v1.h
+// gen_amalgamated begin source: src/tracing/service/service_ring_buffer_drainer.cc
+// gen_amalgamated begin header: src/tracing/service/service_ring_buffer_drainer.h
 // gen_amalgamated begin header: include/perfetto/ext/tracing/core/client_identity.h
 /*
  * Copyright (C) 2023 The Android Open Source Project
@@ -60555,6 +68503,157 @@ class ClientIdentity {
 }  // namespace perfetto

 #endif  // INCLUDE_PERFETTO_EXT_TRACING_CORE_CLIENT_IDENTITY_H_
+/*
+ * Copyright (C) 2026 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_TRACING_SERVICE_SERVICE_RING_BUFFER_DRAINER_H_
+#define SRC_TRACING_SERVICE_SERVICE_RING_BUFFER_DRAINER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+
+// gen_amalgamated expanded: #include "perfetto/ext/base/thread_checker.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/weak_runner.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/basic_types.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/client_identity.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_reader.h"
+
+namespace perfetto {
+class TraceBuffer;
+namespace tracing_v2 {
+
+namespace test {
+class ServiceRingBufferDrainerTestPeer;
+}  // namespace test
+
+// Copies published data from one producer connection's v2 ring buffer into
+// authorized trace buffers that support SMB v2. ProducerRingBufferArbiter
+// manages the producer side. See AttachV2RingBuffer in producer_port.proto for
+// the IPC flow.
+//
+// All operations, including construction and destruction, run on the service
+// sequence.
+//
+//   ProducerEndpointImpl
+//     |-- owns ---> ServiceRingBufferDrainer (this class)
+//                     |-- shares -> SharedMemory: the ring buffer mapping
+//                     |-- owns ---> SharedRingBuffer: the view
+//                     |-- owns ---> SharedRingBufferReader
+//                     |-- uses ---> Delegate (the ProducerEndpointImpl)
+//                     |-- uses ---> the service task runner
+//
+// Over IPC, |memory_| holds the service's separate mapping. In-process, the
+// producer and service share the same mapping.
+class ServiceRingBufferDrainer : public SharedRingBufferReader::Delegate {
+ public:
+  // Delegate calls must not destroy or reenter the drainer.
+  class Delegate {
+   public:
+    virtual ~Delegate();
+
+    // Returns a trace buffer that the producer may write to and that supports
+    // SMB v2, or nullptr. The service owns the buffer, which must remain valid
+    // during OnChunkRead().
+    virtual TraceBuffer* GetV2RingBufferDestination(BufferID) = 0;
+
+    // Calls |callback| inline for each buffer that GetV2RingBufferDestination()
+    // would return. Buffer references are valid only during the callback.
+    // The callback must not change the set of destinations or destroy a buffer.
+    virtual void ForEachV2RingBufferDestination(
+        const std::function<void(TraceBuffer&)>& callback) = 0;
+
+    // Adds |count| to the service's chunks_discarded statistic for chunks that
+    // never reached a trace buffer. Excludes loss reported by the producer.
+    virtual void OnRingBufferChunksDiscarded(uint64_t count) = 0;
+  };
+
+  // Shares ownership of |memory|. The mapping and |chunk_size_bytes| must
+  // describe a valid ring buffer layout. |delegate| and |task_runner| must
+  // remain valid until destruction.
+  ServiceRingBufferDrainer(std::shared_ptr<SharedMemory> memory,
+                           uint32_t chunk_size_bytes,
+                           ProducerID,
+                           ClientIdentity,
+                           Delegate* delegate,
+                           base::TaskRunner* task_runner);
+
+  // Cancels pending retry tasks. Destruction does not call Drain() or copy any
+  // remaining data from |ring_buffer_|.
+  ~ServiceRingBufferDrainer() override;
+  ServiceRingBufferDrainer(const ServiceRingBufferDrainer&) = delete;
+  ServiceRingBufferDrainer& operator=(const ServiceRingBufferDrainer&) = delete;
+  ServiceRingBufferDrainer(ServiceRingBufferDrainer&&) = delete;
+  ServiceRingBufferDrainer& operator=(ServiceRingBufferDrainer&&) = delete;
+
+  // Reads at most |ring_buffer_.num_chunks()| logical positions. Reaching the
+  // position or retry limit schedules another pass after a delay. At most one
+  // retry task is pending at a time.
+  //
+  // A protocol error stops all future reads and records one ABI violation in
+  // each authorized destination. Data already copied is kept. The producer
+  // stays connected without notification or a v1 fallback. When the ring buffer
+  // fills, its writers drop packets, or stall and time out.
+  void Drain();
+
+  // Mapping size for the service's memory guardrail.
+  size_t size_bytes() const { return memory_->size(); }
+
+ private:
+  friend class test::ServiceRingBufferDrainerTestPeer;
+
+  // SharedRingBufferReader::Delegate implementation.
+  // The reader calls these inline during Drain().
+  void OnChunkRead(const SharedRingBufferReader::ChunkContents&) override;
+  void OnDataLoss(WriterID) override;
+
+  // Marks a gap in the sequence for |writer_id| in its destination buffer.
+  void RecordWriterLoss(WriterID writer_id);
+
+  // Trusted identity supplied by the endpoint.
+  const ProducerID producer_id_;
+  const ClientIdentity client_identity_;
+
+  Delegate* const delegate_;
+
+  // Declared before |ring_buffer_| and |reader_| so the mapping outlives them.
+  std::shared_ptr<SharedMemory> memory_;
+
+  // Constructing |ring_buffer_| leaves the contents of |memory_| unchanged,
+  // preserving any data the producer has already written.
+  SharedRingBuffer ring_buffer_;
+  SharedRingBufferReader reader_;
+
+  bool retry_scheduled_ = false;
+
+  PERFETTO_THREAD_CHECKER(thread_checker_)
+
+  // Declared last to cancel retry tasks before reader or mapping destruction.
+  base::WeakRunner weak_runner_;
+};
+
+}  // namespace tracing_v2
+}  // namespace perfetto
+
+#endif  // SRC_TRACING_SERVICE_SERVICE_RING_BUFFER_DRAINER_H_
+// gen_amalgamated begin header: src/tracing/service/trace_buffer.h
 // gen_amalgamated begin header: src/tracing/service/histogram.h
 /*
  * Copyright (C) 2023 The Android Open Source Project
@@ -60649,7 +68748,6 @@ class Histogram {
 }  // namespace perfetto

 #endif  // SRC_TRACING_SERVICE_HISTOGRAM_H_
-// gen_amalgamated begin header: src/tracing/service/trace_buffer.h
 /*
  * Copyright (C) 2018 The Android Open Source Project
  *
@@ -60672,6 +68770,7 @@ class Histogram {
 #include <stdint.h>
 #include <array>
 #include <memory>
+#include <new>

 // gen_amalgamated expanded: #include "perfetto/ext/base/flat_hash_map.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/basic_types.h"
@@ -60679,9 +68778,14 @@ class Histogram {
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_stats.h"
 // gen_amalgamated expanded: #include "src/tracing/service/histogram.h"

+namespace protozero {
+struct ConstBytes;
+}  // namespace protozero
+
 namespace perfetto {

 class TracePacket;
+class TraceBufferProtoVms;
 class TraceBuffer_WriterStats;

 // Virtual interface for trace buffers to enable multiple implementations.
@@ -60743,6 +68847,54 @@ class TraceBuffer {
                                      size_t patches_size,
                                      bool other_patches_pending) = 0;

+  // Returns true if the buffer accepts tracing v2 data through
+  // CopyChunkV2Untrusted(). The service offers tracing v2 only to data sources
+  // that write into such a buffer.
+  virtual bool supports_tracing_v2() const = 0;
+
+  // Copies a batch of SMB v2 fragments into the buffer. The caller must pass
+  // fragments and batches in write order for each {ProducerID, WriterID}.
+  // The caller owns the fragment array and payloads. Both must remain valid
+  // and unchanged until this call returns.
+  //
+  // Each fragment is a whole packet unless a continuation flag is set.
+  // - |first_continues_from_prev| means the first fragment continues a packet
+  //   that started in this writer's previous batch.
+  // - |last_continues_on_next| means the last fragment continues in this
+  //   writer's next batch.
+  // The caller decodes these flags from its transport. If the transport marks
+  // a batch as lossy, call RecordChunkV2DataLoss() instead of copying it.
+  //
+  // Returns true if the whole batch was stored. Returns false if the batch
+  // was rejected:
+  // - The rejection of a nonempty batch records loss as RecordChunkV2DataLoss()
+  //   does. An empty batch changes nothing.
+  // - Invalid input increments abi_violations. Other rejections increment
+  //   chunks_discarded.
+  //
+  // If supports_tracing_v2() is false, returns false and changes nothing.
+  virtual bool CopyChunkV2Untrusted(const PacketSequenceProperties&,
+                                    const protozero::ConstBytes* fragments,
+                                    size_t num_fragments,
+                                    bool first_continues_from_prev,
+                                    bool last_continues_on_next) = 0;
+
+  // Records a gap after the last SMB v2 batch that the writer appended.
+  // Packets already in the buffer remain readable. The gap stays pending
+  // until the next stored batch carries it or the writer's state is discarded.
+  // Reads report the gap on the first readable packet after it. This does not
+  // increment any statistics.
+  //
+  // Does nothing if the buffer holds no SMB v2 state for the writer. A caller
+  // that cannot identify the destination can call this on every candidate
+  // buffer without creating state for writers that never used it.
+  virtual void RecordChunkV2DataLoss(ProducerID, WriterID) = 0;
+
+  // Increments abi_violations for a violation that the caller found outside
+  // this buffer, e.g. a protocol error in a producer's ring buffer. This only
+  // updates the statistic and does not record a gap on any packet sequence.
+  virtual void RecordAbiViolation() = 0;
+
   // To read the contents of the buffer the caller needs to:
   //   BeginRead()
   //   while (ReadNextTracePacket(packet_fragments)) { ... }
@@ -60763,10 +68915,23 @@ class TraceBuffer {
       PacketSequenceProperties* sequence_properties,
       uint32_t* previous_packet_on_sequence_dropped) = 0;

+  // Attempts to reclaim unused buffer space.
+  // The caller must release all packets from previous reads before this call,
+  // because their slices can point into the buffer.
+  // Call BeginRead() before reading again.
+  virtual void MaybeCompact() = 0;
+
   // Creates a read-only clone of the trace buffer. The read iterators of the
   // new buffer will be reset, as if no Read() had been called.
   virtual std::unique_ptr<TraceBuffer> CloneReadOnly() const = 0;

+  // Returns the ProtoVM state owned by this buffer, or nullptr if the
+  // implementation cannot run a ProtoVM. The pointer remains valid until the
+  // buffer is destroyed.
+  // The service uses it to set up VMs and emit their state. The buffer feeds
+  // packets to the VMs before overwriting them.
+  virtual TraceBufferProtoVms* protovms() = 0;
+
   virtual void set_read_only() = 0;
   virtual const TraceStats::BufferStats& stats() const = 0;
   virtual const WriterStats& writer_stats() const = 0;
@@ -60791,6 +68956,19 @@ class TraceBuffer_WriterStats {
                                            base::QuadraticProbe,
                                            /*AppendOnly=*/true>;

+  TraceBuffer_WriterStats() = default;
+  TraceBuffer_WriterStats(const TraceBuffer_WriterStats& other) {
+    for (auto it = other.map_.GetIterator(); it; ++it)
+      map_.Insert(it.key(), it.value());
+  }
+  TraceBuffer_WriterStats& operator=(const TraceBuffer_WriterStats& other) {
+    if (this != &other) {
+      this->~TraceBuffer_WriterStats();
+      new (this) TraceBuffer_WriterStats(other);
+    }
+    return *this;
+  }
+
   void Insert(ProducerAndWriterID key, HistValue val) {
     map_.Insert(key, {}).first->Add(val);
   }
@@ -60804,6 +68982,405 @@ class TraceBuffer_WriterStats {
 }  // namespace perfetto

 #endif  // SRC_TRACING_SERVICE_TRACE_BUFFER_H_
+/*
+ * Copyright (C) 2026 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.
+ */
+
+// gen_amalgamated expanded: #include "src/tracing/service/service_ring_buffer_drainer.h"
+
+#include <utility>
+
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "src/tracing/service/trace_buffer.h"
+
+namespace perfetto::tracing_v2 {
+
+namespace {
+
+// Delay retries so writers that repeatedly win chunk state transitions
+// cannot keep the service sequence busy.
+constexpr uint32_t kDrainRetryDelayMs = 1;
+
+}  // namespace
+
+ServiceRingBufferDrainer::Delegate::~Delegate() = default;
+
+ServiceRingBufferDrainer::ServiceRingBufferDrainer(
+    std::shared_ptr<SharedMemory> memory,
+    uint32_t chunk_size_bytes,
+    ProducerID producer_id,
+    ClientIdentity client_identity,
+    Delegate* delegate,
+    base::TaskRunner* task_runner)
+    : producer_id_(producer_id),
+      client_identity_(client_identity),
+      delegate_(delegate),
+      memory_(std::move(memory)),
+      ring_buffer_(static_cast<uint8_t*>(memory_->start()),
+                   memory_->size(),
+                   chunk_size_bytes),
+      reader_(&ring_buffer_, this),
+      weak_runner_(task_runner) {}
+
+ServiceRingBufferDrainer::~ServiceRingBufferDrainer() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+}
+
+void ServiceRingBufferDrainer::Drain() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+
+  if (PERFETTO_UNLIKELY(reader_.has_protocol_error()))
+    return;
+
+  // Bound each pass to num_chunks() logical positions, including positions
+  // reserved during the pass.
+  const SharedRingBufferReader::Stats stats_before = reader_.GetStats();
+  const auto result = reader_.Drain(ring_buffer_.num_chunks());
+  const SharedRingBufferReader::Stats stats_after = reader_.GetStats();
+
+  // The reader discards malformed chunks and chunks in an unknown format. It
+  // reports them as loss for the writer. Count them here, once per pass.
+  const uint64_t num_rejected =
+      (stats_after.malformed_chunks - stats_before.malformed_chunks) +
+      (stats_after.unsupported_format_chunks -
+       stats_before.unsupported_format_chunks);
+  if (PERFETTO_UNLIKELY(num_rejected != 0))
+    delegate_->OnRingBufferChunksDiscarded(num_rejected);
+
+  if (PERFETTO_UNLIKELY(reader_.has_protocol_error())) {
+    // Record the error in every authorized destination. Later drains return
+    // above, so each destination counts this violation only once.
+    delegate_->ForEachV2RingBufferDestination(
+        [](TraceBuffer& buffer) { buffer.RecordAbiViolation(); });
+    return;
+  }
+
+  if (result.needs_another_drain() && !retry_scheduled_) {
+    retry_scheduled_ = true;
+    weak_runner_.PostDelayedTask(
+        [this] {
+          // Clear the flag so Drain() can schedule another retry if needed.
+          retry_scheduled_ = false;
+          Drain();
+        },
+        kDrainRetryDelayMs);
+  }
+}
+
+void ServiceRingBufferDrainer::OnChunkRead(
+    const SharedRingBufferReader::ChunkContents& chunk) {
+  if (PERFETTO_UNLIKELY(!chunk.writer_id || chunk.writer_id > kMaxWriterID)) {
+    delegate_->OnRingBufferChunksDiscarded(1);
+    return;
+  }
+
+  TraceBuffer* buffer =
+      delegate_->GetV2RingBufferDestination(chunk.target_buffer);
+  if (PERFETTO_UNLIKELY(!buffer)) {
+    delegate_->OnRingBufferChunksDiscarded(1);
+    RecordWriterLoss(chunk.writer_id);
+    return;
+  }
+
+  // The reader reports a chunk with kFlagDataLoss through OnDataLoss(). Only
+  // the continuation flags can reach this point.
+  PERFETTO_DCHECK(!(chunk.payload_flags & kFlagDataLoss));
+  const TraceBuffer::PacketSequenceProperties sequence{
+      producer_id_, client_identity_, chunk.writer_id};
+  // On rejection, the trace buffer counts the chunk in its own statistics and
+  // records the loss on the sequence.
+  //
+  // TODO(sashwinbalaji): the payload is copied twice: from shared memory into
+  // the reader's scratch, then into a TBv2 chunk. The reader could pass the
+  // decoded sizes and one payload range, so that TBv2 copies once. Measure
+  // the drain cost in a profile first.
+  buffer->CopyChunkV2Untrusted(
+      sequence, chunk.fragments, chunk.num_fragments,
+      chunk.payload_flags & kFlagContinuesFromPrevChunk,
+      chunk.payload_flags & kFlagContinuesOnNextChunk);
+}
+
+void ServiceRingBufferDrainer::OnDataLoss(WriterID writer_id) {
+  // Record the loss in |writer_id|'s sequence so it can be reported through
+  // previous_packet_dropped.
+  //
+  // Drain() counts chunks discarded by the reader in chunks_discarded.
+  // Loss reported by the producer does not increase that counter.
+  RecordWriterLoss(writer_id);
+}
+
+void ServiceRingBufferDrainer::RecordWriterLoss(WriterID writer_id) {
+  if (!writer_id || writer_id > kMaxWriterID)
+    return;
+  // The destination of a lost chunk is unknown. RecordChunkV2DataLoss() changes
+  // only a buffer that holds this writer's sequence.
+  // - A writer has one destination, so at most one buffer matches.
+  // - Before the writer's first stored chunk, no buffer matches.
+  delegate_->ForEachV2RingBufferDestination(
+      [this, writer_id](TraceBuffer& buffer) {
+        buffer.RecordChunkV2DataLoss(producer_id_, writer_id);
+      });
+}
+
+}  // namespace perfetto::tracing_v2
+// gen_amalgamated begin source: src/tracing/service/trace_buffer_protovms.cc
+// gen_amalgamated begin header: src/tracing/service/trace_buffer_protovms.h
+/*
+ * Copyright (C) 2026 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_TRACING_SERVICE_TRACE_BUFFER_PROTOVMS_H_
+#define SRC_TRACING_SERVICE_TRACE_BUFFER_PROTOVMS_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+// gen_amalgamated expanded: #include "perfetto/base/flat_set.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/basic_types.h"
+
+namespace perfetto {
+
+class TracePacket;
+
+namespace protovm {
+class Vm;
+}
+
+// Owns the ProtoVM instances for one trace buffer (go/perfetto-proto-vm).
+// When the buffer is about to overwrite a packet, it passes the packet to
+// MaybeProcessOverwrittenPacket() to merge it into a ProtoVM's incremental
+// state. The service emits this state when it reads the buffer.
+//
+// The buffer decides which packets to pass here. They must be length-delimited
+// protobuf and arrive in write order within each packet sequence.
+class TraceBufferProtoVms {
+ public:
+  // A ProtoVM instance and the producers whose packets it processes.
+  struct Vm {
+    Vm();
+    ~Vm();
+    Vm(Vm&&) noexcept;
+
+    Vm CloneReadOnly() const;
+
+    std::unique_ptr<protovm::Vm> instance;
+    std::string data_source_name;
+    uint64_t program_hash = 0;
+    uint32_t memory_limit_kb = 0;
+    base::FlatSet<ProducerID> producers;
+  };
+
+  TraceBufferProtoVms();
+  ~TraceBufferProtoVms();
+
+  TraceBufferProtoVms(const TraceBufferProtoVms&) = delete;
+  TraceBufferProtoVms& operator=(const TraceBufferProtoVms&) = delete;
+
+  // Adds |producer_id| to the ProtoVM that runs |program_bytes| for
+  // |data_source_name|. Creates that ProtoVM if it does not exist.
+  void MaybeSetUp(const std::string& data_source_name,
+                  const std::string& program_bytes,
+                  uint32_t memory_limit_kb,
+                  ProducerID producer_id);
+
+  // Tries each ProtoVM registered for |producer| until ApplyPatch() succeeds.
+  // Does nothing if the producer has no ProtoVM.
+  void MaybeProcessOverwrittenPacket(const TracePacket&, ProducerID producer);
+
+  // Replaces this object's instances with read-only clones from |src|.
+  // If any clone fails, clears all instances from this object.
+  void CloneReadOnlyFrom(const TraceBufferProtoVms& src);
+
+  bool HasProducer(ProducerID) const;
+  size_t GetMemoryUsageBytes() const;
+
+  bool empty() const { return vms_.empty(); }
+  const std::vector<Vm>& instances() const { return vms_; }
+
+ private:
+  std::vector<Vm> vms_;
+
+  // Contiguous storage for a packet's slices, as required by ProtoVM's
+  // protozero decoder. We reuse it across packets and only allocate when
+  // it needs to grow.
+  std::string patch_;
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACING_SERVICE_TRACE_BUFFER_PROTOVMS_H_
+/*
+ * Copyright (C) 2026 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.
+ */
+
+// gen_amalgamated expanded: #include "src/tracing/service/trace_buffer_protovms.h"
+
+#include <algorithm>
+#include <utility>
+
+// gen_amalgamated expanded: #include "perfetto/base/logging.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/murmur_hash.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_packet.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
+// gen_amalgamated expanded: #include "src/protovm/vm.h"
+
+namespace perfetto {
+
+TraceBufferProtoVms::Vm::Vm() = default;
+TraceBufferProtoVms::Vm::~Vm() = default;
+TraceBufferProtoVms::Vm::Vm(Vm&&) noexcept = default;
+
+TraceBufferProtoVms::Vm TraceBufferProtoVms::Vm::CloneReadOnly() const {
+  Vm cloned_vm;
+  cloned_vm.data_source_name = data_source_name;
+  cloned_vm.program_hash = program_hash;
+  cloned_vm.memory_limit_kb = memory_limit_kb;
+  cloned_vm.producers = producers;
+  cloned_vm.instance = instance->CloneReadOnly();
+  return cloned_vm;
+}
+
+TraceBufferProtoVms::TraceBufferProtoVms() = default;
+TraceBufferProtoVms::~TraceBufferProtoVms() = default;
+
+void TraceBufferProtoVms::MaybeSetUp(const std::string& data_source_name,
+                                     const std::string& program_bytes,
+                                     uint32_t memory_limit_kb,
+                                     ProducerID producer_id) {
+  Vm* vm = nullptr;
+  // Re-use existing ProtoVM instance, if any.
+  uint64_t program_hash = base::MurmurHashValue(program_bytes);
+  auto vm_it = std::find_if(vms_.begin(), vms_.end(), [&](const Vm& other) {
+    return other.data_source_name == data_source_name &&
+           other.program_hash == program_hash;
+  });
+  if (vm_it != vms_.end()) {
+    vm = &(*vm_it);
+  } else {
+    // Otherwise instantiate new ProtoVM
+    Vm new_vm;
+    new_vm.data_source_name = data_source_name;
+    new_vm.program_hash = program_hash;
+    new_vm.memory_limit_kb = memory_limit_kb;
+
+    protozero::ConstBytes program_bytes_view{
+        reinterpret_cast<const uint8_t*>(program_bytes.data()),
+        program_bytes.size()};
+
+    new_vm.instance = std::make_unique<protovm::Vm>(
+        program_bytes_view, new_vm.memory_limit_kb * 1024);
+    if (!new_vm.instance) {
+      PERFETTO_ELOG("Failed to allocate ProtoVM");
+      return;
+    }
+
+    vms_.push_back(std::move(new_vm));
+    vm = &vms_.back();
+  }
+  // Update ProtoVM's producer IDs
+  vm->producers.insert(producer_id);
+}
+
+void TraceBufferProtoVms::MaybeProcessOverwrittenPacket(
+    const TracePacket& packet,
+    ProducerID producer) {
+  patch_.clear();
+
+  for (auto& vm : vms_) {
+    if (vm.producers.find(producer) == vm.producers.end()) {
+      continue;
+    }
+    // TODO(keanmariotti): add an optimized path for the case
+    // "packet.slices().size() == 1" (zero copy)
+    if (patch_.empty()) {
+      packet.GetRawBytes(&patch_);
+    }
+    protozero::ConstBytes bytes{reinterpret_cast<const uint8_t*>(patch_.data()),
+                                patch_.size()};
+    auto status = vm.instance->ApplyPatch(bytes);
+    if (status.IsOk()) {
+      break;
+    }
+    if (status.IsAbort()) {
+      // TODO(keanmariotti): consider doing something more here. Ideally
+      // triggering a field upload containing the stacktrace.
+      PERFETTO_ELOG("ProtoVM abort while applying patch. Stacktrace:\n%s",
+                    base::Join(status.stacktrace(), "\n").c_str());
+    }
+  }
+}
+
+void TraceBufferProtoVms::CloneReadOnlyFrom(const TraceBufferProtoVms& src) {
+  vms_.clear();
+  for (const auto& vm : src.vms_) {
+    auto vm_cloned = vm.CloneReadOnly();
+    if (!vm_cloned.instance) {
+      PERFETTO_ELOG("Failed to clone ProtoVMs");
+      vms_.clear();
+      break;
+    }
+    vms_.push_back(std::move(vm_cloned));
+  }
+}
+
+bool TraceBufferProtoVms::HasProducer(ProducerID producer) const {
+  return std::any_of(vms_.begin(), vms_.end(), [&](const Vm& vm) {
+    return vm.producers.count(producer) != 0;
+  });
+}
+
+size_t TraceBufferProtoVms::GetMemoryUsageBytes() const {
+  size_t total_bytes = 0;
+  for (const Vm& vm : vms_)
+    total_bytes += vm.instance->GetMemoryUsageBytes();
+  return total_bytes;
+}
+
+}  // namespace perfetto
+// gen_amalgamated begin source: src/tracing/service/trace_buffer_v1.cc
+// gen_amalgamated begin header: src/tracing/service/trace_buffer_v1.h
 /*
  * Copyright (C) 2018 The Android Open Source Project
  *
@@ -61013,6 +69590,21 @@ class TraceBufferV1 : public TraceBuffer {
                              size_t patches_size,
                              bool other_patches_pending) override;

+  // Returns false as TraceBufferV1 only accepts SMB v1 chunks.
+  bool supports_tracing_v2() const override;
+
+  // Returns false without changing the buffer or its statistics.
+  bool CopyChunkV2Untrusted(const PacketSequenceProperties&,
+                            const protozero::ConstBytes* fragments,
+                            size_t num_fragments,
+                            bool first_continues_from_prev,
+                            bool last_continues_on_next) override;
+
+  // No-op because TraceBufferV1 holds no SMB v2 state.
+  void RecordChunkV2DataLoss(ProducerID, WriterID) override;
+
+  void RecordAbiViolation() override;
+
   // To read the contents of the buffer the caller needs to:
   //   BeginRead()
   //   while (ReadNextTracePacket(packet_fragments)) { ... }
@@ -61054,11 +69646,17 @@ class TraceBufferV1 : public TraceBuffer {
       PacketSequenceProperties* sequence_properties,
       uint32_t* previous_packet_on_sequence_dropped) override;

+  // No-op. TraceBufferV1 does not compact its buffer.
+  void MaybeCompact() override;
+
   // Creates a read-only clone of the trace buffer. Calls to
   // CopyChunkUntrusted() and TryPatchChunkContents() on the returned cloned
   // TraceBuffer will CHECK().
   std::unique_ptr<TraceBuffer> CloneReadOnly() const override;

+  // Returns nullptr because TraceBufferV1 cannot run a ProtoVM.
+  TraceBufferProtoVms* protovms() override;
+
   void set_read_only() override { read_only_ = true; }
   const TraceStats::BufferStats& stats() const override { return stats_; }
   const WriterStats& writer_stats() const override { return writer_stats_; }
@@ -61984,6 +70582,24 @@ bool TraceBufferV1::TryPatchChunkContents(ProducerID producer_id,
   return true;
 }

+bool TraceBufferV1::supports_tracing_v2() const {
+  return false;
+}
+
+bool TraceBufferV1::CopyChunkV2Untrusted(const PacketSequenceProperties&,
+                                         const protozero::ConstBytes*,
+                                         size_t,
+                                         bool,
+                                         bool) {
+  return false;
+}
+
+void TraceBufferV1::RecordChunkV2DataLoss(ProducerID, WriterID) {}
+
+void TraceBufferV1::RecordAbiViolation() {
+  stats_.set_abi_violations(stats_.abi_violations() + 1);
+}
+
 void TraceBufferV1::BeginRead() {
   read_iter_ = GetReadIterForSequence(index_.begin());
 #if PERFETTO_DCHECK_IS_ON()
@@ -62247,6 +70863,8 @@ bool TraceBufferV1::ReadNextTracePacket(
   }  // for(;;MoveNext()) [iterate over chunks].
 }

+void TraceBufferV1::MaybeCompact() {}
+
 TraceBufferV1::ReadAheadResult TraceBufferV1::ReadAhead(TracePacket* packet) {
   static_assert(static_cast<ChunkID>(kMaxChunkID + 1) == 0,
                 "relying on kMaxChunkID to wrap naturally");
@@ -62433,6 +71051,10 @@ std::unique_ptr<TraceBuffer> TraceBufferV1::CloneReadOnly() const {
   return buf;
 }

+TraceBufferProtoVms* TraceBufferV1::protovms() {
+  return nullptr;
+}
+
 TraceBufferV1::TraceBufferV1(CloneCtor, const TraceBufferV1& src)
     : overwrite_policy_(src.overwrite_policy_),
       read_only_(true),
@@ -62876,203 +71498,6 @@ class CircularQueue {
 }  // namespace perfetto

 #endif  // INCLUDE_PERFETTO_EXT_BASE_CIRCULAR_QUEUE_H_
-// gen_amalgamated begin header: include/perfetto/ext/base/small_vector.h
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef INCLUDE_PERFETTO_EXT_BASE_SMALL_VECTOR_H_
-#define INCLUDE_PERFETTO_EXT_BASE_SMALL_VECTOR_H_
-
-#include <algorithm>
-#include <type_traits>
-#include <utility>
-
-// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
-
-namespace perfetto {
-namespace base {
-
-// Uses inline storage first, switches to dynamic storage when it overflows.
-template <typename T, size_t kSize>
-class SmallVector {
- public:
-  static constexpr size_t kInlineSize = kSize;
-
-  explicit SmallVector() = default;
-
-  ~SmallVector() {
-    clear();
-    if (PERFETTO_UNLIKELY(is_using_heap()))
-      AlignedFree(begin_);
-    begin_ = end_ = end_of_storage_ = nullptr;
-  }
-
-  // Move operators.
-  SmallVector(SmallVector&& other) noexcept(
-      std::is_nothrow_move_constructible<T>::value) {
-    if (other.is_using_heap()) {
-      // Move the heap content, no need to move the individual objects as their
-      // location won't change.
-      begin_ = other.begin_;
-      end_ = other.end_;
-      end_of_storage_ = other.end_of_storage_;
-    } else {
-      const size_t other_size = other.size();
-      PERFETTO_DCHECK(other_size <= capacity());
-      for (size_t i = 0; i < other_size; i++) {
-        // Move the entries and destroy the ones in the moved-from object.
-        new (&begin_[i]) T(std::move(other.begin_[i]));
-        other.begin_[i].~T();
-      }
-      end_ = begin_ + other_size;
-    }
-    auto* const other_inline_storage = other.inline_storage_begin();
-    other.end_ = other.begin_ = other_inline_storage;
-    other.end_of_storage_ = other_inline_storage + kInlineSize;
-  }
-
-  SmallVector& operator=(SmallVector&& other) noexcept(
-      std::is_nothrow_move_constructible<T>::value) {
-    this->~SmallVector();
-    new (this) SmallVector<T, kSize>(std::move(other));
-    return *this;
-  }
-
-  // Copy operators.
-  SmallVector(const SmallVector& other) {
-    const size_t other_size = other.size();
-    if (other_size > capacity())
-      Grow(other_size);
-    // Copy-construct the elements.
-    for (size_t i = 0; i < other_size; ++i)
-      new (&begin_[i]) T(other.begin_[i]);
-    end_ = begin_ + other_size;
-  }
-
-  SmallVector& operator=(const SmallVector& other) {
-    if (PERFETTO_UNLIKELY(this == &other))
-      return *this;
-    this->~SmallVector();
-    new (this) SmallVector<T, kSize>(other);
-    return *this;
-  }
-
-  T* data() { return begin_; }
-  const T* data() const { return begin_; }
-
-  T* begin() { return begin_; }
-  const T* begin() const { return begin_; }
-
-  T* end() { return end_; }
-  const T* end() const { return end_; }
-
-  size_t size() const { return static_cast<size_t>(end_ - begin_); }
-
-  bool empty() const { return end_ == begin_; }
-
-  size_t capacity() const {
-    return static_cast<size_t>(end_of_storage_ - begin_);
-  }
-
-  T& front() {
-    PERFETTO_DCHECK(!empty());
-    return begin_[0];
-  }
-  const T& front() const {
-    PERFETTO_DCHECK(!empty());
-    return begin_[0];
-  }
-
-  T& back() {
-    PERFETTO_DCHECK(!empty());
-    return end_[-1];
-  }
-  const T& back() const {
-    PERFETTO_DCHECK(!empty());
-    return end_[-1];
-  }
-
-  T& operator[](size_t index) {
-    PERFETTO_DCHECK(index < size());
-    return begin_[index];
-  }
-
-  const T& operator[](size_t index) const {
-    PERFETTO_DCHECK(index < size());
-    return begin_[index];
-  }
-
-  template <typename... Args>
-  void emplace_back(Args&&... args) {
-    T* end = end_;
-    if (PERFETTO_UNLIKELY(end == end_of_storage_))
-      end = Grow();
-    new (end) T(std::forward<Args>(args)...);
-    end_ = end + 1;
-  }
-
-  void pop_back() {
-    PERFETTO_DCHECK(!empty());
-    back().~T();
-    --end_;
-  }
-
-  // Clear without reverting back to inline storage.
-  void clear() {
-    while (!empty())
-      pop_back();
-  }
-
- private:
-  PERFETTO_NO_INLINE T* Grow(size_t desired_capacity = 0) {
-    size_t cur_size = size();
-    size_t new_capacity = desired_capacity;
-    if (desired_capacity <= cur_size)
-      new_capacity = std::max(capacity() * 2, size_t(128));
-    T* new_storage =
-        static_cast<T*>(AlignedAlloc(alignof(T), new_capacity * sizeof(T)));
-    for (size_t i = 0; i < cur_size; ++i) {
-      // Move the elements into the new heap buffer and destroy the old ones.
-      new (&new_storage[i]) T(std::move(begin_[i]));
-      begin_[i].~T();
-    }
-    if (is_using_heap())
-      AlignedFree(begin_);
-    begin_ = new_storage;
-    end_ = new_storage + cur_size;
-    end_of_storage_ = new_storage + new_capacity;
-    return end_;
-  }
-
-  T* inline_storage_begin() { return reinterpret_cast<T*>(inline_storage_); }
-  bool is_using_heap() { return begin_ != inline_storage_begin(); }
-
-  T* begin_ = inline_storage_begin();
-  T* end_ = begin_;
-  T* end_of_storage_ = begin_ + kInlineSize;
-
-  alignas(T) char inline_storage_[sizeof(T) * kInlineSize];
-};
-
-}  // namespace base
-}  // namespace perfetto
-
-#endif  // INCLUDE_PERFETTO_EXT_BASE_SMALL_VECTOR_H_
 /*
  * Copyright (C) 2025 The Android Open Source Project
  *
@@ -63098,8 +71523,8 @@ class SmallVector {
 #include <limits>
 #include <optional>
 #include <unordered_map>
+#include <vector>

-// gen_amalgamated expanded: #include "perfetto/base/flat_set.h"
 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/circular_queue.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/flat_hash_map.h"
@@ -63112,18 +71537,17 @@ class SmallVector {
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/slice.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_packet.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_stats.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/field.h"
 // gen_amalgamated expanded: #include "src/tracing/service/histogram.h"
+// gen_amalgamated expanded: #include "src/tracing/service/proto_group_rewriter.h"
 // gen_amalgamated expanded: #include "src/tracing/service/trace_buffer.h"
+// gen_amalgamated expanded: #include "src/tracing/service/trace_buffer_protovms.h"

 namespace perfetto {

 class TracePacket;
 class TraceBufferV2;

-namespace protovm {
-class Vm;
-}
-
 namespace internal {

 // See /docs/design-docs/trace-buffer.md for details about the design and
@@ -63132,8 +71556,8 @@ namespace internal {
 // +---------------------------------------------------------------------------+
 // | TBChunk                                                                   |
 // +---------------------------------------------------------------------------+
-// TBChunk is the struct, stored in the trace buffer memory as a result of
-// calling CopyChunkUntrusted from a SMB chunk.
+// TBChunk stores fragments copied into the trace buffer, either from SMB chunks
+// by CopyChunkUntrusted() or from the newer SMB v2 by CopyChunkV2Untrusted().
 // TBChunk exists only in the TraceBuffer PagedMemory `data_`, never on the
 // stack or on the heap. It is followed by the fragments and alignment padding.
 // A TBChunk is very similar to a SMB chunk with the following caveats:
@@ -63161,6 +71585,7 @@ struct TBChunk {
   }

   // The ChunkID, as specified by the TraceWriter in the original SMB chunk.
+  // Unused for SMB v2 sequences, whose chunks are appended in order.
   ChunkID chunk_id = 0;

   // A combination of producer and writer ID. This forms the primary key to
@@ -63186,9 +71611,10 @@ struct TBChunk {
   // unconsumed fragment header (the varint with the size).
   uint16_t payload_avail = 0;

-  // These are == the SharedMemoryABI's chunk flags, with the addition of
-  // MSB flags added by TraceBufferV2 like kChunkIncomplete (0x80) which doesn't
-  // exist at the ABI level, but are synthesized here.
+  // SharedMemoryABI chunk flags, with additional bits set by TraceBufferV2:
+  // kChunkIncomplete (0x80) for scraped chunks and kChunkLossBefore (0x40) for
+  // a gap before an SMB v2 chunk. These additional bits never come from
+  // the producer.
   uint8_t flags = 0;

   // This is used for (D)CHECKS to verify the integrity of the chunk.
@@ -63272,11 +71698,38 @@ struct SequenceState {
   // copies it into previous_packet_dropped and resets it. Zero means no loss.
   uint32_t data_loss_reasons = 0;

-  // An ordered list of chunk offsets, sorted by their ChunkID. Each member
-  // corresponsds to the offset within buf_ for the chunk.
+  // An ordered list of chunk offsets, sorted by ChunkID for SMB sequences or
+  // by append order for SMB v2 sequences.
   // We store buffer offsets rather than pointers to make buffer cloning easier.
   // This is effectively a deque of TBChunk* (% a call to GetTBChunkAt(off)).
   base::CircularQueue<size_t> chunks;
+
+  // All chunks of a sequence use the same format: either the legacy SMB v1 or
+  // the newer proto-group based SMB v2. SMB v2 chunks are immutable, arrive in
+  // order and need rewriting at readback.
+  bool is_smb_v2 = false;
+
+  // A gap after the last appended SMB v2 chunk, not yet stamped on a
+  // chunk.
+  // - SMB sequences need no such state. A gap shows up as a non-successor
+  //   ChunkID, so the reader finds it at the right position by itself.
+  // - SMB v2 chunks have no ChunkID. The gap must be recorded when it
+  //   happens, at the position between two chunks.
+  // - data_loss_reasons cannot hold it. The reader reports that on the next
+  //   packet it reads, which can be an older packet buffered before the gap.
+  // The next append moves this flag to kChunkLossBefore on its chunk. The
+  // reader then reports the loss on the first packet after the gap.
+  //
+  // Example for one writer:
+  //
+  //   Step                      Buffer        pending_chunk_v2_data_loss
+  //   Append(A)                 [A]           false
+  //   RecordChunkV2DataLoss()   [A]           true
+  //   Append(C)                 [A] [C*]      false, moved to C as *
+  //
+  //   * = kChunkLossBefore. A read returns A with no loss, then C with
+  //   DATA_LOSS_READ_GAP.
+  bool pending_chunk_v2_data_loss = false;
 };

 // +---------------------------------------------------------------------------+
@@ -63464,6 +71917,10 @@ class ChunkSeqReader {
 // +---------------------------------------------------------------------------+
 // | TraceBufferV2                                                             |
 // +---------------------------------------------------------------------------+
+// TODO(b/559531402): Rename trace_buffer_v1 to trace_buffer_legacy
+// (TraceBufferLegacy), and trace_buffer_v2 to trace_buffer_impl
+// (TraceBufferImpl). The "v2" here is easy to confuse with tracing v2 (the
+// SMB v2 transport), which this buffer also stores.
 class TraceBufferV2 : public TraceBuffer {
  public:
   using TBChunk = internal::TBChunk;
@@ -63471,20 +71928,6 @@ class TraceBufferV2 : public TraceBuffer {
   using Patch = TraceBuffer::Patch;
   using PacketSequenceProperties = TraceBuffer::PacketSequenceProperties;

-  // Represents a ProtoVM instance and some metadata
-  struct Vm {
-    Vm();
-    ~Vm();
-    Vm(Vm&&) noexcept;
-    Vm CloneReadOnly() const;
-
-    std::unique_ptr<protovm::Vm> instance;
-    std::string data_source_name;
-    uint64_t program_hash = 0;
-    uint32_t memory_limit_kb = 0;
-    base::FlatSet<ProducerID> producers;
-  };
-
   // Can return nullptr if the memory allocation fails.
   static std::unique_ptr<TraceBufferV2> Create(size_t size_in_bytes,
                                                OverwritePolicy = kOverwrite);
@@ -63540,12 +71983,34 @@ class TraceBufferV2 : public TraceBuffer {
                              size_t patches_size,
                              bool other_patches_pending) override;

-  void MaybeSetUpProtoVm(const std::string& data_source_name,
-                         const std::string& program_bytes,
-                         uint32_t memory_limit_kb,
-                         ProducerID producer_id);
+  // Returns true. Both SMB v1 and SMB v2 writers can use this buffer, but each
+  // {ProducerID, WriterID} sequence must use one format throughout its
+  // lifetime.
+  bool supports_tracing_v2() const override;

-  const std::vector<Vm>& GetProtoVmInstances() const { return protovms_; }
+  // Stores the batch in one TBChunk. See TraceBuffer::CopyChunkV2Untrusted()
+  // for ownership, ordering, continuation flags and loss reporting.
+  //
+  // Invalid input increments abi_violations. This includes a null fragment
+  // array, a nonempty fragment with a null payload, a batch larger than one
+  // TBChunk or than the whole buffer, or a writer ID whose sequence already
+  // holds SMB v1 chunks.
+  //
+  // Rejection because a kDiscard buffer is full or the producer has a ProtoVM
+  // on this buffer increments chunks_discarded. Once a kDiscard buffer stops
+  // accepting writes, it rejects later batches without validating their input.
+  bool CopyChunkV2Untrusted(const PacketSequenceProperties& sequence,
+                            const protozero::ConstBytes* fragments,
+                            size_t num_fragments,
+                            bool first_continues_from_prev,
+                            bool last_continues_on_next) override;
+
+  // Keeps the gap in the writer's SequenceState until the next stored batch
+  // carries it as kChunkLossBefore. If DeleteStaleEmptySequences() prunes the
+  // state before another batch arrives, the pending gap is forgotten.
+  void RecordChunkV2DataLoss(ProducerID, WriterID) override;
+
+  void RecordAbiViolation() override;

   // To read the contents of the buffer the caller needs to:
   //   BeginRead()
@@ -63556,13 +72021,18 @@ class TraceBufferV2 : public TraceBuffer {
   void BeginRead() override;

   // Returns the next packet in the buffer, if any, and the producer/writer
-  // identity that wrote it (as passed in the CopyChunkUntrusted() call).
+  // identity supplied when the sequence was created.
   // Returns false if no packets can be read at this point.
   // If a packet was read successfully, |previous_packet_on_sequence_dropped|
   // signals whether any data loss has been detected on the sequence
   // (e.g. because its chunk was overridden due to the ring buffer wrapping or
   // due to an ABI violation), and to |false| otherwise.
   //
+  // SMB v2 packets are reassembled and rewritten to length-delimited
+  // protobuf. Malformed packets and packets that length-delimited protobuf
+  // cannot encode are dropped, with loss reported on the next readable packet
+  // in the sequence.
+  //
   // This function returns only complete packets. Specifically:
   // When there is at least one complete packet in the buffer, this function
   // returns true and populates the TracePacket argument with the boundaries of
@@ -63587,22 +72057,38 @@ class TraceBufferV2 : public TraceBuffer {
       PacketSequenceProperties* sequence_properties,
       uint32_t* previous_packet_on_sequence_dropped) override;

+  // Compacts the buffer by moving live chunks to the front in ring order.
+  // It clears the unused tail and retains pages for the old |used_size_| plus
+  // 1/|kSpareCapacityDivisor| of it, capped at the buffer size.
+  // It advises the OS to release the whole pages after that range.
+  // If live chunks are on both sides of the write cursor, it skips compaction.
+  //
+  // Compaction can invalidate packet slices from earlier reads, so the caller
+  // must not use those packets after MaybeCompact().
+  //
+  // This is a no-op if the buffer is read-only or if it is a kDiscard buffer.
+  void MaybeCompact() override;
+
   // Creates a read-only clone of the trace buffer. The read iterators of the
-  // new buffer will be reset, as if no Read() had been called. Calls to
-  // CopyChunkUntrusted() and TryPatchChunkContents() on the returned cloned
-  // TraceBuffer will CHECK().
+  // new buffer will be reset.
+  //
+  // Calls to CopyChunkUntrusted(), CopyChunkV2Untrusted(),
+  // TryPatchChunkContents(), RecordChunkV2DataLoss() or RecordAbiViolation()
+  // on the returned clone will CHECK().
   std::unique_ptr<TraceBuffer> CloneReadOnly() const override;

+  TraceBufferProtoVms* protovms() override;
+
+  void set_read_only() override { read_only_ = true; }
+  const TraceStats::BufferStats& stats() const override { return stats_; }
+  const WriterStats& writer_stats() const override { return writer_stats_; }
   size_t size() const override { return size_; }
   size_t used_size() const override { return used_size_; }
   size_t GetMemoryUsageBytes() const override;
   OverwritePolicy overwrite_policy() const override {
     return overwrite_policy_;
   }
-  const TraceStats::BufferStats& stats() const override { return stats_; }
-  const WriterStats& writer_stats() const override { return writer_stats_; }
-  bool has_data() const override { return used_size_ > 0; }
-  void set_read_only() override { read_only_ = true; }
+  bool has_data() const override { return has_data_; }
   BufType buf_type() const override { return kV2; }

   void DumpForTesting();
@@ -63627,14 +72113,29 @@ class TraceBufferV2 : public TraceBuffer {

   bool Initialize(size_t size);
   TBChunk* CreateTBChunk(size_t off, size_t payload_size);
+
+  // Prepares contiguous storage at wr_. The caller checks that size <= size_.
+  // May evict chunks and wrap. Does not prune sequences or change stats for
+  // rejected writes. Returns false if the write reaches the end of a kDiscard
+  // buffer. The caller then seals the buffer with DiscardWrite().
+  bool MakeSpaceToWrite(size_t size);
   void DeleteNextChunksFor(size_t bytes_to_clear);

+  // Makes a reassembled SMB v2 packet length-delimited protobuf. A packet
+  // without nested messages keeps its slices into the buffer, as an SMB
+  // packet does. Otherwise the packet gets one owned slice. Returns false,
+  // leaving the packet unchanged, for:
+  // - malformed input, counted in abi_violations.
+  // - a packet that length-delimited protobuf cannot encode, counted in
+  //   oversized_packets_dropped.
+  bool RewriteProtoGroupPacket(TracePacket*);
+
   void DcheckIsAlignedAndWithinBounds(size_t off) const {
     PERFETTO_DCHECK((off & (alignof(TBChunk) - 1)) == 0);
     PERFETTO_DCHECK(off <= size_ - sizeof(TBChunk));
   }

-  // This should only be used when followed by a placement new.
+  // Used for placement new and to update the checksum after moving a chunk.
   TBChunk* GetTBChunkAtUnchecked(size_t off) {
     DcheckIsAlignedAndWithinBounds(off);
     return reinterpret_cast<TBChunk*>(begin() + off);
@@ -63665,7 +72166,6 @@ class TraceBufferV2 : public TraceBuffer {

   void DiscardWrite();
   void DeleteStaleEmptySequences();
-  void MaybeProcessOverwrittenPacketWithProtoVm(const TracePacket&, ProducerID);

   uint8_t* begin() const { return reinterpret_cast<uint8_t*>(data_.Get()); }
   uint8_t* end() const { return begin() + size_; }
@@ -63674,11 +72174,24 @@ class TraceBufferV2 : public TraceBuffer {
   base::PagedMemory data_;
   size_t size_ = 0;  // Size in bytes of |data_|.

-  // High watermark. The number of bytes (<= |size_|) written into the buffer
-  // before the first wraparound. This increases as data is written into the
-  // buffer and then saturates at |size_|.
+  // High watermark of the written bytes (<= |size_|).
+  // Writes increase it until the write cursor wraps around, and MaybeCompact()
+  // sets it to the total size of the remaining chunks.
+  // Reads and overwrites stop here, and [used_size_, size_) is zero-filled.
   size_t used_size_ = 0;

+  // Where the last madvise in MaybeCompact() began.
+  // The whole pages from here to |size_| are released or were never written.
+  // Writes after that can go past it. If they do, the next MaybeCompact() first
+  // raises it to |used_size_|, to cover the pages they touched.
+  // MaybeCompact() then uses it to skip a madvise that would release nothing.
+  size_t prev_madvise_begin_ = 0;
+
+  // Set when the first chunk is written, and never cleared.
+  // Unlike |used_size_|, this stays set when compaction empties the buffer.
+  // As in TraceBufferV1, clear_before_clone uses it to skip unused buffers.
+  bool has_data_ = false;
+
   size_t wr_ = 0;  // Write cursor (offset since start()).
   size_t rd_ = 0;  // Read cursor. Reset to wr_ on every BeginRead().
   std::optional<ChunkSeqReader> chunk_seq_reader_;
@@ -63706,8 +72219,8 @@ class TraceBufferV2 : public TraceBuffer {
   // This is used to sort SequenceState by least-recently cleared.
   uint64_t seq_age_ = 0;

-  // This buffer is a read-only snapshot obtained via Clone(). If this is true
-  // calls to CopyChunkUntrusted() and TryPatchChunkContents() will CHECK().
+  // This buffer is a read-only snapshot obtained via Clone(). If this is true,
+  // appending chunks, patching or recording loss will CHECK().
   bool read_only_ = false;

   // Only used when |overwrite_policy_ == kDiscard|. This is set the first time
@@ -63720,7 +72233,7 @@ class TraceBufferV2 : public TraceBuffer {
   bool suppress_client_dchecks_for_testing_ = false;

   // ProtoVMs used to process overwritten packets (go/perfetto-proto-vm)
-  std::vector<Vm> protovms_;
+  TraceBufferProtoVms protovms_;

   // Used to collect slices of the overwritten packet. Note that this is a
   // member variable (instead of local) so that the memory (internal
@@ -63729,12 +72242,8 @@ class TraceBufferV2 : public TraceBuffer {
   // few times during the initial iterations).
   TracePacket overwritten_packet_;

-  // Storage used to re-write overwritten packets (from TBv2) into contiguous
-  // memory to be used as ProtoVM patches (must be continguous to be decoded
-  // with protozero). Note that this is a member variable (instead of local) so
-  // that the memory is re-used across overwritten packets, thus involving
-  // allocations only when the storage needs to be expanded.
-  std::string protovm_patch_;
+  // Rewrites SMB v2 packets at readback.
+  tracing_v2::ProtoGroupRewriter rewriter_;
 };

 }  // namespace perfetto
@@ -63760,16 +72269,15 @@ class TraceBufferV2 : public TraceBuffer {

 #include <algorithm>
 #include <memory>
+#include <type_traits>

 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/murmur_hash.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/basic_types.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/client_identity.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory_abi.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
-// gen_amalgamated expanded: #include "src/protovm/vm.h"

 // gen_amalgamated expanded: #include "protos/perfetto/trace/trace_packet.pbzero.h"

@@ -63809,6 +72317,11 @@ constexpr uint8_t kChunkIncomplete = 0x80;
 // Mask out the flags that don't come from the ABI like kChunkIncomplete.
 constexpr uint8_t kFlagsMask = SharedMemoryABI::ChunkHeader::kFlagsMask;

+// SMB v2 sequences have no ChunkID. A gap is recorded on the first chunk
+// appended after the loss, so earlier packets do not inherit the loss marker.
+constexpr uint8_t kChunkLossBefore = 0x40;
+static_assert((kChunkLossBefore & (kFlagsMask | kChunkIncomplete)) == 0);
+
 // Compares two ChunkID(s) in a wrapping 32-bit ID space.
 // Returns:
 //   -1 if a comes before b in modular (wrapping) order.
@@ -63830,6 +72343,11 @@ constexpr size_t kKeepLastEmptySeq = 1024;

 // The threshold when we start scanning and deleting the oldest sequences.
 constexpr size_t kEmptySequencesGcTreshold = kKeepLastEmptySeq + 128;
+
+// Controls how much spare capacity compaction retains so later writes can
+// reuse pages and avoid page faults.
+constexpr size_t kSpareCapacityDivisor = 4;
+
 }  // namespace.

 namespace internal {
@@ -63978,7 +72496,8 @@ TBChunk* ChunkSeqIterator::NextChunkInSequence() {
   size_t next_chunk_off = chunk_list.at(next_list_idx);      // O(1)
   TBChunk* next_chunk = buf_->GetTBChunkAt(next_chunk_off);  // O(1)

-  if (last_chunk_id.has_value() && next_chunk->chunk_id != *last_chunk_id + 1)
+  if (!seq_->is_smb_v2 && last_chunk_id.has_value() &&
+      next_chunk->chunk_id != *last_chunk_id + 1)
     sequence_gap_detected_ = true;

   chunk_ = next_chunk;
@@ -64043,6 +72562,9 @@ ChunkSeqReader::ChunkSeqReader(TraceBufferV2* buf,
                            : 0,
                        iter_->chunk_id, end_->chunk_id);

+  if (seq_->is_smb_v2)
+    return;
+
   if (seq_->last_chunk_consumed.has_value()) {
     const auto& last = *seq_->last_chunk_consumed;
     // Re-admit: an incomplete chunk that was evicted has been re-committed
@@ -64075,6 +72597,12 @@ bool ChunkSeqReader::ReadNextPacketInSeqOrder(TracePacket* out_packet) {
   // This is because this class is used both while doing readbacks and,
   // importantly, in DeleteNextChunksFor() when overwriting.
   for (;;) {
+    // Report an SMB v2 gap when we reach the chunk after it. Clear the
+    // flag so later packets in this chunk do not report the same gap again.
+    if (PERFETTO_UNLIKELY(iter_->flags & kChunkLossBefore)) {
+      AddSeqDataLoss(seq_, DataLossReason::DATA_LOSS_READ_GAP);
+      iter_->flags &= static_cast<uint8_t>(~kChunkLossBefore);
+    }
     std::optional<Frag> maybe_frag = frag_iter_.NextFragmentInChunk();
     if (!maybe_frag.has_value()) {
       // Once we exhaust all fragments, move to the next chunk in the sequence.
@@ -64255,8 +72783,10 @@ ChunkSeqReader::ReassembleFragmentedPacket(TracePacket* out_packet,
       outcome.result = FragReassemblyResult::kNotEnoughData;
       break;
     }
-    if (chunk_iter.sequence_gap_detected()) {
-      // There is a gap in the sequence ID.
+    if (PERFETTO_UNLIKELY(chunk_iter.sequence_gap_detected() ||
+                          (next_chunk->flags & kChunkLossBefore))) {
+      // SMB sequences detect gaps via chunk_id discontinuity.
+      // SMB v2 sequences use the kChunkLossBefore flag instead.
       outcome.result = FragReassemblyResult::kDataLoss;
       outcome.reason = DataLossReason::DATA_LOSS_REASSEMBLY_GAP;
       break;
@@ -64334,6 +72864,8 @@ std::unique_ptr<TraceBufferV2> TraceBufferV2::Create(size_t size_in_bytes,
   // efficiency.
   static_assert(sizeof(TBChunk) == 16);
   static_assert(alignof(TBChunk) == 4);
+  // CloneReadOnly() and MaybeCompact() copy chunks as raw bytes.
+  static_assert(std::is_trivially_copyable_v<TBChunk>);
   std::unique_ptr<TraceBufferV2> trace_buffer(new TraceBufferV2(pol));
   if (!trace_buffer->Initialize(size_in_bytes))
     return nullptr;
@@ -64413,6 +72945,17 @@ bool TraceBufferV2::ReadNextTracePacket(
       // If it returns false, we should continue in buffer order.
       if (chunk_seq_reader_->ReadNextPacketInSeqOrder(out_packet)) {
         SequenceState& s = *chunk_seq_reader_->seq();
+        if (s.is_smb_v2) {
+          if (out_packet->size() == 0)
+            continue;  // Skip empty fragments.
+
+          if (PERFETTO_UNLIKELY(!RewriteProtoGroupPacket(out_packet))) {
+            out_packet->Clear();
+            internal::AddSeqDataLoss(&s,
+                                     DataLossReason::DATA_LOSS_CHUNK_CORRUPTED);
+            continue;
+          }
+        }
         *sequence_properties = {s.producer_id, s.client_identity, s.writer_id};
         *previous_packet_on_sequence_dropped = s.data_loss_reasons;
         s.data_loss_reasons = 0;
@@ -64437,6 +72980,151 @@ bool TraceBufferV2::ReadNextTracePacket(
   }  // for(;;)
 }

+void TraceBufferV2::MaybeCompact() {
+  TRACE_BUFFER_V2_DLOG("MaybeCompact(), wr_=%zu", wr_);
+  chunk_seq_reader_.reset();
+  if (overwrite_policy_ != kOverwrite || read_only_) {
+    return;
+  }
+
+  // The read cycle is complete, so usually only a few chunks remain.
+  size_t num_chunks = 0;
+  for (const auto& seq_entry : sequences_)
+    num_chunks += seq_entry.second.chunks.size();
+
+  // Store pointers to the chunk offsets in each SequenceState::chunks queue
+  // so we can update those offsets without another lookup.
+  // TODO(sashwinbalaji): Using these queues avoids scanning the buffer but
+  // leaves removed padding unaccounted for in padding_bytes_cleared.
+  std::vector<size_t*> chunk_offset_ptrs;
+  chunk_offset_ptrs.reserve(num_chunks);
+  for (auto& seq_entry : sequences_) {
+    for (size_t& chunk_offset : seq_entry.second.chunks)
+      chunk_offset_ptrs.emplace_back(&chunk_offset);
+  }
+
+  // Sort the pointer vector by chunk offset, from lowest to highest.
+  // This reorders only the vector, so the queue entries stay in sequence order.
+  // The loop below updates each chunk's offset in its original queue entry.
+  std::sort(chunk_offset_ptrs.begin(), chunk_offset_ptrs.end(),
+            [](const size_t* lhs, const size_t* rhs) { return *lhs < *rhs; });
+
+  // If live chunks are on both sides of wr_, skip compaction.
+  // The chunks at or after wr_ are older, so address order is not ring order.
+  //
+  // In this diagram and the ones below:
+  // - [C0], [D0], [C1], ... are live chunks of two sequences, C and D.
+  //   The number is the order of the chunk within its sequence.
+  // - "." is padding (read chunks).
+  // - "~" is unused space, past the new used_size_.
+  //
+  //   0                        wr_                      used_size_
+  //   |..[C1]..[D1]..[C2]......|..[C0]....[D0]..........|
+  const bool chunks_on_both_sides = !chunk_offset_ptrs.empty() &&
+                                    *chunk_offset_ptrs.front() < wr_ &&
+                                    *chunk_offset_ptrs.back() >= wr_;
+  if (PERFETTO_UNLIKELY(chunks_on_both_sides)) {
+    stats_.set_compactions_skipped(stats_.compactions_skipped() + 1);
+    return;
+  }
+
+  // All live chunks are on one side of wr_, so address order is ring order.
+  // Move each chunk to the lowest available offset, in address order.
+  // Each destination starts at or before its source, so a move cannot
+  // overwrite a chunk that comes later in address order.
+  //
+  // All live chunks are before wr_:
+  //
+  //   0                        wr_                      used_size_
+  //   |.[C0].[D0]..[C1]..[C2]..|........................|
+  //
+  // All live chunks are at or after wr_ because reads consumed every chunk
+  // before wr_ after the write cursor wrapped.
+  //
+  //   0                        wr_                      used_size_
+  //   |........................|.[C0].[D0]..[C1]..[C2]..|
+  //
+  // After compaction:
+  //
+  //   0                dst                              used_size_
+  //   |[C0][D0][C1][C2]|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
+  size_t dst = 0;
+  size_t prev_chunk_end = 0;
+  for (size_t* offset_ptr : chunk_offset_ptrs) {
+    const size_t src = *offset_ptr;
+    TBChunk* chunk = GetTBChunkAt(src);
+    PERFETTO_DCHECK(!chunk->is_padding());
+    const size_t outer_size = chunk->outer_size();
+    PERFETTO_CHECK(src >= prev_chunk_end);
+    prev_chunk_end = src + outer_size;
+    if (dst != src) {
+      memmove(begin() + dst, chunk, outer_size);
+      *offset_ptr = dst;
+      TBChunk* moved_chunk = GetTBChunkAtUnchecked(dst);
+      moved_chunk->checksum = TBChunk::Checksum(dst, moved_chunk->size);
+    }
+    dst += outer_size;
+  }
+
+  TRACE_BUFFER_V2_DLOG("Compacted %zu -> %zu bytes", used_size_, dst);
+  const size_t old_used_size = used_size_;
+  PERFETTO_CHECK(dst <= old_used_size && old_used_size <= size_);
+  wr_ = dst;
+  used_size_ = dst;
+
+  // Bytes beyond |old_used_size| are zero from allocation or earlier
+  // compactions.
+  // Clear [dst, old_used_size) on each compaction so the unused tail stays zero
+  // even when used_size_ shrinks.
+  // ReadNextTracePacket() visits offset 0 even when the buffer is empty and so
+  // if dst == 0, this also leaves the empty padding chunk that it needs.
+  memset(begin() + dst, 0, old_used_size - dst);
+
+  const size_t spare_size =
+      std::min(old_used_size / kSpareCapacityDivisor, size_ - old_used_size);
+  const size_t cur_madvise_begin = old_used_size + spare_size;
+
+  // Writes since the last compaction touched pages up to |old_used_size|.
+  prev_madvise_begin_ = std::max(prev_madvise_begin_, old_used_size);
+
+  // Skip the madvise if it starts at or past |prev_madvise_begin_|, because
+  // the pages from there on are already released.
+  if (cur_madvise_begin < prev_madvise_begin_) {
+    data_.AdviseDontNeedAfter(cur_madvise_begin);
+    prev_madvise_begin_ = cur_madvise_begin;
+  }
+  stats_.set_compactions(stats_.compactions() + 1);
+}
+
+bool TraceBufferV2::RewriteProtoGroupPacket(TracePacket* packet) {
+  PERFETTO_DCHECK(packet->size() > 0);
+
+  // The packet's slices point into TBChunks. The rewriter reads them in
+  // place, and writes the output into one slice that the packet owns.
+  //
+  // TODO(sashwinbalaji): Allocate the rewritten packets from an arena owned by
+  // the read batch. Today each rewritten packet is one heap allocation.
+  // Readers use packets in batches (an IPC reply or a file write) and drop
+  // them together, so one arena per batch fits their lifetime.
+  std::optional<Slice> rewritten;
+  switch (rewriter_.Rewrite(packet->slices(), &rewritten)) {
+    case tracing_v2::RewriteResult::kRewritten:
+      break;
+    case tracing_v2::RewriteResult::kUnchanged:
+      return true;
+    case tracing_v2::RewriteResult::kMalformedInput:
+      stats_.set_abi_violations(stats_.abi_violations() + 1);
+      return false;
+    case tracing_v2::RewriteResult::kGroupTooLarge:
+      stats_.set_oversized_packets_dropped(stats_.oversized_packets_dropped() +
+                                           1);
+      return false;
+  }
+  packet->Clear();
+  packet->AddSlice(std::move(*rewritten));
+  return true;
+}
+
 void TraceBufferV2::CopyChunkUntrusted(
     ProducerID producer_id_trusted,
     const ClientIdentity& client_identity_trusted,
@@ -64457,6 +73145,10 @@ void TraceBufferV2::CopyChunkUntrusted(
   if (PERFETTO_UNLIKELY(discard_writes_))
     return DiscardWrite();

+  // Only ABI flags may come from the producer. In particular, loss markers
+  // and the incomplete-chunk flag are set by the service.
+  chunk_flags &= kFlagsMask;
+
   // chunk_complete is true in the majority of cases, and is false only when
   // the service performs SMB scraping (upon flush).
   // If the chunk hasn't been completed, we should only consider the first
@@ -64528,6 +73220,13 @@ void TraceBufferV2::CopyChunkUntrusted(
   }

   SequenceState& seq = seq_it->second;
+  // A sequence uses one input format. SMB v1 and SMB v2 writers share one
+  // WriterID space per producer, so an SMB v2 sequence with this key
+  // belongs to another writer. Leave that writer's sequence unchanged.
+  if (seq.is_smb_v2) {
+    stats_.set_abi_violations(stats_.abi_violations() + 1);
+    return;
+  }
   if (trace_writer_data_drop) {
     stats_.set_trace_writer_packet_loss(stats_.trace_writer_packet_loss() + 1);
     internal::AddSeqDataLoss(&seq, DataLossReason::DATA_LOSS_WRITER_ABORT);
@@ -64619,9 +73318,10 @@ void TraceBufferV2::CopyChunkUntrusted(
     // Nothing for the erase to lose, nothing for the relocation to duplicate.
     const bool copy_fully_consumed = recommit_chunk->payload_avail == 0;

-    // The producer's final commit (not another scrape), with new fragments.
+    // Any re-commit with new fragments, whether the producer's final commit or
+    // a later scrape of the same still-open chunk.
     const bool commit_adds_new_data =
-        chunk_complete && all_frags_size > recommit_chunk->payload_size;
+        all_frags_size > recommit_chunk->payload_size;

     // EraseCurrentChunk() only supports the first chunk of a sequence. Later
     // chunks may stay physically ahead of the relocated one, which is fine:
@@ -64668,30 +73368,11 @@ void TraceBufferV2::CopyChunkUntrusted(
     internal::ChunkSeqIterator(this, &seq).EraseCurrentChunk();
   }  // if (recommit_chunk)

-  // If there isn't enough room from the given write position: write a padding
-  // record to clear the end of the buffer, wrap and start at offset 0.
-  const size_t cached_size_to_end = size_to_end();
-  if (PERFETTO_UNLIKELY(tbchunk_outer_size > cached_size_to_end)) {
-    // If we reached the end of the buffer and we are using discard policy,
-    // this is where we stop. This buffer will no longer accept data.
-    if (overwrite_policy_ == kDiscard)
-      return DiscardWrite();
-
-    // Skip the tail cleanup if the previous write landed exactly at the end
-    // of the buffer (|wr_| == |size_|): there is no leftover tail to clear.
-    if (cached_size_to_end > 0)
-      DeleteNextChunksFor(cached_size_to_end);
-
-    wr_ = 0;
-    stats_.set_write_wrap_count(stats_.write_wrap_count() + 1);
-    PERFETTO_DCHECK(size_to_end() >= tbchunk_outer_size);
-  }
-
-  // Deletes all chunks from |wptr_| to |wptr_| + |record_size|.
-  DeleteNextChunksFor(tbchunk_outer_size);
+  if (!MakeSpaceToWrite(tbchunk_outer_size))
+    return DiscardWrite();

   // |insert_pos| is invalid if any chunk was removed from this sequence since
-  // it was computed: either by the DeleteNextChunksFor above, or by the
+  // it was computed: either by MakeSpaceToWrite() above, or by the
   // relocation erase.
   // Why don't we compute the insert_pos here? Because we also need to check
   // for re-commits (which are rare, but possible) and don't want to iterate
@@ -64722,6 +73403,7 @@ void TraceBufferV2::CopyChunkUntrusted(
         stats_.chunks_committed_out_of_order() + 1);
   }
   chunk_list.InsertAfter(insert_pos, wr_);
+  has_data_ = true;
   if (chunk_list.size() == 1 && !seq_is_new) {
     PERFETTO_DCHECK(empty_sequences_ > 0);
     --empty_sequences_;
@@ -64743,6 +73425,166 @@ void TraceBufferV2::CopyChunkUntrusted(
     DeleteStaleEmptySequences();
 }

+bool TraceBufferV2::supports_tracing_v2() const {
+  return true;
+}
+
+bool TraceBufferV2::CopyChunkV2Untrusted(
+    const PacketSequenceProperties& sequence,
+    const protozero::ConstBytes* fragments,
+    size_t num_fragments,
+    bool first_continues_from_prev,
+    bool last_continues_on_next) {
+  PERFETTO_CHECK(!read_only_);
+
+  if (PERFETTO_UNLIKELY(num_fragments == 0))
+    return false;
+
+  // Each rejection site updates its own stats. This only records the gap.
+  auto reject = [&] {
+    RecordChunkV2DataLoss(sequence.producer_id_trusted, sequence.writer_id);
+    return false;
+  };
+  auto reject_abi_violation = [&] {
+    stats_.set_abi_violations(stats_.abi_violations() + 1);
+    return reject();
+  };
+
+  if (PERFETTO_UNLIKELY(discard_writes_)) {
+    DiscardWrite();
+    return reject();
+  }
+
+  if (!fragments)
+    return reject_abi_violation();
+
+  // ProtoVM expects length-delimited protobuf on the overwrite path.
+  if (protovms_.HasProducer(sequence.producer_id_trusted)) {
+    stats_.set_chunks_discarded(stats_.chunks_discarded() + 1);
+    return reject();
+  }
+
+  // The batch becomes one TBChunk with the same payload layout as an SMB chunk,
+  // so readback uses FragIterator for both:
+  //
+  //   +---------+-----------+--------+-----------+--------+-----+---------+
+  //   | TBChunk | varint s0 | frag 0 | varint s1 | frag 1 | ... | padding |
+  //   +---------+-----------+--------+-----------+--------+-----+---------+
+  //             |<----------- total_payload <= kMaxSize ------->|
+  //
+  // Validate the entire batch before making space: a rejected batch must not
+  // evict any buffered packets. A batch larger than one TBChunk breaks the
+  // writer's chunk contract. Each addend is at most kMaxSize + 3, so
+  // |total_payload| cannot overflow.
+  size_t total_payload = 0;
+  for (size_t i = 0; i < num_fragments; i++) {
+    const size_t frag_size = fragments[i].size;
+    if ((frag_size && !fragments[i].data) || frag_size > TBChunk::kMaxSize)
+      return reject_abi_violation();
+    uint8_t varint[3];  // A varint up to TBChunk::kMaxSize takes 3 bytes.
+    const size_t varint_size = static_cast<size_t>(
+        proto_utils::WriteVarInt(frag_size, varint) - varint);
+    total_payload += varint_size + frag_size;
+    if (total_payload > TBChunk::kMaxSize)
+      return reject_abi_violation();
+  }
+
+  // As in CopyChunkUntrusted(): a chunk larger than the whole buffer. Rare,
+  // e.g. a 16 KB buffer and a 32 KB ring chunk.
+  const size_t tbchunk_outer_size = TBChunk::OuterSize(total_payload);
+  if (PERFETTO_UNLIKELY(tbchunk_outer_size > size_))
+    return reject_abi_violation();
+
+  auto seq_key =
+      MkProducerAndWriterID(sequence.producer_id_trusted, sequence.writer_id);
+  writer_stats_.Insert(seq_key, static_cast<HistValue>(total_payload));
+
+  auto [seq_it, seq_is_new] = sequences_.try_emplace(
+      seq_key, SequenceState(sequence.producer_id_trusted, sequence.writer_id,
+                             sequence.client_identity_trusted));
+  SequenceState& seq = seq_it->second;
+  if (seq_is_new) {
+    seq.is_smb_v2 = true;
+    seq.age_for_gc = ++seq_age_;
+    ++empty_sequences_;
+  } else if (!seq.is_smb_v2) {
+    // As in CopyChunkUntrusted(), a sequence uses one input format. The SMB
+    // sequence belongs to another writer, so no loss is recorded on it.
+    return reject_abi_violation();
+  }
+
+  if (!MakeSpaceToWrite(tbchunk_outer_size)) {
+    DiscardWrite();
+    return reject();
+  }
+
+  TBChunk* chunk = CreateTBChunk(wr_, total_payload);
+  chunk->pri_wri_id = seq_key;
+  chunk->payload_size = static_cast<uint16_t>(total_payload);
+  chunk->payload_avail = static_cast<uint16_t>(total_payload);
+
+  uint8_t flags = 0;
+  if (PERFETTO_UNLIKELY(seq.pending_chunk_v2_data_loss)) {
+    flags |= kChunkLossBefore;
+    seq.pending_chunk_v2_data_loss = false;
+  }
+  if (first_continues_from_prev)
+    flags |= kFirstPacketContFromPrevChunk;
+  if (last_continues_on_next)
+    flags |= kLastPacketContOnNextChunk;
+  chunk->flags = flags;
+
+  // Copy fragment data into the chunk payload with varint length prefixes.
+  uint8_t* dst = chunk->fragments_begin();
+  for (size_t i = 0; i < num_fragments; i++) {
+    const size_t frag_size = fragments[i].size;
+    dst = protozero::proto_utils::WriteVarInt(frag_size, dst);
+    if (frag_size > 0) {
+      memcpy(dst, fragments[i].data, frag_size);
+      dst += frag_size;
+    }
+  }
+  PERFETTO_DCHECK(static_cast<size_t>(dst - chunk->fragments_begin()) ==
+                  total_payload);
+
+  // Append after eviction, which can remove earlier chunks from this sequence.
+  auto& chunk_list = seq.chunks;
+  bool was_empty = chunk_list.empty();
+  chunk_list.emplace_back(wr_);
+  has_data_ = true;
+  if (was_empty) {
+    PERFETTO_DCHECK(empty_sequences_ > 0);
+    --empty_sequences_;
+  }
+
+  wr_ += tbchunk_outer_size;
+  PERFETTO_DCHECK(wr_ <= size_ && wr_ <= used_size_);
+
+  stats_.set_chunks_written(stats_.chunks_written() + 1);
+  stats_.set_bytes_written(stats_.bytes_written() + tbchunk_outer_size);
+
+  if (empty_sequences_ > kEmptySequencesGcTreshold)
+    DeleteStaleEmptySequences();
+
+  return true;
+}
+
+void TraceBufferV2::RecordChunkV2DataLoss(ProducerID producer_id,
+                                          WriterID writer_id) {
+  PERFETTO_CHECK(!read_only_);
+
+  // SMB v1 and SMB v2 writers share one WriterID space per producer. An SMB v1
+  // sequence with this key therefore belongs to another writer.
+  auto seq_it = sequences_.find(MkProducerAndWriterID(producer_id, writer_id));
+  if (seq_it != sequences_.end() && seq_it->second.is_smb_v2)
+    seq_it->second.pending_chunk_v2_data_loss = true;
+}
+
+void TraceBufferV2::RecordAbiViolation() {
+  PERFETTO_CHECK(!read_only_);
+  stats_.set_abi_violations(stats_.abi_violations() + 1);
+}
+
 TraceBufferV2::TBChunk* TraceBufferV2::CreateTBChunk(size_t off, size_t size) {
   DcheckIsAlignedAndWithinBounds(off);
   size_t end = off + TBChunk::OuterSize(size);
@@ -64754,6 +73596,26 @@ TraceBufferV2::TBChunk* TraceBufferV2::CreateTBChunk(size_t off, size_t size) {
   return new (chunk) TBChunk(off, size);
 }

+bool TraceBufferV2::MakeSpaceToWrite(size_t size) {
+  PERFETTO_DCHECK(size <= size_);
+
+  // If the chunk does not fit at the end, clear the remaining tail and wrap.
+  // In discard mode, reaching the end stops all further writes.
+  const size_t tail_size = size_to_end();
+  if (PERFETTO_UNLIKELY(size > tail_size)) {
+    if (overwrite_policy_ == kDiscard)
+      return false;
+    // A previous write can land exactly at the end, leaving no tail to clear.
+    if (tail_size > 0)
+      DeleteNextChunksFor(tail_size);
+    wr_ = 0;
+    stats_.set_write_wrap_count(stats_.write_wrap_count() + 1);
+    PERFETTO_DCHECK(size_to_end() >= size);
+  }
+  DeleteNextChunksFor(size);
+  return true;
+}
+
 // Deletes (by marking the record invalid and removing form the index) all
 // chunks from wr_ to (wr_ + bytes_to_clear).
 // Graphically, assume the initial situation is the following (|wptr_| = 10).
@@ -64782,8 +73644,9 @@ void TraceBufferV2::DeleteNextChunksFor(size_t bytes_to_clear) {
   // This loop erases all the existing chunks in the clear range.
   for (size_t off = wr_, next_off = 0; off < clear_end; off = next_off) {
     if (PERFETTO_UNLIKELY(off >= used_size_)) {
-      // This happens only on the first round of writing, when there are no
-      // chunks in the buffer yet. There is nothing to delete here, easy.
+      // This happens on the first round of writing, or after MaybeCompact()
+      // shrinks |used_size_|, when there are no chunks in this range.
+      // There is nothing to delete here, easy.
       break;
     }
     TBChunk* chunk = GetTBChunkAt(off);
@@ -64805,7 +73668,7 @@ void TraceBufferV2::DeleteNextChunksFor(size_t bytes_to_clear) {
       // protovm. If not, we still need to do reads, but without having to
       // accumulated data in a packet.
       TracePacket* maybe_packet = nullptr;
-      if (!protovms_.empty()) {
+      if (!protovms_.empty() && !csr.seq()->is_smb_v2) {
         overwritten_packet_.Clear();
         maybe_packet = &overwritten_packet_;
       }
@@ -64813,8 +73676,8 @@ void TraceBufferV2::DeleteNextChunksFor(size_t bytes_to_clear) {
         break;
       }
       if (maybe_packet) {
-        MaybeProcessOverwrittenPacketWithProtoVm(*maybe_packet,
-                                                 csr.seq()->producer_id);
+        protovms_.MaybeProcessOverwrittenPacket(*maybe_packet,
+                                                csr.seq()->producer_id);
       }
       has_cleared_unconsumed_fragments = true;
     }
@@ -64858,7 +73721,7 @@ bool TraceBufferV2::TryPatchChunkContents(ProducerID producer_id,

   ProducerAndWriterID seq_key = MkProducerAndWriterID(producer_id, writer_id);
   auto seq_it = sequences_.find(seq_key);
-  if (seq_it == sequences_.end()) {
+  if (seq_it == sequences_.end() || seq_it->second.is_smb_v2) {
     stats_.set_patches_failed(stats_.patches_failed() + 1);
     return false;
   }
@@ -64934,7 +73797,8 @@ void TraceBufferV2::DiscardWrite() {
 // This is to avoid thrashing with a sort every time one sequence becomes empty.
 // Also we have to defer the deletion of SequenceState outside of
 // ReadNextTracePacket() calls, as that caches SequenceState* pointers in
-// seq_iter_. This is called only at the end of each CopyChunkUntrusted().
+// chunk_seq_reader_. This is called only at the end of CopyChunkUntrusted()
+// and CopyChunkV2Untrusted().
 void TraceBufferV2::DeleteStaleEmptySequences() {
   // Build a vector of iterators; sort it; delete the first size() - kThreshold.
   using SeqIterator = decltype(sequences_)::iterator;
@@ -64953,6 +73817,11 @@ void TraceBufferV2::DeleteStaleEmptySequences() {
               return a->second.age_for_gc < b->second.age_for_gc;
             });

+  // TODO(sashwinbalaji): Pruning an SMB v2 sequence discards its
+  // pending_chunk_v2_data_loss. If that writer appends again, the loss is not
+  // reported. SMB sequences have the same limit: pruning discards
+  // last_chunk_consumed. Revisit if field traces show missed loss for writers
+  // that write rarely while many others churn.
   size_t n_oldest = empty_seqs.size() - kKeepLastEmptySeq;
   for (size_t i = 0; i < n_oldest; ++i) {
     sequences_.erase(empty_seqs[i]);
@@ -64961,9 +73830,9 @@ void TraceBufferV2::DeleteStaleEmptySequences() {

   // This is not really required in the current implementation and is here just
   // to spot bugs while refactoring the code in future. This function is
-  // only called by CopyChunkUntrusted(), but the chunk_seq_reader_ (which can
+  // called only by writes, but the chunk_seq_reader_ (which can
   // hold onto a SequenceState* pointer) is reset by every BeginRead() call.
-  // By design BeginRead()/ReadNextTracePacket() cannot overlap with CCU().
+  // By design BeginRead()/ReadNextTracePacket() cannot overlap with writes.
   chunk_seq_reader_.reset();
 }

@@ -64974,12 +73843,12 @@ std::unique_ptr<TraceBuffer> TraceBufferV2::CloneReadOnly() const {
   return buf;
 }

+TraceBufferProtoVms* TraceBufferV2::protovms() {
+  return &protovms_;
+}
+
 size_t TraceBufferV2::GetMemoryUsageBytes() const {
-  size_t total_bytes = size();
-  for (const Vm& vm : protovms_) {
-    total_bytes += vm.instance->GetMemoryUsageBytes();
-  }
-  return total_bytes;
+  return size() + protovms_.GetMemoryUsageBytes();
 }

 TraceBufferV2::TraceBufferV2(CloneCtor, const TraceBufferV2& src)
@@ -64995,6 +73864,7 @@ TraceBufferV2::TraceBufferV2(CloneCtor, const TraceBufferV2& src)
   data_.EnsureCommitted(src.used_size_);
   memcpy(data_.Get(), src.data_.Get(), src.used_size_);
   used_size_ = src.used_size_;
+  has_data_ = src.has_data_;
   wr_ = src.wr_;

   stats_ = src.stats_;
@@ -65003,28 +73873,13 @@ TraceBufferV2::TraceBufferV2(CloneCtor, const TraceBufferV2& src)
   stats_.set_readaheads_failed(0);
   stats_.set_readaheads_succeeded(0);

-  // Finally copy over the SequenceState map.
+  // Finally copy over the SequenceState map and WriterStats map. WriterStats
+  // must be copied because EmitTraceProvenance() derives the producer->sequence
+  // mapping from it, until b/568173038 is addressed.
   sequences_ = src.sequences_;
+  writer_stats_ = src.writer_stats_;

-  for (const auto& vm : src.protovms_) {
-    auto vm_cloned = vm.CloneReadOnly();
-    if (!vm_cloned.instance) {
-      PERFETTO_ELOG("Failed to clone ProtoVMs");
-      protovms_.clear();
-      break;
-    }
-    protovms_.push_back(std::move(vm_cloned));
-  }
-}
-
-TraceBufferV2::Vm TraceBufferV2::Vm::CloneReadOnly() const {
-  Vm cloned_vm;
-  cloned_vm.data_source_name = data_source_name;
-  cloned_vm.program_hash = program_hash;
-  cloned_vm.memory_limit_kb = memory_limit_kb;
-  cloned_vm.producers = producers;
-  cloned_vm.instance = instance->CloneReadOnly();
-  return cloned_vm;
+  protovms_.CloneReadOnlyFrom(src.protovms_);
 }

 void TraceBufferV2::DumpForTesting() {
@@ -65044,8 +73899,8 @@ void TraceBufferV2::DumpForTesting() {
       PERFETTO_DLOG(
           "[%06zu-%06zu] size=%05u(%05u) id=%05u pr_wr=%08x flags=%08x", rd,
           rd + c->outer_size(), c->payload_size,
-          c->payload_size - c->payload_avail, c->chunk_id, c->pri_wri_id,
-          c->flags);
+          static_cast<unsigned>(c->payload_size - c->payload_avail),
+          c->chunk_id, c->pri_wri_id, c->flags);
       rd += c->outer_size();
       continue;
     }
@@ -65059,79 +73914,6 @@ void TraceBufferV2::DumpForTesting() {
   PERFETTO_DLOG("------------------------------------------------------------");
 }

-TraceBufferV2::Vm::Vm() = default;
-TraceBufferV2::Vm::~Vm() = default;
-TraceBufferV2::Vm::Vm(Vm&&) noexcept = default;
-
-void TraceBufferV2::MaybeSetUpProtoVm(const std::string& data_source_name,
-                                      const std::string& program_bytes,
-                                      uint32_t memory_limit_kb,
-                                      ProducerID producer_id) {
-  Vm* vm = nullptr;
-  // Re-use existing ProtoVM instance, if any.
-  uint64_t program_hash = base::MurmurHashValue(program_bytes);
-  auto vm_it =
-      std::find_if(protovms_.begin(), protovms_.end(), [&](const Vm& vm) {
-        return vm.data_source_name == data_source_name &&
-               vm.program_hash == program_hash;
-      });
-  if (vm_it != protovms_.end()) {
-    vm = &(*vm_it);
-  } else {
-    // Otherwise instantiate new ProtoVM
-    Vm new_vm;
-    new_vm.data_source_name = data_source_name;
-    new_vm.program_hash = program_hash;
-    new_vm.memory_limit_kb = memory_limit_kb;
-
-    protozero::ConstBytes program_bytes_view{
-        reinterpret_cast<const uint8_t*>(program_bytes.data()),
-        program_bytes.size()};
-
-    new_vm.instance = std::make_unique<protovm::Vm>(
-        program_bytes_view, new_vm.memory_limit_kb * 1024);
-    if (!new_vm.instance) {
-      PERFETTO_ELOG("Failed to allocate ProtoVM");
-      return;
-    }
-
-    protovms_.push_back(std::move(new_vm));
-    vm = &protovms_.back();
-  }
-  // Update ProtoVM's producer IDs
-  vm->producers.insert(producer_id);
-}
-
-void TraceBufferV2::MaybeProcessOverwrittenPacketWithProtoVm(
-    const TracePacket& packet,
-    ProducerID producer) {
-  protovm_patch_.clear();
-
-  for (auto& vm : protovms_) {
-    if (vm.producers.find(producer) == vm.producers.end()) {
-      continue;
-    }
-    // TODO(keanmariotti): add an optimized path for the case
-    // "packet.slices().size() == 1" (zero copy)
-    if (protovm_patch_.empty()) {
-      packet.GetRawBytes(&protovm_patch_);
-    }
-    protozero::ConstBytes bytes{
-        reinterpret_cast<const uint8_t*>(protovm_patch_.data()),
-        protovm_patch_.size()};
-    auto status = vm.instance->ApplyPatch(bytes);
-    if (status.IsOk()) {
-      break;
-    }
-    if (status.IsAbort()) {
-      // TODO(keanmariotti): consider doing something more here. Ideally
-      // triggering a field upload containing the stacktrace.
-      PERFETTO_ELOG("ProtoVM abort while applying patch. Stacktrace:\n%s",
-                    base::Join(status.stacktrace(), "\n").c_str());
-    }
-  }
-}
-
 }  // namespace perfetto
 // gen_amalgamated begin source: src/tracing/service/tracing_service_endpoints_impl.cc
 // gen_amalgamated begin header: src/tracing/service/tracing_service_endpoints_impl.h
@@ -65173,6 +73955,8 @@ void TraceBufferV2::MaybeProcessOverwrittenPacketWithProtoVm(
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory_abi.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/tracing_service.h"
 // gen_amalgamated expanded: #include "perfetto/tracing/core/forward_decls.h"
+// gen_amalgamated expanded: #include "src/tracing/service/service_ring_buffer_drainer.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/producer_ring_buffer_arbiter.h"

 // This header contains the declarations for the 3 abtract classes
 // (ProducerEndpointImpl, ConsumerEndpointImpl, RelayEndpointImpl).
@@ -65192,7 +73976,9 @@ struct TracingSession;
 struct TriggerInfo;

 // The implementation behind the service endpoint exposed to each producer.
-class ProducerEndpointImpl : public TracingService::ProducerEndpoint {
+class ProducerEndpointImpl
+    : public TracingService::ProducerEndpoint,
+      public tracing_v2::ServiceRingBufferDrainer::Delegate {
  public:
   ProducerEndpointImpl(ProducerID,
                        const ClientIdentity& client_identity,
@@ -65203,7 +73989,8 @@ class ProducerEndpointImpl : public TracingService::ProducerEndpoint {
                        const std::string& machine_name,
                        const std::string& sdk_version,
                        bool in_process,
-                       bool smb_scraping_enabled);
+                       bool smb_scraping_enabled,
+                       uint32_t protocol_abi_versions);
   ~ProducerEndpointImpl() override;

   // TracingService::ProducerEndpoint implementation.
@@ -65221,6 +74008,9 @@ class ProducerEndpointImpl : public TracingService::ProducerEndpoint {
   std::unique_ptr<TraceWriter> CreateTraceWriter(
       BufferID,
       BufferExhaustedPolicy) override;
+  std::unique_ptr<TraceWriter> CreateTraceWriterV2(
+      BufferID,
+      BufferExhaustedPolicy) override;
   SharedMemoryArbiter* MaybeSharedMemoryArbiter() override;
   bool IsShmemProvidedByProducer() const override;
   void NotifyFlushComplete(FlushRequestID) override;
@@ -65252,6 +74042,24 @@ class ProducerEndpointImpl : public TracingService::ProducerEndpoint {
     return std::nullopt;
   }

+  // TracingService::ProducerEndpoint implementation for tracing v2.
+  void InitializeV2RingBuffer(const DataSourceConfig&) override;
+  void AttachV2RingBuffer(const std::shared_ptr<SharedMemory>&,
+                          uint32_t chunk_size_bytes,
+                          std::function<void(bool)>) override;
+  void DrainV2RingBuffer() override;
+
+  // tracing_v2::ServiceRingBufferDrainer::Delegate implementation.
+  TraceBuffer* GetV2RingBufferDestination(BufferID) override;
+  void ForEachV2RingBufferDestination(
+      const std::function<void(TraceBuffer&)>& callback) override;
+  void OnRingBufferChunksDiscarded(uint64_t count) override;
+
+  // Mapping size for the memory guardrail. Zero if no ring buffer is attached.
+  size_t ring_buffer_size_bytes() const {
+    return v2_ring_buffer_drainer_ ? v2_ring_buffer_drainer_->size_bytes() : 0;
+  }
+
   bool IsShmemEmulated() { return shmem_abi_.use_shmem_emulation(); }

   bool IsAndroidProcessFrozen();
@@ -65299,6 +74107,32 @@ class ProducerEndpointImpl : public TracingService::ProducerEndpoint {
   // SharedMemoryArbiterImpl methods themselves are thread-safe.
   std::unique_ptr<SharedMemoryArbiterImpl> inproc_shmem_arbiter_;

+  // Bitmask of the common versions supplied to ConnectProducer(). Fixed for
+  // the connection.
+  const uint32_t protocol_abi_versions_;
+
+  // Owns the ring buffer and creates v2 writers.
+  //
+  // States:
+  // - nullopt: initialization has not been attempted.
+  // - nullptr: sizing or allocation failed.
+  //   Later InitializeV2RingBuffer() calls on the same connection do not retry
+  //   initialization, to avoid changing the pointer while v2 writer threads
+  //   read it without a lock.
+  // - Non-null pointer: the arbiter exists and tracks service attachment.
+  //
+  // Set by InitializeV2RingBuffer() when an instance selects v2, before the
+  // first in-process v2 writer.
+  // Writer threads then read it without a lock.
+  // For an IPC producer, the arbiter lives in the producer process.
+  std::optional<std::unique_ptr<tracing_v2::ProducerRingBufferArbiter>>
+      v2_ring_buffer_arbiter_;
+
+  // |v2_ring_buffer_drainer_| owns the reader and keeps the ring buffer mapped.
+  // The reader's destination checks use |allowed_target_buffers_|.
+  // DisconnectProducer() runs the final drain through |v2_ring_buffer_drainer_|
+  // before |allowed_target_buffers_| is destroyed.
+  std::unique_ptr<tracing_v2::ServiceRingBufferDrainer> v2_ring_buffer_drainer_;
   PERFETTO_THREAD_CHECKER(thread_checker_)
   base::WeakRunner weak_runner_;
 };
@@ -65984,6 +74818,7 @@ struct TracingSession {
 // gen_amalgamated expanded: #include "src/tracing/service/clock.h"
 // gen_amalgamated expanded: #include "src/tracing/service/dependencies.h"
 // gen_amalgamated expanded: #include "src/tracing/service/random.h"
+// gen_amalgamated expanded: #include "src/tracing/service/trace_buffer.h"
 // gen_amalgamated expanded: #include "src/tracing/service/tracing_service_endpoints_impl.h"
 // gen_amalgamated expanded: #include "src/tracing/service/tracing_service_session.h"
 // gen_amalgamated expanded: #include "src/tracing/service/tracing_service_structs.h"
@@ -66007,14 +74842,12 @@ class Consumer;
 class Producer;
 class SharedMemory;
 class SharedMemoryArbiterImpl;
-class TraceBuffer;
 class TracePacket;

 namespace tracing_service {
 // The tracing service business logic.
 class TracingServiceImpl : public TracingService {
  public:
-  static constexpr size_t kMaxShmSize = 32 * 1024 * 1024ul;
   static constexpr uint8_t kSyncMarker[] = {0x82, 0x47, 0x7a, 0x76, 0xb2, 0x8d,
                                             0x42, 0xba, 0x81, 0xdc, 0x33, 0x32,
                                             0x6d, 0x57, 0xa0, 0x79};
@@ -66118,7 +74951,12 @@ class TracingServiceImpl : public TracingService {
       size_t shared_memory_page_size_hint_bytes = 0,
       std::unique_ptr<SharedMemory> shm = nullptr,
       const std::string& sdk_version = {},
-      const std::string& machine_name = {}) override;
+      const std::string& machine_name = {},
+      uint32_t protocol_abi_versions = kProtocolAbiV1) override;
+
+  // The endpoint reports discarded ring buffer chunks on the service
+  // sequence. Adds them to chunks_discarded.
+  void OnRingBufferChunksDiscarded(uint64_t count);

   std::unique_ptr<TracingService::ConsumerEndpoint> ConnectConsumer(
       Consumer*,
@@ -66239,6 +75077,7 @@ class TracingServiceImpl : public TracingService {
                      bool success);
   void ScrapeSharedMemoryBuffers(TracingSession*, ProducerEndpointImpl*);
   void PeriodicClearIncrementalStateTask(TracingSessionID, bool post_next_only);
+  // Returns nullptr if there is no buffer with this ID.
   TraceBuffer* GetBufferByID(BufferID);
   void FlushDataSourceInstances(
       TracingSession*,
@@ -66287,8 +75126,11 @@ class TracingServiceImpl : public TracingService {
                           std::vector<TracePacket>* packets);

   // If `*tracing_session` has compression enabled, compress `*packets`.
+  // `skip_compression_of_first_n_packets` is the number of packets to skip
+  // compression of (i.e. TraceConfig, UUID packets).
   void MaybeCompressPackets(TracingSession* tracing_session,
-                            std::vector<TracePacket>* packets);
+                            std::vector<TracePacket>* packets,
+                            size_t skip_compression_of_first_n_packets);

   // If `*tracing_session` is configured to write into a file, writes `packets`
   // into the file.
@@ -66297,6 +75139,12 @@ class TracingServiceImpl : public TracingService {
   // been an error), false otherwise.
   bool WriteIntoFile(TracingSession* tracing_session,
                      std::vector<TracePacket> packets);
+
+  // Attempts to compact each buffer in `*tracing_session`.
+  // Call only after the ReadBuffers() packets are no longer in use, because
+  // compaction can move the data their slices reference.
+  void MaybeCompactBuffers(TracingSession* tracing_session);
+
   void OnStartTriggersTimeout(TracingSessionID tsid);
   void MaybeLogUploadEvent(const TraceConfig&,
                            const base::Uuid&,
@@ -66378,20 +75226,30 @@ class TracingServiceImpl : public TracingService {
  */

 // gen_amalgamated expanded: #include "src/tracing/service/tracing_service_endpoints_impl.h"
+
+#include <cinttypes>
+
 // gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/file_utils.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/metatrace.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/string_view.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/version.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/consumer.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/producer.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_writer.h"
+// gen_amalgamated expanded: #include "perfetto/tracing/core/data_source_config.h"
 // gen_amalgamated expanded: #include "perfetto/tracing/core/tracing_service_capabilities.h"
 // gen_amalgamated expanded: #include "perfetto/tracing/core/tracing_service_state.h"
+// gen_amalgamated expanded: #include "src/tracing/core/in_process_shared_memory.h"
+// gen_amalgamated expanded: #include "src/tracing/core/null_trace_writer.h"
 // gen_amalgamated expanded: #include "src/tracing/core/shared_memory_arbiter_impl.h"
+// gen_amalgamated expanded: #include "src/tracing/service/service_ring_buffer_drainer.h"
+// gen_amalgamated expanded: #include "src/tracing/service/trace_buffer.h"
 // gen_amalgamated expanded: #include "src/tracing/service/tracing_service_impl.h"
 // gen_amalgamated expanded: #include "src/tracing/service/tracing_service_structs.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_abi.h"

 // gen_amalgamated expanded: #include "protos/perfetto/common/builtin_clock.pbzero.h"

@@ -66790,7 +75648,8 @@ ProducerEndpointImpl::ProducerEndpointImpl(
     const std::string& machine_name,
     const std::string& sdk_version,
     bool in_process,
-    bool smb_scraping_enabled)
+    bool smb_scraping_enabled,
+    uint32_t protocol_abi_versions)
     : id_(id),
       client_identity_(client_identity),
       service_(service),
@@ -66800,6 +75659,7 @@ ProducerEndpointImpl::ProducerEndpointImpl(
       sdk_version_(sdk_version),
       in_process_(in_process),
       smb_scraping_enabled_(smb_scraping_enabled),
+      protocol_abi_versions_(protocol_abi_versions),
       weak_runner_(task_runner) {}

 ProducerEndpointImpl::~ProducerEndpointImpl() {
@@ -67000,11 +75860,30 @@ bool ProducerEndpointImpl::IsShmemProvidedByProducer() const {
 std::unique_ptr<TraceWriter> ProducerEndpointImpl::CreateTraceWriter(
     BufferID buf_id,
     BufferExhaustedPolicy buffer_exhausted_policy) {
+  if (!(protocol_abi_versions_ & kProtocolAbiV1)) {
+    PERFETTO_ELOG(
+        "Cannot create a v1 trace writer: v1 is not in the common protocol "
+        "mask (%x)",
+        protocol_abi_versions_);
+    return std::make_unique<NullTraceWriter>();
+  }
   PERFETTO_DCHECK(MaybeSharedMemoryArbiter());
   return MaybeSharedMemoryArbiter()->CreateTraceWriter(buf_id,
                                                        buffer_exhausted_policy);
 }

+// Can be called on any thread.
+std::unique_ptr<TraceWriter> ProducerEndpointImpl::CreateTraceWriterV2(
+    BufferID buf_id,
+    BufferExhaustedPolicy buffer_exhausted_policy) {
+  auto* arbiter = v2_ring_buffer_arbiter_.has_value()
+                      ? v2_ring_buffer_arbiter_->get()
+                      : nullptr;
+  if (!arbiter)
+    return std::make_unique<NullTraceWriter>();
+  return arbiter->CreateTraceWriter(buf_id, buffer_exhausted_policy);
+}
+
 void ProducerEndpointImpl::NotifyFlushComplete(FlushRequestID id) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
   PERFETTO_DCHECK(MaybeSharedMemoryArbiter());
@@ -67116,6 +75995,100 @@ bool ProducerEndpointImpl::IsAndroidProcessFrozen() {
   return false;
 }

+void ProducerEndpointImpl::InitializeV2RingBuffer(
+    const DataSourceConfig& config) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (v2_ring_buffer_arbiter_.has_value() || !in_process_ ||
+      !(protocol_abi_versions_ & kProtocolAbiV2)) {
+    return;
+  }
+
+  auto arbiter_or = tracing_v2::ProducerRingBufferArbiter::Create(
+      weak_runner_.task_runner(), this, config, shmem_size_hint_bytes_,
+      [](size_t size) -> base::StatusOr<std::shared_ptr<SharedMemory>> {
+        return std::shared_ptr<SharedMemory>(
+            InProcessSharedMemory::Create(size));
+      });
+  if (!arbiter_or.ok()) {
+    PERFETTO_ELOG("tracing v2: cannot create ring buffer: %s",
+                  arbiter_or.status().c_message());
+    v2_ring_buffer_arbiter_ = nullptr;
+    return;
+  }
+  v2_ring_buffer_arbiter_ = std::move(*arbiter_or);
+
+  auto* arbiter = v2_ring_buffer_arbiter_->get();
+  AttachV2RingBuffer(
+      arbiter->memory(), arbiter->ring_buffer()->chunk_size(),
+      [weak_arbiter = arbiter->GetWeakPtr()](bool reader_attached) {
+        if (weak_arbiter)
+          weak_arbiter->OnReaderAttachReply(reader_attached);
+      });
+}
+
+void ProducerEndpointImpl::AttachV2RingBuffer(
+    const std::shared_ptr<SharedMemory>& memory,
+    uint32_t chunk_size_bytes,
+    std::function<void(bool)> callback) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+
+  base::Status status;
+  if (!(protocol_abi_versions_ & kProtocolAbiV2)) {
+    status = base::ErrStatus("the connection has no tracing v2");
+  } else if (v2_ring_buffer_drainer_) {
+    status = base::ErrStatus("a ring buffer is already attached");
+  } else if (!memory || memory->size() > TracingService::kMaxShmSize) {
+    status = base::ErrStatus("no mapping, or the mapping is too large");
+  } else {
+    status = tracing_v2::NumChunksForRingBufferLayout(
+                 memory->start(), memory->size(), chunk_size_bytes)
+                 .status();
+  }
+  if (!status.ok()) {
+    PERFETTO_DLOG("Producer %" PRIu16 " \"%s\": ring buffer rejected: %s", id_,
+                  name_.c_str(), status.c_message());
+    callback(false);
+    return;
+  }
+
+  v2_ring_buffer_drainer_ =
+      std::make_unique<tracing_v2::ServiceRingBufferDrainer>(
+          memory, chunk_size_bytes, id_, client_identity_, this,
+          weak_runner_.task_runner());
+  service_->UpdateMemoryGuardrail();
+  DrainV2RingBuffer();
+  callback(true);
+}
+
+void ProducerEndpointImpl::DrainV2RingBuffer() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (v2_ring_buffer_drainer_)
+    v2_ring_buffer_drainer_->Drain();
+}
+
+TraceBuffer* ProducerEndpointImpl::GetV2RingBufferDestination(BufferID id) {
+  if (!is_allowed_target_buffer(id))
+    return nullptr;
+
+  // The producer supplies |id| through shared memory. Check that the
+  // destination buffer supports tracing v2 otherwise return nullptr.
+  TraceBuffer* buffer = service_->GetBufferByID(id);
+  return buffer && buffer->supports_tracing_v2() ? buffer : nullptr;
+}
+
+void ProducerEndpointImpl::ForEachV2RingBufferDestination(
+    const std::function<void(TraceBuffer&)>& callback) {
+  for (BufferID id : allowed_target_buffers_) {
+    TraceBuffer* buffer = service_->GetBufferByID(id);
+    if (buffer && buffer->supports_tracing_v2())
+      callback(*buffer);
+  }
+}
+
+void ProducerEndpointImpl::OnRingBufferChunksDiscarded(uint64_t count) {
+  service_->OnRingBufferChunksDiscarded(count);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // RelayEndpointImpl implementation
 ////////////////////////////////////////////////////////////////////////////////
@@ -67308,11 +76281,13 @@ inline base::StatusOr<base::SchedPolicyAndPrio> CreateSchedPolicyFromConfig(
 // gen_amalgamated expanded: #include "src/tracing/service/packet_stream_validator.h"
 // gen_amalgamated expanded: #include "src/tracing/service/random.h"
 // gen_amalgamated expanded: #include "src/tracing/service/trace_buffer.h"
+// gen_amalgamated expanded: #include "src/tracing/service/trace_buffer_protovms.h"
 // gen_amalgamated expanded: #include "src/tracing/service/trace_buffer_v1.h"
 // gen_amalgamated expanded: #include "src/tracing/service/trace_buffer_v2.h"
 // gen_amalgamated expanded: #include "src/tracing/service/tracing_service_endpoints_impl.h"
 // gen_amalgamated expanded: #include "src/tracing/service/tracing_service_session.h"
 // gen_amalgamated expanded: #include "src/tracing/service/tracing_service_structs.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_abi.h"
 #if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 // gen_amalgamated expanded: #include "src/tracing/service/zlib_compressor.h"
 #endif
@@ -67441,7 +76416,7 @@ std::tuple<size_t /*shm_size*/, size_t /*page_size*/> EnsureValidShmSizes(
     shm_size = TracingServiceImpl::kDefaultShmSize;

   page_size = std::min<size_t>(page_size, kMaxPageSize);
-  shm_size = std::min<size_t>(shm_size, TracingServiceImpl::kMaxShmSize);
+  shm_size = std::min<size_t>(shm_size, TracingService::kMaxShmSize);

   // The tracing page size has to be multiple of 4K. On some systems (e.g. Mac
   // on Arm64) the system page size can be larger (e.g., 16K). That doesn't
@@ -67545,6 +76520,21 @@ void AppendOwnedSlicesToPacket(std::unique_ptr<uint8_t[]> data,
   }
 }

+// Creates an empty trace buffer of the given implementation.
+// Returns nullptr if the memory allocation fails.
+std::unique_ptr<TraceBuffer> CreateTraceBuffer(
+    TraceBuffer::BufType type,
+    size_t size_in_bytes,
+    TraceBuffer::OverwritePolicy policy) {
+  switch (type) {
+    case TraceBuffer::kV1:
+      return TraceBufferV1::Create(size_in_bytes, policy);
+    case TraceBuffer::kV2:
+      return TraceBufferV2::Create(size_in_bytes, policy);
+  }
+  return nullptr;
+}
+
 // Shmem emulation is only for relay (remote-host) producers whose SMB is copied
 // over IPC. An in-process producer always has a real shared SMB, so it must use
 // kDefault even when it carries a non-default machine id.
@@ -67586,9 +76576,18 @@ TracingServiceImpl::ConnectProducer(Producer* producer,
                                     size_t shared_memory_page_size_hint_bytes,
                                     std::unique_ptr<SharedMemory> shm,
                                     const std::string& sdk_version,
-                                    const std::string& machine_name) {
+                                    const std::string& machine_name,
+                                    uint32_t protocol_abi_versions) {
   PERFETTO_DCHECK_THREAD(thread_checker_);

+  if (!protocol_abi_versions) {
+    PERFETTO_ELOG(
+        "Failed to negotiate a valid tracing protocol version with the tracing "
+        "service: producer=\"%s\"",
+        producer_name.c_str());
+    return nullptr;
+  }
+
   auto uid = client_identity.uid();
   if (lockdown_mode_ && uid != base::GetCurrentUserId()) {
     PERFETTO_DLOG("Lockdown mode. Rejecting producer with UID %lu",
@@ -67618,7 +76617,7 @@ TracingServiceImpl::ConnectProducer(Producer* producer,
   std::unique_ptr<ProducerEndpointImpl> endpoint(new ProducerEndpointImpl(
       id, client_identity, this, weak_runner_.task_runner(), producer,
       producer_name, machine_name, sdk_version, in_process,
-      smb_scraping_enabled));
+      smb_scraping_enabled, protocol_abi_versions));
   auto it_and_inserted = producers_.emplace(id, endpoint.get());
   PERFETTO_DCHECK(it_and_inserted.second);

@@ -68003,11 +77002,35 @@ base::Status TracingServiceImpl::EnableTracing(ConsumerEndpointImpl* consumer,
     }
   }

+  // For each buffer: the name of a data source with a ProtoVM, and of one
+  // that can use tracing v2.
+  // Tracing v2 does not support ProtoVM on the same buffer yet.
+  std::vector<const std::string*> protovm_source(num_buffers);
+  std::vector<const std::string*> tracing_v2_source(num_buffers);
+
   // Check that the config specifies all buffers for its data sources. This
   // is also checked in SetupDataSource, but it is simpler to return a proper
   // error to the consumer from here (and there will be less state to undo).
   for (const TraceConfig::DataSource& cfg_data_source : cfg.data_sources()) {
     const auto& ds_config = cfg_data_source.config();
+    const auto& tracing_v2_config = ds_config.experimental_tracing_v2();
+    if (tracing_v2_config.use_v2_probability_percent() > 100) {
+      return PERFETTO_SVC_ERR(
+          "experimental_tracing_v2.use_v2_probability_percent must be at most "
+          "100");
+    }
+    if (tracing_v2_config.drain_occupancy_percent() < -1 ||
+        tracing_v2_config.drain_occupancy_percent() > 100) {
+      return PERFETTO_SVC_ERR(
+          "experimental_tracing_v2.drain_occupancy_percent must be -1 to 100");
+    }
+    for (const auto& option : tracing_v2_config.chunk_size_options()) {
+      if (!tracing_v2::IsValidChunkSize(option.size_bytes())) {
+        return PERFETTO_SVC_ERR(
+            "experimental_tracing_v2.chunk_size_options: invalid size %u",
+            option.size_bytes());
+      }
+    }

     // Resolve target buffer: if target_buffer_name is set, look it up.
     size_t target_buffer = ds_config.target_buffer();
@@ -68050,6 +77073,19 @@ base::Status TracingServiceImpl::EnableTracing(ConsumerEndpointImpl* consumer,
           "%zu)",
           ds_config.name().c_str(), target_buffer, num_buffers);
     }
+
+    if (ds_config.has_protovm_config())
+      protovm_source[target_buffer] = &ds_config.name();
+    if (tracing_v2_config.use_v2_probability_percent() > 0)
+      tracing_v2_source[target_buffer] = &ds_config.name();
+    if (protovm_source[target_buffer] && tracing_v2_source[target_buffer]) {
+      return PERFETTO_SVC_ERR(
+          "Data source \"%s\" has a ProtoVM and data source \"%s\" has "
+          "experimental_tracing_v2 on the same buffer %zu. Tracing v2 does not "
+          "support ProtoVM",
+          protovm_source[target_buffer]->c_str(),
+          tracing_v2_source[target_buffer]->c_str(), target_buffer);
+    }
   }

   uint32_t current_exclusive_prio = 0;
@@ -68427,10 +77463,10 @@ base::Status TracingServiceImpl::EnableTracing(ConsumerEndpointImpl* consumer,
     std::unique_ptr<TraceBuffer> new_buffer;
     switch (buffer_cfg.experimental_mode()) {
       case TraceConfig::BufferConfig::TRACE_BUFFER_V2:
-        new_buffer = TraceBufferV2::Create(buf_size, policy);
+        new_buffer = CreateTraceBuffer(TraceBuffer::kV2, buf_size, policy);
         break;
       case TraceConfig::BufferConfig::MODE_UNSPECIFIED:
-        new_buffer = TraceBufferV1::Create(buf_size, policy);
+        new_buffer = CreateTraceBuffer(TraceBuffer::kV1, buf_size, policy);
         break;
     }
     auto it_and_inserted = buffers_.emplace(global_id, std::move(new_buffer));
@@ -69521,13 +78557,6 @@ void TracingServiceImpl::CompleteFlush(TracingSessionID tsid,
 void TracingServiceImpl::ScrapeSharedMemoryBuffers(
     TracingSession* tracing_session,
     ProducerEndpointImpl* producer) {
-  if (!producer->smb_scraping_enabled_ || producer->IsShmemEmulated())
-    return;
-
-  // Can't copy chunks if we don't know about any trace writers.
-  if (producer->writers_.empty())
-    return;
-
   // Performance optimization: On flush or session disconnect, this method is
   // called for each producer. If the producer doesn't participate in the
   // session, there's no need to scrape its chunks right now. We can tell if a
@@ -69542,6 +78571,16 @@ void TracingServiceImpl::ScrapeSharedMemoryBuffers(
   if (!producer_in_session)
     return;

+  // Drain v2 data even when v1 SMB scraping is disabled.
+  producer->DrainV2RingBuffer();
+
+  if (!producer->smb_scraping_enabled_ || producer->IsShmemEmulated())
+    return;
+
+  // Can't copy chunks if we don't know about any trace writers.
+  if (producer->writers_.empty())
+    return;
+
   PERFETTO_DLOG("Scraping SMB for producer %" PRIu16, producer->id_);

   // Find and copy any uncommitted chunks from the SMB.
@@ -69749,6 +78788,26 @@ bool TracingServiceImpl::ReadBuffersIntoConsumer(
             return;
           ReadBuffersIntoConsumer(tsid, weak_consumer.get());
         });
+  } else {
+    // Compact only once ReadBuffers() has nothing more to read
+    // (has_more == false).
+    // By then the read has consumed as much data as it can, so compaction has
+    // the fewest chunks to memmove.
+    // If we compacted after every batch, we would move the unread chunks again
+    // each time and end up with the same layout.
+    //
+    // The packets passed to OnTraceData() point into the buffer, so compaction
+    // has to wait until it returns.
+    // But we can't compact inline after it, because the consumer's
+    // OnTraceData() can call FreeBuffers() or destroy the service.
+    // So we post a task.
+    // The task looks up the session again in case FreeBuffers() removed it,
+    // and weak_runner_ drops the task if the service is destroyed.
+    weak_runner_.PostTask([this, tsid] {
+      TracingSession* tracing_session = GetTracingSession(tsid);
+      if (tracing_session)
+        MaybeCompactBuffers(tracing_session);
+    });
   }

   // Keep this as tail call, just in case the consumer re-enters.
@@ -69808,6 +78867,8 @@ bool TracingServiceImpl::ReadBuffersIntoFile(
           return;
         }

+        MaybeCompactBuffers(tracing_session);
+
         if (tracing_session->fflush_post_write) {
           // Ensure all data was written to the file.
           base::FlushFile(tracing_session->write_into_file.get());
@@ -69890,6 +78951,19 @@ std::vector<TracePacket> TracingServiceImpl::ReadBuffers(
                       &packets);
   }

+  if (!tracing_session->config.builtin_data_sources().disable_trace_config()) {
+    MaybeEmitTraceConfig(tracing_session, &packets);
+  }
+  if (!tracing_session->did_emit_initial_packets) {
+    EmitUuid(tracing_session, &packets);
+  }
+
+  // All packets emitted above (clock snapshot, trace config, uuid) will not be
+  // compressed, regardless of the config. This is to allow services that
+  // consume the trace on-device to take decisions based on the metadata of the
+  // trace, without having to uncompress.
+  const size_t skip_compression_of_first_n_packets = packets.size();
+
   for (auto& snapshot : tracing_session->clock_snapshot_ring_buffer) {
     PERFETTO_DCHECK(!snapshot.empty());
     EmitClockSnapshot(tracing_session, std::move(snapshot), &packets);
@@ -69902,12 +78976,10 @@ std::vector<TracePacket> TracingServiceImpl::ReadBuffers(
   }

   if (!tracing_session->config.builtin_data_sources().disable_trace_config()) {
-    MaybeEmitTraceConfig(tracing_session, &packets);
     MaybeEmitCloneTrigger(tracing_session, &packets);
     MaybeEmitReceivedTriggers(tracing_session, &packets);
   }
   if (!tracing_session->did_emit_initial_packets) {
-    EmitUuid(tracing_session, &packets);
     if (!tracing_session->config.builtin_data_sources()
              .disable_extension_descriptors()) {
       EmitExtensionDescriptors(tracing_session, &packets);
@@ -70061,7 +79133,8 @@ std::vector<TracePacket> TracingServiceImpl::ReadBuffers(
     }
   }

-  MaybeCompressPackets(tracing_session, &packets);
+  MaybeCompressPackets(tracing_session, &packets,
+                       skip_compression_of_first_n_packets);

   if (!*has_more) {
     // We've observed some extremely high memory usage by scudo after
@@ -70136,7 +79209,28 @@ void TracingServiceImpl::MaybeFilterPackets(TracingSession* tracing_session,

 void TracingServiceImpl::MaybeCompressPackets(
     TracingSession* tracing_session,
-    [[maybe_unused]] std::vector<TracePacket>* packets) {
+    [[maybe_unused]] std::vector<TracePacket>* packets,
+    [[maybe_unused]] size_t skip_compression_of_first_n_packets) {
+  [[maybe_unused]] auto compress_with_fn = [&](auto compress_fn) {
+    if (skip_compression_of_first_n_packets == 0) {
+      compress_fn(packets);
+      return;
+    }
+    if (skip_compression_of_first_n_packets >= packets->size()) {
+      return;
+    }
+    const auto nskip =
+        static_cast<ssize_t>(skip_compression_of_first_n_packets);
+    std::vector<TracePacket> packets_to_compress(
+        std::make_move_iterator(packets->begin() + nskip),
+        std::make_move_iterator(packets->end()));
+    packets->erase(packets->begin() + nskip, packets->end());
+    compress_fn(&packets_to_compress);
+    packets->insert(packets->end(),
+                    std::make_move_iterator(packets_to_compress.begin()),
+                    std::make_move_iterator(packets_to_compress.end()));
+  };
+
   // Compress with the codec the config selects, preferring the newest (highest
   // proto field number) this build supports. Leaves the packets uncompressed if
   // none is available.
@@ -70147,7 +79241,9 @@ void TracingServiceImpl::MaybeCompressPackets(
       tracing_session->config.compression();
 #if PERFETTO_BUILDFLAG(PERFETTO_ZSTD)
   if (compression.has_zstd()) {
-    ZstdCompressFn(packets, compression.zstd().level());
+    compress_with_fn([&](std::vector<TracePacket>* target) {
+      ZstdCompressFn(target, compression.zstd().level());
+    });
     return;
   }
 #endif
@@ -70156,7 +79252,8 @@ void TracingServiceImpl::MaybeCompressPackets(
   // predating `compression` still get compressed.
   if (compression.has_deflate() || tracing_session->config.compression_type() ==
                                        TraceConfig::COMPRESSION_TYPE_DEFLATE) {
-    ZlibCompressFn(packets);
+    compress_with_fn(
+        [](std::vector<TracePacket>* target) { ZlibCompressFn(target); });
     return;
   }
 #endif
@@ -70233,6 +79330,18 @@ bool TracingServiceImpl::WriteIntoFile(TracingSession* tracing_session,
   return stop_writing_into_file;
 }

+void TracingServiceImpl::MaybeCompactBuffers(TracingSession* tracing_session) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (!tracing_session->config.experimental_trace_buffer_v2_compaction())
+    return;
+
+  for (BufferID buf_id : tracing_session->buffers_index) {
+    auto tbuf_iter = buffers_.find(buf_id);
+    if (tbuf_iter != buffers_.end())
+      tbuf_iter->second->MaybeCompact();
+  }
+}
+
 void TracingServiceImpl::FreeBuffers(TracingSessionID tsid,
                                      const std::string& error) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
@@ -70307,12 +79416,12 @@ void TracingServiceImpl::MaybeSetUpProtoVm(
     const RegisteredDataSource& ds_instance,
     BufferID buffer_id) {
   if (!ds_config.has_protovm_config() &&
-      !ds_instance.descriptor.has_protovm_program()) {
+      ds_instance.descriptor.protovm_program_raw().empty()) {
     return;  // Data source has no ProtoVM
   }
   // TODO(keanmariotti): report the errors below in a trace packet as well, so
   // that we can surface them on the UI.
-  if (!ds_instance.descriptor.has_protovm_program()) {
+  if (ds_instance.descriptor.protovm_program_raw().empty()) {
     PERFETTO_ELOG(
         "ProtoVM config for data source %s specifies a ProtoVM, but the data "
         "source instance (ProducerID: %d) doesn't specify a ProtoVM program",
@@ -70335,17 +79444,16 @@ void TracingServiceImpl::MaybeSetUpProtoVm(
   }
   auto it = buffers_.find(buffer_id);
   PERFETTO_CHECK(it != buffers_.cend());
-  if (it->second->buf_type() != TraceBuffer::BufType::kV2) {
+  TraceBufferProtoVms* protovms = it->second->protovms();
+  if (!protovms) {
     PERFETTO_ELOG(
         "Data source %s is configured to run a ProtoVM, but the corresponding "
-        "buffer is not of type V2",
+        "buffer is of type V1, which cannot run one.",
         ds_config.name().c_str());
     return;
   }
-  auto* buffer = static_cast<TraceBufferV2*>(it->second.get());
-  buffer->MaybeSetUpProtoVm(
-      ds_config.name(),
-      ds_instance.descriptor.protovm_program().SerializeAsString(),
+  protovms->MaybeSetUp(
+      ds_config.name(), ds_instance.descriptor.protovm_program_raw(),
       ds_config.protovm_config().memory_limit_kb(), ds_instance.producer_id);
 }

@@ -70628,6 +79736,22 @@ DataSourceInstance* TracingServiceImpl::SetupDataSource(
     return nullptr;
   }

+  const BufferID global_id = tracing_session->buffers_index[relative_buffer_id];
+  PERFETTO_DCHECK(global_id);
+
+  const TraceBuffer* target_buffer = GetBufferByID(global_id);
+  const bool supports_tracing_v2 =
+      (producer->protocol_abi_versions_ & kProtocolAbiV2) && target_buffer &&
+      target_buffer->supports_tracing_v2();
+  if (!(producer->protocol_abi_versions_ & kProtocolAbiV1) &&
+      !supports_tracing_v2) {
+    PERFETTO_ELOG(
+        "Cannot set up data source \"%s\" on producer \"%s\": "
+        "target_buffer %u supports no common protocol version",
+        ds_cfg.name().c_str(), producer->name_.c_str(), relative_buffer_id);
+    return nullptr;
+  }
+
   // Create a copy of the DataSourceConfig specified in the trace config. This
   // will be passed to the producer after translating the |target_buffer| id.
   // The |target_buffer| parameter passed by the consumer in the trace config is
@@ -70656,6 +79780,14 @@ DataSourceInstance* TracingServiceImpl::SetupDataSource(
   }

   DataSourceConfig& ds_config = ds_instance->config;
+  // When v2 is unavailable, keep an absent field unset so startup configs
+  // still match in older SDKs.
+  //
+  // If the consumer supplied a value, overwrite it with the service's decision
+  // about whether this data source can use v2.
+  if (supports_tracing_v2 || ds_config.has_supports_tracing_v2())
+    ds_config.set_supports_tracing_v2(supports_tracing_v2);
+
   ds_config.set_trace_duration_ms(tracing_session->config.duration_ms());

   // Rationale for `if (prefer) set_prefer(true)`, rather than `set(prefer)`:
@@ -70680,8 +79812,6 @@ DataSourceInstance* TracingServiceImpl::SetupDataSource(
         DataSourceConfig::SESSION_INITIATOR_UNSPECIFIED);
   }
   ds_config.set_tracing_session_id(tracing_session->id);
-  BufferID global_id = tracing_session->buffers_index[relative_buffer_id];
-  PERFETTO_DCHECK(global_id);
   ds_config.set_target_buffer(global_id);

   MaybeSetUpProtoVm(ds_config, data_source, global_id);
@@ -70928,6 +80058,11 @@ ProducerID TracingServiceImpl::GetNextProducerID() {
   return last_producer_id_;
 }

+void TracingServiceImpl::OnRingBufferChunksDiscarded(uint64_t count) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  chunks_discarded_ += count;
+}
+
 TraceBuffer* TracingServiceImpl::GetBufferByID(BufferID buffer_id) {
   auto buf_iter = buffers_.find(buffer_id);
   if (buf_iter == buffers_.end())
@@ -70967,6 +80102,7 @@ void TracingServiceImpl::UpdateMemoryGuardrail() {
   for (const auto& id_to_producer : producers_) {
     if (id_to_producer.second->shared_memory())
       total_buffer_bytes += id_to_producer.second->shared_memory()->size();
+    total_buffer_bytes += id_to_producer.second->ring_buffer_size_bytes();
   }

   // Sum up all the trace buffers.
@@ -70987,7 +80123,7 @@ void TracingServiceImpl::UpdateMemoryGuardrail() {
     }
   }

-  // Set the guard rail to 64MB + the sum of all the buffers over a 30 second
+  // Set the guard rail to 32MB + the sum of all the buffers over a 30 second
   // interval.
   uint64_t guardrail = base::kWatchdogDefaultMemorySlack + total_buffer_bytes;
   base::Watchdog::GetInstance()->SetMemoryLimit(guardrail, 30 * 1000);
@@ -71148,6 +80284,12 @@ void TracingServiceImpl::SetServiceTracePacketHeader(
     protos::pbzero::TracePacket* tp) {
   tp->set_trusted_uid(static_cast<int32_t>(uid_));
   tp->set_trusted_packet_sequence_id(kServicePacketSequenceID);
+  // Every timestamp the service stamps on its own packets comes from
+  // GetBootTimeNs(), so declare the clock domain explicitly. Without this,
+  // Trace Processor's "no clock id" fallback resolves to the trace's primary
+  // clock, which is not BOOTTIME in general
+  // (https://github.com/google/perfetto/discussions/7112).
+  tp->set_timestamp_clock_id(protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
   // When a local machine was adopted (an in-process producer with a non-default
   // machine id; see ConnectProducer), attribute the service's own packets to it
   // so the trace has no separate host machine. Host and relay sessions leave
@@ -71359,6 +80501,9 @@ void TracingServiceImpl::EmitSystemInfo(std::vector<TracePacket>* packets) {
     info->set_android_storage_model(sys_info.android_storage_model);
   if (!sys_info.android_ram_model.empty())
     info->set_android_ram_model(sys_info.android_ram_model);
+  for (const auto& compatible : sys_info.device_tree_compatibles) {
+    info->add_device_tree_compatibles(compatible);
+  }
   if (!sys_info.android_serial_console.empty())
     info->set_android_serial_console(sys_info.android_serial_console);

@@ -71377,6 +80522,8 @@ void TracingServiceImpl::EmitTraceProvenance(
     TraceBuffer* buf = GetBufferByID(buf_id);
     if (!buf)
       continue;
+    // TODO(b/568173038): derive sequences from the buffer's own sequence
+    // tracking instead of writer_stats() once TraceBufferV1 is gone.
     for (auto it = buf->writer_stats().GetIterator(); it; ++it) {
       ProducerID producer_id;
       WriterID writer_id;
@@ -71646,11 +80793,10 @@ void TracingServiceImpl::MaybeEmitProtoVmInstances(
   for (auto buffer_id : tracing_session->buffers_index) {
     auto it = buffers_.find(buffer_id);
     PERFETTO_CHECK(it != buffers_.cend());
-    if (it->second->buf_type() != TraceBuffer::BufType::kV2) {
+    const TraceBufferProtoVms* protovms = it->second->protovms();
+    if (!protovms)
       continue;
-    }
-    auto* buffer = static_cast<TraceBufferV2*>(it->second.get());
-    for (const TraceBufferV2::Vm& vm : buffer->GetProtoVmInstances()) {
+    for (const TraceBufferProtoVms::Vm& vm : protovms->instances()) {
       auto* vm_instance = get_packet_protovms()->add_instance();
       vm_instance->AppendString(
           protos::pbzero::TracePacket::ProtoVms::Instance::kProgramFieldNumber,
@@ -71874,18 +81020,9 @@ base::Status TracingServiceImpl::FlushAndCloneSession(
       continue;

     // Some leftover data was left in the buffer. Recreate it to empty it.
-    const auto buf_policy = buf->overwrite_policy();
-    const auto buf_size = buf->size();
-    const auto buf_type = buf->buf_type();
     std::unique_ptr<TraceBuffer> old_buf = std::move(buf);
-    switch (buf_type) {
-      case TraceBuffer::kV1:
-        buf = TraceBufferV1::Create(buf_size, buf_policy);
-        break;
-      case TraceBuffer::kV2:
-        buf = TraceBufferV2::Create(buf_size, buf_policy);
-        break;
-    }
+    buf = CreateTraceBuffer(old_buf->buf_type(), old_buf->size(),
+                            old_buf->overwrite_policy());
     if (!buf) {
       // This is extremely rare but could happen on 32-bit. If the new buffer
       // allocation failed, put back the buffer where it was and fail the clone.
@@ -72054,18 +81191,9 @@ bool TracingServiceImpl::DoCloneBuffers(const TracingSession& src,
     std::unique_ptr<TraceBuffer>& src_buf = buf_iter->second;
     std::unique_ptr<TraceBuffer> new_buf;
     if (src.config.buffers()[buf_idx].transfer_on_clone()) {
-      const auto buf_policy = src_buf->overwrite_policy();
-      const auto buf_size = src_buf->size();
-      const auto buf_type = src_buf->buf_type();
       new_buf = std::move(src_buf);
-      switch (buf_type) {
-        case TraceBuffer::kV1:
-          src_buf = TraceBufferV1::Create(buf_size, buf_policy);
-          break;
-        case TraceBuffer::kV2:
-          src_buf = TraceBufferV2::Create(buf_size, buf_policy);
-          break;
-      }
+      src_buf = CreateTraceBuffer(new_buf->buf_type(), new_buf->size(),
+                                  new_buf->overwrite_policy());
       if (!src_buf) {
         // If the allocation fails put the buffer back and let the code below
         // handle the failure gracefully.
@@ -72395,7 +81523,9 @@ std::unique_ptr<ProducerEndpoint> InProcessTracingBackend::ConnectProducer(
                         args.producer_name, args.shmem_size_hint_bytes,
                         /*in_process=*/true,
                         TracingService::ProducerSMBScrapingMode::kEnabled,
-                        args.shmem_page_size_hint_bytes);
+                        args.shmem_page_size_hint_bytes, /*shm=*/nullptr,
+                        /*sdk_version=*/{}, /*machine_name=*/{},
+                        kProtocolAbiV1 | kProtocolAbiV2);
 }

 std::unique_ptr<ConsumerEndpoint> InProcessTracingBackend::ConnectConsumer(
@@ -72436,16 +81566,18 @@ TracingService* InProcessTracingBackend::GetOrCreateService(
 // gen_amalgamated expanded: #include "protos/perfetto/config/data_source_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/qnx/qnx_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/linux/journald_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/stack_sampling_profiler.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/sampling_heap_profiler.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/chrome/histogram_samples.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/system_info/system_info_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/track_event/track_event_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/test_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/sys_stats/sys_stats_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/common/sys_stats_counters.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/smaps_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/profiling/perf_event_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/common/perf_events.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/profiling/java_hprof_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/smaps_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/profiling/heapprofd_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/process_stats/process_stats_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/statsd/statsd_tracing_config.gen.h"
@@ -72479,6 +81611,7 @@ TracingService* InProcessTracingBackend::GetOrCreateService(
 // gen_amalgamated expanded: #include "protos/perfetto/config/android/app_wakelock_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/android/android_sdk_sysprop_guard_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/android/android_system_property_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_process_state_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/android/android_polled_state_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/android/android_log_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/common/android_log_constants.gen.h"
@@ -74287,16 +83420,18 @@ void EnableTracingRequest::Serialize(::protozero::Message* msg) const {
 // gen_amalgamated expanded: #include "protos/perfetto/config/data_source_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/qnx/qnx_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/linux/journald_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/stack_sampling_profiler.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/chrome/sampling_heap_profiler.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/chrome/histogram_samples.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/system_info/system_info_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/track_event/track_event_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/test_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/sys_stats/sys_stats_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/common/sys_stats_counters.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/smaps_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/profiling/perf_event_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/common/perf_events.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/profiling/java_hprof_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/profiling/smaps_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/profiling/heapprofd_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/process_stats/process_stats_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/priority_boost/priority_boost_config.gen.h"
@@ -74331,6 +83466,7 @@ void EnableTracingRequest::Serialize(::protozero::Message* msg) const {
 // gen_amalgamated expanded: #include "protos/perfetto/config/android/app_wakelock_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/android/android_sdk_sysprop_guard_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/android/android_system_property_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/android/android_process_state_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/android/android_polled_state_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/config/android/android_log_config.gen.h"
 // gen_amalgamated expanded: #include "protos/perfetto/common/android_log_constants.gen.h"
@@ -75834,6 +84970,199 @@ void RegisterDataSourceRequest::Serialize(::protozero::Message* msg) const {
 }


+DrainV2RingBufferResponse::DrainV2RingBufferResponse() = default;
+DrainV2RingBufferResponse::~DrainV2RingBufferResponse() = default;
+DrainV2RingBufferResponse::DrainV2RingBufferResponse(const DrainV2RingBufferResponse&) = default;
+DrainV2RingBufferResponse& DrainV2RingBufferResponse::operator=(const DrainV2RingBufferResponse&) = default;
+DrainV2RingBufferResponse::DrainV2RingBufferResponse(DrainV2RingBufferResponse&&) noexcept = default;
+DrainV2RingBufferResponse& DrainV2RingBufferResponse::operator=(DrainV2RingBufferResponse&&) = default;
+
+bool DrainV2RingBufferResponse::operator==(const DrainV2RingBufferResponse& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
+}
+
+bool DrainV2RingBufferResponse::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string DrainV2RingBufferResponse::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> DrainV2RingBufferResponse::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void DrainV2RingBufferResponse::Serialize(::protozero::Message* msg) const {
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+DrainV2RingBufferRequest::DrainV2RingBufferRequest() = default;
+DrainV2RingBufferRequest::~DrainV2RingBufferRequest() = default;
+DrainV2RingBufferRequest::DrainV2RingBufferRequest(const DrainV2RingBufferRequest&) = default;
+DrainV2RingBufferRequest& DrainV2RingBufferRequest::operator=(const DrainV2RingBufferRequest&) = default;
+DrainV2RingBufferRequest::DrainV2RingBufferRequest(DrainV2RingBufferRequest&&) noexcept = default;
+DrainV2RingBufferRequest& DrainV2RingBufferRequest::operator=(DrainV2RingBufferRequest&&) = default;
+
+bool DrainV2RingBufferRequest::operator==(const DrainV2RingBufferRequest& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
+}
+
+bool DrainV2RingBufferRequest::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string DrainV2RingBufferRequest::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> DrainV2RingBufferRequest::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void DrainV2RingBufferRequest::Serialize(::protozero::Message* msg) const {
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+AttachV2RingBufferResponse::AttachV2RingBufferResponse() = default;
+AttachV2RingBufferResponse::~AttachV2RingBufferResponse() = default;
+AttachV2RingBufferResponse::AttachV2RingBufferResponse(const AttachV2RingBufferResponse&) = default;
+AttachV2RingBufferResponse& AttachV2RingBufferResponse::operator=(const AttachV2RingBufferResponse&) = default;
+AttachV2RingBufferResponse::AttachV2RingBufferResponse(AttachV2RingBufferResponse&&) noexcept = default;
+AttachV2RingBufferResponse& AttachV2RingBufferResponse::operator=(AttachV2RingBufferResponse&&) = default;
+
+bool AttachV2RingBufferResponse::operator==(const AttachV2RingBufferResponse& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_);
+}
+
+bool AttachV2RingBufferResponse::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string AttachV2RingBufferResponse::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> AttachV2RingBufferResponse::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void AttachV2RingBufferResponse::Serialize(::protozero::Message* msg) const {
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
+AttachV2RingBufferRequest::AttachV2RingBufferRequest() = default;
+AttachV2RingBufferRequest::~AttachV2RingBufferRequest() = default;
+AttachV2RingBufferRequest::AttachV2RingBufferRequest(const AttachV2RingBufferRequest&) = default;
+AttachV2RingBufferRequest& AttachV2RingBufferRequest::operator=(const AttachV2RingBufferRequest&) = default;
+AttachV2RingBufferRequest::AttachV2RingBufferRequest(AttachV2RingBufferRequest&&) noexcept = default;
+AttachV2RingBufferRequest& AttachV2RingBufferRequest::operator=(AttachV2RingBufferRequest&&) = default;
+
+bool AttachV2RingBufferRequest::operator==(const AttachV2RingBufferRequest& other) const {
+  return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
+   && ::protozero::internal::gen_helpers::EqualsField(chunk_size_bytes_, other.chunk_size_bytes_);
+}
+
+bool AttachV2RingBufferRequest::ParseFromArray(const void* raw, size_t size) {
+  unknown_fields_.clear();
+  bool packed_error = false;
+
+  ::protozero::ProtoDecoder dec(raw, size);
+  for (auto field = dec.ReadField(); field.valid(); field = dec.ReadField()) {
+    if (field.id() < _has_field_.size()) {
+      _has_field_.set(field.id());
+    }
+    switch (field.id()) {
+      case 1 /* chunk_size_bytes */:
+        field.get(&chunk_size_bytes_);
+        break;
+      default:
+        field.SerializeAndAppendTo(&unknown_fields_);
+        break;
+    }
+  }
+  return !packed_error && !dec.bytes_left();
+}
+
+std::string AttachV2RingBufferRequest::SerializeAsString() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsString();
+}
+
+std::vector<uint8_t> AttachV2RingBufferRequest::SerializeAsArray() const {
+  ::protozero::internal::gen_helpers::MessageSerializer msg;
+  Serialize(msg.get());
+  return msg.SerializeAsArray();
+}
+
+void AttachV2RingBufferRequest::Serialize(::protozero::Message* msg) const {
+  // Field 1: chunk_size_bytes
+  if (_has_field_[1]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(1, chunk_size_bytes_, msg);
+  }
+
+  protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
+}
+
+
 InitializeConnectionResponse::InitializeConnectionResponse() = default;
 InitializeConnectionResponse::~InitializeConnectionResponse() = default;
 InitializeConnectionResponse::InitializeConnectionResponse(const InitializeConnectionResponse&) = default;
@@ -75845,7 +85174,8 @@ bool InitializeConnectionResponse::operator==(const InitializeConnectionResponse
   return ::protozero::internal::gen_helpers::EqualsField(unknown_fields_, other.unknown_fields_)
    && ::protozero::internal::gen_helpers::EqualsField(using_shmem_provided_by_producer_, other.using_shmem_provided_by_producer_)
    && ::protozero::internal::gen_helpers::EqualsField(direct_smb_patching_supported_, other.direct_smb_patching_supported_)
-   && ::protozero::internal::gen_helpers::EqualsField(use_shmem_emulation_, other.use_shmem_emulation_);
+   && ::protozero::internal::gen_helpers::EqualsField(use_shmem_emulation_, other.use_shmem_emulation_)
+   && ::protozero::internal::gen_helpers::EqualsField(protocol_abi_versions_, other.protocol_abi_versions_);
 }

 bool InitializeConnectionResponse::ParseFromArray(const void* raw, size_t size) {
@@ -75867,6 +85197,9 @@ bool InitializeConnectionResponse::ParseFromArray(const void* raw, size_t size)
       case 3 /* use_shmem_emulation */:
         field.get(&use_shmem_emulation_);
         break;
+      case 4 /* protocol_abi_versions */:
+        field.get(&protocol_abi_versions_);
+        break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
         break;
@@ -75903,6 +85236,11 @@ void InitializeConnectionResponse::Serialize(::protozero::Message* msg) const {
     ::protozero::internal::gen_helpers::SerializeTinyVarInt(3, use_shmem_emulation_, msg);
   }

+  // Field 4: protocol_abi_versions
+  if (_has_field_[4]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(4, protocol_abi_versions_, msg);
+  }
+
   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

@@ -75922,7 +85260,8 @@ bool InitializeConnectionRequest::operator==(const InitializeConnectionRequest&
    && ::protozero::internal::gen_helpers::EqualsField(smb_scraping_mode_, other.smb_scraping_mode_)
    && ::protozero::internal::gen_helpers::EqualsField(producer_provided_shmem_, other.producer_provided_shmem_)
    && ::protozero::internal::gen_helpers::EqualsField(sdk_version_, other.sdk_version_)
-   && ::protozero::internal::gen_helpers::EqualsField(shm_key_windows_, other.shm_key_windows_);
+   && ::protozero::internal::gen_helpers::EqualsField(shm_key_windows_, other.shm_key_windows_)
+   && ::protozero::internal::gen_helpers::EqualsField(supported_protocol_abi_versions_, other.supported_protocol_abi_versions_);
 }

 bool InitializeConnectionRequest::ParseFromArray(const void* raw, size_t size) {
@@ -75956,6 +85295,9 @@ bool InitializeConnectionRequest::ParseFromArray(const void* raw, size_t size) {
       case 7 /* shm_key_windows */:
         ::protozero::internal::gen_helpers::DeserializeString(field, &shm_key_windows_);
         break;
+      case 9 /* supported_protocol_abi_versions */:
+        field.get(&supported_protocol_abi_versions_);
+        break;
       default:
         field.SerializeAndAppendTo(&unknown_fields_);
         break;
@@ -76012,6 +85354,11 @@ void InitializeConnectionRequest::Serialize(::protozero::Message* msg) const {
     ::protozero::internal::gen_helpers::SerializeString(7, shm_key_windows_, msg);
   }

+  // Field 9: supported_protocol_abi_versions
+  if (_has_field_[9]) {
+    ::protozero::internal::gen_helpers::SerializeVarInt(9, supported_protocol_abi_versions_, msg);
+  }
+
   protozero::internal::gen_helpers::SerializeUnknownFields(unknown_fields_, msg);
 }

@@ -78869,62 +88216,6 @@ std::unique_ptr<LinuxFileWatch> WatchUnixSocketCreation(
 }  // namespace perfetto
 // gen_amalgamated begin source: src/ipc/buffered_frame_deserializer.cc
 // gen_amalgamated begin header: src/ipc/buffered_frame_deserializer.h
-// gen_amalgamated begin header: include/perfetto/ext/ipc/basic_types.h
-/*
- * 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.
- */
-
-#ifndef INCLUDE_PERFETTO_EXT_IPC_BASIC_TYPES_H_
-#define INCLUDE_PERFETTO_EXT_IPC_BASIC_TYPES_H_
-
-#include <stddef.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
-// gen_amalgamated expanded: #include "perfetto/protozero/cpp_message_obj.h"
-
-namespace perfetto {
-namespace ipc {
-
-using ProtoMessage = ::protozero::CppMessageObj;
-using ServiceID = uint32_t;
-using MethodID = uint32_t;
-using ClientID = uint64_t;
-using RequestID = uint64_t;
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-// AF_UNIX on Windows is supported only on Windows 10 from build 17063.
-// Also it doesn't bring major advantages compared to a TCP socket.
-// See go/perfetto-win .
-constexpr bool kUseTCPSocket = true;
-#else
-// Android, Linux, Mac, Fuchsia use local sockets.
-constexpr bool kUseTCPSocket = false;
-#endif
-
-// This determines the maximum size allowed for an IPC message. Trying to send
-// or receive a larger message will hit DCHECK(s) and auto-disconnect.
-constexpr size_t kIPCBufferSize = 128 * 1024;
-
-constexpr uid_t kInvalidUid = static_cast<uid_t>(-1);
-
-}  // namespace ipc
-}  // namespace perfetto
-
-#endif  // INCLUDE_PERFETTO_EXT_IPC_BASIC_TYPES_H_
 /*
  * Copyright (C) 2017 The Android Open Source Project
  *
@@ -79123,8 +88414,7 @@ BufferedFrameDeserializer::BeginReceive() {

     // Surely we are going to use at least the first page, but we may not need
     // the rest for a bit.
-    const auto page_size = base::GetSysPageSize();
-    buf_.AdviseDontNeed(buf() + page_size, capacity_ - page_size);
+    buf_.AdviseDontNeedAfter(base::GetSysPageSize());
   }

   PERFETTO_CHECK(capacity_ > size_);
@@ -79211,11 +88501,8 @@ bool BufferedFrameDeserializer::EndReceive(size_t recv_size) {
     if (consumed_size > page_size) {
       size_t size_rounded_up = (size_ / page_size + 1) * page_size;
       if (size_rounded_up < capacity_) {
-        char* madvise_begin = buf() + size_rounded_up;
-        const size_t madvise_size = capacity_ - size_rounded_up;
-        PERFETTO_CHECK(madvise_begin > buf() + size_);
-        PERFETTO_CHECK(madvise_begin + madvise_size <= buf() + capacity_);
-        buf_.AdviseDontNeed(madvise_begin, madvise_size);
+        PERFETTO_CHECK(size_rounded_up > size_);
+        buf_.AdviseDontNeedAfter(size_rounded_up);
       }
     }
   }
@@ -81680,1188 +90967,494 @@ void ConsumerPortProxy::Attach(const AttachRequest& request, DeferredAttachRespo
               fd);
 }

-void ConsumerPortProxy::GetTraceStats(const GetTraceStatsRequest& request, DeferredGetTraceStatsResponse reply, int fd) {
-  BeginInvoke("GetTraceStats", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ConsumerPortProxy::ObserveEvents(const ObserveEventsRequest& request, DeferredObserveEventsResponse reply, int fd) {
-  BeginInvoke("ObserveEvents", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ConsumerPortProxy::QueryServiceState(const QueryServiceStateRequest& request, DeferredQueryServiceStateResponse reply, int fd) {
-  BeginInvoke("QueryServiceState", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ConsumerPortProxy::QueryCapabilities(const QueryCapabilitiesRequest& request, DeferredQueryCapabilitiesResponse reply, int fd) {
-  BeginInvoke("QueryCapabilities", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ConsumerPortProxy::SaveTraceForBugreport(const SaveTraceForBugreportRequest& request, DeferredSaveTraceForBugreportResponse reply, int fd) {
-  BeginInvoke("SaveTraceForBugreport", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ConsumerPortProxy::CloneSession(const CloneSessionRequest& request, DeferredCloneSessionResponse reply, int fd) {
-  BeginInvoke("CloneSession", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-// gen_amalgamated begin source: gen/protos/perfetto/ipc/producer_port.ipc.cc
-// gen_amalgamated begin header: gen/protos/perfetto/ipc/producer_port.ipc.h
-// DO NOT EDIT. Autogenerated by Perfetto IPC
-#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_IPC_PRODUCER_PORT_PROTO_H_
-#define PERFETTO_PROTOS_PROTOS_PERFETTO_IPC_PRODUCER_PORT_PROTO_H_
-
-// gen_amalgamated expanded: #include "perfetto/ext/ipc/deferred.h"
-// gen_amalgamated expanded: #include "perfetto/ext/ipc/service.h"
-// gen_amalgamated expanded: #include "perfetto/ext/ipc/service_descriptor.h"
-// gen_amalgamated expanded: #include "perfetto/ext/ipc/service_proxy.h"
-
-// gen_amalgamated expanded: #include "protos/perfetto/ipc/producer_port.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/commit_data_request.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/config/data_source_config.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/data_source_descriptor.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {
-
-class ProducerPort : public ::perfetto::ipc::Service {
- private:
-  static ::perfetto::ipc::ServiceDescriptor* NewDescriptor();
-
- public:
-  ~ProducerPort() override;
-
-  static const ::perfetto::ipc::ServiceDescriptor& GetDescriptorStatic();
-
-  // Service implementation.
-  const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override;
-
-  // Methods from the .proto file
-  using DeferredInitializeConnectionResponse = ::perfetto::ipc::Deferred<InitializeConnectionResponse>;
-  virtual void InitializeConnection(const InitializeConnectionRequest&, DeferredInitializeConnectionResponse) = 0;
-
-  using DeferredRegisterDataSourceResponse = ::perfetto::ipc::Deferred<RegisterDataSourceResponse>;
-  virtual void RegisterDataSource(const RegisterDataSourceRequest&, DeferredRegisterDataSourceResponse) = 0;
-
-  using DeferredUnregisterDataSourceResponse = ::perfetto::ipc::Deferred<UnregisterDataSourceResponse>;
-  virtual void UnregisterDataSource(const UnregisterDataSourceRequest&, DeferredUnregisterDataSourceResponse) = 0;
-
-  using DeferredCommitDataResponse = ::perfetto::ipc::Deferred<CommitDataResponse>;
-  virtual void CommitData(const CommitDataRequest&, DeferredCommitDataResponse) = 0;
-
-  using DeferredGetAsyncCommandResponse = ::perfetto::ipc::Deferred<GetAsyncCommandResponse>;
-  virtual void GetAsyncCommand(const GetAsyncCommandRequest&, DeferredGetAsyncCommandResponse) = 0;
-
-  using DeferredRegisterTraceWriterResponse = ::perfetto::ipc::Deferred<RegisterTraceWriterResponse>;
-  virtual void RegisterTraceWriter(const RegisterTraceWriterRequest&, DeferredRegisterTraceWriterResponse) = 0;
-
-  using DeferredUnregisterTraceWriterResponse = ::perfetto::ipc::Deferred<UnregisterTraceWriterResponse>;
-  virtual void UnregisterTraceWriter(const UnregisterTraceWriterRequest&, DeferredUnregisterTraceWriterResponse) = 0;
-
-  using DeferredNotifyDataSourceStartedResponse = ::perfetto::ipc::Deferred<NotifyDataSourceStartedResponse>;
-  virtual void NotifyDataSourceStarted(const NotifyDataSourceStartedRequest&, DeferredNotifyDataSourceStartedResponse) = 0;
-
-  using DeferredNotifyDataSourceStoppedResponse = ::perfetto::ipc::Deferred<NotifyDataSourceStoppedResponse>;
-  virtual void NotifyDataSourceStopped(const NotifyDataSourceStoppedRequest&, DeferredNotifyDataSourceStoppedResponse) = 0;
-
-  using DeferredActivateTriggersResponse = ::perfetto::ipc::Deferred<ActivateTriggersResponse>;
-  virtual void ActivateTriggers(const ActivateTriggersRequest&, DeferredActivateTriggersResponse) = 0;
-
-  using DeferredSyncResponse = ::perfetto::ipc::Deferred<SyncResponse>;
-  virtual void Sync(const SyncRequest&, DeferredSyncResponse) = 0;
-
-  using DeferredUpdateDataSourceResponse = ::perfetto::ipc::Deferred<UpdateDataSourceResponse>;
-  virtual void UpdateDataSource(const UpdateDataSourceRequest&, DeferredUpdateDataSourceResponse) = 0;
-
-};
-
-
-class ProducerPortProxy : public ::perfetto::ipc::ServiceProxy {
- public:
-   explicit ProducerPortProxy(::perfetto::ipc::ServiceProxy::EventListener*);
-   ~ProducerPortProxy() override;
-
-  // ServiceProxy implementation.
-  const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override;
-
-  // Methods from the .proto file
-  using DeferredInitializeConnectionResponse = ::perfetto::ipc::Deferred<InitializeConnectionResponse>;
-  void InitializeConnection(const InitializeConnectionRequest&, DeferredInitializeConnectionResponse, int fd = -1);
-
-  using DeferredRegisterDataSourceResponse = ::perfetto::ipc::Deferred<RegisterDataSourceResponse>;
-  void RegisterDataSource(const RegisterDataSourceRequest&, DeferredRegisterDataSourceResponse, int fd = -1);
-
-  using DeferredUnregisterDataSourceResponse = ::perfetto::ipc::Deferred<UnregisterDataSourceResponse>;
-  void UnregisterDataSource(const UnregisterDataSourceRequest&, DeferredUnregisterDataSourceResponse, int fd = -1);
-
-  using DeferredCommitDataResponse = ::perfetto::ipc::Deferred<CommitDataResponse>;
-  void CommitData(const CommitDataRequest&, DeferredCommitDataResponse, int fd = -1);
-
-  using DeferredGetAsyncCommandResponse = ::perfetto::ipc::Deferred<GetAsyncCommandResponse>;
-  void GetAsyncCommand(const GetAsyncCommandRequest&, DeferredGetAsyncCommandResponse, int fd = -1);
-
-  using DeferredRegisterTraceWriterResponse = ::perfetto::ipc::Deferred<RegisterTraceWriterResponse>;
-  void RegisterTraceWriter(const RegisterTraceWriterRequest&, DeferredRegisterTraceWriterResponse, int fd = -1);
-
-  using DeferredUnregisterTraceWriterResponse = ::perfetto::ipc::Deferred<UnregisterTraceWriterResponse>;
-  void UnregisterTraceWriter(const UnregisterTraceWriterRequest&, DeferredUnregisterTraceWriterResponse, int fd = -1);
-
-  using DeferredNotifyDataSourceStartedResponse = ::perfetto::ipc::Deferred<NotifyDataSourceStartedResponse>;
-  void NotifyDataSourceStarted(const NotifyDataSourceStartedRequest&, DeferredNotifyDataSourceStartedResponse, int fd = -1);
-
-  using DeferredNotifyDataSourceStoppedResponse = ::perfetto::ipc::Deferred<NotifyDataSourceStoppedResponse>;
-  void NotifyDataSourceStopped(const NotifyDataSourceStoppedRequest&, DeferredNotifyDataSourceStoppedResponse, int fd = -1);
-
-  using DeferredActivateTriggersResponse = ::perfetto::ipc::Deferred<ActivateTriggersResponse>;
-  void ActivateTriggers(const ActivateTriggersRequest&, DeferredActivateTriggersResponse, int fd = -1);
-
-  using DeferredSyncResponse = ::perfetto::ipc::Deferred<SyncResponse>;
-  void Sync(const SyncRequest&, DeferredSyncResponse, int fd = -1);
-
-  using DeferredUpdateDataSourceResponse = ::perfetto::ipc::Deferred<UpdateDataSourceResponse>;
-  void UpdateDataSource(const UpdateDataSourceRequest&, DeferredUpdateDataSourceResponse, int fd = -1);
-
-};
-
-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-
-#endif  // PERFETTO_PROTOS_PROTOS_PERFETTO_IPC_PRODUCER_PORT_PROTO_H_
-// DO NOT EDIT. Autogenerated by Perfetto IPC
-// gen_amalgamated expanded: #include "protos/perfetto/ipc/producer_port.ipc.h"
-// gen_amalgamated expanded: #include "perfetto/ext/ipc/codegen_helpers.h"
-
-#include <memory>
-
-namespace perfetto {
-namespace protos {
-namespace gen {
-::perfetto::ipc::ServiceDescriptor* ProducerPort::NewDescriptor() {
-  auto* desc = new ::perfetto::ipc::ServiceDescriptor();
-  desc->service_name = "ProducerPort";
-
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "InitializeConnection",
-     &_IPC_Decoder<InitializeConnectionRequest>,
-     &_IPC_Decoder<InitializeConnectionResponse>,
-     &_IPC_Invoker<ProducerPort, InitializeConnectionRequest, InitializeConnectionResponse, &ProducerPort::InitializeConnection>});
-
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "RegisterDataSource",
-     &_IPC_Decoder<RegisterDataSourceRequest>,
-     &_IPC_Decoder<RegisterDataSourceResponse>,
-     &_IPC_Invoker<ProducerPort, RegisterDataSourceRequest, RegisterDataSourceResponse, &ProducerPort::RegisterDataSource>});
-
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "UnregisterDataSource",
-     &_IPC_Decoder<UnregisterDataSourceRequest>,
-     &_IPC_Decoder<UnregisterDataSourceResponse>,
-     &_IPC_Invoker<ProducerPort, UnregisterDataSourceRequest, UnregisterDataSourceResponse, &ProducerPort::UnregisterDataSource>});
-
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "CommitData",
-     &_IPC_Decoder<CommitDataRequest>,
-     &_IPC_Decoder<CommitDataResponse>,
-     &_IPC_Invoker<ProducerPort, CommitDataRequest, CommitDataResponse, &ProducerPort::CommitData>});
-
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "GetAsyncCommand",
-     &_IPC_Decoder<GetAsyncCommandRequest>,
-     &_IPC_Decoder<GetAsyncCommandResponse>,
-     &_IPC_Invoker<ProducerPort, GetAsyncCommandRequest, GetAsyncCommandResponse, &ProducerPort::GetAsyncCommand>});
-
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "RegisterTraceWriter",
-     &_IPC_Decoder<RegisterTraceWriterRequest>,
-     &_IPC_Decoder<RegisterTraceWriterResponse>,
-     &_IPC_Invoker<ProducerPort, RegisterTraceWriterRequest, RegisterTraceWriterResponse, &ProducerPort::RegisterTraceWriter>});
-
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "UnregisterTraceWriter",
-     &_IPC_Decoder<UnregisterTraceWriterRequest>,
-     &_IPC_Decoder<UnregisterTraceWriterResponse>,
-     &_IPC_Invoker<ProducerPort, UnregisterTraceWriterRequest, UnregisterTraceWriterResponse, &ProducerPort::UnregisterTraceWriter>});
-
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "NotifyDataSourceStarted",
-     &_IPC_Decoder<NotifyDataSourceStartedRequest>,
-     &_IPC_Decoder<NotifyDataSourceStartedResponse>,
-     &_IPC_Invoker<ProducerPort, NotifyDataSourceStartedRequest, NotifyDataSourceStartedResponse, &ProducerPort::NotifyDataSourceStarted>});
-
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "NotifyDataSourceStopped",
-     &_IPC_Decoder<NotifyDataSourceStoppedRequest>,
-     &_IPC_Decoder<NotifyDataSourceStoppedResponse>,
-     &_IPC_Invoker<ProducerPort, NotifyDataSourceStoppedRequest, NotifyDataSourceStoppedResponse, &ProducerPort::NotifyDataSourceStopped>});
-
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "ActivateTriggers",
-     &_IPC_Decoder<ActivateTriggersRequest>,
-     &_IPC_Decoder<ActivateTriggersResponse>,
-     &_IPC_Invoker<ProducerPort, ActivateTriggersRequest, ActivateTriggersResponse, &ProducerPort::ActivateTriggers>});
-
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "Sync",
-     &_IPC_Decoder<SyncRequest>,
-     &_IPC_Decoder<SyncResponse>,
-     &_IPC_Invoker<ProducerPort, SyncRequest, SyncResponse, &ProducerPort::Sync>});
-
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "UpdateDataSource",
-     &_IPC_Decoder<UpdateDataSourceRequest>,
-     &_IPC_Decoder<UpdateDataSourceResponse>,
-     &_IPC_Invoker<ProducerPort, UpdateDataSourceRequest, UpdateDataSourceResponse, &ProducerPort::UpdateDataSource>});
-  desc->methods.shrink_to_fit();
-  return desc;
-}
-
-
-const ::perfetto::ipc::ServiceDescriptor& ProducerPort::GetDescriptorStatic() {
-  static auto* instance = NewDescriptor();
-  return *instance;
-}
-
-// Host-side definitions.
-ProducerPort::~ProducerPort() = default;
-
-const ::perfetto::ipc::ServiceDescriptor& ProducerPort::GetDescriptor() {
-  return GetDescriptorStatic();
-}
-
-// Client-side definitions.
-ProducerPortProxy::ProducerPortProxy(::perfetto::ipc::ServiceProxy::EventListener* event_listener)
-    : ::perfetto::ipc::ServiceProxy(event_listener) {}
-
-ProducerPortProxy::~ProducerPortProxy() = default;
-
-const ::perfetto::ipc::ServiceDescriptor& ProducerPortProxy::GetDescriptor() {
-  return ProducerPort::GetDescriptorStatic();
-}
-
-void ProducerPortProxy::InitializeConnection(const InitializeConnectionRequest& request, DeferredInitializeConnectionResponse reply, int fd) {
-  BeginInvoke("InitializeConnection", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ProducerPortProxy::RegisterDataSource(const RegisterDataSourceRequest& request, DeferredRegisterDataSourceResponse reply, int fd) {
-  BeginInvoke("RegisterDataSource", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ProducerPortProxy::UnregisterDataSource(const UnregisterDataSourceRequest& request, DeferredUnregisterDataSourceResponse reply, int fd) {
-  BeginInvoke("UnregisterDataSource", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ProducerPortProxy::CommitData(const CommitDataRequest& request, DeferredCommitDataResponse reply, int fd) {
-  BeginInvoke("CommitData", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ProducerPortProxy::GetAsyncCommand(const GetAsyncCommandRequest& request, DeferredGetAsyncCommandResponse reply, int fd) {
-  BeginInvoke("GetAsyncCommand", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ProducerPortProxy::RegisterTraceWriter(const RegisterTraceWriterRequest& request, DeferredRegisterTraceWriterResponse reply, int fd) {
-  BeginInvoke("RegisterTraceWriter", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ProducerPortProxy::UnregisterTraceWriter(const UnregisterTraceWriterRequest& request, DeferredUnregisterTraceWriterResponse reply, int fd) {
-  BeginInvoke("UnregisterTraceWriter", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ProducerPortProxy::NotifyDataSourceStarted(const NotifyDataSourceStartedRequest& request, DeferredNotifyDataSourceStartedResponse reply, int fd) {
-  BeginInvoke("NotifyDataSourceStarted", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ProducerPortProxy::NotifyDataSourceStopped(const NotifyDataSourceStoppedRequest& request, DeferredNotifyDataSourceStoppedResponse reply, int fd) {
-  BeginInvoke("NotifyDataSourceStopped", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ProducerPortProxy::ActivateTriggers(const ActivateTriggersRequest& request, DeferredActivateTriggersResponse reply, int fd) {
-  BeginInvoke("ActivateTriggers", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ProducerPortProxy::Sync(const SyncRequest& request, DeferredSyncResponse reply, int fd) {
-  BeginInvoke("Sync", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-
-void ProducerPortProxy::UpdateDataSource(const UpdateDataSourceRequest& request, DeferredUpdateDataSourceResponse reply, int fd) {
-  BeginInvoke("UpdateDataSource", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
-              fd);
-}
-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-// gen_amalgamated begin source: gen/protos/perfetto/ipc/relay_port.ipc.cc
-// gen_amalgamated begin header: gen/protos/perfetto/ipc/relay_port.ipc.h
-// DO NOT EDIT. Autogenerated by Perfetto IPC
-#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_IPC_RELAY_PORT_PROTO_H_
-#define PERFETTO_PROTOS_PROTOS_PERFETTO_IPC_RELAY_PORT_PROTO_H_
-
-// gen_amalgamated expanded: #include "perfetto/ext/ipc/deferred.h"
-// gen_amalgamated expanded: #include "perfetto/ext/ipc/service.h"
-// gen_amalgamated expanded: #include "perfetto/ext/ipc/service_descriptor.h"
-// gen_amalgamated expanded: #include "perfetto/ext/ipc/service_proxy.h"
-
-// gen_amalgamated expanded: #include "protos/perfetto/ipc/relay_port.gen.h"
-// gen_amalgamated expanded: #include "protos/perfetto/common/system_info.gen.h"
-
-namespace perfetto {
-namespace protos {
-namespace gen {
-
-class RelayPort : public ::perfetto::ipc::Service {
- private:
-  static ::perfetto::ipc::ServiceDescriptor* NewDescriptor();
-
- public:
-  ~RelayPort() override;
-
-  static const ::perfetto::ipc::ServiceDescriptor& GetDescriptorStatic();
-
-  // Service implementation.
-  const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override;
-
-  // Methods from the .proto file
-  using DeferredInitRelayResponse = ::perfetto::ipc::Deferred<InitRelayResponse>;
-  virtual void InitRelay(const InitRelayRequest&, DeferredInitRelayResponse) = 0;
-
-  using DeferredSyncClockResponse = ::perfetto::ipc::Deferred<SyncClockResponse>;
-  virtual void SyncClock(const SyncClockRequest&, DeferredSyncClockResponse) = 0;
-
-};
-
-
-class RelayPortProxy : public ::perfetto::ipc::ServiceProxy {
- public:
-   explicit RelayPortProxy(::perfetto::ipc::ServiceProxy::EventListener*);
-   ~RelayPortProxy() override;
-
-  // ServiceProxy implementation.
-  const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override;
-
-  // Methods from the .proto file
-  using DeferredInitRelayResponse = ::perfetto::ipc::Deferred<InitRelayResponse>;
-  void InitRelay(const InitRelayRequest&, DeferredInitRelayResponse, int fd = -1);
-
-  using DeferredSyncClockResponse = ::perfetto::ipc::Deferred<SyncClockResponse>;
-  void SyncClock(const SyncClockRequest&, DeferredSyncClockResponse, int fd = -1);
-
-};
-
-}  // namespace perfetto
-}  // namespace protos
-}  // namespace gen
-
-#endif  // PERFETTO_PROTOS_PROTOS_PERFETTO_IPC_RELAY_PORT_PROTO_H_
-// DO NOT EDIT. Autogenerated by Perfetto IPC
-// gen_amalgamated expanded: #include "protos/perfetto/ipc/relay_port.ipc.h"
-// gen_amalgamated expanded: #include "perfetto/ext/ipc/codegen_helpers.h"
-
-#include <memory>
-
-namespace perfetto {
-namespace protos {
-namespace gen {
-::perfetto::ipc::ServiceDescriptor* RelayPort::NewDescriptor() {
-  auto* desc = new ::perfetto::ipc::ServiceDescriptor();
-  desc->service_name = "RelayPort";
-
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "InitRelay",
-     &_IPC_Decoder<InitRelayRequest>,
-     &_IPC_Decoder<InitRelayResponse>,
-     &_IPC_Invoker<RelayPort, InitRelayRequest, InitRelayResponse, &RelayPort::InitRelay>});
-
-  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
-     "SyncClock",
-     &_IPC_Decoder<SyncClockRequest>,
-     &_IPC_Decoder<SyncClockResponse>,
-     &_IPC_Invoker<RelayPort, SyncClockRequest, SyncClockResponse, &RelayPort::SyncClock>});
-  desc->methods.shrink_to_fit();
-  return desc;
-}
-
-
-const ::perfetto::ipc::ServiceDescriptor& RelayPort::GetDescriptorStatic() {
-  static auto* instance = NewDescriptor();
-  return *instance;
-}
-
-// Host-side definitions.
-RelayPort::~RelayPort() = default;
-
-const ::perfetto::ipc::ServiceDescriptor& RelayPort::GetDescriptor() {
-  return GetDescriptorStatic();
+void ConsumerPortProxy::GetTraceStats(const GetTraceStatsRequest& request, DeferredGetTraceStatsResponse reply, int fd) {
+  BeginInvoke("GetTraceStats", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
 }

-// Client-side definitions.
-RelayPortProxy::RelayPortProxy(::perfetto::ipc::ServiceProxy::EventListener* event_listener)
-    : ::perfetto::ipc::ServiceProxy(event_listener) {}
+void ConsumerPortProxy::ObserveEvents(const ObserveEventsRequest& request, DeferredObserveEventsResponse reply, int fd) {
+  BeginInvoke("ObserveEvents", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
+}

-RelayPortProxy::~RelayPortProxy() = default;
+void ConsumerPortProxy::QueryServiceState(const QueryServiceStateRequest& request, DeferredQueryServiceStateResponse reply, int fd) {
+  BeginInvoke("QueryServiceState", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
+}

-const ::perfetto::ipc::ServiceDescriptor& RelayPortProxy::GetDescriptor() {
-  return RelayPort::GetDescriptorStatic();
+void ConsumerPortProxy::QueryCapabilities(const QueryCapabilitiesRequest& request, DeferredQueryCapabilitiesResponse reply, int fd) {
+  BeginInvoke("QueryCapabilities", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
 }

-void RelayPortProxy::InitRelay(const InitRelayRequest& request, DeferredInitRelayResponse reply, int fd) {
-  BeginInvoke("InitRelay", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+void ConsumerPortProxy::SaveTraceForBugreport(const SaveTraceForBugreportRequest& request, DeferredSaveTraceForBugreportResponse reply, int fd) {
+  BeginInvoke("SaveTraceForBugreport", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
               fd);
 }

-void RelayPortProxy::SyncClock(const SyncClockRequest& request, DeferredSyncClockResponse reply, int fd) {
-  BeginInvoke("SyncClock", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+void ConsumerPortProxy::CloneSession(const CloneSessionRequest& request, DeferredCloneSessionResponse reply, int fd) {
+  BeginInvoke("CloneSession", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
               fd);
 }
 }  // namespace perfetto
 }  // namespace protos
 }  // namespace gen
-// gen_amalgamated begin source: src/tracing/ipc/default_socket.cc
-/*
- * 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.
- */
-
-// gen_amalgamated expanded: #include "perfetto/tracing/default_socket.h"
-
-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/android_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/utils.h"
-// gen_amalgamated expanded: #include "perfetto/ext/ipc/basic_types.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/basic_types.h"
+// gen_amalgamated begin source: gen/protos/perfetto/ipc/producer_port.ipc.cc
+// gen_amalgamated begin header: gen/protos/perfetto/ipc/producer_port.ipc.h
+// DO NOT EDIT. Autogenerated by Perfetto IPC
+#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_IPC_PRODUCER_PORT_PROTO_H_
+#define PERFETTO_PROTOS_PROTOS_PERFETTO_IPC_PRODUCER_PORT_PROTO_H_

-#include <stdlib.h>
+// gen_amalgamated expanded: #include "perfetto/ext/ipc/deferred.h"
+// gen_amalgamated expanded: #include "perfetto/ext/ipc/service.h"
+// gen_amalgamated expanded: #include "perfetto/ext/ipc/service_descriptor.h"
+// gen_amalgamated expanded: #include "perfetto/ext/ipc/service_proxy.h"

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-#include <unistd.h>
-#endif
+// gen_amalgamated expanded: #include "protos/perfetto/ipc/producer_port.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/commit_data_request.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/config/data_source_config.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/data_source_descriptor.gen.h"

 namespace perfetto {
-namespace {
+namespace protos {
+namespace gen {

-const char* kRunPerfettoBaseDir = "/run/perfetto/";
+class ProducerPort : public ::perfetto::ipc::Service {
+ private:
+  static ::perfetto::ipc::ServiceDescriptor* NewDescriptor();

-// On Linux and CrOS, check /run/perfetto/ before using /tmp/ as the socket
-// base directory.
-bool UseRunPerfettoBaseDir() {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX)
-  // Note that the trailing / in |kRunPerfettoBaseDir| ensures we are checking
-  // against a directory, not a file.
-  int res = PERFETTO_EINTR(access(kRunPerfettoBaseDir, X_OK));
-  if (!res)
-    return true;
+ public:
+  ~ProducerPort() override;

-  // If the path doesn't exist (ENOENT), fail silently to the caller. Otherwise,
-  // fail with an explicit error message.
-  if (errno != ENOENT
-#if PERFETTO_BUILDFLAG(PERFETTO_CHROMIUM_BUILD)
-      // access(2) won't return EPERM, but Chromium sandbox returns EPERM if the
-      // sandbox doesn't allow the call (e.g. in the child processes).
-      && errno != EPERM
-#endif
-  ) {
-    PERFETTO_PLOG("%s exists but cannot be accessed. Falling back on /tmp/ ",
-                  kRunPerfettoBaseDir);
-  }
-  return false;
-#else
-  base::ignore_result(kRunPerfettoBaseDir);
-  return false;
-#endif
-}
+  static const ::perfetto::ipc::ServiceDescriptor& GetDescriptorStatic();

-}  // anonymous namespace
+  // Service implementation.
+  const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override;

-const char* GetProducerSocket() {
-  const char* name = getenv("PERFETTO_PRODUCER_SOCK_NAME");
-  if (name == nullptr) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    name = "127.0.0.1:32278";
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-    name = "/dev/socket/traced_producer";
-#else
-    // Use /run/perfetto if it exists. Then fallback to /tmp.
-    static const char* producer_socket =
-        UseRunPerfettoBaseDir() ? "/run/perfetto/traced-producer.sock"
-                                : "/tmp/perfetto-producer";
-    name = producer_socket;
-#endif
-  }
-  base::ignore_result(UseRunPerfettoBaseDir);  // Silence unused func warnings.
-  return name;
-}
+  // Methods from the .proto file
+  using DeferredInitializeConnectionResponse = ::perfetto::ipc::Deferred<InitializeConnectionResponse>;
+  virtual void InitializeConnection(const InitializeConnectionRequest&, DeferredInitializeConnectionResponse) = 0;

-std::string GetRelaySocket() {
-  // The relay socket is optional and is connected only when the env var is set.
-  // In Android, if the env var isn't set then we check the
-  // |traced_relay.relay_port| system property.
-  const char* name = getenv("PERFETTO_RELAY_SOCK_NAME");
-  if (name != nullptr)
-    return std::string(name);
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  return base::GetAndroidProp("traced_relay.relay_port");
-#else
-  return std::string();
-#endif
-}
+  using DeferredRegisterDataSourceResponse = ::perfetto::ipc::Deferred<RegisterDataSourceResponse>;
+  virtual void RegisterDataSource(const RegisterDataSourceRequest&, DeferredRegisterDataSourceResponse) = 0;

-std::vector<std::string> TokenizeProducerSockets(
-    const char* producer_socket_names) {
-  return base::SplitString(producer_socket_names, ",");
-}
+  using DeferredUnregisterDataSourceResponse = ::perfetto::ipc::Deferred<UnregisterDataSourceResponse>;
+  virtual void UnregisterDataSource(const UnregisterDataSourceRequest&, DeferredUnregisterDataSourceResponse) = 0;

-const char* GetConsumerSocket() {
-  const char* name = getenv("PERFETTO_CONSUMER_SOCK_NAME");
-  if (name == nullptr) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    name = "127.0.0.1:32279";
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-    name = "/dev/socket/traced_consumer";
-#else
-    // Use /run/perfetto if it exists. Then fallback to /tmp.
-    static const char* consumer_socket =
-        UseRunPerfettoBaseDir() ? "/run/perfetto/traced-consumer.sock"
-                                : "/tmp/perfetto-consumer";
-    name = consumer_socket;
-#endif
-  }
-  return name;
-}
+  using DeferredCommitDataResponse = ::perfetto::ipc::Deferred<CommitDataResponse>;
+  virtual void CommitData(const CommitDataRequest&, DeferredCommitDataResponse) = 0;

-}  // namespace perfetto
-// gen_amalgamated begin source: src/tracing/ipc/memfd.cc
-// gen_amalgamated begin header: src/tracing/ipc/memfd.h
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+  using DeferredGetAsyncCommandResponse = ::perfetto::ipc::Deferred<GetAsyncCommandResponse>;
+  virtual void GetAsyncCommand(const GetAsyncCommandRequest&, DeferredGetAsyncCommandResponse) = 0;

-#ifndef SRC_TRACING_IPC_MEMFD_H_
-#define SRC_TRACING_IPC_MEMFD_H_
+  using DeferredRegisterTraceWriterResponse = ::perfetto::ipc::Deferred<RegisterTraceWriterResponse>;
+  virtual void RegisterTraceWriter(const RegisterTraceWriterRequest&, DeferredRegisterTraceWriterResponse) = 0;

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+  using DeferredUnregisterTraceWriterResponse = ::perfetto::ipc::Deferred<UnregisterTraceWriterResponse>;
+  virtual void UnregisterTraceWriter(const UnregisterTraceWriterRequest&, DeferredUnregisterTraceWriterResponse) = 0;

-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
+  using DeferredNotifyDataSourceStartedResponse = ::perfetto::ipc::Deferred<NotifyDataSourceStartedResponse>;
+  virtual void NotifyDataSourceStarted(const NotifyDataSourceStartedRequest&, DeferredNotifyDataSourceStartedResponse) = 0;

-// Some android build bots use a sysroot that doesn't support memfd when
-// compiling for the host, so we define the flags we need ourselves.
+  using DeferredNotifyDataSourceStoppedResponse = ::perfetto::ipc::Deferred<NotifyDataSourceStoppedResponse>;
+  virtual void NotifyDataSourceStopped(const NotifyDataSourceStoppedRequest&, DeferredNotifyDataSourceStoppedResponse) = 0;

-// from memfd.h
-#ifndef MFD_CLOEXEC
-#define MFD_CLOEXEC 0x0001U
-#define MFD_ALLOW_SEALING 0x0002U
-#endif
+  using DeferredActivateTriggersResponse = ::perfetto::ipc::Deferred<ActivateTriggersResponse>;
+  virtual void ActivateTriggers(const ActivateTriggersRequest&, DeferredActivateTriggersResponse) = 0;

-// from fcntl.h
-#ifndef F_ADD_SEALS
-#define F_ADD_SEALS 1033
-#define F_GET_SEALS 1034
-#define F_SEAL_SEAL 0x0001
-#define F_SEAL_SHRINK 0x0002
-#define F_SEAL_GROW 0x0004
-#define F_SEAL_WRITE 0x0008
-#endif
+  using DeferredSyncResponse = ::perfetto::ipc::Deferred<SyncResponse>;
+  virtual void Sync(const SyncRequest&, DeferredSyncResponse) = 0;

-namespace perfetto {
+  using DeferredUpdateDataSourceResponse = ::perfetto::ipc::Deferred<UpdateDataSourceResponse>;
+  virtual void UpdateDataSource(const UpdateDataSourceRequest&, DeferredUpdateDataSourceResponse) = 0;

-// Whether the operating system supports memfd.
-bool HasMemfdSupport();
+  using DeferredAttachV2RingBufferResponse = ::perfetto::ipc::Deferred<AttachV2RingBufferResponse>;
+  virtual void AttachV2RingBuffer(const AttachV2RingBufferRequest&, DeferredAttachV2RingBufferResponse) = 0;

-// Call memfd(2) if available on platform and return the fd as result. This call
-// also makes a kernel version check for safety on older kernels (b/116769556).
-// Returns an invalid ScopedFile on failure.
-base::ScopedFile CreateMemfd(const char* name, unsigned int flags);
+  using DeferredDrainV2RingBufferResponse = ::perfetto::ipc::Deferred<DrainV2RingBufferResponse>;
+  virtual void DrainV2RingBuffer(const DrainV2RingBufferRequest&, DeferredDrainV2RingBufferResponse) = 0;

-}  // namespace perfetto
+};

-#endif  // SRC_TRACING_IPC_MEMFD_H_
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */

-// gen_amalgamated expanded: #include "src/tracing/ipc/memfd.h"
+class ProducerPortProxy : public ::perfetto::ipc::ServiceProxy {
+ public:
+   explicit ProducerPortProxy(::perfetto::ipc::ServiceProxy::EventListener*);
+   ~ProducerPortProxy() override;

-#include <errno.h>
+  // ServiceProxy implementation.
+  const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override;

-#define PERFETTO_MEMFD_ENABLED()             \
-  PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-      PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX)
+  // Methods from the .proto file
+  using DeferredInitializeConnectionResponse = ::perfetto::ipc::Deferred<InitializeConnectionResponse>;
+  void InitializeConnection(const InitializeConnectionRequest&, DeferredInitializeConnectionResponse, int fd = -1);

-#if PERFETTO_MEMFD_ENABLED()
+  using DeferredRegisterDataSourceResponse = ::perfetto::ipc::Deferred<RegisterDataSourceResponse>;
+  void RegisterDataSource(const RegisterDataSourceRequest&, DeferredRegisterDataSourceResponse, int fd = -1);

-#include <stdio.h>
-#include <string.h>
-#include <sys/syscall.h>
-#include <sys/utsname.h>
-#include <unistd.h>
+  using DeferredUnregisterDataSourceResponse = ::perfetto::ipc::Deferred<UnregisterDataSourceResponse>;
+  void UnregisterDataSource(const UnregisterDataSourceRequest&, DeferredUnregisterDataSourceResponse, int fd = -1);

-// Some android build bots use a sysroot that doesn't support memfd when
-// compiling for the host, so we redefine it if necessary.
-#if !defined(__NR_memfd_create)
-#if defined(__x86_64__)
-#define __NR_memfd_create 319
-#elif defined(__i386__)
-#define __NR_memfd_create 356
-#elif defined(__aarch64__)
-#define __NR_memfd_create 279
-#elif defined(__arm__)
-#define __NR_memfd_create 385
-#else
-#error "unsupported sysroot without memfd support"
-#endif
-#endif  // !defined(__NR_memfd_create)
+  using DeferredCommitDataResponse = ::perfetto::ipc::Deferred<CommitDataResponse>;
+  void CommitData(const CommitDataRequest&, DeferredCommitDataResponse, int fd = -1);

-namespace perfetto {
-bool HasMemfdSupport() {
-  static bool kSupportsMemfd = [] {
-    // Check kernel version supports memfd_create(). Some older kernels segfault
-    // executing memfd_create() rather than returning ENOSYS (b/116769556).
-    static constexpr int kRequiredMajor = 3;
-    static constexpr int kRequiredMinor = 17;
-    struct utsname uts;
-    int major, minor;
-    if (uname(&uts) == 0 && strcmp(uts.sysname, "Linux") == 0 &&
-        sscanf(uts.release, "%d.%d", &major, &minor) == 2 &&
-        ((major < kRequiredMajor ||
-          (major == kRequiredMajor && minor < kRequiredMinor)))) {
-      return false;
-    }
+  using DeferredGetAsyncCommandResponse = ::perfetto::ipc::Deferred<GetAsyncCommandResponse>;
+  void GetAsyncCommand(const GetAsyncCommandRequest&, DeferredGetAsyncCommandResponse, int fd = -1);

-    base::ScopedFile fd;
-    fd.reset(static_cast<int>(syscall(__NR_memfd_create, "perfetto_shmem",
-                                      MFD_CLOEXEC | MFD_ALLOW_SEALING)));
-    return !!fd;
-  }();
-  return kSupportsMemfd;
-}
+  using DeferredRegisterTraceWriterResponse = ::perfetto::ipc::Deferred<RegisterTraceWriterResponse>;
+  void RegisterTraceWriter(const RegisterTraceWriterRequest&, DeferredRegisterTraceWriterResponse, int fd = -1);

-base::ScopedFile CreateMemfd(const char* name, unsigned int flags) {
-  if (!HasMemfdSupport()) {
-    errno = ENOSYS;
-    return base::ScopedFile();
-  }
-  return base::ScopedFile(
-      static_cast<int>(syscall(__NR_memfd_create, name, flags)));
-}
-}  // namespace perfetto
+  using DeferredUnregisterTraceWriterResponse = ::perfetto::ipc::Deferred<UnregisterTraceWriterResponse>;
+  void UnregisterTraceWriter(const UnregisterTraceWriterRequest&, DeferredUnregisterTraceWriterResponse, int fd = -1);

-#else  // PERFETTO_MEMFD_ENABLED()
+  using DeferredNotifyDataSourceStartedResponse = ::perfetto::ipc::Deferred<NotifyDataSourceStartedResponse>;
+  void NotifyDataSourceStarted(const NotifyDataSourceStartedRequest&, DeferredNotifyDataSourceStartedResponse, int fd = -1);

-namespace perfetto {
-bool HasMemfdSupport() {
-  return false;
-}
-base::ScopedFile CreateMemfd(const char*, unsigned int) {
-  errno = ENOSYS;
-  return base::ScopedFile();
-}
-}  // namespace perfetto
+  using DeferredNotifyDataSourceStoppedResponse = ::perfetto::ipc::Deferred<NotifyDataSourceStoppedResponse>;
+  void NotifyDataSourceStopped(const NotifyDataSourceStoppedRequest&, DeferredNotifyDataSourceStoppedResponse, int fd = -1);

-#endif  // PERFETTO_MEMFD_ENABLED()
-// gen_amalgamated begin source: src/tracing/ipc/posix_shared_memory.cc
-// gen_amalgamated begin header: src/tracing/ipc/posix_shared_memory.h
-/*
- * 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.
- */
+  using DeferredActivateTriggersResponse = ::perfetto::ipc::Deferred<ActivateTriggersResponse>;
+  void ActivateTriggers(const ActivateTriggersRequest&, DeferredActivateTriggersResponse, int fd = -1);

-#ifndef SRC_TRACING_IPC_POSIX_SHARED_MEMORY_H_
-#define SRC_TRACING_IPC_POSIX_SHARED_MEMORY_H_
+  using DeferredSyncResponse = ::perfetto::ipc::Deferred<SyncResponse>;
+  void Sync(const SyncRequest&, DeferredSyncResponse, int fd = -1);

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+  using DeferredUpdateDataSourceResponse = ::perfetto::ipc::Deferred<UpdateDataSourceResponse>;
+  void UpdateDataSource(const UpdateDataSourceRequest&, DeferredUpdateDataSourceResponse, int fd = -1);

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
+  using DeferredAttachV2RingBufferResponse = ::perfetto::ipc::Deferred<AttachV2RingBufferResponse>;
+  void AttachV2RingBuffer(const AttachV2RingBufferRequest&, DeferredAttachV2RingBufferResponse, int fd = -1);

-#include <stddef.h>
+  using DeferredDrainV2RingBufferResponse = ::perfetto::ipc::Deferred<DrainV2RingBufferResponse>;
+  void DrainV2RingBuffer(const DrainV2RingBufferRequest&, DeferredDrainV2RingBufferResponse, int fd = -1);

-#include <memory>
+};

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory.h"
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen

-namespace perfetto {
+#endif  // PERFETTO_PROTOS_PROTOS_PERFETTO_IPC_PRODUCER_PORT_PROTO_H_
+// DO NOT EDIT. Autogenerated by Perfetto IPC
+// gen_amalgamated expanded: #include "protos/perfetto/ipc/producer_port.ipc.h"
+// gen_amalgamated expanded: #include "perfetto/ext/ipc/codegen_helpers.h"

-// Implements the SharedMemory and its factory for the posix-based transport.
-class PosixSharedMemory : public SharedMemory {
- public:
-  class Factory : public SharedMemory::Factory {
-   public:
-    ~Factory() override;
-    std::unique_ptr<SharedMemory> CreateSharedMemory(size_t) override;
-  };
+#include <memory>

-  // Create a brand new SHM region.
-  static std::unique_ptr<PosixSharedMemory> Create(size_t size);
+namespace perfetto {
+namespace protos {
+namespace gen {
+::perfetto::ipc::ServiceDescriptor* ProducerPort::NewDescriptor() {
+  auto* desc = new ::perfetto::ipc::ServiceDescriptor();
+  desc->service_name = "ProducerPort";

-  // Mmaps a file descriptor to an existing SHM region. If
-  // |require_seals_if_supported| is true and the system supports
-  // memfd_create(), the FD is required to be a sealed memfd with F_SEAL_SEAL,
-  // F_SEAL_GROW, and F_SEAL_SHRINK seals set (otherwise, nullptr is returned).
-  // May also return nullptr if mapping fails for another reason (e.g. OOM).
-  static std::unique_ptr<PosixSharedMemory> AttachToFd(
-      base::ScopedFile,
-      bool require_seals_if_supported = true);
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "InitializeConnection",
+     &_IPC_Decoder<InitializeConnectionRequest>,
+     &_IPC_Decoder<InitializeConnectionResponse>,
+     &_IPC_Invoker<ProducerPort, InitializeConnectionRequest, InitializeConnectionResponse, &ProducerPort::InitializeConnection>});

-  ~PosixSharedMemory() override;
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "RegisterDataSource",
+     &_IPC_Decoder<RegisterDataSourceRequest>,
+     &_IPC_Decoder<RegisterDataSourceResponse>,
+     &_IPC_Invoker<ProducerPort, RegisterDataSourceRequest, RegisterDataSourceResponse, &ProducerPort::RegisterDataSource>});

-  int fd() const { return fd_.get(); }
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "UnregisterDataSource",
+     &_IPC_Decoder<UnregisterDataSourceRequest>,
+     &_IPC_Decoder<UnregisterDataSourceResponse>,
+     &_IPC_Invoker<ProducerPort, UnregisterDataSourceRequest, UnregisterDataSourceResponse, &ProducerPort::UnregisterDataSource>});

-  // SharedMemory implementation.
-  using SharedMemory::start;  // Equal priority to const and non-const versions
-  const void* start() const override { return start_; }
-  size_t size() const override { return size_; }
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "CommitData",
+     &_IPC_Decoder<CommitDataRequest>,
+     &_IPC_Decoder<CommitDataResponse>,
+     &_IPC_Invoker<ProducerPort, CommitDataRequest, CommitDataResponse, &ProducerPort::CommitData>});

- private:
-  static std::unique_ptr<PosixSharedMemory> MapFD(base::ScopedFile, size_t);
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "GetAsyncCommand",
+     &_IPC_Decoder<GetAsyncCommandRequest>,
+     &_IPC_Decoder<GetAsyncCommandResponse>,
+     &_IPC_Invoker<ProducerPort, GetAsyncCommandRequest, GetAsyncCommandResponse, &ProducerPort::GetAsyncCommand>});

-  PosixSharedMemory(void* start, size_t size, base::ScopedFile);
-  PosixSharedMemory(const PosixSharedMemory&) = delete;
-  PosixSharedMemory& operator=(const PosixSharedMemory&) = delete;
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "RegisterTraceWriter",
+     &_IPC_Decoder<RegisterTraceWriterRequest>,
+     &_IPC_Decoder<RegisterTraceWriterResponse>,
+     &_IPC_Invoker<ProducerPort, RegisterTraceWriterRequest, RegisterTraceWriterResponse, &ProducerPort::RegisterTraceWriter>});

-  void* const start_;
-  const size_t size_;
-  base::ScopedFile fd_;
-};
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "UnregisterTraceWriter",
+     &_IPC_Decoder<UnregisterTraceWriterRequest>,
+     &_IPC_Decoder<UnregisterTraceWriterResponse>,
+     &_IPC_Invoker<ProducerPort, UnregisterTraceWriterRequest, UnregisterTraceWriterResponse, &ProducerPort::UnregisterTraceWriter>});

-}  // namespace perfetto
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "NotifyDataSourceStarted",
+     &_IPC_Decoder<NotifyDataSourceStartedRequest>,
+     &_IPC_Decoder<NotifyDataSourceStartedResponse>,
+     &_IPC_Invoker<ProducerPort, NotifyDataSourceStartedRequest, NotifyDataSourceStartedResponse, &ProducerPort::NotifyDataSourceStarted>});

-#endif  // OS_LINUX || OS_ANDROID || OS_APPLE
-#endif  // SRC_TRACING_IPC_POSIX_SHARED_MEMORY_H_
-/*
- * 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.
- */
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "NotifyDataSourceStopped",
+     &_IPC_Decoder<NotifyDataSourceStoppedRequest>,
+     &_IPC_Decoder<NotifyDataSourceStoppedResponse>,
+     &_IPC_Invoker<ProducerPort, NotifyDataSourceStoppedRequest, NotifyDataSourceStoppedResponse, &ProducerPort::NotifyDataSourceStopped>});

-// gen_amalgamated expanded: #include "src/tracing/ipc/posix_shared_memory.h"
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "ActivateTriggers",
+     &_IPC_Decoder<ActivateTriggersRequest>,
+     &_IPC_Decoder<ActivateTriggersResponse>,
+     &_IPC_Invoker<ProducerPort, ActivateTriggersRequest, ActivateTriggersResponse, &ProducerPort::ActivateTriggers>});

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FREEBSD) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "Sync",
+     &_IPC_Decoder<SyncRequest>,
+     &_IPC_Decoder<SyncResponse>,
+     &_IPC_Invoker<ProducerPort, SyncRequest, SyncResponse, &ProducerPort::Sync>});

-#include <fcntl.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <unistd.h>
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "UpdateDataSource",
+     &_IPC_Decoder<UpdateDataSourceRequest>,
+     &_IPC_Decoder<UpdateDataSourceResponse>,
+     &_IPC_Invoker<ProducerPort, UpdateDataSourceRequest, UpdateDataSourceResponse, &ProducerPort::UpdateDataSource>});

-#include <memory>
-#include <utility>
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "AttachV2RingBuffer",
+     &_IPC_Decoder<AttachV2RingBufferRequest>,
+     &_IPC_Decoder<AttachV2RingBufferResponse>,
+     &_IPC_Invoker<ProducerPort, AttachV2RingBufferRequest, AttachV2RingBufferResponse, &ProducerPort::AttachV2RingBuffer>});

-// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/temp_file.h"
-// gen_amalgamated expanded: #include "src/tracing/ipc/memfd.h"
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "DrainV2RingBuffer",
+     &_IPC_Decoder<DrainV2RingBufferRequest>,
+     &_IPC_Decoder<DrainV2RingBufferResponse>,
+     &_IPC_Invoker<ProducerPort, DrainV2RingBufferRequest, DrainV2RingBufferResponse, &ProducerPort::DrainV2RingBuffer>});
+  desc->methods.shrink_to_fit();
+  return desc;
+}

-namespace perfetto {

-namespace {
-int kFileSeals = F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL;
-}  // namespace
+const ::perfetto::ipc::ServiceDescriptor& ProducerPort::GetDescriptorStatic() {
+  static auto* instance = NewDescriptor();
+  return *instance;
+}

-// static
-std::unique_ptr<PosixSharedMemory> PosixSharedMemory::Create(size_t size) {
-  base::ScopedFile fd =
-      CreateMemfd("perfetto_shmem", MFD_CLOEXEC | MFD_ALLOW_SEALING);
-  bool is_memfd = !!fd;
+// Host-side definitions.
+ProducerPort::~ProducerPort() = default;

-  // In-tree builds only allow mem_fd, so we can inspect the seals to verify the
-  // fd is appropriately sealed. We'll crash in the PERFETTO_CHECK(fd) below if
-  // memfd_create failed.
-#if !PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
-  if (!fd) {
-    // TODO: if this fails on Android we should fall back on ashmem.
-    PERFETTO_DPLOG("memfd_create() failed");
-    fd = base::TempFile::CreateUnlinked().ReleaseFD();
-  }
-#endif
+const ::perfetto::ipc::ServiceDescriptor& ProducerPort::GetDescriptor() {
+  return GetDescriptorStatic();
+}

-  PERFETTO_CHECK(fd);
-  int res = ftruncate(fd.get(), static_cast<off_t>(size));
-  PERFETTO_CHECK(res == 0);
+// Client-side definitions.
+ProducerPortProxy::ProducerPortProxy(::perfetto::ipc::ServiceProxy::EventListener* event_listener)
+    : ::perfetto::ipc::ServiceProxy(event_listener) {}

-  if (is_memfd) {
-    // When memfd is supported, file seals should be, too.
-    res = fcntl(*fd, F_ADD_SEALS, kFileSeals);
-    PERFETTO_DCHECK(res == 0);
-  }
+ProducerPortProxy::~ProducerPortProxy() = default;

-  return MapFD(std::move(fd), size);
+const ::perfetto::ipc::ServiceDescriptor& ProducerPortProxy::GetDescriptor() {
+  return ProducerPort::GetDescriptorStatic();
 }

-// static
-std::unique_ptr<PosixSharedMemory> PosixSharedMemory::AttachToFd(
-    base::ScopedFile fd,
-    bool require_seals_if_supported) {
-  bool requires_seals = require_seals_if_supported;
+void ProducerPortProxy::InitializeConnection(const InitializeConnectionRequest& request, DeferredInitializeConnectionResponse reply, int fd) {
+  BeginInvoke("InitializeConnection", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
+}

-#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
-  // In-tree kernels all support memfd.
-  PERFETTO_CHECK(HasMemfdSupport());
-#else
-  // In out-of-tree builds, we only require seals if the kernel supports memfd.
-  if (requires_seals)
-    requires_seals = HasMemfdSupport();
-#endif
+void ProducerPortProxy::RegisterDataSource(const RegisterDataSourceRequest& request, DeferredRegisterDataSourceResponse reply, int fd) {
+  BeginInvoke("RegisterDataSource", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
+}

-  if (requires_seals) {
-    // If the system supports memfd, we require a sealed memfd.
-    int res = fcntl(*fd, F_GET_SEALS);
-    if (res == -1 || (res & kFileSeals) != kFileSeals) {
-      PERFETTO_PLOG("Couldn't verify file seals on shmem FD");
-      return nullptr;
-    }
-  }
+void ProducerPortProxy::UnregisterDataSource(const UnregisterDataSourceRequest& request, DeferredUnregisterDataSourceResponse reply, int fd) {
+  BeginInvoke("UnregisterDataSource", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
+}

-  struct stat stat_buf = {};
-  int res = fstat(fd.get(), &stat_buf);
-  PERFETTO_CHECK(res == 0 && stat_buf.st_size > 0);
-  return MapFD(std::move(fd), static_cast<size_t>(stat_buf.st_size));
+void ProducerPortProxy::CommitData(const CommitDataRequest& request, DeferredCommitDataResponse reply, int fd) {
+  BeginInvoke("CommitData", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
 }

-// static
-std::unique_ptr<PosixSharedMemory> PosixSharedMemory::MapFD(base::ScopedFile fd,
-                                                            size_t size) {
-  PERFETTO_DCHECK(fd);
-  PERFETTO_DCHECK(size > 0);
-  void* start =
-      mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0);
-  PERFETTO_CHECK(start != MAP_FAILED);
-  return std::unique_ptr<PosixSharedMemory>(
-      new PosixSharedMemory(start, size, std::move(fd)));
+void ProducerPortProxy::GetAsyncCommand(const GetAsyncCommandRequest& request, DeferredGetAsyncCommandResponse reply, int fd) {
+  BeginInvoke("GetAsyncCommand", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
 }

-PosixSharedMemory::PosixSharedMemory(void* start,
-                                     size_t size,
-                                     base::ScopedFile fd)
-    : start_(start), size_(size), fd_(std::move(fd)) {}
+void ProducerPortProxy::RegisterTraceWriter(const RegisterTraceWriterRequest& request, DeferredRegisterTraceWriterResponse reply, int fd) {
+  BeginInvoke("RegisterTraceWriter", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
+}

-PosixSharedMemory::~PosixSharedMemory() {
-  munmap(start(), size());
+void ProducerPortProxy::UnregisterTraceWriter(const UnregisterTraceWriterRequest& request, DeferredUnregisterTraceWriterResponse reply, int fd) {
+  BeginInvoke("UnregisterTraceWriter", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
 }

-PosixSharedMemory::Factory::~Factory() {}
+void ProducerPortProxy::NotifyDataSourceStarted(const NotifyDataSourceStartedRequest& request, DeferredNotifyDataSourceStartedResponse reply, int fd) {
+  BeginInvoke("NotifyDataSourceStarted", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
+}

-std::unique_ptr<SharedMemory> PosixSharedMemory::Factory::CreateSharedMemory(
-    size_t size) {
-  return PosixSharedMemory::Create(size);
+void ProducerPortProxy::NotifyDataSourceStopped(const NotifyDataSourceStoppedRequest& request, DeferredNotifyDataSourceStoppedResponse reply, int fd) {
+  BeginInvoke("NotifyDataSourceStopped", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
 }

-}  // namespace perfetto
+void ProducerPortProxy::ActivateTriggers(const ActivateTriggersRequest& request, DeferredActivateTriggersResponse reply, int fd) {
+  BeginInvoke("ActivateTriggers", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
+}

-#endif  // OS_LINUX || OS_ANDROID || OS_APPLE
-// gen_amalgamated begin source: src/tracing/ipc/shared_memory_windows.cc
-// gen_amalgamated begin header: src/tracing/ipc/shared_memory_windows.h
-/*
- * Copyright (C) 2021 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.
- */
+void ProducerPortProxy::Sync(const SyncRequest& request, DeferredSyncResponse reply, int fd) {
+  BeginInvoke("Sync", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
+}

-#ifndef SRC_TRACING_IPC_SHARED_MEMORY_WINDOWS_H_
-#define SRC_TRACING_IPC_SHARED_MEMORY_WINDOWS_H_
+void ProducerPortProxy::UpdateDataSource(const UpdateDataSourceRequest& request, DeferredUpdateDataSourceResponse reply, int fd) {
+  BeginInvoke("UpdateDataSource", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
+}

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+void ProducerPortProxy::AttachV2RingBuffer(const AttachV2RingBufferRequest& request, DeferredAttachV2RingBufferResponse reply, int fd) {
+  BeginInvoke("AttachV2RingBuffer", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
+}

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+void ProducerPortProxy::DrainV2RingBuffer(const DrainV2RingBufferRequest& request, DeferredDrainV2RingBufferResponse reply, int fd) {
+  BeginInvoke("DrainV2RingBuffer", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
+}
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+// gen_amalgamated begin source: gen/protos/perfetto/ipc/relay_port.ipc.cc
+// gen_amalgamated begin header: gen/protos/perfetto/ipc/relay_port.ipc.h
+// DO NOT EDIT. Autogenerated by Perfetto IPC
+#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_IPC_RELAY_PORT_PROTO_H_
+#define PERFETTO_PROTOS_PROTOS_PERFETTO_IPC_RELAY_PORT_PROTO_H_

-#include <string>
+// gen_amalgamated expanded: #include "perfetto/ext/ipc/deferred.h"
+// gen_amalgamated expanded: #include "perfetto/ext/ipc/service.h"
+// gen_amalgamated expanded: #include "perfetto/ext/ipc/service_descriptor.h"
+// gen_amalgamated expanded: #include "perfetto/ext/ipc/service_proxy.h"

-// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/scoped_file.h"
-// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory.h"
+// gen_amalgamated expanded: #include "protos/perfetto/ipc/relay_port.gen.h"
+// gen_amalgamated expanded: #include "protos/perfetto/common/system_info.gen.h"

 namespace perfetto {
+namespace protos {
+namespace gen {
+
+class RelayPort : public ::perfetto::ipc::Service {
+ private:
+  static ::perfetto::ipc::ServiceDescriptor* NewDescriptor();

-// Implements the SharedMemory and its factory for the Windows IPC transport.
-// This used only for standalone builds and NOT in chromium, which instead uses
-// a custom Mojo wrapper (MojoSharedMemory in chromium's //services/tracing/).
-class SharedMemoryWindows : public SharedMemory {
  public:
-  class Factory : public SharedMemory::Factory {
-   public:
-    ~Factory() override;
-    std::unique_ptr<SharedMemory> CreateSharedMemory(size_t) override;
-  };
+  ~RelayPort() override;

-  // Create a brand new SHM region.
-  enum Flags { kNone = 0, kInheritableHandles };
-  static std::unique_ptr<SharedMemoryWindows> Create(
-      size_t size,
-      Flags flags = Flags::kNone);
-  static std::unique_ptr<SharedMemoryWindows> Attach(const std::string& key);
-  static std::unique_ptr<SharedMemoryWindows> AttachToHandleWithKey(
-      base::ScopedPlatformHandle fd,
-      const std::string& key);
-  ~SharedMemoryWindows() override;
-  const std::string& key() const { return key_; }
-  const base::ScopedPlatformHandle& handle() const { return handle_; }
+  static const ::perfetto::ipc::ServiceDescriptor& GetDescriptorStatic();

-  // SharedMemory implementation.
-  using SharedMemory::start;  // Equal priority to const and non-const versions
-  const void* start() const override { return start_; }
-  size_t size() const override { return size_; }
+  // Service implementation.
+  const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override;

- private:
-  SharedMemoryWindows(void* start,
-                      size_t size,
-                      std::string,
-                      base::ScopedPlatformHandle);
-  SharedMemoryWindows(const SharedMemoryWindows&) = delete;
-  SharedMemoryWindows& operator=(const SharedMemoryWindows&) = delete;
+  // Methods from the .proto file
+  using DeferredInitRelayResponse = ::perfetto::ipc::Deferred<InitRelayResponse>;
+  virtual void InitRelay(const InitRelayRequest&, DeferredInitRelayResponse) = 0;
+
+  using DeferredSyncClockResponse = ::perfetto::ipc::Deferred<SyncClockResponse>;
+  virtual void SyncClock(const SyncClockRequest&, DeferredSyncClockResponse) = 0;

-  void* const start_;
-  const size_t size_;
-  std::string key_;
-  base::ScopedPlatformHandle handle_;
 };

-}  // namespace perfetto

-#endif  // OS_WIN
+class RelayPortProxy : public ::perfetto::ipc::ServiceProxy {
+ public:
+   explicit RelayPortProxy(::perfetto::ipc::ServiceProxy::EventListener*);
+   ~RelayPortProxy() override;

-#endif  // SRC_TRACING_IPC_SHARED_MEMORY_WINDOWS_H_
-/*
- * Copyright (C) 2021 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.
- */
+  // ServiceProxy implementation.
+  const ::perfetto::ipc::ServiceDescriptor& GetDescriptor() override;

-// gen_amalgamated expanded: #include "src/tracing/ipc/shared_memory_windows.h"
+  // Methods from the .proto file
+  using DeferredInitRelayResponse = ::perfetto::ipc::Deferred<InitRelayResponse>;
+  void InitRelay(const InitRelayRequest&, DeferredInitRelayResponse, int fd = -1);

-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  using DeferredSyncClockResponse = ::perfetto::ipc::Deferred<SyncClockResponse>;
+  void SyncClock(const SyncClockRequest&, DeferredSyncClockResponse, int fd = -1);

-#include <memory>
-#include <random>
+};

-#include <windows.h>
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen

-// gen_amalgamated expanded: #include "perfetto/base/logging.h"
-// gen_amalgamated expanded: #include "perfetto/ext/base/string_utils.h"
+#endif  // PERFETTO_PROTOS_PROTOS_PERFETTO_IPC_RELAY_PORT_PROTO_H_
+// DO NOT EDIT. Autogenerated by Perfetto IPC
+// gen_amalgamated expanded: #include "protos/perfetto/ipc/relay_port.ipc.h"
+// gen_amalgamated expanded: #include "perfetto/ext/ipc/codegen_helpers.h"

-namespace perfetto {
+#include <memory>

-// static
-std::unique_ptr<SharedMemoryWindows> SharedMemoryWindows::Create(size_t size,
-                                                                 Flags flags) {
-  base::ScopedPlatformHandle shmem_handle;
-  std::random_device rnd_dev;
-  uint64_t rnd_key = (static_cast<uint64_t>(rnd_dev()) << 32) | rnd_dev();
-  std::string key = "perfetto_shm_" + base::Uint64ToHexStringNoPrefix(rnd_key);
+namespace perfetto {
+namespace protos {
+namespace gen {
+::perfetto::ipc::ServiceDescriptor* RelayPort::NewDescriptor() {
+  auto* desc = new ::perfetto::ipc::ServiceDescriptor();
+  desc->service_name = "RelayPort";

-  SECURITY_ATTRIBUTES security_attributes = {};
-  security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
-  if (flags & Flags::kInheritableHandles)
-    security_attributes.bInheritHandle = TRUE;
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "InitRelay",
+     &_IPC_Decoder<InitRelayRequest>,
+     &_IPC_Decoder<InitRelayResponse>,
+     &_IPC_Invoker<RelayPort, InitRelayRequest, InitRelayResponse, &RelayPort::InitRelay>});

-  shmem_handle.reset(CreateFileMappingA(
-      INVALID_HANDLE_VALUE,  // Use paging file.
-      &security_attributes, PAGE_READWRITE,
-      static_cast<DWORD>(size >> 32),  // maximum object size (high-order DWORD)
-      static_cast<DWORD>(size),        // maximum object size (low-order DWORD)
-      key.c_str()));
+  desc->methods.emplace_back(::perfetto::ipc::ServiceDescriptor::Method{
+     "SyncClock",
+     &_IPC_Decoder<SyncClockRequest>,
+     &_IPC_Decoder<SyncClockResponse>,
+     &_IPC_Invoker<RelayPort, SyncClockRequest, SyncClockResponse, &RelayPort::SyncClock>});
+  desc->methods.shrink_to_fit();
+  return desc;
+}

-  if (!shmem_handle) {
-    PERFETTO_PLOG("CreateFileMapping() call failed");
-    return nullptr;
-  }
-  void* start =
-      MapViewOfFile(*shmem_handle, FILE_MAP_ALL_ACCESS, /*offsetHigh=*/0,
-                    /*offsetLow=*/0, size);
-  if (!start) {
-    PERFETTO_PLOG("MapViewOfFile() failed");
-    return nullptr;
-  }

-  return std::unique_ptr<SharedMemoryWindows>(new SharedMemoryWindows(
-      start, size, std::move(key), std::move(shmem_handle)));
+const ::perfetto::ipc::ServiceDescriptor& RelayPort::GetDescriptorStatic() {
+  static auto* instance = NewDescriptor();
+  return *instance;
 }

-// static
-std::unique_ptr<SharedMemoryWindows> SharedMemoryWindows::Attach(
-    const std::string& key) {
-  base::ScopedPlatformHandle shmem_handle;
-  shmem_handle.reset(
-      OpenFileMappingA(FILE_MAP_ALL_ACCESS, /*inherit=*/false, key.c_str()));
-  if (!shmem_handle) {
-    PERFETTO_PLOG("Failed to OpenFileMapping()");
-    return nullptr;
-  }
-
-  void* start =
-      MapViewOfFile(*shmem_handle, FILE_MAP_ALL_ACCESS, /*offsetHigh=*/0,
-                    /*offsetLow=*/0, /*dwNumberOfBytesToMap=*/0);
-  if (!start) {
-    PERFETTO_PLOG("MapViewOfFile() failed");
-    return nullptr;
-  }
+// Host-side definitions.
+RelayPort::~RelayPort() = default;

-  MEMORY_BASIC_INFORMATION info{};
-  if (!VirtualQuery(start, &info, sizeof(info))) {
-    PERFETTO_PLOG("VirtualQuery() failed");
-    return nullptr;
-  }
-  size_t size = info.RegionSize;
-  return std::unique_ptr<SharedMemoryWindows>(
-      new SharedMemoryWindows(start, size, key, std::move(shmem_handle)));
+const ::perfetto::ipc::ServiceDescriptor& RelayPort::GetDescriptor() {
+  return GetDescriptorStatic();
 }

-// static
-std::unique_ptr<SharedMemoryWindows> SharedMemoryWindows::AttachToHandleWithKey(
-    base::ScopedPlatformHandle shmem_handle,
-    const std::string& key) {
-  void* start =
-      MapViewOfFile(*shmem_handle, FILE_MAP_ALL_ACCESS, /*offsetHigh=*/0,
-                    /*offsetLow=*/0, /*dwNumberOfBytesToMap=*/0);
-  if (!start) {
-    PERFETTO_PLOG("MapViewOfFile() failed");
-    return nullptr;
-  }
+// Client-side definitions.
+RelayPortProxy::RelayPortProxy(::perfetto::ipc::ServiceProxy::EventListener* event_listener)
+    : ::perfetto::ipc::ServiceProxy(event_listener) {}

-  MEMORY_BASIC_INFORMATION info{};
-  if (!VirtualQuery(start, &info, sizeof(info))) {
-    PERFETTO_PLOG("VirtualQuery() failed");
-    return nullptr;
-  }
-  size_t size = info.RegionSize;
+RelayPortProxy::~RelayPortProxy() = default;

-  return std::unique_ptr<SharedMemoryWindows>(
-      new SharedMemoryWindows(start, size, key, std::move(shmem_handle)));
+const ::perfetto::ipc::ServiceDescriptor& RelayPortProxy::GetDescriptor() {
+  return RelayPort::GetDescriptorStatic();
 }

-SharedMemoryWindows::SharedMemoryWindows(void* start,
-                                         size_t size,
-                                         std::string key,
-                                         base::ScopedPlatformHandle handle)
-    : start_(start),
-      size_(size),
-      key_(std::move(key)),
-      handle_(std::move(handle)) {}
-
-SharedMemoryWindows::~SharedMemoryWindows() {
-  if (start_)
-    UnmapViewOfFile(start_);
+void RelayPortProxy::InitRelay(const InitRelayRequest& request, DeferredInitRelayResponse reply, int fd) {
+  BeginInvoke("InitRelay", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
 }

-SharedMemoryWindows::Factory::~Factory() = default;
-
-std::unique_ptr<SharedMemory> SharedMemoryWindows::Factory::CreateSharedMemory(
-    size_t size) {
-  return SharedMemoryWindows::Create(size);
+void RelayPortProxy::SyncClock(const SyncClockRequest& request, DeferredSyncClockResponse reply, int fd) {
+  BeginInvoke("SyncClock", request, ::perfetto::ipc::DeferredBase(std::move(reply)),
+              fd);
 }
-
 }  // namespace perfetto
-
-#endif  // !OS_WIN
+}  // namespace protos
+}  // namespace gen
 // gen_amalgamated begin source: src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
 // gen_amalgamated begin header: src/tracing/ipc/consumer/consumer_ipc_client_impl.h
 // gen_amalgamated begin header: include/perfetto/ext/tracing/ipc/consumer_ipc_client.h
@@ -83711,6 +92304,8 @@ class PERFETTO_EXPORT_COMPONENT ProducerIPCClient {

 #include <stdint.h>

+#include <memory>
+#include <optional>
 #include <set>
 #include <vector>

@@ -83722,6 +92317,7 @@ class PERFETTO_EXPORT_COMPONENT ProducerIPCClient {
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/tracing_service.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/ipc/producer_ipc_client.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/producer_ring_buffer_arbiter.h"

 // gen_amalgamated expanded: #include "protos/perfetto/ipc/producer_port.ipc.h"

@@ -83731,6 +92327,10 @@ namespace base {
 class TaskRunner;
 }  // namespace base

+namespace test {
+class ProducerIPCClientTestPeer;
+}  // namespace test
+
 class Producer;
 class SharedMemoryArbiter;

@@ -83740,6 +92340,10 @@ class SharedMemoryArbiter;
 // actual IPC transport.
 // If create_socket_async is set, it will be called to create and connect to a
 // socket to the service. If unset, the producer will create and connect itself.
+//
+// TODO(sashwinbalaji): Check all paths through Disconnect(), OnDisconnect(),
+// and ScheduleDisconnect() again. Check for unnecessary steps, incorrect
+// cleanup or callback order, and object lifetime errors.
 class ProducerIPCClientImpl : public TracingService::ProducerEndpoint,
                               public ipc::ServiceProxy::EventListener {
  public:
@@ -83769,10 +92373,18 @@ class ProducerIPCClientImpl : public TracingService::ProducerEndpoint,
   void NotifyDataSourceStopped(DataSourceInstanceID) override;
   void ActivateTriggers(const std::vector<std::string>&) override;
   void Sync(std::function<void()> callback) override;
+  void InitializeV2RingBuffer(const DataSourceConfig&) override;
+  void AttachV2RingBuffer(const std::shared_ptr<SharedMemory>&,
+                          uint32_t chunk_size_bytes,
+                          std::function<void(bool)> callback) override;
+  void DrainV2RingBuffer() override;

   std::unique_ptr<TraceWriter> CreateTraceWriter(
       BufferID target_buffer,
       BufferExhaustedPolicy) override;
+  std::unique_ptr<TraceWriter> CreateTraceWriterV2(
+      BufferID,
+      BufferExhaustedPolicy) override;
   SharedMemoryArbiter* MaybeSharedMemoryArbiter() override;
   bool IsShmemProvidedByProducer() const override;
   void NotifyFlushComplete(FlushRequestID) override;
@@ -83788,15 +92400,22 @@ class ProducerIPCClientImpl : public TracingService::ProducerEndpoint,
   ipc::Client* GetClientForTesting() { return ipc_channel_.get(); }

  private:
+  // Defined in tracing_integration_test.cc.
+  friend class test::ProducerIPCClientTestPeer;
+
   // Drops the provider connection if a protocol error was detected while
   // processing an IPC command.
   void ScheduleDisconnect();

   // Invoked soon after having established the connection with the service.
-  void OnConnectionInitialized(bool connection_succeeded,
-                               bool using_shmem_provided_by_producer,
-                               bool direct_smb_patching_supported,
-                               bool use_shmem_emulation);
+  // |offered_versions| is the bitmask sent in the request.
+  void OnConnectionInitialized(
+      uint32_t offered_versions,
+      ipc::AsyncResult<protos::gen::InitializeConnectionResponse> response);
+
+  bool HasNegotiatedV2Abi() const {
+    return !!(protocol_abi_versions_ & kProtocolAbiV2);
+  }

   // Invoked when the remote Service sends an IPC to tell us to do something
   // (e.g. start/stop a data source).
@@ -83824,6 +92443,8 @@ class ProducerIPCClientImpl : public TracingService::ProducerEndpoint,
   std::map<WriterID, BufferID> writers_for_scraping_;

   std::unique_ptr<SharedMemory> shared_memory_;
+  // Bitmask of the versions agreed in InitializeConnection, zero until then.
+  uint32_t protocol_abi_versions_ = 0;
   std::unique_ptr<SharedMemoryArbiter> shared_memory_arbiter_;
   size_t shared_buffer_page_size_kb_ = 0;
   std::set<DataSourceInstanceID> data_sources_setup_;
@@ -83836,6 +92457,23 @@ class ProducerIPCClientImpl : public TracingService::ProducerEndpoint,
   bool direct_smb_patching_supported_ = false;
   bool use_shmem_emulation_ = false;
   std::vector<std::function<void()>> pending_sync_reqs_;
+  // Owns the ring buffer and creates v2 writers.
+  //
+  // States:
+  // - nullopt: initialization has not been attempted.
+  // - nullptr: sizing or allocation failed.
+  //   Later InitializeV2RingBuffer() calls on the same connection do not retry
+  //   initialization, to avoid changing the pointer while v2 writer threads
+  //   read it without a lock.
+  // - Non-null pointer: the arbiter exists and tracks service attachment.
+  //
+  // Set by InitializeV2RingBuffer() when an instance selects v2, before the
+  // first v2 writer.
+  // Writer threads then read it without a lock.
+  // OnDisconnect() does not reset this member because v2 writer threads can
+  // still read it or use the arbiter.
+  std::optional<std::unique_ptr<tracing_v2::ProducerRingBufferArbiter>>
+      v2_ring_buffer_arbiter_;
   base::WeakPtrFactory<ProducerIPCClientImpl> weak_factory_{this};
   PERFETTO_THREAD_CHECKER(thread_checker_)
 };
@@ -83865,8 +92503,11 @@ class ProducerIPCClientImpl : public TracingService::ProducerEndpoint,

 #include <string.h>

+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
 // gen_amalgamated expanded: #include "perfetto/base/task_runner.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_macros.h"
+// gen_amalgamated expanded: #include "perfetto/ext/base/status_or.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/unix_socket.h"
 // gen_amalgamated expanded: #include "perfetto/ext/base/version.h"
 // gen_amalgamated expanded: #include "perfetto/ext/ipc/client.h"
@@ -83875,10 +92516,13 @@ class ProducerIPCClientImpl : public TracingService::ProducerEndpoint,
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory_abi.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/shared_memory_arbiter.h"
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/trace_writer.h"
+// gen_amalgamated expanded: #include "perfetto/ext/tracing/core/tracing_service.h"
 // gen_amalgamated expanded: #include "perfetto/tracing/core/data_source_config.h"
 // gen_amalgamated expanded: #include "perfetto/tracing/core/data_source_descriptor.h"
 // gen_amalgamated expanded: #include "perfetto/tracing/core/trace_config.h"
 // gen_amalgamated expanded: #include "src/tracing/core/in_process_shared_memory.h"
+// gen_amalgamated expanded: #include "src/tracing/core/null_trace_writer.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_abi.h"

 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 // gen_amalgamated expanded: #include "src/tracing/ipc/shared_memory_windows.h"
@@ -84000,14 +92644,19 @@ ProducerIPCClientImpl::~ProducerIPCClientImpl() {

 void ProducerIPCClientImpl::Disconnect() {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  if (!producer_port_)
+  // Disconnect() is complete when both the port and channel are gone.
+  // ScheduleDisconnect() drops only the port and leaves the channel open.
+  if (!producer_port_ && !ipc_channel_)
     return;
-  // Reset the producer port so that no further IPCs are received and IPC
-  // callbacks are no longer executed. Also reset the IPC channel so that the
-  // service is notified of the disconnection.
+
+  // Clear |connected_| so callbacks invoked during port destruction see a
+  // disconnected endpoint.
+  connected_ = false;
+  // Reset |producer_port_| to stop further IPC replies, then close
+  // |ipc_channel_| so the service sees the disconnection.
   producer_port_.reset();
   ipc_channel_.reset();
-  // Perform disconnect synchronously.
+  // Perform disconnect synchronously. This may delete |this|.
   OnDisconnect();
 }

@@ -84016,19 +92665,22 @@ void ProducerIPCClientImpl::OnConnect() {
   PERFETTO_DCHECK_THREAD(thread_checker_);
   connected_ = true;

-  // The IPC layer guarantees that any outstanding callback will be dropped on
-  // the floor if producer_port_ is destroyed between the request and the reply.
-  // Binding |this| is hence safe.
+  uint32_t offered_versions = kProtocolAbiV1;
+  offered_versions |= tracing_v2::IpcSupportsTracingV2() ? kProtocolAbiV2 : 0u;
+  protos::gen::InitializeConnectionRequest req;
+  req.set_supported_protocol_abi_versions(offered_versions);
+
+  // When this client is destroyed, the port calls pending callbacks with a
+  // failure result. By then, members used by OnConnectionInitialized() have
+  // already been destroyed. Use a weak pointer to skip the handler in that
+  // case.
   ipc::Deferred<protos::gen::InitializeConnectionResponse> on_init;
   on_init.Bind(
-      [this](ipc::AsyncResult<protos::gen::InitializeConnectionResponse> resp) {
-        OnConnectionInitialized(
-            resp.success(),
-            resp.success() ? resp->using_shmem_provided_by_producer() : false,
-            resp.success() ? resp->direct_smb_patching_supported() : false,
-            resp.success() ? resp->use_shmem_emulation() : false);
+      [weak_this = weak_factory_.GetWeakPtr(), offered_versions](
+          ipc::AsyncResult<protos::gen::InitializeConnectionResponse> resp) {
+        if (weak_this)
+          weak_this->OnConnectionInitialized(offered_versions, std::move(resp));
       });
-  protos::gen::InitializeConnectionRequest req;
   req.set_producer_name(name_);
   req.set_shared_memory_size_hint_bytes(
       static_cast<uint32_t>(shared_memory_size_hint_bytes_));
@@ -84085,6 +92737,11 @@ void ProducerIPCClientImpl::OnDisconnect() {
   PERFETTO_DLOG("Tracing service connection failure");
   connected_ = false;
   data_sources_setup_.clear();
+  auto* arbiter = v2_ring_buffer_arbiter_.has_value()
+                      ? v2_ring_buffer_arbiter_->get()
+                      : nullptr;
+  if (arbiter)
+    arbiter->Disconnect();
   producer_->OnDisconnect();  // Note: may delete |this|.
 }

@@ -84092,8 +92749,10 @@ void ProducerIPCClientImpl::ScheduleDisconnect() {
   // |ipc_channel| doesn't allow disconnection in the middle of handling
   // an IPC call, so the connection drop must take place over two phases.

-  // First, synchronously drop the |producer_port_| so that no more IPC
-  // messages are handled.
+  // First, clear |connected_| so pending Sync() callbacks see a disconnected
+  // endpoint when the port is destroyed. Then synchronously drop
+  // |producer_port_| so that no more IPC messages are handled.
+  connected_ = false;
   producer_port_.reset();

   // Then schedule an async task for performing the remainder of the
@@ -84107,22 +92766,44 @@ void ProducerIPCClientImpl::ScheduleDisconnect() {
 }

 void ProducerIPCClientImpl::OnConnectionInitialized(
-    bool connection_succeeded,
-    bool using_shmem_provided_by_producer,
-    bool direct_smb_patching_supported,
-    bool use_shmem_emulation) {
+    uint32_t offered_versions,
+    ipc::AsyncResult<protos::gen::InitializeConnectionResponse> response) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  // If connection_succeeded == false, the OnDisconnect() call will follow next
-  // and there we'll notify the |producer_|. TODO: add a test for this.
-  if (!connection_succeeded)
+  // The IPC proxy accesses this callback after it returns, so keep the port
+  // alive until the next task.
+  auto reject_connection = [this] {
+    task_runner_->PostTask([weak_this = weak_factory_.GetWeakPtr()] {
+      if (weak_this && weak_this->connected_)
+        weak_this->Disconnect();
+    });
+  };
+
+  if (!response.success()) {
+    reject_connection();
+    return;
+  }
+
+  // An old service sends no versions. This means v1 only.
+  uint32_t versions = response->protocol_abi_versions();
+  if (!versions)
+    versions = kProtocolAbiV1;
+
+  // The service returns only versions that the producer offered. Log both
+  // masks if it does not.
+  if (versions & ~offered_versions) {
+    PERFETTO_ELOG("Unexpected protocol versions (advertised: %x, service: %x)",
+                  offered_versions, versions);
+    reject_connection();
     return;
-  is_shmem_provided_by_producer_ = using_shmem_provided_by_producer;
-  direct_smb_patching_supported_ = direct_smb_patching_supported;
+  }
+  protocol_abi_versions_ = versions;
+  is_shmem_provided_by_producer_ = response->using_shmem_provided_by_producer();
+  direct_smb_patching_supported_ = response->direct_smb_patching_supported();
   // The tracing service may reject using shared memory and tell the client to
   // commit data over the socket. This can happen when the client connects to
   // the service via a relay service:
   // client <-Unix socket-> relay service <- vsock -> tracing service.
-  use_shmem_emulation_ = use_shmem_emulation;
+  use_shmem_emulation_ = response->use_shmem_emulation();
   producer_->OnConnect();

   // Bail out if the service failed to adopt our producer-allocated SMB.
@@ -84179,27 +92860,17 @@ void ProducerIPCClientImpl::OnServiceRequest(
     // FD, which is provided to this code via a blocking callback.
     PERFETTO_CHECK(receive_shmem_fd_cb_fuchsia_);

-    base::ScopedFile shmem_fd(receive_shmem_fd_cb_fuchsia_());
-    if (!shmem_fd) {
-      // Failure to get a shared memory buffer is a protocol violation and
-      // therefore we should drop the Protocol connection.
-      PERFETTO_ELOG("Could not get shared memory FD from embedder.");
-      ScheduleDisconnect();
-      return;
-    }
-
-    ipc_shared_memory =
-        PosixSharedMemory::AttachToFd(std::move(shmem_fd),
-                                      /*require_seals_if_supported=*/false);
+    ipc_shared_memory = PosixSharedMemory::AttachToFd(
+        base::ScopedFile(receive_shmem_fd_cb_fuchsia_()),
+        /*require_seals_if_supported=*/false, TracingService::kMaxShmSize);
 #else
-    base::ScopedFile shmem_fd = ipc_channel_->TakeReceivedFD();
-    if (shmem_fd) {
-      // TODO(primiano): handle mmap failure in case of OOM.
-      ipc_shared_memory =
-          PosixSharedMemory::AttachToFd(std::move(shmem_fd),
-                                        /*require_seals_if_supported=*/false);
-    }
+    // The FD can be invalid, for example with a producer-provided SMB.
+    // |ipc_shared_memory| is then null. The checks below handle it.
+    ipc_shared_memory = PosixSharedMemory::AttachToFd(
+        ipc_channel_->TakeReceivedFD(),
+        /*require_seals_if_supported=*/false, TracingService::kMaxShmSize);
 #endif
+
     if (use_shmem_emulation_) {
       PERFETTO_CHECK(!ipc_shared_memory);
       // Need to create an emulated shmem buffer when the transport doesn't
@@ -84207,6 +92878,16 @@ void ProducerIPCClientImpl::OnServiceRequest(
       ipc_shared_memory = InProcessSharedMemory::Create(
           /*size=*/InProcessSharedMemory::kShmemEmulationSize);
     }
+
+    // No SMB from the service (none sent, or it failed to map), and none
+    // provided by the producer. The producer cannot trace without one.
+    // Drop the connection: the producer gets OnDisconnect() and can reconnect.
+    if (!ipc_shared_memory && !is_shmem_provided_by_producer_) {
+      PERFETTO_ELOG("No usable shared memory from the service, disconnecting.");
+      ScheduleDisconnect();
+      return;
+    }
+
     if (ipc_shared_memory) {
       auto shmem_mode = use_shmem_emulation_
                             ? SharedMemoryABI::ShmemMode::kShmemEmulation
@@ -84268,6 +92949,7 @@ void ProducerIPCClientImpl::RegisterDataSource(
   if (!connected_) {
     PERFETTO_DLOG(
         "Cannot RegisterDataSource(), not connected to tracing service");
+    return;
   }
   protos::gen::RegisterDataSourceRequest req;
   *req.mutable_data_source_descriptor() = descriptor;
@@ -84286,6 +92968,7 @@ void ProducerIPCClientImpl::UpdateDataSource(
   if (!connected_) {
     PERFETTO_DLOG(
         "Cannot UpdateDataSource(), not connected to tracing service");
+    return;
   }
   protos::gen::UpdateDataSourceRequest req;
   *req.mutable_data_source_descriptor() = descriptor;
@@ -84371,6 +93054,87 @@ void ProducerIPCClientImpl::CommitData(const CommitDataRequest& req,
   producer_port_->CommitData(req, std::move(async_response));
 }

+void ProducerIPCClientImpl::InitializeV2RingBuffer(
+    const DataSourceConfig& config) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  // V2 IPC requires a sealed memfd.
+  // TODO(sashwinbalaji): Decide how tracing v2 shares the ring buffer over IPC
+  // on Windows and macOS, which have no memfd.
+#if PERFETTO_MEMFD_ENABLED()
+  if (v2_ring_buffer_arbiter_.has_value() || !connected_ ||
+      !HasNegotiatedV2Abi()) {
+    return;
+  }
+
+  auto arbiter_or = tracing_v2::ProducerRingBufferArbiter::Create(
+      task_runner_, this, config, shared_memory_size_hint_bytes_,
+      [](size_t size) -> base::StatusOr<std::shared_ptr<SharedMemory>> {
+        ASSIGN_OR_RETURN(auto memory, PosixSharedMemory::Create(size));
+        return std::shared_ptr<SharedMemory>(std::move(memory));
+      });
+  if (!arbiter_or.ok()) {
+    PERFETTO_ELOG("tracing v2: cannot create ring buffer: %s",
+                  arbiter_or.status().c_message());
+    v2_ring_buffer_arbiter_ = nullptr;
+    return;
+  }
+  v2_ring_buffer_arbiter_ = std::move(*arbiter_or);
+
+  auto* arbiter = v2_ring_buffer_arbiter_->get();
+  AttachV2RingBuffer(
+      arbiter->memory(), arbiter->ring_buffer()->chunk_size(),
+      [weak_arbiter = arbiter->GetWeakPtr()](bool reader_attached) {
+        if (weak_arbiter)
+          weak_arbiter->OnReaderAttachReply(reader_attached);
+      });
+#else
+  base::ignore_result(config);
+#endif  // PERFETTO_MEMFD_ENABLED()
+}
+
+void ProducerIPCClientImpl::AttachV2RingBuffer(
+    const std::shared_ptr<SharedMemory>& memory,
+    uint32_t chunk_size_bytes,
+    std::function<void(bool)> callback) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+
+  // OnConnect() offers v2 only if tracing_v2::IpcSupportsTracingV2().
+  if (!connected_ || !HasNegotiatedV2Abi() || !memory) {
+    callback(false);
+    return;
+  }
+
+#if PERFETTO_TRACING_V2_IPC()
+  protos::gen::AttachV2RingBufferRequest req;
+  req.set_chunk_size_bytes(chunk_size_bytes);
+  ipc::Deferred<protos::gen::AttachV2RingBufferResponse> reply;
+  reply.Bind(
+      [callback = std::move(callback)](
+          ipc::AsyncResult<protos::gen::AttachV2RingBufferResponse> result) {
+        callback(result.success());
+      });
+  // The IPC layer sends the descriptor before this call returns. The service
+  // maps it on its side.
+  //
+  // The producer that called this method owns |memory| and keeps it mapped for
+  // its writers.
+  const int fd = static_cast<PosixSharedMemory*>(memory.get())->fd();
+  producer_port_->AttachV2RingBuffer(req, std::move(reply), fd);
+#else
+  base::ignore_result(chunk_size_bytes);
+  callback(false);
+#endif
+}
+
+void ProducerIPCClientImpl::DrainV2RingBuffer() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  if (!connected_ || !HasNegotiatedV2Abi())
+    return;
+  producer_port_->DrainV2RingBuffer(
+      protos::gen::DrainV2RingBufferRequest(),
+      ipc::Deferred<protos::gen::DrainV2RingBufferResponse>());
+}
+
 void ProducerIPCClientImpl::NotifyDataSourceStarted(DataSourceInstanceID id) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
   if (!connected_) {
@@ -84435,10 +93199,29 @@ std::unique_ptr<TraceWriter> ProducerIPCClientImpl::CreateTraceWriter(
     BufferExhaustedPolicy buffer_exhausted_policy) {
   // This method can be called by different threads. |shared_memory_arbiter_| is
   // thread-safe but be aware of accessing any other state in this function.
+  // |protocol_abi_versions_| does not change after setup.
+  if (!(protocol_abi_versions_ & kProtocolAbiV1)) {
+    PERFETTO_ELOG(
+        "Cannot create a v1 trace writer: v1 is not in the common protocol "
+        "mask (%x)",
+        protocol_abi_versions_);
+    return std::make_unique<NullTraceWriter>();
+  }
   return shared_memory_arbiter_->CreateTraceWriter(target_buffer,
                                                    buffer_exhausted_policy);
 }

+std::unique_ptr<TraceWriter> ProducerIPCClientImpl::CreateTraceWriterV2(
+    BufferID target_buffer,
+    BufferExhaustedPolicy policy) {
+  auto* arbiter = v2_ring_buffer_arbiter_.has_value()
+                      ? v2_ring_buffer_arbiter_->get()
+                      : nullptr;
+  if (!arbiter)
+    return std::make_unique<NullTraceWriter>();
+  return arbiter->CreateTraceWriter(target_buffer, policy);
+}
+
 SharedMemoryArbiter* ProducerIPCClientImpl::MaybeSharedMemoryArbiter() {
   return shared_memory_arbiter_.get();
 }
@@ -85253,6 +94036,10 @@ class ProducerIPCService : public protos::gen::ProducerPort {
   void GetAsyncCommand(const protos::gen::GetAsyncCommandRequest&,
                        DeferredGetAsyncCommandResponse) override;
   void Sync(const protos::gen::SyncRequest&, DeferredSyncResponse) override;
+  void AttachV2RingBuffer(const protos::gen::AttachV2RingBufferRequest&,
+                          DeferredAttachV2RingBufferResponse) override;
+  void DrainV2RingBuffer(const protos::gen::DrainV2RingBufferRequest&,
+                         DeferredDrainV2RingBufferResponse) override;
   void OnClientDisconnected() override;

  private:
@@ -85351,6 +94138,7 @@ class ProducerIPCService : public protos::gen::ProducerPort {
 // gen_amalgamated expanded: #include "perfetto/ext/tracing/core/tracing_service.h"
 // gen_amalgamated expanded: #include "perfetto/tracing/core/data_source_config.h"
 // gen_amalgamated expanded: #include "perfetto/tracing/core/data_source_descriptor.h"
+// gen_amalgamated expanded: #include "src/tracing/v2/shared_ring_buffer_abi.h"

 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 // gen_amalgamated expanded: #include "src/tracing/ipc/shared_memory_windows.h"
@@ -85364,6 +94152,32 @@ class ProducerIPCService : public protos::gen::ProducerPort {

 namespace perfetto {

+namespace {
+
+// The proto and C++ enums use the same bits.
+static_assert(static_cast<uint32_t>(protos::gen::PROTOCOL_ABI_VERSION_V1) ==
+              kProtocolAbiV1);
+static_assert(static_cast<uint32_t>(protos::gen::PROTOCOL_ABI_VERSION_V2) ==
+              kProtocolAbiV2);
+
+// Returns the bitmask of common protocol versions, or 0 if none match.
+uint32_t GetCommonProtocolAbiVersions(uint32_t producer_versions,
+                                      bool use_shmem_emulation) {
+  // Old producers send no versions and support only v1.
+  if (!producer_versions)
+    return kProtocolAbiV1;
+
+  // v2 needs memfd support and cannot use shared memory emulation.
+  uint32_t supported = kProtocolAbiV1;
+  if (tracing_v2::IpcSupportsTracingV2() && !use_shmem_emulation)
+    supported |= kProtocolAbiV2;
+
+  // Unknown bits from a newer producer drop out here.
+  return producer_versions & supported;
+}
+
+}  // namespace
+
 ProducerIPCService::ProducerIPCService(TracingService* core_service)
     : core_service_(core_service), weak_ptr_factory_(this) {}

@@ -85425,8 +94239,9 @@ void ProducerIPCService::InitializeConnection(
     base::ScopedFile shmem_fd = ipc::Service::TakeReceivedFD();

     if (shmem_fd) {
-      shmem = PosixSharedMemory::AttachToFd(
-          std::move(shmem_fd), /*require_seals_if_supported=*/true);
+      shmem = PosixSharedMemory::AttachToFd(std::move(shmem_fd),
+                                            /*require_seals_if_supported=*/true,
+                                            TracingService::kMaxShmSize);
       if (!shmem) {
         PERFETTO_ELOG(
             "Couldn't map producer-provided SMB, falling back to "
@@ -85443,13 +94258,23 @@ void ProducerIPCService::InitializeConnection(
   // Copy the data fields to be emitted to trace packets into ClientIdentity.
   ClientIdentity client_identity(client_info.uid(), client_info.pid(),
                                  client_info.machine_id());
+  const uint32_t protocol_abi_versions =
+      GetCommonProtocolAbiVersions(req.supported_protocol_abi_versions(),
+                                   ipc::Service::use_shmem_emulation());
+  if (!protocol_abi_versions) {
+    PERFETTO_ELOG(
+        "Producer \"%s\" supports no protocol version of this service",
+        req.producer_name().c_str());
+    return response.Reject();
+  }
+
   // ConnectProducer will call OnConnect() on the next task.
   producer->service_endpoint = core_service_->ConnectProducer(
       producer.get(), client_identity, req.producer_name(),
       req.shared_memory_size_hint_bytes(),
       /*in_process=*/false, smb_scraping_mode,
       req.shared_memory_page_size_hint_bytes(), std::move(shmem),
-      req.sdk_version(), client_info.machine_name());
+      req.sdk_version(), client_info.machine_name(), protocol_abi_versions);

   // Could happen if the service has too many producers connected.
   if (!producer->service_endpoint) {
@@ -85470,6 +94295,7 @@ void ProducerIPCService::InitializeConnection(
   async_res->set_using_shmem_provided_by_producer(using_producer_shmem);
   async_res->set_direct_smb_patching_supported(true);
   async_res->set_use_shmem_emulation(use_shmem_emulation);
+  async_res->set_protocol_abi_versions(protocol_abi_versions);
   response.Resolve(std::move(async_res));
 }

@@ -85712,6 +94538,52 @@ void ProducerIPCService::GetAsyncCommand(
     producer->SendSetupTracing();
 }

+void ProducerIPCService::AttachV2RingBuffer(
+    const protos::gen::AttachV2RingBufferRequest& req,
+    DeferredAttachV2RingBufferResponse resp) {
+  // Take the descriptor first. It is closed on every rejection path.
+  auto fd = ipc::Service::TakeReceivedFD();
+  auto* producer = GetProducerForCurrentRequest();
+  if (!producer) {
+    resp.Reject();
+    return;
+  }
+
+  std::shared_ptr<SharedMemory> memory;
+#if PERFETTO_TRACING_V2_IPC()
+  memory = PosixSharedMemory::AttachToFd(std::move(fd),
+                                         /*require_seals_if_supported=*/true,
+                                         TracingService::kMaxShmSize);
+#endif
+  if (!memory) {
+    PERFETTO_DLOG("AttachV2RingBuffer() rejected: could not map the memfd");
+    resp.Reject();
+    return;
+  }
+
+  // |resp| cannot be copied, but std::function requires a copyable callback.
+  // Capture a shared_ptr so callback copies refer to the same response.
+  auto pending =
+      std::make_shared<DeferredAttachV2RingBufferResponse>(std::move(resp));
+  producer->service_endpoint->AttachV2RingBuffer(
+      memory, req.chunk_size_bytes(), [pending](bool accepted) {
+        if (!accepted) {
+          pending->Reject();
+          return;
+        }
+        pending->Resolve(ipc::AsyncResult<
+                         protos::gen::AttachV2RingBufferResponse>::Create());
+      });
+}
+
+void ProducerIPCService::DrainV2RingBuffer(
+    const protos::gen::DrainV2RingBufferRequest&,
+    DeferredDrainV2RingBufferResponse) {
+  auto* producer = GetProducerForCurrentRequest();
+  if (producer)
+    producer->service_endpoint->DrainV2RingBuffer();
+}
+
 void ProducerIPCService::Sync(const protos::gen::SyncRequest&,
                               DeferredSyncResponse resp) {
   RemoteProducer* producer = GetProducerForCurrentRequest();
@@ -86499,7 +95371,7 @@ std::unique_ptr<ProducerEndpoint> SystemProducerTracingBackend::ConnectProducer(
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
     shm = SharedMemoryWindows::Create(shmem_size_hint);
 #else
-    shm = PosixSharedMemory::Create(shmem_size_hint);
+    shm = PosixSharedMemory::Create(shmem_size_hint).ValueOrDie();
 #endif
     arbiter = SharedMemoryArbiter::CreateUnboundInstance(
         shm.get(), shmem_page_size_hint, SharedMemoryABI::ShmemMode::kDefault);
diff --git a/deps/perfetto/sdk/perfetto.h b/deps/perfetto/sdk/perfetto.h
index 5bf6732f9b7..29ce162dd55 100644
--- a/deps/perfetto/sdk/perfetto.h
+++ b/deps/perfetto/sdk/perfetto.h
@@ -543,6 +543,17 @@ static const int kPerfettoBuildFlagsCount = sizeof(kPerfettoBuildFlags) / sizeof
 #define PERFETTO_NORETURN __declspec(noreturn)
 #endif

+// Promises that what a pointer reaches is reached through nothing else in its
+// scope, so writes through it cannot change what other pointers read. Not
+// `__restrict`: the macOS SDK defines that away to nothing in C++.
+#if defined(__GNUC__) || defined(__clang__)
+#define PERFETTO_RESTRICT __restrict__
+#elif defined(_MSC_VER)
+#define PERFETTO_RESTRICT __restrict
+#else
+#define PERFETTO_RESTRICT
+#endif
+
 #if defined(__GNUC__) || defined(__clang__)
 #define PERFETTO_DEBUG_FUNCTION_IDENTIFIER() __PRETTY_FUNCTION__
 #elif defined(_MSC_VER)
@@ -595,7 +606,7 @@ extern "C" void __msan_unpoison(void const volatile*, size_t);
 #endif  // __clang__

 #if defined(__GNUC__) || defined(__clang__)
-#define PERFETTO_IS_LITTLE_ENDIAN() __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define PERFETTO_IS_LITTLE_ENDIAN() (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
 #else
 // Assume all MSVC targets are little endian.
 #define PERFETTO_IS_LITTLE_ENDIAN() 1
@@ -1036,6 +1047,7 @@ inline void MaybeSerializeLastLogsForCrashReporting() {}
 #include <mach/mach_init.h>
 #include <mach/mach_port.h>
 #include <mach/mach_time.h>
+#include <mach/task.h>
 #include <mach/thread_act.h>
 #endif

@@ -1534,6 +1546,38 @@ static inline uint32_t PerfettoPbMakeTag(int32_t field_id,
          PERFETTO_STATIC_CAST(uint32_t, wire_type);
 }

+// Constants for tracing v2's append-only proto group format:
+//
+//   open nested field f:  varint((f << 3) | 3)
+//   close current nested: 0x04
+//   root:                 no wrapper and no close byte
+//
+// Unlike standard protobuf groups, the closing byte does not repeat the field
+// number. It encodes field zero (invalid in standard protobuf) with wire
+// type 4. At a field boundary, it closes the innermost message. The packet
+// boundary ends the root.
+//
+// The parent writes both the start tag and the closing byte of a nested
+// message. A root has no parent, so nothing writes a closing byte for it.
+//
+// The packet must be rewritten to length-delimited protobuf before standard
+// decoders can read it. See RFC 0014 for the design:
+// https://github.com/google/perfetto/discussions/4508.
+//
+// The wire types stay outside PerfettoPbWireType because the decoders do not
+// support groups. Keep in sync with kWireTypeStartGroup and kProtoGroupEndByte
+// in perfetto/protozero/proto_utils.h.
+enum {
+  PERFETTO_PB_WIRE_TYPE_START_GROUP = 3,
+  PERFETTO_PB_PROTO_GROUP_END_BYTE = 0x04,
+};
+
+// Creates the tag that opens a nested message in proto group mode.
+static inline uint32_t PerfettoPbMakeTagStartGroup(int32_t field_id) {
+  return ((PERFETTO_STATIC_CAST(uint32_t, field_id)) << 3) |
+         PERFETTO_STATIC_CAST(uint32_t, PERFETTO_PB_WIRE_TYPE_START_GROUP);
+}
+
 enum {
   // Maximum bytes size of a 64-bit integer encoded as a VarInt.
   PERFETTO_PB_VARINT_MAX_SIZE_64 = 10,
@@ -1695,6 +1739,7 @@ static inline uint32_t PerfettoPbFloatToFixed32(float value) {
 #include <cinttypes>
 #include <type_traits>

+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
 // gen_amalgamated expanded: #include "perfetto/public/compiler.h"
 // gen_amalgamated expanded: #include "perfetto/public/pb_utils.h"
@@ -1721,6 +1766,41 @@ enum class ProtoWireType : uint32_t {
   kFixed32 = 5,
 };

+// Wire types for proto group boundaries. These stay outside ProtoWireType
+// because protozero decoders do not support groups. See kProtoGroupEndByte.
+// Keep in sync with PERFETTO_PB_WIRE_TYPE_START_GROUP in public/pb_utils.h.
+constexpr uint32_t kWireTypeStartGroup = 3;
+constexpr uint32_t kWireTypeEndGroup = 4;
+
+// Closing byte for tracing v2's append-only proto group format:
+//
+//   open nested field f:  varint((f << 3) | 3)
+//   close current nested: 0x04
+//   root:                 no wrapper and no close byte
+//
+// Unlike standard protobuf groups, the closing byte does not repeat the field
+// number. It encodes field zero (invalid in standard protobuf) with wire
+// type 4. At a field boundary, it closes the innermost message. The packet
+// boundary ends the root.
+//
+// The parent writes both the start tag and the closing byte of a nested
+// message, as it writes the tag and the length field of a length-delimited
+// one. A root has no parent, so nothing writes a closing byte for it.
+//
+// The packet must be rewritten to length-delimited protobuf before standard
+// decoders can read it. See RFC 0014 for the design:
+// https://github.com/google/perfetto/discussions/4508.
+// Keep in sync with PERFETTO_PB_PROTO_GROUP_END_BYTE in pb_utils.h.
+constexpr uint8_t kProtoGroupEndByte = static_cast<uint8_t>(kWireTypeEndGroup);
+
+// Enforces the "Keep in sync" rules above.
+static_assert(kWireTypeStartGroup ==
+                  static_cast<uint32_t>(PERFETTO_PB_WIRE_TYPE_START_GROUP),
+              "C and C++ start group wire types differ");
+static_assert(kProtoGroupEndByte ==
+                  static_cast<uint8_t>(PERFETTO_PB_PROTO_GROUP_END_BYTE),
+              "C and C++ proto group closing bytes differ");
+
 // This is the type defined in the proto for each field. This information
 // is used to decide the translation strategy when writing the trace.
 enum class ProtoSchemaType {
@@ -1873,6 +1953,11 @@ constexpr uint32_t MakeTagLengthDelimited(uint32_t field_id) {
          static_cast<uint32_t>(ProtoWireType::kLengthDelimited);
 }

+// Opens a nested message in the proto group encoding described above.
+constexpr uint32_t MakeTagStartGroup(uint32_t field_id) {
+  return (field_id << kFieldTypeNumBits) | kWireTypeStartGroup;
+}
+
 // Proto types: sint64, sint32.
 template <typename T>
 inline typename std::make_unsigned<T>::type ZigZagEncode(T value) {
@@ -2587,6 +2672,8 @@ class InterceptorConfig;
 class ConsoleConfig;
 class ChromeConfig;
 class SystemInfoConfig;
+class DataSourceConfig_ExperimentalTracingV2Config;
+class DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption;
 class ProtoVmConfig;
 class PriorityBoostConfig;
 enum DataSourceConfig_SessionInitiator : int;
@@ -2618,6 +2705,7 @@ enum DataSourceConfig_BufferExhaustedPolicy : int {

 class PERFETTO_EXPORT_COMPONENT DataSourceConfig : public ::protozero::CppMessageObj {
  public:
+  using ExperimentalTracingV2Config = DataSourceConfig_ExperimentalTracingV2Config;
   using SessionInitiator = DataSourceConfig_SessionInitiator;
   static constexpr auto SESSION_INITIATOR_UNSPECIFIED = DataSourceConfig_SessionInitiator_SESSION_INITIATOR_UNSPECIFIED;
   static constexpr auto SESSION_INITIATOR_TRUSTED_SYSTEM = DataSourceConfig_SessionInitiator_SESSION_INITIATOR_TRUSTED_SYSTEM;
@@ -2643,6 +2731,8 @@ class PERFETTO_EXPORT_COMPONENT DataSourceConfig : public ::protozero::CppMessag
     kBufferExhaustedPolicyFieldNumber = 9,
     kPriorityBoostFieldNumber = 10,
     kProtovmConfigFieldNumber = 12,
+    kSupportsTracingV2FieldNumber = 147,
+    kExperimentalTracingV2FieldNumber = 148,
     kFtraceConfigFieldNumber = 100,
     kInodeFileConfigFieldNumber = 102,
     kProcessStatsConfigFieldNumber = 103,
@@ -2685,6 +2775,10 @@ class PERFETTO_EXPORT_COMPONENT DataSourceConfig : public ::protozero::CppMessag
     kAndroidAflagsConfigFieldNumber = 140,
     kJournaldConfigFieldNumber = 141,
     kDisplayVideoConfigFieldNumber = 142,
+    kChromiumStackSamplingProfilerFieldNumber = 143,
+    kChromiumSamplingHeapProfilerFieldNumber = 144,
+    kAndroidProcessStateConfigFieldNumber = 145,
+    kProcessSmapsConfigFieldNumber = 146,
     kQnxConfigFieldNumber = 150,
     kLegacyConfigFieldNumber = 1000,
     kForTestingFieldNumber = 1001,
@@ -2752,6 +2846,14 @@ class PERFETTO_EXPORT_COMPONENT DataSourceConfig : public ::protozero::CppMessag
   const ProtoVmConfig& protovm_config() const { return *protovm_config_; }
   ProtoVmConfig* mutable_protovm_config() { _has_field_.set(12); return protovm_config_.get(); }

+  bool has_supports_tracing_v2() const { return _has_field_[147]; }
+  bool supports_tracing_v2() const { return supports_tracing_v2_; }
+  void set_supports_tracing_v2(bool value) { supports_tracing_v2_ = value; _has_field_.set(147); }
+
+  bool has_experimental_tracing_v2() const { return _has_field_[148]; }
+  const DataSourceConfig_ExperimentalTracingV2Config& experimental_tracing_v2() const { return *experimental_tracing_v2_; }
+  DataSourceConfig_ExperimentalTracingV2Config* mutable_experimental_tracing_v2() { _has_field_.set(148); return experimental_tracing_v2_.get(); }
+
   const std::string& ftrace_config_raw() const { return ftrace_config_; }
   void set_ftrace_config_raw(const std::string& raw) { ftrace_config_ = raw; _has_field_.set(100); }

@@ -2881,6 +2983,18 @@ class PERFETTO_EXPORT_COMPONENT DataSourceConfig : public ::protozero::CppMessag
   const std::string& display_video_config_raw() const { return display_video_config_; }
   void set_display_video_config_raw(const std::string& raw) { display_video_config_ = raw; _has_field_.set(142); }

+  const std::string& chromium_stack_sampling_profiler_raw() const { return chromium_stack_sampling_profiler_; }
+  void set_chromium_stack_sampling_profiler_raw(const std::string& raw) { chromium_stack_sampling_profiler_ = raw; _has_field_.set(143); }
+
+  const std::string& chromium_sampling_heap_profiler_raw() const { return chromium_sampling_heap_profiler_; }
+  void set_chromium_sampling_heap_profiler_raw(const std::string& raw) { chromium_sampling_heap_profiler_ = raw; _has_field_.set(144); }
+
+  const std::string& android_process_state_config_raw() const { return android_process_state_config_; }
+  void set_android_process_state_config_raw(const std::string& raw) { android_process_state_config_ = raw; _has_field_.set(145); }
+
+  const std::string& process_smaps_config_raw() const { return process_smaps_config_; }
+  void set_process_smaps_config_raw(const std::string& raw) { process_smaps_config_ = raw; _has_field_.set(146); }
+
   const std::string& qnx_config_raw() const { return qnx_config_; }
   void set_qnx_config_raw(const std::string& raw) { qnx_config_ = raw; _has_field_.set(150); }

@@ -2905,6 +3019,8 @@ class PERFETTO_EXPORT_COMPONENT DataSourceConfig : public ::protozero::CppMessag
   DataSourceConfig_BufferExhaustedPolicy buffer_exhausted_policy_{};
   ::protozero::CopyablePtr<PriorityBoostConfig> priority_boost_;
   ::protozero::CopyablePtr<ProtoVmConfig> protovm_config_;
+  bool supports_tracing_v2_{};
+  ::protozero::CopyablePtr<DataSourceConfig_ExperimentalTracingV2Config> experimental_tracing_v2_;
   std::string ftrace_config_;  // [lazy=true]
   std::string inode_file_config_;  // [lazy=true]
   std::string process_stats_config_;  // [lazy=true]
@@ -2947,6 +3063,10 @@ class PERFETTO_EXPORT_COMPONENT DataSourceConfig : public ::protozero::CppMessag
   std::string android_aflags_config_;  // [lazy=true]
   std::string journald_config_;  // [lazy=true]
   std::string display_video_config_;  // [lazy=true]
+  std::string chromium_stack_sampling_profiler_;  // [lazy=true]
+  std::string chromium_sampling_heap_profiler_;  // [lazy=true]
+  std::string android_process_state_config_;  // [lazy=true]
+  std::string process_smaps_config_;  // [lazy=true]
   std::string qnx_config_;  // [lazy=true]
   std::string legacy_config_{};
   ::protozero::CopyablePtr<TestConfig> for_testing_;
@@ -2958,6 +3078,97 @@ class PERFETTO_EXPORT_COMPONENT DataSourceConfig : public ::protozero::CppMessag
   std::bitset<1002> _has_field_{};
 };

+
+class PERFETTO_EXPORT_COMPONENT DataSourceConfig_ExperimentalTracingV2Config : public ::protozero::CppMessageObj {
+ public:
+  using ChunkSizeOption = DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption;
+  enum FieldNumbers {
+    kUseV2ProbabilityPercentFieldNumber = 1,
+    kChunkSizeOptionsFieldNumber = 2,
+    kDrainOccupancyPercentFieldNumber = 3,
+  };
+
+  DataSourceConfig_ExperimentalTracingV2Config();
+  ~DataSourceConfig_ExperimentalTracingV2Config() override;
+  DataSourceConfig_ExperimentalTracingV2Config(DataSourceConfig_ExperimentalTracingV2Config&&) noexcept;
+  DataSourceConfig_ExperimentalTracingV2Config& operator=(DataSourceConfig_ExperimentalTracingV2Config&&);
+  DataSourceConfig_ExperimentalTracingV2Config(const DataSourceConfig_ExperimentalTracingV2Config&);
+  DataSourceConfig_ExperimentalTracingV2Config& operator=(const DataSourceConfig_ExperimentalTracingV2Config&);
+  bool operator==(const DataSourceConfig_ExperimentalTracingV2Config&) const;
+  bool operator!=(const DataSourceConfig_ExperimentalTracingV2Config& other) const { return !(*this == other); }
+
+  bool ParseFromArray(const void*, size_t) override;
+  std::string SerializeAsString() const override;
+  std::vector<uint8_t> SerializeAsArray() const override;
+  void Serialize(::protozero::Message*) const;
+
+  bool has_use_v2_probability_percent() const { return _has_field_[1]; }
+  uint32_t use_v2_probability_percent() const { return use_v2_probability_percent_; }
+  void set_use_v2_probability_percent(uint32_t value) { use_v2_probability_percent_ = value; _has_field_.set(1); }
+
+  const std::vector<DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption>& chunk_size_options() const { return chunk_size_options_; }
+  std::vector<DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption>* mutable_chunk_size_options() { return &chunk_size_options_; }
+  int chunk_size_options_size() const;
+  void clear_chunk_size_options();
+  DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption* add_chunk_size_options();
+
+  bool has_drain_occupancy_percent() const { return _has_field_[3]; }
+  int32_t drain_occupancy_percent() const { return drain_occupancy_percent_; }
+  void set_drain_occupancy_percent(int32_t value) { drain_occupancy_percent_ = value; _has_field_.set(3); }
+
+ private:
+  uint32_t use_v2_probability_percent_{};
+  std::vector<DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption> chunk_size_options_;
+  int32_t drain_occupancy_percent_{};
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+
+  std::bitset<4> _has_field_{};
+};
+
+
+class PERFETTO_EXPORT_COMPONENT DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption : public ::protozero::CppMessageObj {
+ public:
+  enum FieldNumbers {
+    kSizeBytesFieldNumber = 1,
+    kWeightFieldNumber = 2,
+  };
+
+  DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption();
+  ~DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption() override;
+  DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption(DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption&&) noexcept;
+  DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption& operator=(DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption&&);
+  DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption(const DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption&);
+  DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption& operator=(const DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption&);
+  bool operator==(const DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption&) const;
+  bool operator!=(const DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption& other) const { return !(*this == other); }
+
+  bool ParseFromArray(const void*, size_t) override;
+  std::string SerializeAsString() const override;
+  std::vector<uint8_t> SerializeAsArray() const override;
+  void Serialize(::protozero::Message*) const;
+
+  bool has_size_bytes() const { return _has_field_[1]; }
+  uint32_t size_bytes() const { return size_bytes_; }
+  void set_size_bytes(uint32_t value) { size_bytes_ = value; _has_field_.set(1); }
+
+  bool has_weight() const { return _has_field_[2]; }
+  uint32_t weight() const { return weight_; }
+  void set_weight(uint32_t value) { weight_ = value; _has_field_.set(2); }
+
+ private:
+  uint32_t size_bytes_{};
+  uint32_t weight_{};
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+
+  std::bitset<3> _has_field_{};
+};
+
 }  // namespace perfetto
 }  // namespace protos
 }  // namespace gen
@@ -2994,6 +3205,256 @@ class PERFETTO_EXPORT_COMPONENT DataSourceConfig : public ::protozero::CppMessag
 // gen_amalgamated begin header: include/perfetto/tracing/trace_writer_base.h
 // gen_amalgamated begin header: include/perfetto/protozero/message_handle.h
 // gen_amalgamated begin header: include/perfetto/protozero/message.h
+// gen_amalgamated begin header: include/perfetto/base/endian.h
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_BASE_ENDIAN_H_
+#define INCLUDE_PERFETTO_BASE_ENDIAN_H_
+
+#include <stdint.h>
+#include <stdlib.h>  // For MSVC
+#include <string.h>
+
+// gen_amalgamated expanded: #include "perfetto/base/build_config.h"
+// gen_amalgamated expanded: #include "perfetto/base/compiler.h"
+
+namespace perfetto {
+namespace base {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_COMPILER_MSVC)
+inline uint16_t ByteSwap16(uint16_t x) {
+  return _byteswap_ushort(x);
+}
+inline uint32_t ByteSwap32(uint32_t x) {
+  return _byteswap_ulong(x);
+}
+inline uint64_t ByteSwap64(uint64_t x) {
+  return _byteswap_uint64(x);
+}
+#else
+inline uint16_t ByteSwap16(uint16_t x) {
+  return __builtin_bswap16(x);
+}
+inline uint32_t ByteSwap32(uint32_t x) {
+  return __builtin_bswap32(x);
+}
+inline uint64_t ByteSwap64(uint64_t x) {
+  return __builtin_bswap64(x);
+}
+#endif
+
+#if PERFETTO_IS_LITTLE_ENDIAN()
+inline uint16_t HostToLE16(uint16_t x) {
+  return x;
+}
+inline uint32_t HostToLE32(uint32_t x) {
+  return x;
+}
+inline uint64_t HostToLE64(uint64_t x) {
+  return x;
+}
+inline uint16_t LE16ToHost(uint16_t x) {
+  return x;
+}
+inline uint32_t LE32ToHost(uint32_t x) {
+  return x;
+}
+inline uint64_t LE64ToHost(uint64_t x) {
+  return x;
+}
+inline uint16_t HostToBE16(uint16_t x) {
+  return ByteSwap16(x);
+}
+inline uint32_t HostToBE32(uint32_t x) {
+  return ByteSwap32(x);
+}
+inline uint64_t HostToBE64(uint64_t x) {
+  return ByteSwap64(x);
+}
+inline uint16_t BE16ToHost(uint16_t x) {
+  return ByteSwap16(x);
+}
+inline uint32_t BE32ToHost(uint32_t x) {
+  return ByteSwap32(x);
+}
+inline uint64_t BE64ToHost(uint64_t x) {
+  return ByteSwap64(x);
+}
+inline float HostToLEFloat(float x) {
+  return x;
+}
+inline double HostToLEDouble(double x) {
+  return x;
+}
+inline float LEFloatToHost(float x) {
+  return x;
+}
+inline double LEDoubleToHost(double x) {
+  return x;
+}
+inline float HostToBEFloat(float x) {
+  uint32_t u;
+  memcpy(&u, &x, sizeof(u));
+  u = ByteSwap32(u);
+  memcpy(&x, &u, sizeof(u));
+  return x;
+}
+inline double HostToBEDouble(double x) {
+  uint64_t u;
+  memcpy(&u, &x, sizeof(u));
+  u = ByteSwap64(u);
+  memcpy(&x, &u, sizeof(u));
+  return x;
+}
+inline float BEFloatToHost(float x) {
+  uint32_t u;
+  memcpy(&u, &x, sizeof(u));
+  u = ByteSwap32(u);
+  memcpy(&x, &u, sizeof(u));
+  return x;
+}
+inline double BEDoubleToHost(double x) {
+  uint64_t u;
+  memcpy(&u, &x, sizeof(u));
+  u = ByteSwap64(u);
+  memcpy(&x, &u, sizeof(u));
+  return x;
+}
+#else
+inline uint16_t HostToLE16(uint16_t x) {
+  return ByteSwap16(x);
+}
+inline uint32_t HostToLE32(uint32_t x) {
+  return ByteSwap32(x);
+}
+inline uint64_t HostToLE64(uint64_t x) {
+  return ByteSwap64(x);
+}
+inline uint16_t LE16ToHost(uint16_t x) {
+  return ByteSwap16(x);
+}
+inline uint32_t LE32ToHost(uint32_t x) {
+  return ByteSwap32(x);
+}
+inline uint64_t LE64ToHost(uint64_t x) {
+  return ByteSwap64(x);
+}
+inline uint16_t HostToBE16(uint16_t x) {
+  return x;
+}
+inline uint32_t HostToBE32(uint32_t x) {
+  return x;
+}
+inline uint64_t HostToBE64(uint64_t x) {
+  return x;
+}
+inline uint16_t BE16ToHost(uint16_t x) {
+  return x;
+}
+inline uint32_t BE32ToHost(uint32_t x) {
+  return x;
+}
+inline uint64_t BE64ToHost(uint64_t x) {
+  return x;
+}
+inline float HostToLEFloat(float x) {
+  uint32_t u;
+  memcpy(&u, &x, sizeof(u));
+  u = ByteSwap32(u);
+  memcpy(&x, &u, sizeof(u));
+  return x;
+}
+inline double HostToLEDouble(double x) {
+  uint64_t u;
+  memcpy(&u, &x, sizeof(u));
+  u = ByteSwap64(u);
+  memcpy(&x, &u, sizeof(u));
+  return x;
+}
+inline float LEFloatToHost(float x) {
+  uint32_t u;
+  memcpy(&u, &x, sizeof(u));
+  u = ByteSwap32(u);
+  memcpy(&x, &u, sizeof(u));
+  return x;
+}
+inline double LEDoubleToHost(double x) {
+  uint64_t u;
+  memcpy(&u, &x, sizeof(u));
+  u = ByteSwap64(u);
+  memcpy(&x, &u, sizeof(u));
+  return x;
+}
+inline float HostToBEFloat(float x) {
+  return x;
+}
+inline double HostToBEDouble(double x) {
+  return x;
+}
+inline float BEFloatToHost(float x) {
+  return x;
+}
+inline double BEDoubleToHost(double x) {
+  return x;
+}
+#endif
+
+inline uint32_t HostToLE(uint32_t x) {
+  return HostToLE32(x);
+}
+inline int32_t HostToLE(int32_t x) {
+  return static_cast<int32_t>(HostToLE32(static_cast<uint32_t>(x)));
+}
+inline uint64_t HostToLE(uint64_t x) {
+  return HostToLE64(x);
+}
+inline int64_t HostToLE(int64_t x) {
+  return static_cast<int64_t>(HostToLE64(static_cast<uint64_t>(x)));
+}
+inline float HostToLE(float x) {
+  return HostToLEFloat(x);
+}
+inline double HostToLE(double x) {
+  return HostToLEDouble(x);
+}
+
+inline uint32_t LEToHost(uint32_t x) {
+  return LE32ToHost(x);
+}
+inline int32_t LEToHost(int32_t x) {
+  return static_cast<int32_t>(LE32ToHost(static_cast<uint32_t>(x)));
+}
+inline uint64_t LEToHost(uint64_t x) {
+  return LE64ToHost(x);
+}
+inline int64_t LEToHost(int64_t x) {
+  return static_cast<int64_t>(LE64ToHost(static_cast<uint64_t>(x)));
+}
+inline float LEToHost(float x) {
+  return LEFloatToHost(x);
+}
+inline double LEToHost(double x) {
+  return LEDoubleToHost(x);
+}
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_BASE_ENDIAN_H_
 // gen_amalgamated begin header: include/perfetto/protozero/scattered_stream_writer.h
 /*
  * Copyright (C) 2017 The Android Open Source Project
@@ -3200,6 +3661,7 @@ class PERFETTO_EXPORT_COMPONENT ScatteredStreamWriter {
 #include <string>
 #include <type_traits>

+// gen_amalgamated expanded: #include "perfetto/base/endian.h"
 // gen_amalgamated expanded: #include "perfetto/base/export.h"
 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/contiguous_memory_range.h"
@@ -3234,16 +3696,46 @@ class PERFETTO_EXPORT_COMPONENT Message {
   // static_assert()s in the Reset() method.
   Message() = default;

-  // Clears up the state, allowing the message to be reused as a fresh one.
-  void Reset(ScatteredStreamWriter*, MessageArena*);
-
-  // Commits all the changes to the buffer (backfills the size field of this and
-  // all nested messages) and seals the message. Returns the size of the message
-  // (and all nested sub-messages), without taking into account any chunking.
+  // Encoding for nested messages. The root selects it in Reset(). Each child
+  // inherits its parent's encoding.
+  // Keep in sync with PerfettoPbMsgEncoding in perfetto/public/pb_msg.h.
+  enum class Encoding : uint8_t {
+    // Standard protobuf. Finalize() writes each child's length into a reserved
+    // field before the child's contents.
+    kLengthDelimited = 0,
+
+    // Append-only format for tracing v2. It needs no patches:
+    // - A nested message starts with a group start tag and ends with a
+    //   closing byte, instead of a fixed-size length field.
+    // - Use it only inside the SMB, for the tracing v2 protocol. The final
+    //   trace output stays canonical protobuf.
+    // See proto_utils::kProtoGroupEndByte for the wire format.
+    kProtoGroup = 1,
+  };
+
+  // Returns the encoding each new child inherits.
+  Encoding encoding() const { return encoding_; }
+
+  // Clears up the state, allowing the message to be reused as a fresh one
+  // with the specified encoding.
+  void Reset(ScatteredStreamWriter*, MessageArena*, Encoding);
+
+  // Convenience wrapper for the default length-delimited encoding.
+  // See the TODO on RootMessage::Reset().
+  void Reset(ScatteredStreamWriter* stream_writer, MessageArena* arena) {
+    Reset(stream_writer, arena, Encoding::kLengthDelimited);
+  }
+
+  // Finalizes all open children, then seals this message:
+  // - Length-delimited: writes the encoded size into the reserved length field,
+  //   if present. For short messages within the current chunk, compaction can
+  //   reduce the field from proto_utils::kMessageLengthFieldSize bytes to one.
+  // - kProtoGroup: writes nothing. The parent writes the closing byte.
+  //
+  // Returns the encoded size of this message's fields, including nested
+  // messages. It excludes this message's own tag, length field and closing
+  // byte.
   // Finalize is idempotent and can be called several times w/o side effects.
-  // Short messages may be compacted in memory into the size field, since their
-  // size can be represented with fewer than
-  // proto_utils::kMessageLengthFieldSize bytes.
   uint32_t Finalize();

   // Optional. If is_valid() == true, the corresponding memory region (its
@@ -3252,6 +3744,8 @@ class PERFETTO_EXPORT_COMPONENT Message {
   // corresponding size field in the parent message. In most cases this is only
   // used for nested messages and the ScatteredStreamWriter::Delegate (e.g.
   // TraceWriterImpl), takes case of the outer message.
+  //
+  // In kProtoGroup mode, this pointer must always be null.
   uint8_t* size_field() const { return size_field_; }
   void set_size_field(uint8_t* size_field) { size_field_ = size_field; }

@@ -3311,7 +3805,8 @@ class PERFETTO_EXPORT_COMPONENT Message {
     uint8_t* pos = buffer;

     pos = proto_utils::WriteVarInt(proto_utils::MakeTagFixed<T>(field_id), pos);
-    memcpy(pos, &value, sizeof(T));
+    T le_value = perfetto::base::HostToLE(value);
+    memcpy(pos, &le_value, sizeof(T));
     pos += sizeof(T);
     // TODO: Optimize memcpy performance, see http://crbug.com/624311 .
     WriteToStream(buffer, pos);
@@ -3357,6 +3852,12 @@ class PERFETTO_EXPORT_COMPONENT Message {
   // generated by the cppgen_plugin.cc.
   // The caller needs to guarantee that the appended data is properly
   // proto-encoded and each field has a proto preamble.
+  //
+  // In kProtoGroup mode:
+  // - |data| must not contain protobuf group fields. This mode reserves wire
+  //   types 3 and 4 for nested message boundaries.
+  // - A nested message must contain only proto fields. For a raw string or
+  //   bytes payload, use AppendBytes() on the parent.
   void AppendRawProtoBytes(const void* data, size_t size) {
     if (nested_message_)
       EndNestedMessage();
@@ -3371,6 +3872,8 @@ class PERFETTO_EXPORT_COMPONENT Message {
   Message* BeginNestedMessageInternal(uint32_t field_id);

   // Called by Finalize and Append* methods.
+  // Ends the open child: finalizes it, adds its encoded size to this message
+  // and, in kProtoGroup mode, appends its closing byte.
   void EndNestedMessage();

   void WriteToStream(const uint8_t* src_begin, const uint8_t* src_end) {
@@ -3403,6 +3906,8 @@ class PERFETTO_EXPORT_COMPONENT Message {
   // [optional] Pointer to a non-aligned pre-reserved var-int slot of
   // kMessageLengthFieldSize bytes. When set, the Finalize() method will write
   // the size of proto-encoded message in the pointed memory region.
+  //
+  // In kProtoGroup mode, this pointer is unused and always null.
   uint8_t* size_field_;

   // Keeps track of the size of the current message.
@@ -3422,6 +3927,9 @@ class PERFETTO_EXPORT_COMPONENT Message {

   MessageState message_state_;

+  // Selected by the root and inherited by every nested message.
+  Encoding encoding_;
+
 #if PERFETTO_DCHECK_IS_ON()
   // Current generation of message. Incremented on Reset.
   // Used to detect stale handles.
@@ -3838,6 +4346,14 @@ struct DataSourceState {
   // this data source.
   std::atomic<uint32_t> incremental_state_generation{0};

+  // True if the writers of this instance use tracing v2.
+  // The muxer sets it once during setup, before writer threads see the
+  // instance.
+  // It stays true if v2 initialization fails, so the writers discard their
+  // data and do not fall back to v1.
+  // A startup instance keeps v1, also after adoption.
+  bool use_tracing_v2 = false;
+
   // This lock is not held to implement Trace() and it's used only if the trace
   // code wants to access its own data source state.
   // This is to prevent that accessing the data source on an arbitrary embedder
@@ -4563,6 +5079,7 @@ struct FieldWriter<proto_utils::ProtoSchemaType::kBytes> {
 #include <memory>
 #include <type_traits>

+// gen_amalgamated expanded: #include "perfetto/base/endian.h"
 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"

@@ -4645,7 +5162,8 @@ class PackedFixedSizeInt : public PackedBufferBase {
                   "kMaxElementSize needs to be updated");
     GrowIfNeeded();
     PERFETTO_DCHECK(reinterpret_cast<size_t>(write_ptr_) % alignof(T) == 0);
-    memcpy(reinterpret_cast<T*>(write_ptr_), &value, sizeof(T));
+    T le_value = perfetto::base::HostToLE(value);
+    memcpy(reinterpret_cast<T*>(write_ptr_), &le_value, sizeof(T));
     write_ptr_ += sizeof(T);
   }
 };
@@ -4683,6 +5201,7 @@ class PackedFixedSizeInt : public PackedBufferBase {
 #include <type_traits>
 #include <variant>

+// gen_amalgamated expanded: #include "perfetto/base/endian.h"
 // gen_amalgamated expanded: #include "perfetto/base/export.h"
 // gen_amalgamated expanded: #include "perfetto/base/logging.h"
 // gen_amalgamated expanded: #include "perfetto/protozero/field.h"
@@ -4880,7 +5399,7 @@ class PackedRepeatedFieldIterator {
       return *this;
     }

-    if (wire_type == ProtoWireType::kVarInt) {
+    if constexpr (wire_type == ProtoWireType::kVarInt) {
       uint64_t new_value = 0;
       const uint8_t* new_pos =
           proto_utils::ParseVarInt(read_ptr_, data_end_, &new_value);
@@ -4899,6 +5418,7 @@ class PackedRepeatedFieldIterator {
       // NB: the raw buffer is not guaranteed to be aligned, so neither are
       // these copies.
       memcpy(&curr_value_, read_ptr_, sizeof(CppType));
+      curr_value_ = perfetto::base::LEToHost(curr_value_);
       read_ptr_ += kStep;
     }

@@ -4983,7 +5503,8 @@ class UnifiedRepeatedFieldIterator {
 //   static constexpr SelectiveDecodeMask<TracePacket::kTimestampFieldNumber,
 //                                        TracePacket::kInternedDataFieldNumber>
 //       kMask{};
-//   SelectiveTypedProtoDecoder<MAX_FIELD_ID> decoder(data, size, kMask);
+//   SelectiveTypedProtoDecoder<TypedProtoDecoder<MAX_FIELD_ID>> decoder(
+//       data, size, kMask);
 //   for (const Field& f : decoder.unknown_fields()) { ... }
 template <uint32_t... kFieldIds>
 class SelectiveDecodeMask {
@@ -5042,6 +5563,24 @@ class PERFETTO_EXPORT_COMPONENT TypedProtoDecoderBase : public ProtoDecoder {
     return kInvalidField;
   }

+  // True if the message contained at least one field with an id beyond the
+  // highest id known in-tree (typically an out-of-tree extension). Such
+  // fields are not stored here; this lets callers skip a selective re-decode
+  // via unknown_fields() when there is nothing to find.
+  bool has_out_of_range_fields() const { return has_out_of_range_fields_; }
+
+  // True if any field whose bit is set in |mask| is present. |mask| is laid
+  // out like the presence bitmap (bit i of word i / 64 for field id i) and
+  // holds |words| words; bits beyond the known field range are ignored.
+  bool HasAnyField(const uint64_t* mask, size_t words) const {
+    size_t presence_words = (num_fields_ + 63) / 64;
+    for (size_t i = 0; i < words && i < presence_words; ++i) {
+      if (presence_[i] & mask[i])
+        return true;
+    }
+    return false;
+  }
+
   // True if the field with the given id was seen while decoding: tests bit
   // |id| of the presence bitmap. The caller must check that |id| is within
   // the bitmap, i.e. <= the decoder's MAX_FIELD_ID. Note that |id| may
@@ -5221,6 +5760,9 @@ class PERFETTO_EXPORT_COMPONENT TypedProtoDecoderBase : public ProtoDecoder {
   // |size_| is always <= |capacity_|. But |num_fields_| can be > |size_|.
   uint32_t size_;

+  // See has_out_of_range_fields().
+  bool has_out_of_range_fields_ = false;
+
   // Initially equal to kFieldsCapacity of the TypedProtoDecoder
   // specialization. Can grow when falling back on heap-based storage, in which
   // case it represents the size (#fields with each entry of a repeated field
@@ -5254,11 +5796,16 @@ class PERFETTO_EXPORT_COMPONENT TypedProtoDecoderBase : public ProtoDecoder {
 template <int MAX_FIELD_ID, bool = false>
 class TypedProtoDecoder : public TypedProtoDecoderBase {
  public:
+  // The highest field id known to this decoder; see has_out_of_range_fields().
+  static constexpr uint32_t kMaxFieldId = static_cast<uint32_t>(MAX_FIELD_ID);
+
   TypedProtoDecoder(const uint8_t* buffer, size_t length)
-      : TypedProtoDecoder(SkipParseTag{},
-                          buffer,
-                          length,
-                          /*num_fields=*/MAX_FIELD_ID + 1) {
+      : TypedProtoDecoderBase(on_stack_storage_,
+                              presence_storage_,
+                              /*num_fields=*/MAX_FIELD_ID + 1,
+                              PROTOZERO_DECODER_INITIAL_STACK_CAPACITY,
+                              buffer,
+                              length) {
     TypedProtoDecoderBase::ParseAllFields();
   }

@@ -5290,21 +5837,6 @@ class TypedProtoDecoder : public TypedProtoDecoderBase {
     return *this;
   }

- protected:
-  // Sets up the storage but does not parse: for subclasses that drive
-  // ParseAllFields() themselves (see SelectiveTypedProtoDecoder).
-  struct SkipParseTag {};
-  TypedProtoDecoder(SkipParseTag,
-                    const uint8_t* buffer,
-                    size_t length,
-                    uint32_t num_fields)
-      : TypedProtoDecoderBase(on_stack_storage_,
-                              presence_storage_,
-                              num_fields,
-                              PROTOZERO_DECODER_INITIAL_STACK_CAPACITY,
-                              buffer,
-                              length) {}
-
  private:
   void MaybeCopyOnStackStorage(const TypedProtoDecoder& other) {
     // If the moved-from decoder was using on-stack storage, we need to update
@@ -5344,37 +5876,34 @@ class TypedProtoDecoder : public TypedProtoDecoderBase {
 //   static constexpr SelectiveDecodeMask<TracePacket::kTimestampFieldNumber,
 //                                        TracePacket::kInternedDataFieldNumber>
 //       kMask{};
-//   SelectiveTypedProtoDecoder<MAX_FIELD_ID> decoder(data, size, kMask);
+//   SelectiveTypedProtoDecoder<TypedProtoDecoder<MAX_FIELD_ID>> decoder(
+//       data, size, kMask);
 //   for (const Field& f : decoder.unknown_fields()) { ... }
-template <int MAX_FIELD_ID>
-class SelectiveTypedProtoDecoder : public TypedProtoDecoder<MAX_FIELD_ID> {
-  using Base = TypedProtoDecoder<MAX_FIELD_ID>;
-
- public:
-  // The dense window is capped at the mask's extent: |num_fields_| is the
-  // only id bound the decode loop checks, so capping it both bounds the mask
-  // read and makes every id above the mask spill, letting the mask be sized
-  // to the allowlisted ids only rather than to MAX_FIELD_ID.
-  // |mask| is only read during this call.
+template <typename Decoder>
+class SelectiveTypedProtoDecoder : public Decoder {
+ public:
+  // |Decoder| is a TypedProtoDecoder or a generated message decoder, whose
+  // typed accessors this class inherits. The dense window is capped at the
+  // mask's extent: |num_fields_| is the only id bound the decode loop checks,
+  // so capping it both bounds the mask read and makes every id above the
+  // mask spill, letting the mask be sized to the allowlisted ids only rather
+  // than to the decoder's max field id. |mask| is only read during this call.
   template <uint32_t... kFieldIds>
   SelectiveTypedProtoDecoder(const uint8_t* buffer,
                              size_t length,
                              const SelectiveDecodeMask<kFieldIds...>& mask)
-      : Base(typename Base::SkipParseTag{},
-             buffer,
-             length,
-             /*num_fields=*/
-             std::min(static_cast<uint32_t>(MAX_FIELD_ID),
-                      SelectiveDecodeMask<kFieldIds...>::kMaxFieldId) +
-                 1) {
-    // The base constructor cleared the presence bitmap only up to the capped
-    // |num_fields_|. at<>() skips the range check for ids below the on-stack
-    // capacity and tests the presence bit directly, so the rest of the bitmap
-    // must be cleared too.
-    constexpr uint32_t kPresenceWords = (MAX_FIELD_ID + 1 + 63) / 64;
-    const uint32_t cleared_words = (this->num_fields_ + 63) / 64;
-    memset(this->presence_ + cleared_words, 0,
-           (kPresenceWords - cleared_words) * sizeof(uint64_t));
+      : Decoder(nullptr, 0) {
+    // The empty decode above set up the storage and cleared the whole
+    // presence bitmap without parsing anything; point it at the real buffer
+    // and parse selectively.
+    this->begin_ = buffer;
+    this->end_ = buffer + length;
+    this->read_ptr_ = buffer;
+    this->num_fields_ =
+        std::min(Decoder::kMaxFieldId,
+                 SelectiveDecodeMask<kFieldIds...>::kMaxFieldId) +
+        1;
+    this->size_ = std::min(this->num_fields_, this->capacity_ - 1);
     spill_ = spill_storage_;
     uint32_t spill_capacity = kSpillStackCapacity;
     this->ParseAllFieldsSelective(mask.data(), &spill_, &spill_capacity,
@@ -5388,7 +5917,7 @@ class SelectiveTypedProtoDecoder : public TypedProtoDecoder<MAX_FIELD_ID> {
   }

   SelectiveTypedProtoDecoder(SelectiveTypedProtoDecoder&& other) noexcept
-      : Base(std::move(other)),
+      : Decoder(std::move(other)),
         spill_(other.spill_),
         spill_size_(other.spill_size_),
         heap_spill_(std::move(other.heap_spill_)) {
@@ -7599,16 +8128,6 @@ namespace perfetto {
 namespace protos {
 namespace gen {
 class DataSourceDescriptor;
-class VmProgram;
-class VmInstruction;
-class VmOpDel;
-class VmOpSet;
-class VmOpMerge;
-class VmOpRegLoad;
-class VmOpSelect;
-class VmOpSelect_PathComponent;
-enum VmInstruction_AbortLevel : int;
-enum VmCursorEnum : int;
 }  // namespace perfetto
 }  // namespace protos
 }  // namespace gen
@@ -7674,9 +8193,8 @@ class PERFETTO_EXPORT_COMPONENT DataSourceDescriptor : public ::protozero::CppMe
   bool no_flush() const { return no_flush_; }
   void set_no_flush(bool value) { no_flush_ = value; _has_field_.set(9); }

-  bool has_protovm_program() const { return _has_field_[10]; }
-  const VmProgram& protovm_program() const { return *protovm_program_; }
-  VmProgram* mutable_protovm_program() { _has_field_.set(10); return protovm_program_.get(); }
+  const std::string& protovm_program_raw() const { return protovm_program_; }
+  void set_protovm_program_raw(const std::string& raw) { protovm_program_ = raw; _has_field_.set(10); }

   const std::string& gpu_counter_descriptor_raw() const { return gpu_counter_descriptor_; }
   void set_gpu_counter_descriptor_raw(const std::string& raw) { gpu_counter_descriptor_ = raw; _has_field_.set(5); }
@@ -7694,7 +8212,7 @@ class PERFETTO_EXPORT_COMPONENT DataSourceDescriptor : public ::protozero::CppMe
   bool will_notify_on_start_{};
   bool handles_incremental_state_clear_{};
   bool no_flush_{};
-  ::protozero::CopyablePtr<VmProgram> protovm_program_;
+  std::string protovm_program_;  // [lazy=true]
   std::string gpu_counter_descriptor_;  // [lazy=true]
   std::string track_event_descriptor_;  // [lazy=true]
   std::string ftrace_descriptor_;  // [lazy=true]
@@ -7787,6 +8305,8 @@ class InterceptorConfig;
 class ConsoleConfig;
 class ChromeConfig;
 class SystemInfoConfig;
+class DataSourceConfig_ExperimentalTracingV2Config;
+class DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption;
 class ProtoVmConfig;
 class TraceConfig_BufferConfig;
 enum TraceConfig_LockdownModeOperation : int;
@@ -7913,6 +8433,7 @@ class PERFETTO_EXPORT_COMPONENT TraceConfig : public ::protozero::CppMessageObj
   static constexpr auto FFlushMode_MAX = TraceConfig_FFlushMode_FFLUSH_ENABLED;
   enum FieldNumbers {
     kBuffersFieldNumber = 1,
+    kExperimentalTraceBufferV2CompactionFieldNumber = 50,
     kDataSourcesFieldNumber = 2,
     kBuiltinDataSourcesFieldNumber = 20,
     kDurationMsFieldNumber = 3,
@@ -7954,6 +8475,7 @@ class PERFETTO_EXPORT_COMPONENT TraceConfig : public ::protozero::CppMessageObj
     kFflushPostWriteFieldNumber = 45,
     kTraceAllMachinesFieldNumber = 43,
     kTraceAttributesFieldNumber = 48,
+    kPersistTraceAcrossRebootsFieldNumber = 49,
   };

   TraceConfig();
@@ -7976,6 +8498,10 @@ class PERFETTO_EXPORT_COMPONENT TraceConfig : public ::protozero::CppMessageObj
   void clear_buffers();
   TraceConfig_BufferConfig* add_buffers();

+  bool has_experimental_trace_buffer_v2_compaction() const { return _has_field_[50]; }
+  bool experimental_trace_buffer_v2_compaction() const { return experimental_trace_buffer_v2_compaction_; }
+  void set_experimental_trace_buffer_v2_compaction(bool value) { experimental_trace_buffer_v2_compaction_ = value; _has_field_.set(50); }
+
   const std::vector<TraceConfig_DataSource>& data_sources() const { return data_sources_; }
   std::vector<TraceConfig_DataSource>* mutable_data_sources() { return &data_sources_; }
   int data_sources_size() const;
@@ -8149,8 +8675,13 @@ class PERFETTO_EXPORT_COMPONENT TraceConfig : public ::protozero::CppMessageObj
   const TraceAttributes& trace_attributes() const { return *trace_attributes_; }
   TraceAttributes* mutable_trace_attributes() { _has_field_.set(48); return trace_attributes_.get(); }

+  bool has_persist_trace_across_reboots() const { return _has_field_[49]; }
+  bool persist_trace_across_reboots() const { return persist_trace_across_reboots_; }
+  void set_persist_trace_across_reboots(bool value) { persist_trace_across_reboots_ = value; _has_field_.set(49); }
+
  private:
   std::vector<TraceConfig_BufferConfig> buffers_;
+  bool experimental_trace_buffer_v2_compaction_{};
   std::vector<TraceConfig_DataSource> data_sources_;
   ::protozero::CopyablePtr<TraceConfig_BuiltinDataSource> builtin_data_sources_;
   uint32_t duration_ms_{};
@@ -8192,12 +8723,13 @@ class PERFETTO_EXPORT_COMPONENT TraceConfig : public ::protozero::CppMessageObj
   TraceConfig_FFlushMode fflush_post_write_{};
   bool trace_all_machines_{};
   ::protozero::CopyablePtr<TraceAttributes> trace_attributes_;
+  bool persist_trace_across_reboots_{};

   // Allows to preserve unknown protobuf fields for compatibility
   // with future versions of .proto files.
   std::string unknown_fields_;

-  std::bitset<49> _has_field_{};
+  std::bitset<51> _has_field_{};
 };


@@ -10851,6 +11383,11 @@ struct DataSourceParams {
   bool buffer_exhausted_policy_configurable = false;
   bool supports_multiple_instances = true;
   bool requires_callbacks_under_lock = true;
+  // Advertises that the data source supports tracing v2.
+  // This does NOT mean v2 will be used which is decided after the negotiation
+  // with the service, which might not support v2 or might decide not to use it.
+  // See DataSourceState.use_tracing_v2 for the active state.
+  bool supports_tracing_v2 = false;
 };

 struct DataSourceStaticState;
@@ -11334,6 +11871,7 @@ class ProcessDescriptor;
 class ProcessStats;
 class ProcessTree;
 class ProfilePacket;
+class RecoveredTraceInfo;
 class RemoteClockSync;
 class SmapsPacket;
 class StackSample;
@@ -11641,6 +12179,8 @@ class TracePacket_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID
   ::protozero::ConstBytes user_list() const { return at<123>().as_bytes(); }
   bool has_journald_event() const { return at<132>().valid(); }
   ::protozero::ConstBytes journald_event() const { return at<132>().as_bytes(); }
+  bool has_recovered_trace_info() const { return at<138>().valid(); }
+  ::protozero::ConstBytes recovered_trace_info() const { return at<138>().as_bytes(); }
   bool has_for_testing() const { return at<900>().valid(); }
   ::protozero::ConstBytes for_testing() const { return at<900>().as_bytes(); }
   bool has_trusted_uid() const { return at<3>().valid(); }
@@ -11755,6 +12295,7 @@ class TracePacket : public ::protozero::Message {
     kCpuPerUidDataFieldNumber = 119,
     kUserListFieldNumber = 123,
     kJournaldEventFieldNumber = 132,
+    kRecoveredTraceInfoFieldNumber = 138,
     kForTestingFieldNumber = 900,
     kTrustedUidFieldNumber = 3,
     kTrustedPacketSequenceIdFieldNumber = 10,
@@ -13036,6 +13577,20 @@ class TracePacket : public ::protozero::Message {
   }


+  using FieldMetadata_RecoveredTraceInfo =
+    ::protozero::proto_utils::FieldMetadata<
+      138,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      RecoveredTraceInfo,
+      TracePacket>;
+
+  static constexpr FieldMetadata_RecoveredTraceInfo kRecoveredTraceInfo{};
+  template <typename T = RecoveredTraceInfo> T* set_recovered_trace_info() {
+    return BeginNestedMessage<T>(138);
+  }
+
+
   using FieldMetadata_ForTesting =
     ::protozero::proto_utils::FieldMetadata<
       900,
@@ -13714,6 +14269,15 @@ class DataSource : public DataSourceBase {
     // This can be useful for splitting protos that might grow very large.
     uint64_t written() { return tls_inst_->trace_writer->written(); }

+    // Returns the number of times the trace writer for the current thread and
+    // data source instance entered a mode in which it started dropping data
+    // (e.g. because the shared memory buffer was exhausted and the buffer
+    // exhausted policy is kDrop). Note that this does *not* necessarily
+    // correspond to the number of dropped packets, as multiple packets can be
+    // dropped on each entry into the drop mode. A non-zero (or increased)
+    // value indicates that some data written on this thread was lost.
+    uint64_t drop_count() { return tls_inst_->trace_writer->drop_count(); }
+
     // Returns a RAII handle to access the data source instance, guaranteeing
     // that it won't be deleted on another thread (because of trace stopping)
     // while accessing it from within the Trace() lambda.
@@ -13870,6 +14434,7 @@ class DataSource : public DataSourceBase {
         DerivedDataSource::GetDefaultBufferExhaustedPolicy();
     params.buffer_exhausted_policy_configurable =
         DerivedDataSource::kBufferExhaustedPolicyConfigurable;
+    params.supports_tracing_v2 = true;
     return Helper::type().Register(
         descriptor, factory, params, no_flush,
         GetCreateTlsFn(
@@ -14675,9 +15240,20 @@ class RootMessage : public T {
   RootMessage(RootMessage&&) = delete;
   RootMessage& operator=(RootMessage&&) = delete;

-  void Reset(ScatteredStreamWriter* writer) {
+  // Resets the root and its arena for output to |writer| with |encoding|.
+  // See Message::Encoding.
+  void Reset(ScatteredStreamWriter* writer, Message::Encoding encoding) {
     root_arena_.Reset();
-    Message::Reset(writer, &root_arena_);
+    Message::Reset(writer, &root_arena_, encoding);
+  }
+
+  // Convenience wrapper for the default length-delimited encoding.
+  //
+  // TODO(sashwinbalaji): Remove this overload once Chromium calls the one
+  // above, for example in:
+  // https://crsrc.org/c/services/tracing/public/cpp/perfetto/traced_value_proto_writer.cc
+  void Reset(ScatteredStreamWriter* writer) {
+    Reset(writer, Message::Encoding::kLengthDelimited);
   }

  private:
@@ -14765,6 +15341,10 @@ class PERFETTO_EXPORT_COMPONENT ScatteredHeapBuffer
   // writer has written.
   const std::vector<Slice>& GetSlices();

+  // Like GetSlices() but hands them over, leaving this empty. The writer is
+  // reset so it no longer points into the handed-over memory.
+  std::vector<Slice> TakeSlices();
+
   // Stitch all the slices into a single contiguous buffer.
   std::vector<uint8_t> StitchSlices();

@@ -14864,6 +15444,16 @@ class HeapBuffered {
     return shb_.GetSlices();
   }

+  // Finalizes the message and hands over its slices, leaving this empty and
+  // reusable.
+  std::vector<ScatteredHeapBuffer::Slice> TakeSlices() {
+    msg_.Finalize();
+    std::vector<ScatteredHeapBuffer::Slice> slices = shb_.TakeSlices();
+    msg_.Reset(&writer_);
+    PERFETTO_DCHECK(empty());
+    return slices;
+  }
+
   void Reset() {
     shb_.Reset();
     writer_.Reset(protozero::ContiguousMemoryRange{});
@@ -21791,7 +22381,7 @@ class FlowImpl {
   static PERFETTO_ALWAYS_INLINE inline FlowImpl ProcessScoped(
       uint64_t flow_id,
       const char* named_scope) {
-    return Global(flow_id, named_scope);
+    return Global(flow_id ^ Track::process_uuid, named_scope);
   }

   // Same as above, but construct an id from a pointer.
@@ -29094,6 +29684,7 @@ class PERFETTO_EXPORT_COMPONENT SystemInfo : public ::protozero::CppMessageObj {
     kAndroidBuildFingerprintFieldNumber = 2,
     kAndroidDeviceManufacturerFieldNumber = 14,
     kAndroidSocModelFieldNumber = 9,
+    kDeviceTreeCompatiblesFieldNumber = 18,
     kAndroidGuestSocModelFieldNumber = 13,
     kAndroidHardwareRevisionFieldNumber = 10,
     kAndroidStorageModelFieldNumber = 11,
@@ -29139,6 +29730,13 @@ class PERFETTO_EXPORT_COMPONENT SystemInfo : public ::protozero::CppMessageObj {
   const std::string& android_soc_model() const { return android_soc_model_; }
   void set_android_soc_model(const std::string& value) { android_soc_model_ = value; _has_field_.set(9); }

+  const std::vector<std::string>& device_tree_compatibles() const { return device_tree_compatibles_; }
+  std::vector<std::string>* mutable_device_tree_compatibles() { return &device_tree_compatibles_; }
+  int device_tree_compatibles_size() const { return static_cast<int>(device_tree_compatibles_.size()); }
+  void clear_device_tree_compatibles() { device_tree_compatibles_.clear(); }
+  void add_device_tree_compatibles(std::string value) { device_tree_compatibles_.emplace_back(value); }
+  std::string* add_device_tree_compatibles() { device_tree_compatibles_.emplace_back(); return &device_tree_compatibles_.back(); }
+
   bool has_android_guest_soc_model() const { return _has_field_[13]; }
   const std::string& android_guest_soc_model() const { return android_guest_soc_model_; }
   void set_android_guest_soc_model(const std::string& value) { android_guest_soc_model_ = value; _has_field_.set(13); }
@@ -29196,6 +29794,7 @@ class PERFETTO_EXPORT_COMPONENT SystemInfo : public ::protozero::CppMessageObj {
   std::string android_build_fingerprint_{};
   std::string android_device_manufacturer_{};
   std::string android_soc_model_{};
+  std::vector<std::string> device_tree_compatibles_;
   std::string android_guest_soc_model_{};
   std::string android_hardware_revision_{};
   std::string android_storage_model_{};
@@ -29214,7 +29813,7 @@ class PERFETTO_EXPORT_COMPONENT SystemInfo : public ::protozero::CppMessageObj {
   // with future versions of .proto files.
   std::string unknown_fields_;

-  std::bitset<18> _has_field_{};
+  std::bitset<19> _has_field_{};
 };


@@ -29727,12 +30326,15 @@ class PERFETTO_EXPORT_COMPONENT TraceStats_BufferStats : public ::protozero::Cpp
     kChunksReadFieldNumber = 17,
     kChunksCommittedOutOfOrderFieldNumber = 11,
     kWriteWrapCountFieldNumber = 4,
+    kCompactionsFieldNumber = 23,
+    kCompactionsSkippedFieldNumber = 24,
     kPatchesSucceededFieldNumber = 5,
     kPatchesFailedFieldNumber = 6,
     kReadaheadsSucceededFieldNumber = 7,
     kReadaheadsFailedFieldNumber = 8,
     kAbiViolationsFieldNumber = 9,
     kTraceWriterPacketLossFieldNumber = 19,
+    kOversizedPacketsDroppedFieldNumber = 22,
   };

   TraceStats_BufferStats();
@@ -29805,6 +30407,14 @@ class PERFETTO_EXPORT_COMPONENT TraceStats_BufferStats : public ::protozero::Cpp
   uint64_t write_wrap_count() const { return write_wrap_count_; }
   void set_write_wrap_count(uint64_t value) { write_wrap_count_ = value; _has_field_.set(4); }

+  bool has_compactions() const { return _has_field_[23]; }
+  uint64_t compactions() const { return compactions_; }
+  void set_compactions(uint64_t value) { compactions_ = value; _has_field_.set(23); }
+
+  bool has_compactions_skipped() const { return _has_field_[24]; }
+  uint64_t compactions_skipped() const { return compactions_skipped_; }
+  void set_compactions_skipped(uint64_t value) { compactions_skipped_ = value; _has_field_.set(24); }
+
   bool has_patches_succeeded() const { return _has_field_[5]; }
   uint64_t patches_succeeded() const { return patches_succeeded_; }
   void set_patches_succeeded(uint64_t value) { patches_succeeded_ = value; _has_field_.set(5); }
@@ -29829,6 +30439,10 @@ class PERFETTO_EXPORT_COMPONENT TraceStats_BufferStats : public ::protozero::Cpp
   uint64_t trace_writer_packet_loss() const { return trace_writer_packet_loss_; }
   void set_trace_writer_packet_loss(uint64_t value) { trace_writer_packet_loss_ = value; _has_field_.set(19); }

+  bool has_oversized_packets_dropped() const { return _has_field_[22]; }
+  uint64_t oversized_packets_dropped() const { return oversized_packets_dropped_; }
+  void set_oversized_packets_dropped(uint64_t value) { oversized_packets_dropped_ = value; _has_field_.set(22); }
+
  private:
   uint64_t buffer_size_{};
   uint64_t bytes_written_{};
@@ -29844,18 +30458,21 @@ class PERFETTO_EXPORT_COMPONENT TraceStats_BufferStats : public ::protozero::Cpp
   uint64_t chunks_read_{};
   uint64_t chunks_committed_out_of_order_{};
   uint64_t write_wrap_count_{};
+  uint64_t compactions_{};
+  uint64_t compactions_skipped_{};
   uint64_t patches_succeeded_{};
   uint64_t patches_failed_{};
   uint64_t readaheads_succeeded_{};
   uint64_t readaheads_failed_{};
   uint64_t abi_violations_{};
   uint64_t trace_writer_packet_loss_{};
+  uint64_t oversized_packets_dropped_{};

   // Allows to preserve unknown protobuf fields for compatibility
   // with future versions of .proto files.
   std::string unknown_fields_;

-  std::bitset<21> _has_field_{};
+  std::bitset<25> _has_field_{};
 };

 }  // namespace perfetto
@@ -29977,17 +30594,7 @@ class TracingServiceState;
 class TracingServiceState_TracingSession;
 class TracingServiceState_DataSource;
 class DataSourceDescriptor;
-class VmProgram;
-class VmInstruction;
-class VmOpDel;
-class VmOpSet;
-class VmOpMerge;
-class VmOpRegLoad;
-class VmOpSelect;
-class VmOpSelect_PathComponent;
 class TracingServiceState_Producer;
-enum VmInstruction_AbortLevel : int;
-enum VmCursorEnum : int;
 }  // namespace perfetto
 }  // namespace protos
 }  // namespace gen
@@ -31993,6 +32600,10 @@ class DataSourceDescriptor : public ::protozero::Message {
     return BeginNestedMessage<T>(10);
   }

+  void set_protovm_program_raw(const std::string& raw) {
+    return AppendBytes(10, raw.data(), raw.size());
+  }
+

   using FieldMetadata_GpuCounterDescriptor =
     ::protozero::proto_utils::FieldMetadata<
@@ -36896,7 +37507,7 @@ namespace perfetto {
 namespace protos {
 namespace pbzero {

-class SystemInfo_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/17> {
+class SystemInfo_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/18> {
  public:
   SystemInfo_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
   explicit SystemInfo_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
@@ -36909,6 +37520,8 @@ class SystemInfo_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=
   ::protozero::ConstChars android_device_manufacturer() const { return at<14>().as_string(); }
   bool has_android_soc_model() const { return at<9>().valid(); }
   ::protozero::ConstChars android_soc_model() const { return at<9>().as_string(); }
+  bool has_device_tree_compatibles() const { return at<18>().valid(); }
+  ::protozero::RepeatedFieldIterator<::protozero::ConstChars> device_tree_compatibles() const { return GetRepeated<::protozero::ConstChars>(18); }
   bool has_android_guest_soc_model() const { return at<13>().valid(); }
   ::protozero::ConstChars android_guest_soc_model() const { return at<13>().as_string(); }
   bool has_android_hardware_revision() const { return at<10>().valid(); }
@@ -36945,6 +37558,7 @@ class SystemInfo : public ::protozero::Message {
     kAndroidBuildFingerprintFieldNumber = 2,
     kAndroidDeviceManufacturerFieldNumber = 14,
     kAndroidSocModelFieldNumber = 9,
+    kDeviceTreeCompatiblesFieldNumber = 18,
     kAndroidGuestSocModelFieldNumber = 13,
     kAndroidHardwareRevisionFieldNumber = 10,
     kAndroidStorageModelFieldNumber = 11,
@@ -37048,6 +37662,30 @@ class SystemInfo : public ::protozero::Message {
         ::Append(*this, field_id, value);
   }

+  using FieldMetadata_DeviceTreeCompatibles =
+    ::protozero::proto_utils::FieldMetadata<
+      18,
+      ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked,
+      ::protozero::proto_utils::ProtoSchemaType::kString,
+      std::string,
+      SystemInfo>;
+
+  static constexpr FieldMetadata_DeviceTreeCompatibles kDeviceTreeCompatibles{};
+  void add_device_tree_compatibles(const char* data, size_t size) {
+    AppendBytes(FieldMetadata_DeviceTreeCompatibles::kFieldId, data, size);
+  }
+  void add_device_tree_compatibles(::protozero::ConstChars chars) {
+    AppendBytes(FieldMetadata_DeviceTreeCompatibles::kFieldId, chars.data, chars.size);
+  }
+  void add_device_tree_compatibles(std::string value) {
+    static constexpr uint32_t field_id = FieldMetadata_DeviceTreeCompatibles::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kString>
+        ::Append(*this, field_id, value);
+  }
+
   using FieldMetadata_AndroidGuestSocModel =
     ::protozero::proto_utils::FieldMetadata<
       13,
@@ -38277,7 +38915,7 @@ class TraceStats_WriterStats : public ::protozero::Message {
   }
 };

-class TraceStats_BufferStats_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/20> {
+class TraceStats_BufferStats_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/24> {
  public:
   TraceStats_BufferStats_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
   explicit TraceStats_BufferStats_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
@@ -38310,6 +38948,10 @@ class TraceStats_BufferStats_Decoder : public ::protozero::TypedProtoDecoder</*M
   uint64_t chunks_committed_out_of_order() const { return at<11>().as_uint64(); }
   bool has_write_wrap_count() const { return at<4>().valid(); }
   uint64_t write_wrap_count() const { return at<4>().as_uint64(); }
+  bool has_compactions() const { return at<23>().valid(); }
+  uint64_t compactions() const { return at<23>().as_uint64(); }
+  bool has_compactions_skipped() const { return at<24>().valid(); }
+  uint64_t compactions_skipped() const { return at<24>().as_uint64(); }
   bool has_patches_succeeded() const { return at<5>().valid(); }
   uint64_t patches_succeeded() const { return at<5>().as_uint64(); }
   bool has_patches_failed() const { return at<6>().valid(); }
@@ -38322,6 +38964,8 @@ class TraceStats_BufferStats_Decoder : public ::protozero::TypedProtoDecoder</*M
   uint64_t abi_violations() const { return at<9>().as_uint64(); }
   bool has_trace_writer_packet_loss() const { return at<19>().valid(); }
   uint64_t trace_writer_packet_loss() const { return at<19>().as_uint64(); }
+  bool has_oversized_packets_dropped() const { return at<22>().valid(); }
+  uint64_t oversized_packets_dropped() const { return at<22>().as_uint64(); }
 };

 class TraceStats_BufferStats : public ::protozero::Message {
@@ -38342,12 +38986,15 @@ class TraceStats_BufferStats : public ::protozero::Message {
     kChunksReadFieldNumber = 17,
     kChunksCommittedOutOfOrderFieldNumber = 11,
     kWriteWrapCountFieldNumber = 4,
+    kCompactionsFieldNumber = 23,
+    kCompactionsSkippedFieldNumber = 24,
     kPatchesSucceededFieldNumber = 5,
     kPatchesFailedFieldNumber = 6,
     kReadaheadsSucceededFieldNumber = 7,
     kReadaheadsFailedFieldNumber = 8,
     kAbiViolationsFieldNumber = 9,
     kTraceWriterPacketLossFieldNumber = 19,
+    kOversizedPacketsDroppedFieldNumber = 22,
   };
   static constexpr const char* GetName() { return ".perfetto.protos.TraceStats.BufferStats"; }

@@ -38604,6 +39251,42 @@ class TraceStats_BufferStats : public ::protozero::Message {
         ::Append(*this, field_id, value);
   }

+  using FieldMetadata_Compactions =
+    ::protozero::proto_utils::FieldMetadata<
+      23,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint64,
+      uint64_t,
+      TraceStats_BufferStats>;
+
+  static constexpr FieldMetadata_Compactions kCompactions{};
+  void set_compactions(uint64_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_Compactions::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint64>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_CompactionsSkipped =
+    ::protozero::proto_utils::FieldMetadata<
+      24,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint64,
+      uint64_t,
+      TraceStats_BufferStats>;
+
+  static constexpr FieldMetadata_CompactionsSkipped kCompactionsSkipped{};
+  void set_compactions_skipped(uint64_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_CompactionsSkipped::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint64>
+        ::Append(*this, field_id, value);
+  }
+
   using FieldMetadata_PatchesSucceeded =
     ::protozero::proto_utils::FieldMetadata<
       5,
@@ -38711,6 +39394,24 @@ class TraceStats_BufferStats : public ::protozero::Message {
       ::protozero::proto_utils::ProtoSchemaType::kUint64>
         ::Append(*this, field_id, value);
   }
+
+  using FieldMetadata_OversizedPacketsDropped =
+    ::protozero::proto_utils::FieldMetadata<
+      22,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint64,
+      uint64_t,
+      TraceStats_BufferStats>;
+
+  static constexpr FieldMetadata_OversizedPacketsDropped kOversizedPacketsDropped{};
+  void set_oversized_packets_dropped(uint64_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_OversizedPacketsDropped::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint64>
+        ::Append(*this, field_id, value);
+  }
 };

 } // Namespace.
@@ -40262,6 +40963,76 @@ class PERFETTO_EXPORT_COMPONENT AndroidPolledStateConfig : public ::protozero::C
 }  // namespace gen

 #endif  // PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_ANDROID_ANDROID_POLLED_STATE_CONFIG_PROTO_CPP_H_
+// gen_amalgamated begin header: gen/protos/perfetto/config/android/android_process_state_config.gen.h
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_ANDROID_ANDROID_PROCESS_STATE_CONFIG_PROTO_CPP_H_
+#define PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_ANDROID_ANDROID_PROCESS_STATE_CONFIG_PROTO_CPP_H_
+
+#include <stdint.h>
+#include <bitset>
+#include <vector>
+#include <string>
+#include <type_traits>
+
+// gen_amalgamated expanded: #include "perfetto/protozero/cpp_message_obj.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/copyable_ptr.h"
+// gen_amalgamated expanded: #include "perfetto/base/export.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+class AndroidProcessStateConfig;
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+
+namespace protozero {
+class Message;
+}  // namespace protozero
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+class PERFETTO_EXPORT_COMPONENT AndroidProcessStateConfig : public ::protozero::CppMessageObj {
+ public:
+  enum FieldNumbers {
+    kDumpProcessMetadataFieldNumber = 1,
+  };
+
+  AndroidProcessStateConfig();
+  ~AndroidProcessStateConfig() override;
+  AndroidProcessStateConfig(AndroidProcessStateConfig&&) noexcept;
+  AndroidProcessStateConfig& operator=(AndroidProcessStateConfig&&);
+  AndroidProcessStateConfig(const AndroidProcessStateConfig&);
+  AndroidProcessStateConfig& operator=(const AndroidProcessStateConfig&);
+  bool operator==(const AndroidProcessStateConfig&) const;
+  bool operator!=(const AndroidProcessStateConfig& other) const { return !(*this == other); }
+
+  bool ParseFromArray(const void*, size_t) override;
+  std::string SerializeAsString() const override;
+  std::vector<uint8_t> SerializeAsArray() const override;
+  void Serialize(::protozero::Message*) const;
+
+  bool has_dump_process_metadata() const { return _has_field_[1]; }
+  bool dump_process_metadata() const { return dump_process_metadata_; }
+  void set_dump_process_metadata(bool value) { dump_process_metadata_ = value; _has_field_.set(1); }
+
+ private:
+  bool dump_process_metadata_{};
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+
+  std::bitset<2> _has_field_{};
+};
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+
+#endif  // PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_ANDROID_ANDROID_PROCESS_STATE_CONFIG_PROTO_CPP_H_
 // gen_amalgamated begin header: gen/protos/perfetto/config/android/android_sdk_sysprop_guard_config.gen.h
 // DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
 #ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_ANDROID_ANDROID_SDK_SYSPROP_GUARD_CONFIG_PROTO_CPP_H_
@@ -42612,6 +43383,7 @@ class PERFETTO_EXPORT_COMPONENT GpuRenderStagesConfig : public ::protozero::CppM
     kFullLoadstoreFieldNumber = 1,
     kLowOverheadFieldNumber = 2,
     kTraceMetricsFieldNumber = 3,
+    kDrawcallStagesFieldNumber = 4,
   };

   GpuRenderStagesConfig();
@@ -42643,16 +43415,21 @@ class PERFETTO_EXPORT_COMPONENT GpuRenderStagesConfig : public ::protozero::CppM
   void add_trace_metrics(std::string value) { trace_metrics_.emplace_back(value); }
   std::string* add_trace_metrics() { trace_metrics_.emplace_back(); return &trace_metrics_.back(); }

+  bool has_drawcall_stages() const { return _has_field_[4]; }
+  bool drawcall_stages() const { return drawcall_stages_; }
+  void set_drawcall_stages(bool value) { drawcall_stages_ = value; _has_field_.set(4); }
+
  private:
   bool full_loadstore_{};
   bool low_overhead_{};
   std::vector<std::string> trace_metrics_;
+  bool drawcall_stages_{};

   // Allows to preserve unknown protobuf fields for compatibility
   // with future versions of .proto files.
   std::string unknown_fields_;

-  std::bitset<4> _has_field_{};
+  std::bitset<5> _has_field_{};
 };

 }  // namespace perfetto
@@ -44262,6 +45039,9 @@ namespace protos {
 namespace gen {
 class RedactionRule;
 class SmapsConfig;
+class ProcessSmapsConfig;
+class ProcessSmapsConfig_Scope;
+class ProcessSmapsConfig_Scope_UidRange;
 enum RedactionRule_MatchMode : int;
 enum SmapsConfig_VmaField : int;
 }  // namespace perfetto
@@ -44436,6 +45216,154 @@ class PERFETTO_EXPORT_COMPONENT SmapsConfig : public ::protozero::CppMessageObj
   std::bitset<4> _has_field_{};
 };

+
+class PERFETTO_EXPORT_COMPONENT ProcessSmapsConfig : public ::protozero::CppMessageObj {
+ public:
+  using Scope = ProcessSmapsConfig_Scope;
+  enum FieldNumbers {
+    kSmapsConfigFieldNumber = 1,
+    kScopeFieldNumber = 2,
+    kReadPeriodMsFieldNumber = 3,
+    kMaxProcessesPerPeriodFieldNumber = 4,
+  };
+
+  ProcessSmapsConfig();
+  ~ProcessSmapsConfig() override;
+  ProcessSmapsConfig(ProcessSmapsConfig&&) noexcept;
+  ProcessSmapsConfig& operator=(ProcessSmapsConfig&&);
+  ProcessSmapsConfig(const ProcessSmapsConfig&);
+  ProcessSmapsConfig& operator=(const ProcessSmapsConfig&);
+  bool operator==(const ProcessSmapsConfig&) const;
+  bool operator!=(const ProcessSmapsConfig& other) const { return !(*this == other); }
+
+  bool ParseFromArray(const void*, size_t) override;
+  std::string SerializeAsString() const override;
+  std::vector<uint8_t> SerializeAsArray() const override;
+  void Serialize(::protozero::Message*) const;
+
+  bool has_smaps_config() const { return _has_field_[1]; }
+  const SmapsConfig& smaps_config() const { return *smaps_config_; }
+  SmapsConfig* mutable_smaps_config() { _has_field_.set(1); return smaps_config_.get(); }
+
+  bool has_scope() const { return _has_field_[2]; }
+  const ProcessSmapsConfig_Scope& scope() const { return *scope_; }
+  ProcessSmapsConfig_Scope* mutable_scope() { _has_field_.set(2); return scope_.get(); }
+
+  bool has_read_period_ms() const { return _has_field_[3]; }
+  uint32_t read_period_ms() const { return read_period_ms_; }
+  void set_read_period_ms(uint32_t value) { read_period_ms_ = value; _has_field_.set(3); }
+
+  bool has_max_processes_per_period() const { return _has_field_[4]; }
+  uint32_t max_processes_per_period() const { return max_processes_per_period_; }
+  void set_max_processes_per_period(uint32_t value) { max_processes_per_period_ = value; _has_field_.set(4); }
+
+ private:
+  ::protozero::CopyablePtr<SmapsConfig> smaps_config_;
+  ::protozero::CopyablePtr<ProcessSmapsConfig_Scope> scope_;
+  uint32_t read_period_ms_{};
+  uint32_t max_processes_per_period_{};
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+
+  std::bitset<5> _has_field_{};
+};
+
+
+class PERFETTO_EXPORT_COMPONENT ProcessSmapsConfig_Scope : public ::protozero::CppMessageObj {
+ public:
+  using UidRange = ProcessSmapsConfig_Scope_UidRange;
+  enum FieldNumbers {
+    kTargetCmdlineFieldNumber = 1,
+    kTargetUidFieldNumber = 2,
+  };
+
+  ProcessSmapsConfig_Scope();
+  ~ProcessSmapsConfig_Scope() override;
+  ProcessSmapsConfig_Scope(ProcessSmapsConfig_Scope&&) noexcept;
+  ProcessSmapsConfig_Scope& operator=(ProcessSmapsConfig_Scope&&);
+  ProcessSmapsConfig_Scope(const ProcessSmapsConfig_Scope&);
+  ProcessSmapsConfig_Scope& operator=(const ProcessSmapsConfig_Scope&);
+  bool operator==(const ProcessSmapsConfig_Scope&) const;
+  bool operator!=(const ProcessSmapsConfig_Scope& other) const { return !(*this == other); }
+
+  bool ParseFromArray(const void*, size_t) override;
+  std::string SerializeAsString() const override;
+  std::vector<uint8_t> SerializeAsArray() const override;
+  void Serialize(::protozero::Message*) const;
+
+  const std::vector<std::string>& target_cmdline() const { return target_cmdline_; }
+  std::vector<std::string>* mutable_target_cmdline() { return &target_cmdline_; }
+  int target_cmdline_size() const { return static_cast<int>(target_cmdline_.size()); }
+  void clear_target_cmdline() { target_cmdline_.clear(); }
+  void add_target_cmdline(std::string value) { target_cmdline_.emplace_back(value); }
+  std::string* add_target_cmdline() { target_cmdline_.emplace_back(); return &target_cmdline_.back(); }
+
+  const std::vector<ProcessSmapsConfig_Scope_UidRange>& target_uid() const { return target_uid_; }
+  std::vector<ProcessSmapsConfig_Scope_UidRange>* mutable_target_uid() { return &target_uid_; }
+  int target_uid_size() const;
+  void clear_target_uid();
+  ProcessSmapsConfig_Scope_UidRange* add_target_uid();
+
+ private:
+  std::vector<std::string> target_cmdline_;
+  std::vector<ProcessSmapsConfig_Scope_UidRange> target_uid_;
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+
+  std::bitset<3> _has_field_{};
+};
+
+
+class PERFETTO_EXPORT_COMPONENT ProcessSmapsConfig_Scope_UidRange : public ::protozero::CppMessageObj {
+ public:
+  enum FieldNumbers {
+    kMinUidFieldNumber = 1,
+    kMaxUidFieldNumber = 2,
+    kAndroidMatchAcrossProfilesFieldNumber = 3,
+  };
+
+  ProcessSmapsConfig_Scope_UidRange();
+  ~ProcessSmapsConfig_Scope_UidRange() override;
+  ProcessSmapsConfig_Scope_UidRange(ProcessSmapsConfig_Scope_UidRange&&) noexcept;
+  ProcessSmapsConfig_Scope_UidRange& operator=(ProcessSmapsConfig_Scope_UidRange&&);
+  ProcessSmapsConfig_Scope_UidRange(const ProcessSmapsConfig_Scope_UidRange&);
+  ProcessSmapsConfig_Scope_UidRange& operator=(const ProcessSmapsConfig_Scope_UidRange&);
+  bool operator==(const ProcessSmapsConfig_Scope_UidRange&) const;
+  bool operator!=(const ProcessSmapsConfig_Scope_UidRange& other) const { return !(*this == other); }
+
+  bool ParseFromArray(const void*, size_t) override;
+  std::string SerializeAsString() const override;
+  std::vector<uint8_t> SerializeAsArray() const override;
+  void Serialize(::protozero::Message*) const;
+
+  bool has_min_uid() const { return _has_field_[1]; }
+  uint32_t min_uid() const { return min_uid_; }
+  void set_min_uid(uint32_t value) { min_uid_ = value; _has_field_.set(1); }
+
+  bool has_max_uid() const { return _has_field_[2]; }
+  uint32_t max_uid() const { return max_uid_; }
+  void set_max_uid(uint32_t value) { max_uid_ = value; _has_field_.set(2); }
+
+  bool has_android_match_across_profiles() const { return _has_field_[3]; }
+  bool android_match_across_profiles() const { return android_match_across_profiles_; }
+  void set_android_match_across_profiles(bool value) { android_match_across_profiles_ = value; _has_field_.set(3); }
+
+ private:
+  uint32_t min_uid_{};
+  uint32_t max_uid_{};
+  bool android_match_across_profiles_{};
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+
+  std::bitset<4> _has_field_{};
+};
+
 }  // namespace perfetto
 }  // namespace protos
 }  // namespace gen
@@ -46499,6 +47427,82 @@ class PERFETTO_EXPORT_COMPONENT ChromiumHistogramSamplesConfig_HistogramSample :
 }  // namespace gen

 #endif  // PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_CHROME_HISTOGRAM_SAMPLES_PROTO_CPP_H_
+// gen_amalgamated begin header: gen/protos/perfetto/config/chrome/sampling_heap_profiler.gen.h
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_CHROME_SAMPLING_HEAP_PROFILER_PROTO_CPP_H_
+#define PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_CHROME_SAMPLING_HEAP_PROFILER_PROTO_CPP_H_
+
+#include <stdint.h>
+#include <bitset>
+#include <vector>
+#include <string>
+#include <type_traits>
+
+// gen_amalgamated expanded: #include "perfetto/protozero/cpp_message_obj.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/copyable_ptr.h"
+// gen_amalgamated expanded: #include "perfetto/base/export.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+class ChromiumSamplingHeapProfilerConfig;
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+
+namespace protozero {
+class Message;
+}  // namespace protozero
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+class PERFETTO_EXPORT_COMPONENT ChromiumSamplingHeapProfilerConfig : public ::protozero::CppMessageObj {
+ public:
+  enum FieldNumbers {
+    kSamplingIntervalBytesFieldNumber = 1,
+    kSamplingIntervalMsFieldNumber = 2,
+  };
+
+  ChromiumSamplingHeapProfilerConfig();
+  ~ChromiumSamplingHeapProfilerConfig() override;
+  ChromiumSamplingHeapProfilerConfig(ChromiumSamplingHeapProfilerConfig&&) noexcept;
+  ChromiumSamplingHeapProfilerConfig& operator=(ChromiumSamplingHeapProfilerConfig&&);
+  ChromiumSamplingHeapProfilerConfig(const ChromiumSamplingHeapProfilerConfig&);
+  ChromiumSamplingHeapProfilerConfig& operator=(const ChromiumSamplingHeapProfilerConfig&);
+  bool operator==(const ChromiumSamplingHeapProfilerConfig&) const;
+  bool operator!=(const ChromiumSamplingHeapProfilerConfig& other) const { return !(*this == other); }
+
+  bool ParseFromArray(const void*, size_t) override;
+  std::string SerializeAsString() const override;
+  std::vector<uint8_t> SerializeAsArray() const override;
+  void Serialize(::protozero::Message*) const;
+
+  bool has_sampling_interval_bytes() const { return _has_field_[1]; }
+  uint64_t sampling_interval_bytes() const { return sampling_interval_bytes_; }
+  void set_sampling_interval_bytes(uint64_t value) { sampling_interval_bytes_ = value; _has_field_.set(1); }
+
+  bool has_sampling_interval_ms() const { return _has_field_[2]; }
+  uint32_t sampling_interval_ms() const { return sampling_interval_ms_; }
+  void set_sampling_interval_ms(uint32_t value) { sampling_interval_ms_ = value; _has_field_.set(2); }
+
+ private:
+  uint64_t sampling_interval_bytes_{};
+  uint32_t sampling_interval_ms_{};
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+
+  std::bitset<3> _has_field_{};
+};
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+
+#endif  // PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_CHROME_SAMPLING_HEAP_PROFILER_PROTO_CPP_H_
 // gen_amalgamated begin header: gen/protos/perfetto/config/chrome/scenario_config.gen.h
 // DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
 #ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_CHROME_SCENARIO_CONFIG_PROTO_CPP_H_
@@ -46553,6 +47557,8 @@ class InterceptorConfig;
 class ConsoleConfig;
 class ChromeConfig;
 class SystemInfoConfig;
+class DataSourceConfig_ExperimentalTracingV2Config;
+class DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption;
 class ProtoVmConfig;
 class TraceConfig_BufferConfig;
 enum TraceConfig_LockdownModeOperation : int;
@@ -46974,6 +47980,76 @@ class PERFETTO_EXPORT_COMPONENT NestedScenarioConfig : public ::protozero::CppMe
 }  // namespace gen

 #endif  // PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_CHROME_SCENARIO_CONFIG_PROTO_CPP_H_
+// gen_amalgamated begin header: gen/protos/perfetto/config/chrome/stack_sampling_profiler.gen.h
+// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
+#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_CHROME_STACK_SAMPLING_PROFILER_PROTO_CPP_H_
+#define PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_CHROME_STACK_SAMPLING_PROFILER_PROTO_CPP_H_
+
+#include <stdint.h>
+#include <bitset>
+#include <vector>
+#include <string>
+#include <type_traits>
+
+// gen_amalgamated expanded: #include "perfetto/protozero/cpp_message_obj.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/copyable_ptr.h"
+// gen_amalgamated expanded: #include "perfetto/base/export.h"
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+class ChromiumStackSamplingProfilerConfig;
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+
+namespace protozero {
+class Message;
+}  // namespace protozero
+
+namespace perfetto {
+namespace protos {
+namespace gen {
+
+class PERFETTO_EXPORT_COMPONENT ChromiumStackSamplingProfilerConfig : public ::protozero::CppMessageObj {
+ public:
+  enum FieldNumbers {
+    kSamplingIntervalMsFieldNumber = 1,
+  };
+
+  ChromiumStackSamplingProfilerConfig();
+  ~ChromiumStackSamplingProfilerConfig() override;
+  ChromiumStackSamplingProfilerConfig(ChromiumStackSamplingProfilerConfig&&) noexcept;
+  ChromiumStackSamplingProfilerConfig& operator=(ChromiumStackSamplingProfilerConfig&&);
+  ChromiumStackSamplingProfilerConfig(const ChromiumStackSamplingProfilerConfig&);
+  ChromiumStackSamplingProfilerConfig& operator=(const ChromiumStackSamplingProfilerConfig&);
+  bool operator==(const ChromiumStackSamplingProfilerConfig&) const;
+  bool operator!=(const ChromiumStackSamplingProfilerConfig& other) const { return !(*this == other); }
+
+  bool ParseFromArray(const void*, size_t) override;
+  std::string SerializeAsString() const override;
+  std::vector<uint8_t> SerializeAsArray() const override;
+  void Serialize(::protozero::Message*) const;
+
+  bool has_sampling_interval_ms() const { return _has_field_[1]; }
+  uint32_t sampling_interval_ms() const { return sampling_interval_ms_; }
+  void set_sampling_interval_ms(uint32_t value) { sampling_interval_ms_ = value; _has_field_.set(1); }
+
+ private:
+  uint32_t sampling_interval_ms_{};
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+
+  std::bitset<2> _has_field_{};
+};
+
+}  // namespace perfetto
+}  // namespace protos
+}  // namespace gen
+
+#endif  // PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_CHROME_STACK_SAMPLING_PROFILER_PROTO_CPP_H_
 // gen_amalgamated begin header: gen/protos/perfetto/config/chrome/system_metrics.gen.h
 // DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin
 #ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_CHROME_SYSTEM_METRICS_PROTO_CPP_H_
@@ -47139,6 +48215,7 @@ namespace perfetto {
 namespace protos {
 namespace gen {
 class EtwConfig;
+class EtwConfig_DebugId;
 enum EtwConfig_KernelFlag : int;
 }  // namespace perfetto
 }  // namespace protos
@@ -47158,6 +48235,7 @@ enum EtwConfig_KernelFlag : int {

 class PERFETTO_EXPORT_COMPONENT EtwConfig : public ::protozero::CppMessageObj {
  public:
+  using DebugId = EtwConfig_DebugId;
   using KernelFlag = EtwConfig_KernelFlag;
   static constexpr auto CSWITCH = EtwConfig_KernelFlag_CSWITCH;
   static constexpr auto DISPATCHER = EtwConfig_KernelFlag_DISPATCHER;
@@ -47171,6 +48249,7 @@ class PERFETTO_EXPORT_COMPONENT EtwConfig : public ::protozero::CppMessageObj {
     kStackSamplingEventsFieldNumber = 5,
     kDiskProviderEventsFieldNumber = 6,
     kSystemIoProviderEventsFieldNumber = 7,
+    kStackSamplingDebugIdsFieldNumber = 8,
   };

   EtwConfig();
@@ -47236,6 +48315,12 @@ class PERFETTO_EXPORT_COMPONENT EtwConfig : public ::protozero::CppMessageObj {
   void add_system_io_provider_events(std::string value) { system_io_provider_events_.emplace_back(value); }
   std::string* add_system_io_provider_events() { system_io_provider_events_.emplace_back(); return &system_io_provider_events_.back(); }

+  const std::vector<EtwConfig_DebugId>& stack_sampling_debug_ids() const { return stack_sampling_debug_ids_; }
+  std::vector<EtwConfig_DebugId>* mutable_stack_sampling_debug_ids() { return &stack_sampling_debug_ids_; }
+  int stack_sampling_debug_ids_size() const;
+  void clear_stack_sampling_debug_ids();
+  EtwConfig_DebugId* add_stack_sampling_debug_ids();
+
  private:
   std::vector<EtwConfig_KernelFlag> kernel_flags_;
   std::vector<std::string> scheduler_provider_events_;
@@ -47244,12 +48329,54 @@ class PERFETTO_EXPORT_COMPONENT EtwConfig : public ::protozero::CppMessageObj {
   std::vector<std::string> stack_sampling_events_;
   std::vector<std::string> disk_provider_events_;
   std::vector<std::string> system_io_provider_events_;
+  std::vector<EtwConfig_DebugId> stack_sampling_debug_ids_;

   // Allows to preserve unknown protobuf fields for compatibility
   // with future versions of .proto files.
   std::string unknown_fields_;

-  std::bitset<8> _has_field_{};
+  std::bitset<9> _has_field_{};
+};
+
+
+class PERFETTO_EXPORT_COMPONENT EtwConfig_DebugId : public ::protozero::CppMessageObj {
+ public:
+  enum FieldNumbers {
+    kPathFieldNumber = 1,
+    kDebugIdFieldNumber = 2,
+  };
+
+  EtwConfig_DebugId();
+  ~EtwConfig_DebugId() override;
+  EtwConfig_DebugId(EtwConfig_DebugId&&) noexcept;
+  EtwConfig_DebugId& operator=(EtwConfig_DebugId&&);
+  EtwConfig_DebugId(const EtwConfig_DebugId&);
+  EtwConfig_DebugId& operator=(const EtwConfig_DebugId&);
+  bool operator==(const EtwConfig_DebugId&) const;
+  bool operator!=(const EtwConfig_DebugId& other) const { return !(*this == other); }
+
+  bool ParseFromArray(const void*, size_t) override;
+  std::string SerializeAsString() const override;
+  std::vector<uint8_t> SerializeAsArray() const override;
+  void Serialize(::protozero::Message*) const;
+
+  bool has_path() const { return _has_field_[1]; }
+  const std::string& path() const { return path_; }
+  void set_path(const std::string& value) { path_ = value; _has_field_.set(1); }
+
+  bool has_debug_id() const { return _has_field_[2]; }
+  const std::string& debug_id() const { return debug_id_; }
+  void set_debug_id(const std::string& value) { debug_id_ = value; _has_field_.set(2); }
+
+ private:
+  std::string path_{};
+  std::string debug_id_{};
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+
+  std::bitset<3> _has_field_{};
 };

 }  // namespace perfetto
@@ -47384,6 +48511,8 @@ class InterceptorConfig;
 class ConsoleConfig;
 class ChromeConfig;
 class SystemInfoConfig;
+class DataSourceConfig_ExperimentalTracingV2Config;
+class DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption;
 class ProtoVmConfig;
 class TraceConfig_BufferConfig;
 enum TraceConfig_LockdownModeOperation : int;
@@ -48524,6 +49653,67 @@ class AndroidPolledStateConfig : public ::protozero::Message {
   }
 };

+} // Namespace.
+} // Namespace.
+} // Namespace.
+#endif  // Include guard.
+// gen_amalgamated begin header: gen/protos/perfetto/config/android/android_process_state_config.pbzero.h
+// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT.
+
+#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_ANDROID_ANDROID_PROCESS_STATE_CONFIG_PROTO_H_
+#define PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_ANDROID_ANDROID_PROCESS_STATE_CONFIG_PROTO_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+// gen_amalgamated expanded: #include "perfetto/protozero/field_writer.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+
+
+namespace perfetto {
+namespace protos {
+namespace pbzero {
+
+class AndroidProcessStateConfig_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/1> {
+ public:
+  AndroidProcessStateConfig_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
+  explicit AndroidProcessStateConfig_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
+  explicit AndroidProcessStateConfig_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
+  bool has_dump_process_metadata() const { return at<1>().valid(); }
+  bool dump_process_metadata() const { return at<1>().as_bool(); }
+};
+
+class AndroidProcessStateConfig : public ::protozero::Message {
+ public:
+  using Decoder = AndroidProcessStateConfig_Decoder;
+  enum : int32_t {
+    kDumpProcessMetadataFieldNumber = 1,
+  };
+  static constexpr const char* GetName() { return ".perfetto.protos.AndroidProcessStateConfig"; }
+
+
+  using FieldMetadata_DumpProcessMetadata =
+    ::protozero::proto_utils::FieldMetadata<
+      1,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kBool,
+      bool,
+      AndroidProcessStateConfig>;
+
+  static constexpr FieldMetadata_DumpProcessMetadata kDumpProcessMetadata{};
+  void set_dump_process_metadata(bool value) {
+    static constexpr uint32_t field_id = FieldMetadata_DumpProcessMetadata::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kBool>
+        ::Append(*this, field_id, value);
+  }
+};
+
 } // Namespace.
 } // Namespace.
 } // Namespace.
@@ -52309,7 +53499,7 @@ namespace perfetto {
 namespace protos {
 namespace pbzero {

-class GpuRenderStagesConfig_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/3> {
+class GpuRenderStagesConfig_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/4> {
  public:
   GpuRenderStagesConfig_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
   explicit GpuRenderStagesConfig_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
@@ -52320,6 +53510,8 @@ class GpuRenderStagesConfig_Decoder : public ::protozero::TypedProtoDecoder</*MA
   bool low_overhead() const { return at<2>().as_bool(); }
   bool has_trace_metrics() const { return at<3>().valid(); }
   ::protozero::RepeatedFieldIterator<::protozero::ConstChars> trace_metrics() const { return GetRepeated<::protozero::ConstChars>(3); }
+  bool has_drawcall_stages() const { return at<4>().valid(); }
+  bool drawcall_stages() const { return at<4>().as_bool(); }
 };

 class GpuRenderStagesConfig : public ::protozero::Message {
@@ -52329,6 +53521,7 @@ class GpuRenderStagesConfig : public ::protozero::Message {
     kFullLoadstoreFieldNumber = 1,
     kLowOverheadFieldNumber = 2,
     kTraceMetricsFieldNumber = 3,
+    kDrawcallStagesFieldNumber = 4,
   };
   static constexpr const char* GetName() { return ".perfetto.protos.GpuRenderStagesConfig"; }

@@ -52392,6 +53585,24 @@ class GpuRenderStagesConfig : public ::protozero::Message {
       ::protozero::proto_utils::ProtoSchemaType::kString>
         ::Append(*this, field_id, value);
   }
+
+  using FieldMetadata_DrawcallStages =
+    ::protozero::proto_utils::FieldMetadata<
+      4,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kBool,
+      bool,
+      GpuRenderStagesConfig>;
+
+  static constexpr FieldMetadata_DrawcallStages kDrawcallStages{};
+  void set_drawcall_stages(bool value) {
+    static constexpr uint32_t field_id = FieldMetadata_DrawcallStages::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kBool>
+        ::Append(*this, field_id, value);
+  }
 };

 } // Namespace.
@@ -55432,7 +56643,10 @@ class PerfEventConfig_CallstackSampling : public ::protozero::Message {
 namespace perfetto {
 namespace protos {
 namespace pbzero {
+class ProcessSmapsConfig_Scope;
+class ProcessSmapsConfig_Scope_UidRange;
 class RedactionRule;
+class SmapsConfig;
 namespace perfetto_pbzero_enum_RedactionRule {
 enum MatchMode : int32_t;
 }  // namespace perfetto_pbzero_enum_RedactionRule
@@ -55807,6 +57021,239 @@ class SmapsConfig : public ::protozero::Message {

 };

+class ProcessSmapsConfig_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/4> {
+ public:
+  ProcessSmapsConfig_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
+  explicit ProcessSmapsConfig_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
+  explicit ProcessSmapsConfig_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
+  bool has_smaps_config() const { return at<1>().valid(); }
+  ::protozero::ConstBytes smaps_config() const { return at<1>().as_bytes(); }
+  bool has_scope() const { return at<2>().valid(); }
+  ::protozero::ConstBytes scope() const { return at<2>().as_bytes(); }
+  bool has_read_period_ms() const { return at<3>().valid(); }
+  uint32_t read_period_ms() const { return at<3>().as_uint32(); }
+  bool has_max_processes_per_period() const { return at<4>().valid(); }
+  uint32_t max_processes_per_period() const { return at<4>().as_uint32(); }
+};
+
+class ProcessSmapsConfig : public ::protozero::Message {
+ public:
+  using Decoder = ProcessSmapsConfig_Decoder;
+  enum : int32_t {
+    kSmapsConfigFieldNumber = 1,
+    kScopeFieldNumber = 2,
+    kReadPeriodMsFieldNumber = 3,
+    kMaxProcessesPerPeriodFieldNumber = 4,
+  };
+  static constexpr const char* GetName() { return ".perfetto.protos.ProcessSmapsConfig"; }
+
+  using Scope = ::perfetto::protos::pbzero::ProcessSmapsConfig_Scope;
+
+  using FieldMetadata_SmapsConfig =
+    ::protozero::proto_utils::FieldMetadata<
+      1,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      SmapsConfig,
+      ProcessSmapsConfig>;
+
+  static constexpr FieldMetadata_SmapsConfig kSmapsConfig{};
+  template <typename T = SmapsConfig> T* set_smaps_config() {
+    return BeginNestedMessage<T>(1);
+  }
+
+
+  using FieldMetadata_Scope =
+    ::protozero::proto_utils::FieldMetadata<
+      2,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      ProcessSmapsConfig_Scope,
+      ProcessSmapsConfig>;
+
+  static constexpr FieldMetadata_Scope kScope{};
+  template <typename T = ProcessSmapsConfig_Scope> T* set_scope() {
+    return BeginNestedMessage<T>(2);
+  }
+
+
+  using FieldMetadata_ReadPeriodMs =
+    ::protozero::proto_utils::FieldMetadata<
+      3,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      ProcessSmapsConfig>;
+
+  static constexpr FieldMetadata_ReadPeriodMs kReadPeriodMs{};
+  void set_read_period_ms(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_ReadPeriodMs::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_MaxProcessesPerPeriod =
+    ::protozero::proto_utils::FieldMetadata<
+      4,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      ProcessSmapsConfig>;
+
+  static constexpr FieldMetadata_MaxProcessesPerPeriod kMaxProcessesPerPeriod{};
+  void set_max_processes_per_period(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_MaxProcessesPerPeriod::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+};
+
+class ProcessSmapsConfig_Scope_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/2> {
+ public:
+  ProcessSmapsConfig_Scope_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
+  explicit ProcessSmapsConfig_Scope_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
+  explicit ProcessSmapsConfig_Scope_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
+  bool has_target_cmdline() const { return at<1>().valid(); }
+  ::protozero::RepeatedFieldIterator<::protozero::ConstChars> target_cmdline() const { return GetRepeated<::protozero::ConstChars>(1); }
+  bool has_target_uid() const { return at<2>().valid(); }
+  ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> target_uid() const { return GetRepeated<::protozero::ConstBytes>(2); }
+};
+
+class ProcessSmapsConfig_Scope : public ::protozero::Message {
+ public:
+  using Decoder = ProcessSmapsConfig_Scope_Decoder;
+  enum : int32_t {
+    kTargetCmdlineFieldNumber = 1,
+    kTargetUidFieldNumber = 2,
+  };
+  static constexpr const char* GetName() { return ".perfetto.protos.ProcessSmapsConfig.Scope"; }
+
+  using UidRange = ::perfetto::protos::pbzero::ProcessSmapsConfig_Scope_UidRange;
+
+  using FieldMetadata_TargetCmdline =
+    ::protozero::proto_utils::FieldMetadata<
+      1,
+      ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked,
+      ::protozero::proto_utils::ProtoSchemaType::kString,
+      std::string,
+      ProcessSmapsConfig_Scope>;
+
+  static constexpr FieldMetadata_TargetCmdline kTargetCmdline{};
+  void add_target_cmdline(const char* data, size_t size) {
+    AppendBytes(FieldMetadata_TargetCmdline::kFieldId, data, size);
+  }
+  void add_target_cmdline(::protozero::ConstChars chars) {
+    AppendBytes(FieldMetadata_TargetCmdline::kFieldId, chars.data, chars.size);
+  }
+  void add_target_cmdline(std::string value) {
+    static constexpr uint32_t field_id = FieldMetadata_TargetCmdline::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kString>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_TargetUid =
+    ::protozero::proto_utils::FieldMetadata<
+      2,
+      ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      ProcessSmapsConfig_Scope_UidRange,
+      ProcessSmapsConfig_Scope>;
+
+  static constexpr FieldMetadata_TargetUid kTargetUid{};
+  template <typename T = ProcessSmapsConfig_Scope_UidRange> T* add_target_uid() {
+    return BeginNestedMessage<T>(2);
+  }
+
+};
+
+class ProcessSmapsConfig_Scope_UidRange_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/3> {
+ public:
+  ProcessSmapsConfig_Scope_UidRange_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
+  explicit ProcessSmapsConfig_Scope_UidRange_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
+  explicit ProcessSmapsConfig_Scope_UidRange_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
+  bool has_min_uid() const { return at<1>().valid(); }
+  uint32_t min_uid() const { return at<1>().as_uint32(); }
+  bool has_max_uid() const { return at<2>().valid(); }
+  uint32_t max_uid() const { return at<2>().as_uint32(); }
+  bool has_android_match_across_profiles() const { return at<3>().valid(); }
+  bool android_match_across_profiles() const { return at<3>().as_bool(); }
+};
+
+class ProcessSmapsConfig_Scope_UidRange : public ::protozero::Message {
+ public:
+  using Decoder = ProcessSmapsConfig_Scope_UidRange_Decoder;
+  enum : int32_t {
+    kMinUidFieldNumber = 1,
+    kMaxUidFieldNumber = 2,
+    kAndroidMatchAcrossProfilesFieldNumber = 3,
+  };
+  static constexpr const char* GetName() { return ".perfetto.protos.ProcessSmapsConfig.Scope.UidRange"; }
+
+
+  using FieldMetadata_MinUid =
+    ::protozero::proto_utils::FieldMetadata<
+      1,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      ProcessSmapsConfig_Scope_UidRange>;
+
+  static constexpr FieldMetadata_MinUid kMinUid{};
+  void set_min_uid(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_MinUid::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_MaxUid =
+    ::protozero::proto_utils::FieldMetadata<
+      2,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      ProcessSmapsConfig_Scope_UidRange>;
+
+  static constexpr FieldMetadata_MaxUid kMaxUid{};
+  void set_max_uid(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_MaxUid::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_AndroidMatchAcrossProfiles =
+    ::protozero::proto_utils::FieldMetadata<
+      3,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kBool,
+      bool,
+      ProcessSmapsConfig_Scope_UidRange>;
+
+  static constexpr FieldMetadata_AndroidMatchAcrossProfiles kAndroidMatchAcrossProfiles{};
+  void set_android_match_across_profiles(bool value) {
+    static constexpr uint32_t field_id = FieldMetadata_AndroidMatchAcrossProfiles::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kBool>
+        ::Append(*this, field_id, value);
+  }
+};
+
 } // Namespace.
 } // Namespace.
 } // Namespace.
@@ -62257,6 +63704,88 @@ class ChromiumHistogramSamplesConfig_HistogramSample : public ::protozero::Messa
   }
 };

+} // Namespace.
+} // Namespace.
+} // Namespace.
+#endif  // Include guard.
+// gen_amalgamated begin header: gen/protos/perfetto/config/chrome/sampling_heap_profiler.pbzero.h
+// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT.
+
+#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_CHROME_SAMPLING_HEAP_PROFILER_PROTO_H_
+#define PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_CHROME_SAMPLING_HEAP_PROFILER_PROTO_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+// gen_amalgamated expanded: #include "perfetto/protozero/field_writer.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+
+
+namespace perfetto {
+namespace protos {
+namespace pbzero {
+
+class ChromiumSamplingHeapProfilerConfig_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/2> {
+ public:
+  ChromiumSamplingHeapProfilerConfig_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
+  explicit ChromiumSamplingHeapProfilerConfig_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
+  explicit ChromiumSamplingHeapProfilerConfig_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
+  bool has_sampling_interval_bytes() const { return at<1>().valid(); }
+  uint64_t sampling_interval_bytes() const { return at<1>().as_uint64(); }
+  bool has_sampling_interval_ms() const { return at<2>().valid(); }
+  uint32_t sampling_interval_ms() const { return at<2>().as_uint32(); }
+};
+
+class ChromiumSamplingHeapProfilerConfig : public ::protozero::Message {
+ public:
+  using Decoder = ChromiumSamplingHeapProfilerConfig_Decoder;
+  enum : int32_t {
+    kSamplingIntervalBytesFieldNumber = 1,
+    kSamplingIntervalMsFieldNumber = 2,
+  };
+  static constexpr const char* GetName() { return ".perfetto.protos.ChromiumSamplingHeapProfilerConfig"; }
+
+
+  using FieldMetadata_SamplingIntervalBytes =
+    ::protozero::proto_utils::FieldMetadata<
+      1,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint64,
+      uint64_t,
+      ChromiumSamplingHeapProfilerConfig>;
+
+  static constexpr FieldMetadata_SamplingIntervalBytes kSamplingIntervalBytes{};
+  void set_sampling_interval_bytes(uint64_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_SamplingIntervalBytes::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint64>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_SamplingIntervalMs =
+    ::protozero::proto_utils::FieldMetadata<
+      2,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      ChromiumSamplingHeapProfilerConfig>;
+
+  static constexpr FieldMetadata_SamplingIntervalMs kSamplingIntervalMs{};
+  void set_sampling_interval_ms(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_SamplingIntervalMs::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+};
+
 } // Namespace.
 } // Namespace.
 } // Namespace.
@@ -62958,6 +64487,67 @@ class TriggerRule_HistogramTrigger : public ::protozero::Message {
   }
 };

+} // Namespace.
+} // Namespace.
+} // Namespace.
+#endif  // Include guard.
+// gen_amalgamated begin header: gen/protos/perfetto/config/chrome/stack_sampling_profiler.pbzero.h
+// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT.
+
+#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_CHROME_STACK_SAMPLING_PROFILER_PROTO_H_
+#define PERFETTO_PROTOS_PROTOS_PERFETTO_CONFIG_CHROME_STACK_SAMPLING_PROFILER_PROTO_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+// gen_amalgamated expanded: #include "perfetto/protozero/field_writer.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+
+
+namespace perfetto {
+namespace protos {
+namespace pbzero {
+
+class ChromiumStackSamplingProfilerConfig_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/1> {
+ public:
+  ChromiumStackSamplingProfilerConfig_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
+  explicit ChromiumStackSamplingProfilerConfig_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
+  explicit ChromiumStackSamplingProfilerConfig_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
+  bool has_sampling_interval_ms() const { return at<1>().valid(); }
+  uint32_t sampling_interval_ms() const { return at<1>().as_uint32(); }
+};
+
+class ChromiumStackSamplingProfilerConfig : public ::protozero::Message {
+ public:
+  using Decoder = ChromiumStackSamplingProfilerConfig_Decoder;
+  enum : int32_t {
+    kSamplingIntervalMsFieldNumber = 1,
+  };
+  static constexpr const char* GetName() { return ".perfetto.protos.ChromiumStackSamplingProfilerConfig"; }
+
+
+  using FieldMetadata_SamplingIntervalMs =
+    ::protozero::proto_utils::FieldMetadata<
+      1,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      ChromiumStackSamplingProfilerConfig>;
+
+  static constexpr FieldMetadata_SamplingIntervalMs kSamplingIntervalMs{};
+  void set_sampling_interval_ms(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_SamplingIntervalMs::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+};
+
 } // Namespace.
 } // Namespace.
 } // Namespace.
@@ -63129,14 +64719,19 @@ class AndroidInputEventConfig;
 class AndroidLogConfig;
 class AndroidPolledStateConfig;
 class AndroidPowerConfig;
+class AndroidProcessStateConfig;
 class AndroidSdkSyspropGuardConfig;
 class AndroidSystemPropertyConfig;
 class AndroidUserListConfig;
 class AppWakelocksConfig;
 class ChromeConfig;
 class ChromiumHistogramSamplesConfig;
+class ChromiumSamplingHeapProfilerConfig;
+class ChromiumStackSamplingProfilerConfig;
 class ChromiumSystemMetricsConfig;
 class CpuPerUidConfig;
+class DataSourceConfig_ExperimentalTracingV2Config;
+class DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption;
 class DisplayVideoConfig;
 class EtwConfig;
 class FrozenFtraceConfig;
@@ -63154,6 +64749,7 @@ class PackagesListConfig;
 class PerfEventConfig;
 class PixelModemConfig;
 class PriorityBoostConfig;
+class ProcessSmapsConfig;
 class ProcessStatsConfig;
 class ProtoLogConfig;
 class ProtoVmConfig;
@@ -63272,6 +64868,10 @@ class DataSourceConfig_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIE
   ::protozero::ConstBytes priority_boost() const { return at<10>().as_bytes(); }
   bool has_protovm_config() const { return at<12>().valid(); }
   ::protozero::ConstBytes protovm_config() const { return at<12>().as_bytes(); }
+  bool has_supports_tracing_v2() const { return at<147>().valid(); }
+  bool supports_tracing_v2() const { return at<147>().as_bool(); }
+  bool has_experimental_tracing_v2() const { return at<148>().valid(); }
+  ::protozero::ConstBytes experimental_tracing_v2() const { return at<148>().as_bytes(); }
   bool has_ftrace_config() const { return at<100>().valid(); }
   ::protozero::ConstBytes ftrace_config() const { return at<100>().as_bytes(); }
   bool has_inode_file_config() const { return at<102>().valid(); }
@@ -63356,6 +64956,14 @@ class DataSourceConfig_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIE
   ::protozero::ConstBytes journald_config() const { return at<141>().as_bytes(); }
   bool has_display_video_config() const { return at<142>().valid(); }
   ::protozero::ConstBytes display_video_config() const { return at<142>().as_bytes(); }
+  bool has_chromium_stack_sampling_profiler() const { return at<143>().valid(); }
+  ::protozero::ConstBytes chromium_stack_sampling_profiler() const { return at<143>().as_bytes(); }
+  bool has_chromium_sampling_heap_profiler() const { return at<144>().valid(); }
+  ::protozero::ConstBytes chromium_sampling_heap_profiler() const { return at<144>().as_bytes(); }
+  bool has_android_process_state_config() const { return at<145>().valid(); }
+  ::protozero::ConstBytes android_process_state_config() const { return at<145>().as_bytes(); }
+  bool has_process_smaps_config() const { return at<146>().valid(); }
+  ::protozero::ConstBytes process_smaps_config() const { return at<146>().as_bytes(); }
   bool has_qnx_config() const { return at<150>().valid(); }
   ::protozero::ConstBytes qnx_config() const { return at<150>().as_bytes(); }
   // field legacy_config omitted because its id is too high
@@ -63378,6 +64986,8 @@ class DataSourceConfig : public ::protozero::Message {
     kBufferExhaustedPolicyFieldNumber = 9,
     kPriorityBoostFieldNumber = 10,
     kProtovmConfigFieldNumber = 12,
+    kSupportsTracingV2FieldNumber = 147,
+    kExperimentalTracingV2FieldNumber = 148,
     kFtraceConfigFieldNumber = 100,
     kInodeFileConfigFieldNumber = 102,
     kProcessStatsConfigFieldNumber = 103,
@@ -63420,12 +65030,17 @@ class DataSourceConfig : public ::protozero::Message {
     kAndroidAflagsConfigFieldNumber = 140,
     kJournaldConfigFieldNumber = 141,
     kDisplayVideoConfigFieldNumber = 142,
+    kChromiumStackSamplingProfilerFieldNumber = 143,
+    kChromiumSamplingHeapProfilerFieldNumber = 144,
+    kAndroidProcessStateConfigFieldNumber = 145,
+    kProcessSmapsConfigFieldNumber = 146,
     kQnxConfigFieldNumber = 150,
     kLegacyConfigFieldNumber = 1000,
     kForTestingFieldNumber = 1001,
   };
   static constexpr const char* GetName() { return ".perfetto.protos.DataSourceConfig"; }

+  using ExperimentalTracingV2Config = ::perfetto::protos::pbzero::DataSourceConfig_ExperimentalTracingV2Config;

   using SessionInitiator = ::perfetto::protos::pbzero::DataSourceConfig_SessionInitiator;
   static inline const char* SessionInitiator_Name(SessionInitiator value) {
@@ -63663,6 +65278,38 @@ class DataSourceConfig : public ::protozero::Message {
   }


+  using FieldMetadata_SupportsTracingV2 =
+    ::protozero::proto_utils::FieldMetadata<
+      147,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kBool,
+      bool,
+      DataSourceConfig>;
+
+  static constexpr FieldMetadata_SupportsTracingV2 kSupportsTracingV2{};
+  void set_supports_tracing_v2(bool value) {
+    static constexpr uint32_t field_id = FieldMetadata_SupportsTracingV2::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kBool>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_ExperimentalTracingV2 =
+    ::protozero::proto_utils::FieldMetadata<
+      148,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      DataSourceConfig_ExperimentalTracingV2Config,
+      DataSourceConfig>;
+
+  static constexpr FieldMetadata_ExperimentalTracingV2 kExperimentalTracingV2{};
+  template <typename T = DataSourceConfig_ExperimentalTracingV2Config> T* set_experimental_tracing_v2() {
+    return BeginNestedMessage<T>(148);
+  }
+
+
   using FieldMetadata_FtraceConfig =
     ::protozero::proto_utils::FieldMetadata<
       100,
@@ -64407,6 +66054,78 @@ class DataSourceConfig : public ::protozero::Message {
   }


+  using FieldMetadata_ChromiumStackSamplingProfiler =
+    ::protozero::proto_utils::FieldMetadata<
+      143,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      ChromiumStackSamplingProfilerConfig,
+      DataSourceConfig>;
+
+  static constexpr FieldMetadata_ChromiumStackSamplingProfiler kChromiumStackSamplingProfiler{};
+  template <typename T = ChromiumStackSamplingProfilerConfig> T* set_chromium_stack_sampling_profiler() {
+    return BeginNestedMessage<T>(143);
+  }
+
+  void set_chromium_stack_sampling_profiler_raw(const std::string& raw) {
+    return AppendBytes(143, raw.data(), raw.size());
+  }
+
+
+  using FieldMetadata_ChromiumSamplingHeapProfiler =
+    ::protozero::proto_utils::FieldMetadata<
+      144,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      ChromiumSamplingHeapProfilerConfig,
+      DataSourceConfig>;
+
+  static constexpr FieldMetadata_ChromiumSamplingHeapProfiler kChromiumSamplingHeapProfiler{};
+  template <typename T = ChromiumSamplingHeapProfilerConfig> T* set_chromium_sampling_heap_profiler() {
+    return BeginNestedMessage<T>(144);
+  }
+
+  void set_chromium_sampling_heap_profiler_raw(const std::string& raw) {
+    return AppendBytes(144, raw.data(), raw.size());
+  }
+
+
+  using FieldMetadata_AndroidProcessStateConfig =
+    ::protozero::proto_utils::FieldMetadata<
+      145,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      AndroidProcessStateConfig,
+      DataSourceConfig>;
+
+  static constexpr FieldMetadata_AndroidProcessStateConfig kAndroidProcessStateConfig{};
+  template <typename T = AndroidProcessStateConfig> T* set_android_process_state_config() {
+    return BeginNestedMessage<T>(145);
+  }
+
+  void set_android_process_state_config_raw(const std::string& raw) {
+    return AppendBytes(145, raw.data(), raw.size());
+  }
+
+
+  using FieldMetadata_ProcessSmapsConfig =
+    ::protozero::proto_utils::FieldMetadata<
+      146,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      ProcessSmapsConfig,
+      DataSourceConfig>;
+
+  static constexpr FieldMetadata_ProcessSmapsConfig kProcessSmapsConfig{};
+  template <typename T = ProcessSmapsConfig> T* set_process_smaps_config() {
+    return BeginNestedMessage<T>(146);
+  }
+
+  void set_process_smaps_config_raw(const std::string& raw) {
+    return AppendBytes(146, raw.data(), raw.size());
+  }
+
+
   using FieldMetadata_QnxConfig =
     ::protozero::proto_utils::FieldMetadata<
       150,
@@ -64464,6 +66183,140 @@ class DataSourceConfig : public ::protozero::Message {

 };

+class DataSourceConfig_ExperimentalTracingV2Config_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/3> {
+ public:
+  DataSourceConfig_ExperimentalTracingV2Config_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
+  explicit DataSourceConfig_ExperimentalTracingV2Config_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
+  explicit DataSourceConfig_ExperimentalTracingV2Config_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
+  bool has_use_v2_probability_percent() const { return at<1>().valid(); }
+  uint32_t use_v2_probability_percent() const { return at<1>().as_uint32(); }
+  bool has_chunk_size_options() const { return at<2>().valid(); }
+  ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> chunk_size_options() const { return GetRepeated<::protozero::ConstBytes>(2); }
+  bool has_drain_occupancy_percent() const { return at<3>().valid(); }
+  int32_t drain_occupancy_percent() const { return at<3>().as_int32(); }
+};
+
+class DataSourceConfig_ExperimentalTracingV2Config : public ::protozero::Message {
+ public:
+  using Decoder = DataSourceConfig_ExperimentalTracingV2Config_Decoder;
+  enum : int32_t {
+    kUseV2ProbabilityPercentFieldNumber = 1,
+    kChunkSizeOptionsFieldNumber = 2,
+    kDrainOccupancyPercentFieldNumber = 3,
+  };
+  static constexpr const char* GetName() { return ".perfetto.protos.DataSourceConfig.ExperimentalTracingV2Config"; }
+
+  using ChunkSizeOption = ::perfetto::protos::pbzero::DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption;
+
+  using FieldMetadata_UseV2ProbabilityPercent =
+    ::protozero::proto_utils::FieldMetadata<
+      1,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      DataSourceConfig_ExperimentalTracingV2Config>;
+
+  static constexpr FieldMetadata_UseV2ProbabilityPercent kUseV2ProbabilityPercent{};
+  void set_use_v2_probability_percent(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_UseV2ProbabilityPercent::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_ChunkSizeOptions =
+    ::protozero::proto_utils::FieldMetadata<
+      2,
+      ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption,
+      DataSourceConfig_ExperimentalTracingV2Config>;
+
+  static constexpr FieldMetadata_ChunkSizeOptions kChunkSizeOptions{};
+  template <typename T = DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption> T* add_chunk_size_options() {
+    return BeginNestedMessage<T>(2);
+  }
+
+
+  using FieldMetadata_DrainOccupancyPercent =
+    ::protozero::proto_utils::FieldMetadata<
+      3,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kInt32,
+      int32_t,
+      DataSourceConfig_ExperimentalTracingV2Config>;
+
+  static constexpr FieldMetadata_DrainOccupancyPercent kDrainOccupancyPercent{};
+  void set_drain_occupancy_percent(int32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_DrainOccupancyPercent::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kInt32>
+        ::Append(*this, field_id, value);
+  }
+};
+
+class DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/2> {
+ public:
+  DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
+  explicit DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
+  explicit DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
+  bool has_size_bytes() const { return at<1>().valid(); }
+  uint32_t size_bytes() const { return at<1>().as_uint32(); }
+  bool has_weight() const { return at<2>().valid(); }
+  uint32_t weight() const { return at<2>().as_uint32(); }
+};
+
+class DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption : public ::protozero::Message {
+ public:
+  using Decoder = DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption_Decoder;
+  enum : int32_t {
+    kSizeBytesFieldNumber = 1,
+    kWeightFieldNumber = 2,
+  };
+  static constexpr const char* GetName() { return ".perfetto.protos.DataSourceConfig.ExperimentalTracingV2Config.ChunkSizeOption"; }
+
+
+  using FieldMetadata_SizeBytes =
+    ::protozero::proto_utils::FieldMetadata<
+      1,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption>;
+
+  static constexpr FieldMetadata_SizeBytes kSizeBytes{};
+  void set_size_bytes(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_SizeBytes::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_Weight =
+    ::protozero::proto_utils::FieldMetadata<
+      2,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption>;
+
+  static constexpr FieldMetadata_Weight kWeight{};
+  void set_weight(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_Weight::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+};
+
 } // Namespace.
 } // Namespace.
 } // Namespace.
@@ -64486,6 +66339,7 @@ class DataSourceConfig : public ::protozero::Message {
 namespace perfetto {
 namespace protos {
 namespace pbzero {
+class EtwConfig_DebugId;
 namespace perfetto_pbzero_enum_EtwConfig {
 enum KernelFlag : int32_t;
 }  // namespace perfetto_pbzero_enum_EtwConfig
@@ -64523,7 +66377,7 @@ const char* EtwConfig_KernelFlag_Name(::perfetto::protos::pbzero::EtwConfig_Kern
   return "PBZERO_UNKNOWN_ENUM_VALUE";
 }

-class EtwConfig_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/7> {
+class EtwConfig_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/8> {
  public:
   EtwConfig_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
   explicit EtwConfig_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
@@ -64542,6 +66396,8 @@ class EtwConfig_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*
   ::protozero::RepeatedFieldIterator<::protozero::ConstChars> disk_provider_events() const { return GetRepeated<::protozero::ConstChars>(6); }
   bool has_system_io_provider_events() const { return at<7>().valid(); }
   ::protozero::RepeatedFieldIterator<::protozero::ConstChars> system_io_provider_events() const { return GetRepeated<::protozero::ConstChars>(7); }
+  bool has_stack_sampling_debug_ids() const { return at<8>().valid(); }
+  ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> stack_sampling_debug_ids() const { return GetRepeated<::protozero::ConstBytes>(8); }
 };

 class EtwConfig : public ::protozero::Message {
@@ -64555,9 +66411,11 @@ class EtwConfig : public ::protozero::Message {
     kStackSamplingEventsFieldNumber = 5,
     kDiskProviderEventsFieldNumber = 6,
     kSystemIoProviderEventsFieldNumber = 7,
+    kStackSamplingDebugIdsFieldNumber = 8,
   };
   static constexpr const char* GetName() { return ".perfetto.protos.EtwConfig"; }

+  using DebugId = ::perfetto::protos::pbzero::EtwConfig_DebugId;

   using KernelFlag = ::perfetto::protos::pbzero::EtwConfig_KernelFlag;
   static inline const char* KernelFlag_Name(KernelFlag value) {
@@ -64727,6 +66585,90 @@ class EtwConfig : public ::protozero::Message {
       ::protozero::proto_utils::ProtoSchemaType::kString>
         ::Append(*this, field_id, value);
   }
+
+  using FieldMetadata_StackSamplingDebugIds =
+    ::protozero::proto_utils::FieldMetadata<
+      8,
+      ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      EtwConfig_DebugId,
+      EtwConfig>;
+
+  static constexpr FieldMetadata_StackSamplingDebugIds kStackSamplingDebugIds{};
+  template <typename T = EtwConfig_DebugId> T* add_stack_sampling_debug_ids() {
+    return BeginNestedMessage<T>(8);
+  }
+
+};
+
+class EtwConfig_DebugId_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/2> {
+ public:
+  EtwConfig_DebugId_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
+  explicit EtwConfig_DebugId_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
+  explicit EtwConfig_DebugId_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
+  bool has_path() const { return at<1>().valid(); }
+  ::protozero::ConstChars path() const { return at<1>().as_string(); }
+  bool has_debug_id() const { return at<2>().valid(); }
+  ::protozero::ConstChars debug_id() const { return at<2>().as_string(); }
+};
+
+class EtwConfig_DebugId : public ::protozero::Message {
+ public:
+  using Decoder = EtwConfig_DebugId_Decoder;
+  enum : int32_t {
+    kPathFieldNumber = 1,
+    kDebugIdFieldNumber = 2,
+  };
+  static constexpr const char* GetName() { return ".perfetto.protos.EtwConfig.DebugId"; }
+
+
+  using FieldMetadata_Path =
+    ::protozero::proto_utils::FieldMetadata<
+      1,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kString,
+      std::string,
+      EtwConfig_DebugId>;
+
+  static constexpr FieldMetadata_Path kPath{};
+  void set_path(const char* data, size_t size) {
+    AppendBytes(FieldMetadata_Path::kFieldId, data, size);
+  }
+  void set_path(::protozero::ConstChars chars) {
+    AppendBytes(FieldMetadata_Path::kFieldId, chars.data, chars.size);
+  }
+  void set_path(std::string value) {
+    static constexpr uint32_t field_id = FieldMetadata_Path::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kString>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_DebugId =
+    ::protozero::proto_utils::FieldMetadata<
+      2,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kString,
+      std::string,
+      EtwConfig_DebugId>;
+
+  static constexpr FieldMetadata_DebugId kDebugId{};
+  void set_debug_id(const char* data, size_t size) {
+    AppendBytes(FieldMetadata_DebugId::kFieldId, data, size);
+  }
+  void set_debug_id(::protozero::ConstChars chars) {
+    AppendBytes(FieldMetadata_DebugId::kFieldId, chars.data, chars.size);
+  }
+  void set_debug_id(std::string value) {
+    static constexpr uint32_t field_id = FieldMetadata_DebugId::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kString>
+        ::Append(*this, field_id, value);
+  }
 };

 } // Namespace.
@@ -66064,13 +68006,15 @@ const char* TraceConfig_BufferConfig_ExperimentalMode_Name(::perfetto::protos::p
   return "PBZERO_UNKNOWN_ENUM_VALUE";
 }

-class TraceConfig_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/48> {
+class TraceConfig_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/50> {
  public:
   TraceConfig_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
   explicit TraceConfig_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
   explicit TraceConfig_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
   bool has_buffers() const { return at<1>().valid(); }
   ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> buffers() const { return GetRepeated<::protozero::ConstBytes>(1); }
+  bool has_experimental_trace_buffer_v2_compaction() const { return at<50>().valid(); }
+  bool experimental_trace_buffer_v2_compaction() const { return at<50>().as_bool(); }
   bool has_data_sources() const { return at<2>().valid(); }
   ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> data_sources() const { return GetRepeated<::protozero::ConstBytes>(2); }
   bool has_builtin_data_sources() const { return at<20>().valid(); }
@@ -66153,6 +68097,8 @@ class TraceConfig_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID
   bool trace_all_machines() const { return at<43>().as_bool(); }
   bool has_trace_attributes() const { return at<48>().valid(); }
   ::protozero::ConstBytes trace_attributes() const { return at<48>().as_bytes(); }
+  bool has_persist_trace_across_reboots() const { return at<49>().valid(); }
+  bool persist_trace_across_reboots() const { return at<49>().as_bool(); }
 };

 class TraceConfig : public ::protozero::Message {
@@ -66160,6 +68106,7 @@ class TraceConfig : public ::protozero::Message {
   using Decoder = TraceConfig_Decoder;
   enum : int32_t {
     kBuffersFieldNumber = 1,
+    kExperimentalTraceBufferV2CompactionFieldNumber = 50,
     kDataSourcesFieldNumber = 2,
     kBuiltinDataSourcesFieldNumber = 20,
     kDurationMsFieldNumber = 3,
@@ -66201,6 +68148,7 @@ class TraceConfig : public ::protozero::Message {
     kFflushPostWriteFieldNumber = 45,
     kTraceAllMachinesFieldNumber = 43,
     kTraceAttributesFieldNumber = 48,
+    kPersistTraceAcrossRebootsFieldNumber = 49,
   };
   static constexpr const char* GetName() { return ".perfetto.protos.TraceConfig"; }

@@ -66273,6 +68221,24 @@ class TraceConfig : public ::protozero::Message {
   }


+  using FieldMetadata_ExperimentalTraceBufferV2Compaction =
+    ::protozero::proto_utils::FieldMetadata<
+      50,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kBool,
+      bool,
+      TraceConfig>;
+
+  static constexpr FieldMetadata_ExperimentalTraceBufferV2Compaction kExperimentalTraceBufferV2Compaction{};
+  void set_experimental_trace_buffer_v2_compaction(bool value) {
+    static constexpr uint32_t field_id = FieldMetadata_ExperimentalTraceBufferV2Compaction::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kBool>
+        ::Append(*this, field_id, value);
+  }
+
   using FieldMetadata_DataSources =
     ::protozero::proto_utils::FieldMetadata<
       2,
@@ -66974,6 +68940,24 @@ class TraceConfig : public ::protozero::Message {
     return BeginNestedMessage<T>(48);
   }

+
+  using FieldMetadata_PersistTraceAcrossReboots =
+    ::protozero::proto_utils::FieldMetadata<
+      49,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kBool,
+      bool,
+      TraceConfig>;
+
+  static constexpr FieldMetadata_PersistTraceAcrossReboots kPersistTraceAcrossReboots{};
+  void set_persist_trace_across_reboots(bool value) {
+    static constexpr uint32_t field_id = FieldMetadata_PersistTraceAcrossReboots::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kBool>
+        ::Append(*this, field_id, value);
+  }
 };

 class TraceConfig_SessionSemaphore_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/2> {
@@ -71535,7 +73519,7 @@ class PackagesList : public ::protozero::Message {
   }
 };

-class PackagesList_PackageInfo_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/5> {
+class PackagesList_PackageInfo_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/6> {
  public:
   PackagesList_PackageInfo_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
   explicit PackagesList_PackageInfo_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
@@ -71550,6 +73534,8 @@ class PackagesList_PackageInfo_Decoder : public ::protozero::TypedProtoDecoder</
   bool profileable_from_shell() const { return at<4>().as_bool(); }
   bool has_version_code() const { return at<5>().valid(); }
   int64_t version_code() const { return at<5>().as_int64(); }
+  bool has_profileable() const { return at<6>().valid(); }
+  bool profileable() const { return at<6>().as_bool(); }
 };

 class PackagesList_PackageInfo : public ::protozero::Message {
@@ -71561,6 +73547,7 @@ class PackagesList_PackageInfo : public ::protozero::Message {
     kDebuggableFieldNumber = 3,
     kProfileableFromShellFieldNumber = 4,
     kVersionCodeFieldNumber = 5,
+    kProfileableFieldNumber = 6,
   };
   static constexpr const char* GetName() { return ".perfetto.protos.PackagesList.PackageInfo"; }

@@ -71660,6 +73647,24 @@ class PackagesList_PackageInfo : public ::protozero::Message {
       ::protozero::proto_utils::ProtoSchemaType::kInt64>
         ::Append(*this, field_id, value);
   }
+
+  using FieldMetadata_Profileable =
+    ::protozero::proto_utils::FieldMetadata<
+      6,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kBool,
+      bool,
+      PackagesList_PackageInfo>;
+
+  static constexpr FieldMetadata_Profileable kProfileable{};
+  void set_profileable(bool value) {
+    static constexpr uint32_t field_id = FieldMetadata_Profileable::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kBool>
+        ::Append(*this, field_id, value);
+  }
 };

 } // Namespace.
@@ -71793,6 +73798,151 @@ class PixelModemEvents : public ::protozero::Message {
   }
 };

+} // Namespace.
+} // Namespace.
+} // Namespace.
+#endif  // Include guard.
+// gen_amalgamated begin header: gen/protos/perfetto/trace/android/recovered_trace_info.pbzero.h
+// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT.
+
+#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_ANDROID_RECOVERED_TRACE_INFO_PROTO_H_
+#define PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_ANDROID_RECOVERED_TRACE_INFO_PROTO_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+// gen_amalgamated expanded: #include "perfetto/protozero/field_writer.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+
+namespace perfetto {
+namespace protos {
+namespace pbzero {
+namespace perfetto_pbzero_enum_RecoveredTraceInfo {
+enum Reason : int32_t;
+}  // namespace perfetto_pbzero_enum_RecoveredTraceInfo
+using RecoveredTraceInfo_Reason = perfetto_pbzero_enum_RecoveredTraceInfo::Reason;
+} // Namespace pbzero.
+} // Namespace protos.
+} // Namespace perfetto.
+
+namespace perfetto {
+namespace protos {
+namespace pbzero {
+
+namespace perfetto_pbzero_enum_RecoveredTraceInfo {
+enum Reason : int32_t {
+  REASON_UNSPECIFIED = 0,
+  REASON_UNEXPECTED_REBOOT = 1,
+};
+} // namespace perfetto_pbzero_enum_RecoveredTraceInfo
+using RecoveredTraceInfo_Reason = perfetto_pbzero_enum_RecoveredTraceInfo::Reason;
+
+
+constexpr RecoveredTraceInfo_Reason RecoveredTraceInfo_Reason_MIN = RecoveredTraceInfo_Reason::REASON_UNSPECIFIED;
+constexpr RecoveredTraceInfo_Reason RecoveredTraceInfo_Reason_MAX = RecoveredTraceInfo_Reason::REASON_UNEXPECTED_REBOOT;
+
+
+PERFETTO_PROTOZERO_CONSTEXPR14_OR_INLINE
+const char* RecoveredTraceInfo_Reason_Name(::perfetto::protos::pbzero::RecoveredTraceInfo_Reason value) {
+  switch (value) {
+  case ::perfetto::protos::pbzero::RecoveredTraceInfo_Reason::REASON_UNSPECIFIED:
+    return "REASON_UNSPECIFIED";
+
+  case ::perfetto::protos::pbzero::RecoveredTraceInfo_Reason::REASON_UNEXPECTED_REBOOT:
+    return "REASON_UNEXPECTED_REBOOT";
+  }
+  return "PBZERO_UNKNOWN_ENUM_VALUE";
+}
+
+class RecoveredTraceInfo_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/3> {
+ public:
+  RecoveredTraceInfo_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
+  explicit RecoveredTraceInfo_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
+  explicit RecoveredTraceInfo_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
+  bool has_reason() const { return at<1>().valid(); }
+  int32_t reason() const { return at<1>().as_int32(); }
+  bool has_original_file_size_bytes() const { return at<2>().valid(); }
+  uint64_t original_file_size_bytes() const { return at<2>().as_uint64(); }
+  bool has_bytes_truncated() const { return at<3>().valid(); }
+  uint64_t bytes_truncated() const { return at<3>().as_uint64(); }
+};
+
+class RecoveredTraceInfo : public ::protozero::Message {
+ public:
+  using Decoder = RecoveredTraceInfo_Decoder;
+  enum : int32_t {
+    kReasonFieldNumber = 1,
+    kOriginalFileSizeBytesFieldNumber = 2,
+    kBytesTruncatedFieldNumber = 3,
+  };
+  static constexpr const char* GetName() { return ".perfetto.protos.RecoveredTraceInfo"; }
+
+
+  using Reason = ::perfetto::protos::pbzero::RecoveredTraceInfo_Reason;
+  static inline const char* Reason_Name(Reason value) {
+    return ::perfetto::protos::pbzero::RecoveredTraceInfo_Reason_Name(value);
+  }
+  static inline const Reason REASON_UNSPECIFIED = Reason::REASON_UNSPECIFIED;
+  static inline const Reason REASON_UNEXPECTED_REBOOT = Reason::REASON_UNEXPECTED_REBOOT;
+
+  using FieldMetadata_Reason =
+    ::protozero::proto_utils::FieldMetadata<
+      1,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kEnum,
+      RecoveredTraceInfo_Reason,
+      RecoveredTraceInfo>;
+
+  static constexpr FieldMetadata_Reason kReason{};
+  void set_reason(RecoveredTraceInfo_Reason value) {
+    static constexpr uint32_t field_id = FieldMetadata_Reason::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kEnum>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_OriginalFileSizeBytes =
+    ::protozero::proto_utils::FieldMetadata<
+      2,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint64,
+      uint64_t,
+      RecoveredTraceInfo>;
+
+  static constexpr FieldMetadata_OriginalFileSizeBytes kOriginalFileSizeBytes{};
+  void set_original_file_size_bytes(uint64_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_OriginalFileSizeBytes::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint64>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_BytesTruncated =
+    ::protozero::proto_utils::FieldMetadata<
+      3,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint64,
+      uint64_t,
+      RecoveredTraceInfo>;
+
+  static constexpr FieldMetadata_BytesTruncated kBytesTruncated{};
+  void set_bytes_truncated(uint64_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_BytesTruncated::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint64>
+        ::Append(*this, field_id, value);
+  }
+};
+
 } // Namespace.
 } // Namespace.
 } // Namespace.
@@ -80223,6 +82373,8 @@ class KvmVcpuWakeupFtraceEvent;
 class KvmWfxArm64FtraceEvent;
 class LocalTimerEntryFtraceEvent;
 class LocalTimerExitFtraceEvent;
+class LongIrqFtraceEvent;
+class LongSoftirqFtraceEvent;
 class LowmemoryKillFtraceEvent;
 class LwisTracingMarkWriteFtraceEvent;
 class MaliGpuPowerStateFtraceEvent;
@@ -80446,7 +82598,7 @@ namespace perfetto {
 namespace protos {
 namespace pbzero {

-class FtraceEvent_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/615> {
+class FtraceEvent_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/617> {
  public:
   FtraceEvent_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
   explicit FtraceEvent_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
@@ -81645,6 +83797,10 @@ class FtraceEvent_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID
   ::protozero::ConstBytes psci_domain_idle_exit() const { return at<614>().as_bytes(); }
   bool has_psci_domain_idle_enter() const { return at<615>().valid(); }
   ::protozero::ConstBytes psci_domain_idle_enter() const { return at<615>().as_bytes(); }
+  bool has_long_irq() const { return at<616>().valid(); }
+  ::protozero::ConstBytes long_irq() const { return at<616>().as_bytes(); }
+  bool has_long_softirq() const { return at<617>().valid(); }
+  ::protozero::ConstBytes long_softirq() const { return at<617>().as_bytes(); }
 };

 class FtraceEvent : public ::protozero::Message {
@@ -82248,6 +84404,8 @@ class FtraceEvent : public ::protozero::Message {
     kDpuDispDpuLineUnderrunFieldNumber = 613,
     kPsciDomainIdleExitFieldNumber = 614,
     kPsciDomainIdleEnterFieldNumber = 615,
+    kLongIrqFieldNumber = 616,
+    kLongSoftirqFieldNumber = 617,
   };
   static constexpr const char* GetName() { return ".perfetto.protos.FtraceEvent"; }

@@ -90621,6 +92779,34 @@ class FtraceEvent : public ::protozero::Message {
     return BeginNestedMessage<T>(615);
   }

+
+  using FieldMetadata_LongIrq =
+    ::protozero::proto_utils::FieldMetadata<
+      616,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      LongIrqFtraceEvent,
+      FtraceEvent>;
+
+  static constexpr FieldMetadata_LongIrq kLongIrq{};
+  template <typename T = LongIrqFtraceEvent> T* set_long_irq() {
+    return BeginNestedMessage<T>(616);
+  }
+
+
+  using FieldMetadata_LongSoftirq =
+    ::protozero::proto_utils::FieldMetadata<
+      617,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      LongSoftirqFtraceEvent,
+      FtraceEvent>;
+
+  static constexpr FieldMetadata_LongSoftirq kLongSoftirq{};
+  template <typename T = LongSoftirqFtraceEvent> T* set_long_softirq() {
+    return BeginNestedMessage<T>(617);
+  }
+
 };

 } // Namespace.
@@ -145757,6 +147943,188 @@ class SchedSwitchWithCtrsFtraceEvent : public ::protozero::Message {
   }
 };

+} // Namespace.
+} // Namespace.
+} // Namespace.
+#endif  // Include guard.
+// gen_amalgamated begin header: gen/protos/perfetto/trace/ftrace/pixel_metrics.pbzero.h
+// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT.
+
+#ifndef PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_FTRACE_PIXEL_METRICS_PROTO_H_
+#define PERFETTO_PROTOS_PROTOS_PERFETTO_TRACE_FTRACE_PIXEL_METRICS_PROTO_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+// gen_amalgamated expanded: #include "perfetto/protozero/field_writer.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/message.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/packed_repeated_fields.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_decoder.h"
+// gen_amalgamated expanded: #include "perfetto/protozero/proto_utils.h"
+
+
+namespace perfetto {
+namespace protos {
+namespace pbzero {
+
+class LongSoftirqFtraceEvent_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/3> {
+ public:
+  LongSoftirqFtraceEvent_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
+  explicit LongSoftirqFtraceEvent_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
+  explicit LongSoftirqFtraceEvent_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
+  bool has_vec_nr() const { return at<1>().valid(); }
+  int32_t vec_nr() const { return at<1>().as_int32(); }
+  bool has_latency() const { return at<2>().valid(); }
+  uint32_t latency() const { return at<2>().as_uint32(); }
+  bool has_count() const { return at<3>().valid(); }
+  uint32_t count() const { return at<3>().as_uint32(); }
+};
+
+class LongSoftirqFtraceEvent : public ::protozero::Message {
+ public:
+  using Decoder = LongSoftirqFtraceEvent_Decoder;
+  enum : int32_t {
+    kVecNrFieldNumber = 1,
+    kLatencyFieldNumber = 2,
+    kCountFieldNumber = 3,
+  };
+  static constexpr const char* GetName() { return ".perfetto.protos.LongSoftirqFtraceEvent"; }
+
+
+  using FieldMetadata_VecNr =
+    ::protozero::proto_utils::FieldMetadata<
+      1,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kInt32,
+      int32_t,
+      LongSoftirqFtraceEvent>;
+
+  static constexpr FieldMetadata_VecNr kVecNr{};
+  void set_vec_nr(int32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_VecNr::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kInt32>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_Latency =
+    ::protozero::proto_utils::FieldMetadata<
+      2,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      LongSoftirqFtraceEvent>;
+
+  static constexpr FieldMetadata_Latency kLatency{};
+  void set_latency(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_Latency::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_Count =
+    ::protozero::proto_utils::FieldMetadata<
+      3,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      LongSoftirqFtraceEvent>;
+
+  static constexpr FieldMetadata_Count kCount{};
+  void set_count(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_Count::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+};
+
+class LongIrqFtraceEvent_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/3> {
+ public:
+  LongIrqFtraceEvent_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
+  explicit LongIrqFtraceEvent_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
+  explicit LongIrqFtraceEvent_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
+  bool has_irq() const { return at<1>().valid(); }
+  int32_t irq() const { return at<1>().as_int32(); }
+  bool has_latency() const { return at<2>().valid(); }
+  uint32_t latency() const { return at<2>().as_uint32(); }
+  bool has_count() const { return at<3>().valid(); }
+  uint32_t count() const { return at<3>().as_uint32(); }
+};
+
+class LongIrqFtraceEvent : public ::protozero::Message {
+ public:
+  using Decoder = LongIrqFtraceEvent_Decoder;
+  enum : int32_t {
+    kIrqFieldNumber = 1,
+    kLatencyFieldNumber = 2,
+    kCountFieldNumber = 3,
+  };
+  static constexpr const char* GetName() { return ".perfetto.protos.LongIrqFtraceEvent"; }
+
+
+  using FieldMetadata_Irq =
+    ::protozero::proto_utils::FieldMetadata<
+      1,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kInt32,
+      int32_t,
+      LongIrqFtraceEvent>;
+
+  static constexpr FieldMetadata_Irq kIrq{};
+  void set_irq(int32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_Irq::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kInt32>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_Latency =
+    ::protozero::proto_utils::FieldMetadata<
+      2,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      LongIrqFtraceEvent>;
+
+  static constexpr FieldMetadata_Latency kLatency{};
+  void set_latency(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_Latency::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_Count =
+    ::protozero::proto_utils::FieldMetadata<
+      3,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      LongIrqFtraceEvent>;
+
+  static constexpr FieldMetadata_Count kCount{};
+  void set_count(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_Count::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+};
+
 } // Namespace.
 } // Namespace.
 } // Namespace.
@@ -148206,7 +150574,7 @@ class SchedGroupTrackerFtraceEvent : public ::protozero::Message {
   }
 };

-class SchedWakeupTaskAttrFtraceEvent_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/5> {
+class SchedWakeupTaskAttrFtraceEvent_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/9> {
  public:
   SchedWakeupTaskAttrFtraceEvent_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
   explicit SchedWakeupTaskAttrFtraceEvent_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
@@ -148221,6 +150589,14 @@ class SchedWakeupTaskAttrFtraceEvent_Decoder : public ::protozero::TypedProtoDec
   uint64_t uclamp_min() const { return at<4>().as_uint64(); }
   bool has_vruntime() const { return at<5>().valid(); }
   uint64_t vruntime() const { return at<5>().as_uint64(); }
+  bool has_sched_qos_user_defined_flag() const { return at<6>().valid(); }
+  uint64_t sched_qos_user_defined_flag() const { return at<6>().as_uint64(); }
+  bool has_rampup_multiplier() const { return at<7>().valid(); }
+  uint32_t rampup_multiplier() const { return at<7>().as_uint32(); }
+  bool has_effect_rampup_multiplier() const { return at<8>().valid(); }
+  uint32_t effect_rampup_multiplier() const { return at<8>().as_uint32(); }
+  bool has_tag_nice() const { return at<9>().valid(); }
+  int32_t tag_nice() const { return at<9>().as_int32(); }
 };

 class SchedWakeupTaskAttrFtraceEvent : public ::protozero::Message {
@@ -148232,6 +150608,10 @@ class SchedWakeupTaskAttrFtraceEvent : public ::protozero::Message {
     kTaskUtilFieldNumber = 3,
     kUclampMinFieldNumber = 4,
     kVruntimeFieldNumber = 5,
+    kSchedQosUserDefinedFlagFieldNumber = 6,
+    kRampupMultiplierFieldNumber = 7,
+    kEffectRampupMultiplierFieldNumber = 8,
+    kTagNiceFieldNumber = 9,
   };
   static constexpr const char* GetName() { return ".perfetto.protos.SchedWakeupTaskAttrFtraceEvent"; }

@@ -148325,6 +150705,78 @@ class SchedWakeupTaskAttrFtraceEvent : public ::protozero::Message {
       ::protozero::proto_utils::ProtoSchemaType::kUint64>
         ::Append(*this, field_id, value);
   }
+
+  using FieldMetadata_SchedQosUserDefinedFlag =
+    ::protozero::proto_utils::FieldMetadata<
+      6,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint64,
+      uint64_t,
+      SchedWakeupTaskAttrFtraceEvent>;
+
+  static constexpr FieldMetadata_SchedQosUserDefinedFlag kSchedQosUserDefinedFlag{};
+  void set_sched_qos_user_defined_flag(uint64_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_SchedQosUserDefinedFlag::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint64>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_RampupMultiplier =
+    ::protozero::proto_utils::FieldMetadata<
+      7,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      SchedWakeupTaskAttrFtraceEvent>;
+
+  static constexpr FieldMetadata_RampupMultiplier kRampupMultiplier{};
+  void set_rampup_multiplier(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_RampupMultiplier::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_EffectRampupMultiplier =
+    ::protozero::proto_utils::FieldMetadata<
+      8,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kUint32,
+      uint32_t,
+      SchedWakeupTaskAttrFtraceEvent>;
+
+  static constexpr FieldMetadata_EffectRampupMultiplier kEffectRampupMultiplier{};
+  void set_effect_rampup_multiplier(uint32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_EffectRampupMultiplier::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kUint32>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_TagNice =
+    ::protozero::proto_utils::FieldMetadata<
+      9,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kInt32,
+      int32_t,
+      SchedWakeupTaskAttrFtraceEvent>;
+
+  static constexpr FieldMetadata_TagNice kTagNice{};
+  void set_tag_nice(int32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_TagNice::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kInt32>
+        ::Append(*this, field_id, value);
+  }
 };

 class SchedMigrateTaskFtraceEvent_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/7> {
@@ -165426,6 +167878,8 @@ namespace perfetto {
 namespace protos {
 namespace pbzero {
 class ObfuscatedClass;
+class ObfuscatedClass_MergedClass;
+class ObfuscatedClass_MergedClasses;
 class ObfuscatedMember;
 } // Namespace pbzero.
 } // Namespace protos.
@@ -165516,7 +167970,7 @@ class DeobfuscationMapping : public ::protozero::Message {

 };

-class ObfuscatedClass_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/4> {
+class ObfuscatedClass_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/5> {
  public:
   ObfuscatedClass_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
   explicit ObfuscatedClass_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
@@ -165529,6 +167983,8 @@ class ObfuscatedClass_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIEL
   ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> obfuscated_members() const { return GetRepeated<::protozero::ConstBytes>(3); }
   bool has_obfuscated_methods() const { return at<4>().valid(); }
   ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> obfuscated_methods() const { return GetRepeated<::protozero::ConstBytes>(4); }
+  bool has_merged_classes() const { return at<5>().valid(); }
+  ::protozero::ConstBytes merged_classes() const { return at<5>().as_bytes(); }
 };

 class ObfuscatedClass : public ::protozero::Message {
@@ -165539,9 +167995,12 @@ class ObfuscatedClass : public ::protozero::Message {
     kDeobfuscatedNameFieldNumber = 2,
     kObfuscatedMembersFieldNumber = 3,
     kObfuscatedMethodsFieldNumber = 4,
+    kMergedClassesFieldNumber = 5,
   };
   static constexpr const char* GetName() { return ".perfetto.protos.ObfuscatedClass"; }

+  using MergedClass = ::perfetto::protos::pbzero::ObfuscatedClass_MergedClass;
+  using MergedClasses = ::perfetto::protos::pbzero::ObfuscatedClass_MergedClasses;

   using FieldMetadata_ObfuscatedName =
     ::protozero::proto_utils::FieldMetadata<
@@ -165618,6 +168077,182 @@ class ObfuscatedClass : public ::protozero::Message {
     return BeginNestedMessage<T>(4);
   }

+
+  using FieldMetadata_MergedClasses =
+    ::protozero::proto_utils::FieldMetadata<
+      5,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      ObfuscatedClass_MergedClasses,
+      ObfuscatedClass>;
+
+  static constexpr FieldMetadata_MergedClasses kMergedClasses{};
+  template <typename T = ObfuscatedClass_MergedClasses> T* set_merged_classes() {
+    return BeginNestedMessage<T>(5);
+  }
+
+};
+
+class ObfuscatedClass_MergedClasses_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/3> {
+ public:
+  ObfuscatedClass_MergedClasses_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
+  explicit ObfuscatedClass_MergedClasses_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
+  explicit ObfuscatedClass_MergedClasses_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
+  bool has_class_id_field_name() const { return at<1>().valid(); }
+  ::protozero::ConstChars class_id_field_name() const { return at<1>().as_string(); }
+  bool has_merged_classes() const { return at<2>().valid(); }
+  ::protozero::RepeatedFieldIterator<::protozero::ConstBytes> merged_classes() const { return GetRepeated<::protozero::ConstBytes>(2); }
+  bool has_unknown_merged_classes() const { return at<3>().valid(); }
+  bool unknown_merged_classes() const { return at<3>().as_bool(); }
+};
+
+class ObfuscatedClass_MergedClasses : public ::protozero::Message {
+ public:
+  using Decoder = ObfuscatedClass_MergedClasses_Decoder;
+  enum : int32_t {
+    kClassIdFieldNameFieldNumber = 1,
+    kMergedClassesFieldNumber = 2,
+    kUnknownMergedClassesFieldNumber = 3,
+  };
+  static constexpr const char* GetName() { return ".perfetto.protos.ObfuscatedClass.MergedClasses"; }
+
+
+  using FieldMetadata_ClassIdFieldName =
+    ::protozero::proto_utils::FieldMetadata<
+      1,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kString,
+      std::string,
+      ObfuscatedClass_MergedClasses>;
+
+  static constexpr FieldMetadata_ClassIdFieldName kClassIdFieldName{};
+  void set_class_id_field_name(const char* data, size_t size) {
+    AppendBytes(FieldMetadata_ClassIdFieldName::kFieldId, data, size);
+  }
+  void set_class_id_field_name(::protozero::ConstChars chars) {
+    AppendBytes(FieldMetadata_ClassIdFieldName::kFieldId, chars.data, chars.size);
+  }
+  void set_class_id_field_name(std::string value) {
+    static constexpr uint32_t field_id = FieldMetadata_ClassIdFieldName::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kString>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_MergedClasses =
+    ::protozero::proto_utils::FieldMetadata<
+      2,
+      ::protozero::proto_utils::RepetitionType::kRepeatedNotPacked,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      ObfuscatedClass_MergedClass,
+      ObfuscatedClass_MergedClasses>;
+
+  static constexpr FieldMetadata_MergedClasses kMergedClasses{};
+  template <typename T = ObfuscatedClass_MergedClass> T* add_merged_classes() {
+    return BeginNestedMessage<T>(2);
+  }
+
+
+  using FieldMetadata_UnknownMergedClasses =
+    ::protozero::proto_utils::FieldMetadata<
+      3,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kBool,
+      bool,
+      ObfuscatedClass_MergedClasses>;
+
+  static constexpr FieldMetadata_UnknownMergedClasses kUnknownMergedClasses{};
+  void set_unknown_merged_classes(bool value) {
+    static constexpr uint32_t field_id = FieldMetadata_UnknownMergedClasses::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kBool>
+        ::Append(*this, field_id, value);
+  }
+};
+
+class ObfuscatedClass_MergedClass_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/3> {
+ public:
+  ObfuscatedClass_MergedClass_Decoder(const uint8_t* data, size_t len) : TypedProtoDecoder(data, len) {}
+  explicit ObfuscatedClass_MergedClass_Decoder(const std::string& raw) : TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), raw.size()) {}
+  explicit ObfuscatedClass_MergedClass_Decoder(const ::protozero::ConstBytes& raw) : TypedProtoDecoder(raw.data, raw.size) {}
+  bool has_name() const { return at<1>().valid(); }
+  ::protozero::ConstChars name() const { return at<1>().as_string(); }
+  bool has_class_id() const { return at<2>().valid(); }
+  int32_t class_id() const { return at<2>().as_int32(); }
+  bool has_merged_classes() const { return at<3>().valid(); }
+  ::protozero::ConstBytes merged_classes() const { return at<3>().as_bytes(); }
+};
+
+class ObfuscatedClass_MergedClass : public ::protozero::Message {
+ public:
+  using Decoder = ObfuscatedClass_MergedClass_Decoder;
+  enum : int32_t {
+    kNameFieldNumber = 1,
+    kClassIdFieldNumber = 2,
+    kMergedClassesFieldNumber = 3,
+  };
+  static constexpr const char* GetName() { return ".perfetto.protos.ObfuscatedClass.MergedClass"; }
+
+
+  using FieldMetadata_Name =
+    ::protozero::proto_utils::FieldMetadata<
+      1,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kString,
+      std::string,
+      ObfuscatedClass_MergedClass>;
+
+  static constexpr FieldMetadata_Name kName{};
+  void set_name(const char* data, size_t size) {
+    AppendBytes(FieldMetadata_Name::kFieldId, data, size);
+  }
+  void set_name(::protozero::ConstChars chars) {
+    AppendBytes(FieldMetadata_Name::kFieldId, chars.data, chars.size);
+  }
+  void set_name(std::string value) {
+    static constexpr uint32_t field_id = FieldMetadata_Name::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kString>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_ClassId =
+    ::protozero::proto_utils::FieldMetadata<
+      2,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kInt32,
+      int32_t,
+      ObfuscatedClass_MergedClass>;
+
+  static constexpr FieldMetadata_ClassId kClassId{};
+  void set_class_id(int32_t value) {
+    static constexpr uint32_t field_id = FieldMetadata_ClassId::kFieldId;
+    // Call the appropriate protozero::Message::Append(field_id, ...)
+    // method based on the type of the field.
+    ::protozero::internal::FieldWriter<
+      ::protozero::proto_utils::ProtoSchemaType::kInt32>
+        ::Append(*this, field_id, value);
+  }
+
+  using FieldMetadata_MergedClasses =
+    ::protozero::proto_utils::FieldMetadata<
+      3,
+      ::protozero::proto_utils::RepetitionType::kNotRepeated,
+      ::protozero::proto_utils::ProtoSchemaType::kMessage,
+      ObfuscatedClass_MergedClasses,
+      ObfuscatedClass_MergedClass>;
+
+  static constexpr FieldMetadata_MergedClasses kMergedClasses{};
+  template <typename T = ObfuscatedClass_MergedClasses> T* set_merged_classes() {
+    return BeginNestedMessage<T>(3);
+  }
+
 };

 class ObfuscatedMember_Decoder : public ::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/6> {
@@ -190398,14 +193033,6 @@ class TracingServiceState;
 class TracingServiceState_TracingSession;
 class TracingServiceState_DataSource;
 class DataSourceDescriptor;
-class VmProgram;
-class VmInstruction;
-class VmOpDel;
-class VmOpSet;
-class VmOpMerge;
-class VmOpRegLoad;
-class VmOpSelect;
-class VmOpSelect_PathComponent;
 class TracingServiceState_Producer;
 class QueryServiceStateRequest;
 class ObserveEventsResponse;
@@ -190449,6 +193076,8 @@ class InterceptorConfig;
 class ConsoleConfig;
 class ChromeConfig;
 class SystemInfoConfig;
+class DataSourceConfig_ExperimentalTracingV2Config;
+class DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption;
 class ProtoVmConfig;
 class TraceConfig_BufferConfig;
 class AttachRequest;
@@ -190470,8 +193099,6 @@ class StartTracingRequest;
 class EnableTracingResponse;
 class EnableTracingRequest;
 enum ObservableEvents_Type : int;
-enum VmInstruction_AbortLevel : int;
-enum VmCursorEnum : int;
 enum ObservableEvents_DataSourceInstanceState : int;
 enum TraceStats_FinalFlushOutcome : int;
 enum TraceConfig_LockdownModeOperation : int;
@@ -191663,6 +194290,8 @@ class InterceptorConfig;
 class ConsoleConfig;
 class ChromeConfig;
 class SystemInfoConfig;
+class DataSourceConfig_ExperimentalTracingV2Config;
+class DataSourceConfig_ExperimentalTracingV2Config_ChunkSizeOption;
 class ProtoVmConfig;
 class PriorityBoostConfig;
 class GetAsyncCommandResponse_SetupDataSource;
@@ -191684,25 +194313,20 @@ class UnregisterDataSourceRequest;
 class UpdateDataSourceResponse;
 class UpdateDataSourceRequest;
 class DataSourceDescriptor;
-class VmProgram;
-class VmInstruction;
-class VmOpDel;
-class VmOpSet;
-class VmOpMerge;
-class VmOpRegLoad;
-class VmOpSelect;
-class VmOpSelect_PathComponent;
 class RegisterDataSourceResponse;
 class RegisterDataSourceRequest;
+class DrainV2RingBufferResponse;
+class DrainV2RingBufferRequest;
+class AttachV2RingBufferResponse;
+class AttachV2RingBufferRequest;
 class InitializeConnectionResponse;
 class InitializeConnectionRequest;
+enum ProtocolAbiVersion : int;
 enum DataSourceConfig_SessionInitiator : int;
 enum DataSourceConfig_BufferExhaustedPolicy : int;
 enum ConsoleConfig_Output : int;
 enum ChromeConfig_ClientPriority : int;
 enum PriorityBoostConfig_BoostPolicy : int;
-enum VmInstruction_AbortLevel : int;
-enum VmCursorEnum : int;
 enum InitializeConnectionRequest_ProducerSMBScrapingMode : int;
 }  // namespace perfetto
 }  // namespace protos
@@ -191715,6 +194339,11 @@ class Message;
 namespace perfetto {
 namespace protos {
 namespace gen {
+enum ProtocolAbiVersion : int {
+  PROTOCOL_ABI_VERSION_UNSPECIFIED = 0,
+  PROTOCOL_ABI_VERSION_V1 = 1,
+  PROTOCOL_ABI_VERSION_V2 = 2,
+};
 enum InitializeConnectionRequest_ProducerSMBScrapingMode : int {
   InitializeConnectionRequest_ProducerSMBScrapingMode_SMB_SCRAPING_UNSPECIFIED = 0,
   InitializeConnectionRequest_ProducerSMBScrapingMode_SMB_SCRAPING_ENABLED = 1,
@@ -192681,12 +195310,135 @@ class PERFETTO_EXPORT_COMPONENT RegisterDataSourceRequest : public ::protozero::
 };


+class PERFETTO_EXPORT_COMPONENT DrainV2RingBufferResponse : public ::protozero::CppMessageObj {
+ public:
+  enum FieldNumbers {
+  };
+
+  DrainV2RingBufferResponse();
+  ~DrainV2RingBufferResponse() override;
+  DrainV2RingBufferResponse(DrainV2RingBufferResponse&&) noexcept;
+  DrainV2RingBufferResponse& operator=(DrainV2RingBufferResponse&&);
+  DrainV2RingBufferResponse(const DrainV2RingBufferResponse&);
+  DrainV2RingBufferResponse& operator=(const DrainV2RingBufferResponse&);
+  bool operator==(const DrainV2RingBufferResponse&) const;
+  bool operator!=(const DrainV2RingBufferResponse& other) const { return !(*this == other); }
+
+  bool ParseFromArray(const void*, size_t) override;
+  std::string SerializeAsString() const override;
+  std::vector<uint8_t> SerializeAsArray() const override;
+  void Serialize(::protozero::Message*) const;
+
+ private:
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+
+  std::bitset<2> _has_field_{};
+};
+
+
+class PERFETTO_EXPORT_COMPONENT DrainV2RingBufferRequest : public ::protozero::CppMessageObj {
+ public:
+  enum FieldNumbers {
+  };
+
+  DrainV2RingBufferRequest();
+  ~DrainV2RingBufferRequest() override;
+  DrainV2RingBufferRequest(DrainV2RingBufferRequest&&) noexcept;
+  DrainV2RingBufferRequest& operator=(DrainV2RingBufferRequest&&);
+  DrainV2RingBufferRequest(const DrainV2RingBufferRequest&);
+  DrainV2RingBufferRequest& operator=(const DrainV2RingBufferRequest&);
+  bool operator==(const DrainV2RingBufferRequest&) const;
+  bool operator!=(const DrainV2RingBufferRequest& other) const { return !(*this == other); }
+
+  bool ParseFromArray(const void*, size_t) override;
+  std::string SerializeAsString() const override;
+  std::vector<uint8_t> SerializeAsArray() const override;
+  void Serialize(::protozero::Message*) const;
+
+ private:
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+
+  std::bitset<2> _has_field_{};
+};
+
+
+class PERFETTO_EXPORT_COMPONENT AttachV2RingBufferResponse : public ::protozero::CppMessageObj {
+ public:
+  enum FieldNumbers {
+  };
+
+  AttachV2RingBufferResponse();
+  ~AttachV2RingBufferResponse() override;
+  AttachV2RingBufferResponse(AttachV2RingBufferResponse&&) noexcept;
+  AttachV2RingBufferResponse& operator=(AttachV2RingBufferResponse&&);
+  AttachV2RingBufferResponse(const AttachV2RingBufferResponse&);
+  AttachV2RingBufferResponse& operator=(const AttachV2RingBufferResponse&);
+  bool operator==(const AttachV2RingBufferResponse&) const;
+  bool operator!=(const AttachV2RingBufferResponse& other) const { return !(*this == other); }
+
+  bool ParseFromArray(const void*, size_t) override;
+  std::string SerializeAsString() const override;
+  std::vector<uint8_t> SerializeAsArray() const override;
+  void Serialize(::protozero::Message*) const;
+
+ private:
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+
+  std::bitset<2> _has_field_{};
+};
+
+
+class PERFETTO_EXPORT_COMPONENT AttachV2RingBufferRequest : public ::protozero::CppMessageObj {
+ public:
+  enum FieldNumbers {
+    kChunkSizeBytesFieldNumber = 1,
+  };
+
+  AttachV2RingBufferRequest();
+  ~AttachV2RingBufferRequest() override;
+  AttachV2RingBufferRequest(AttachV2RingBufferRequest&&) noexcept;
+  AttachV2RingBufferRequest& operator=(AttachV2RingBufferRequest&&);
+  AttachV2RingBufferRequest(const AttachV2RingBufferRequest&);
+  AttachV2RingBufferRequest& operator=(const AttachV2RingBufferRequest&);
+  bool operator==(const AttachV2RingBufferRequest&) const;
+  bool operator!=(const AttachV2RingBufferRequest& other) const { return !(*this == other); }
+
+  bool ParseFromArray(const void*, size_t) override;
+  std::string SerializeAsString() const override;
+  std::vector<uint8_t> SerializeAsArray() const override;
+  void Serialize(::protozero::Message*) const;
+
+  bool has_chunk_size_bytes() const { return _has_field_[1]; }
+  uint32_t chunk_size_bytes() const { return chunk_size_bytes_; }
+  void set_chunk_size_bytes(uint32_t value) { chunk_size_bytes_ = value; _has_field_.set(1); }
+
+ private:
+  uint32_t chunk_size_bytes_{};
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+
+  std::bitset<2> _has_field_{};
+};
+
+
 class PERFETTO_EXPORT_COMPONENT InitializeConnectionResponse : public ::protozero::CppMessageObj {
  public:
   enum FieldNumbers {
     kUsingShmemProvidedByProducerFieldNumber = 1,
     kDirectSmbPatchingSupportedFieldNumber = 2,
     kUseShmemEmulationFieldNumber = 3,
+    kProtocolAbiVersionsFieldNumber = 4,
   };

   InitializeConnectionResponse();
@@ -192715,16 +195467,21 @@ class PERFETTO_EXPORT_COMPONENT InitializeConnectionResponse : public ::protozer
   bool use_shmem_emulation() const { return use_shmem_emulation_; }
   void set_use_shmem_emulation(bool value) { use_shmem_emulation_ = value; _has_field_.set(3); }

+  bool has_protocol_abi_versions() const { return _has_field_[4]; }
+  uint32_t protocol_abi_versions() const { return protocol_abi_versions_; }
+  void set_protocol_abi_versions(uint32_t value) { protocol_abi_versions_ = value; _has_field_.set(4); }
+
  private:
   bool using_shmem_provided_by_producer_{};
   bool direct_smb_patching_supported_{};
   bool use_shmem_emulation_{};
+  uint32_t protocol_abi_versions_{};

   // Allows to preserve unknown protobuf fields for compatibility
   // with future versions of .proto files.
   std::string unknown_fields_;

-  std::bitset<4> _has_field_{};
+  std::bitset<5> _has_field_{};
 };


@@ -192744,6 +195501,7 @@ class PERFETTO_EXPORT_COMPONENT InitializeConnectionRequest : public ::protozero
     kProducerProvidedShmemFieldNumber = 6,
     kSdkVersionFieldNumber = 8,
     kShmKeyWindowsFieldNumber = 7,
+    kSupportedProtocolAbiVersionsFieldNumber = 9,
   };

   InitializeConnectionRequest();
@@ -192788,6 +195546,10 @@ class PERFETTO_EXPORT_COMPONENT InitializeConnectionRequest : public ::protozero
   const std::string& shm_key_windows() const { return shm_key_windows_; }
   void set_shm_key_windows(const std::string& value) { shm_key_windows_ = value; _has_field_.set(7); }

+  bool has_supported_protocol_abi_versions() const { return _has_field_[9]; }
+  uint32_t supported_protocol_abi_versions() const { return supported_protocol_abi_versions_; }
+  void set_supported_protocol_abi_versions(uint32_t value) { supported_protocol_abi_versions_ = value; _has_field_.set(9); }
+
  private:
   uint32_t shared_memory_page_size_hint_bytes_{};
   uint32_t shared_memory_size_hint_bytes_{};
@@ -192796,12 +195558,13 @@ class PERFETTO_EXPORT_COMPONENT InitializeConnectionRequest : public ::protozero
   bool producer_provided_shmem_{};
   std::string sdk_version_{};
   std::string shm_key_windows_{};
+  uint32_t supported_protocol_abi_versions_{};

   // Allows to preserve unknown protobuf fields for compatibility
   // with future versions of .proto files.
   std::string unknown_fields_;

-  std::bitset<9> _has_field_{};
+  std::bitset<10> _has_field_{};
 };

 }  // namespace perfetto