Add perfetto build config

This adds Perfetto support to WebRTC with a GN flag rtc_use_perfetto.
The configuration of perfetto depends on whether or not webrtc is
build within Chrome or not. When in Chrome, WebRTC will depend on
//third_party/perfetto:libperfetto. When building standalone, specific includes required for Perfetto are exposed with the library webrtc_libperfetto.

The perfetto trace API is exposed with a header export in
trace_event.h which is used instead of the legacy API.

The addition of Perfetto means there are 4 compilation modes for
tracing in WebRTC,
1. No tracing implementation.
2. Legacy tracing (AddTraceEvent/GetCategoryEnabled).
3.a. Perfetto statically linked (webrtc_libperfetto).
3.b. Perfetto in Chrome (Chrome's libperfetto).

This CL removes the tracing expectations from
rtc_stats_integrationtest.cc because those directly used the old API.

Integration into Chrome is a follow up CL which depends on
https://chromium-review.googlesource.com/c/chromium/src/+/5471691.

Tested: Ran Chrome with Perfetto and traces appear. WebRTC Unit test tracing working: https://ui.perfetto.dev/#!?s=04ea2613ea36b814394639a1ec4b60be5b5097527f1a485995ecc13469885468
Bug: webrtc:15917
Change-Id: I537d79dc247c2b759689910c621087286a4d8fdc
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/347880
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Auto-Submit: Evan Shrubsole <eshr@google.com>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Mikhail Khokhlov <khokhlov@google.com>
Cr-Commit-Position: refs/heads/main@{#42166}
diff --git a/BUILD.gn b/BUILD.gn
index 571049f..5828a81 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -174,16 +174,18 @@
     defines += [ "RTC_ENABLE_WIN_WGC" ]
   }
 
-  # Some tests need to declare their own trace event handlers. If this define is
-  # not set, the first time TRACE_EVENT_* is called it will store the return
-  # value for the current handler in an static variable, so that subsequent
-  # changes to the handler for that TRACE_EVENT_* will be ignored.
-  # So when tests are included, we set this define, making it possible to use
-  # different event handlers in different tests.
-  if (rtc_include_tests) {
-    defines += [ "WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS=1" ]
-  } else {
-    defines += [ "WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS=0" ]
+  if (!rtc_use_perfetto) {
+    # Some tests need to declare their own trace event handlers. If this define is
+    # not set, the first time TRACE_EVENT_* is called it will store the return
+    # value for the current handler in an static variable, so that subsequent
+    # changes to the handler for that TRACE_EVENT_* will be ignored.
+    # So when tests are included, we set this define, making it possible to use
+    # different event handlers in different tests.
+    if (rtc_include_tests) {
+      defines += [ "WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS=1" ]
+    } else {
+      defines += [ "WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS=0" ]
+    }
   }
   if (build_with_chromium) {
     defines += [ "WEBRTC_CHROMIUM_BUILD" ]
@@ -269,6 +271,30 @@
   }
 }
 
+group("tracing") {
+  if (rtc_use_perfetto) {
+    if (build_with_chromium) {
+      public_deps =  # no-presubmit-check TODO(webrtc:8603)
+          [ "//third_party/perfetto:libperfetto" ]
+    } else {
+      public_deps =  # no-presubmit-check TODO(webrtc:8603)
+          [ ":webrtc_libperfetto" ]
+    }
+  }
+}
+
+if (rtc_use_perfetto) {
+  rtc_library("webrtc_libperfetto") {
+    public_configs = [ "//third_party/perfetto/gn:public_config" ]
+    deps = [
+      "//third_party/perfetto/src/tracing:client_api_without_backends",
+      "//third_party/perfetto/src/tracing:platform_impl",
+    ]
+    public_deps +=  # no-presubmit-check TODO(webrtc:8603)
+        [ "//third_party/perfetto/include/perfetto/tracing" ]
+  }
+}
+
 config("common_config") {
   cflags = []
   cflags_c = []
@@ -478,6 +504,10 @@
       "/U_UNICODE",
     ]
   }
+
+  if (rtc_use_perfetto) {
+    defines += [ "RTC_USE_PERFETTO" ]
+  }
 }
 
 config("common_objc") {
diff --git a/DEPS b/DEPS
index 69c52e8..89ffcb4 100644
--- a/DEPS
+++ b/DEPS
@@ -2668,6 +2668,10 @@
 
   # Abseil flags are allowed in tests and tools.
   "+absl/flags",
+
+  # Perfetto
+  '+third_party/perfetto/include/perfetto/tracing',
+  '+third_party/perfetto/include/perfetto/test',
 ]
 
 specific_include_rules = {
diff --git a/build_overrides/build.gni b/build_overrides/build.gni
index cfa7958..219de19 100644
--- a/build_overrides/build.gni
+++ b/build_overrides/build.gni
@@ -34,6 +34,9 @@
 # so we just ignore that assert. See https://crbug.com/648948 for more info.
 ignore_elf32_limitations = true
 
+perfetto_build_with_embedder = true
+enable_perfetto_trace_processor = true
+
 # Use bundled hermetic Xcode installation maintainted by Chromium,
 # except for local iOS builds where it's unsupported.
 # Allow for mac cross compile on linux machines.
diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc
index 1ff060c..8c28532 100644
--- a/pc/rtc_stats_integrationtest.cc
+++ b/pc/rtc_stats_integrationtest.cc
@@ -50,64 +50,11 @@
 
 const int64_t kGetStatsTimeoutMs = 10000;
 
-const unsigned char* GetCategoryEnabledHandler(const char* name) {
-  if (strcmp("webrtc_stats", name) != 0) {
-    return reinterpret_cast<const unsigned char*>("");
-  }
-  return reinterpret_cast<const unsigned char*>(name);
-}
-
-class RTCStatsReportTraceListener {
- public:
-  static void SetUp() {
-    if (!traced_report_)
-      traced_report_ = new RTCStatsReportTraceListener();
-    traced_report_->last_trace_ = "";
-    SetupEventTracer(&GetCategoryEnabledHandler,
-                     &RTCStatsReportTraceListener::AddTraceEventHandler);
-  }
-
-  static const std::string& last_trace() {
-    RTC_DCHECK(traced_report_);
-    return traced_report_->last_trace_;
-  }
-
- private:
-  static void AddTraceEventHandler(
-      char phase,
-      const unsigned char* category_enabled,
-      const char* name,
-      unsigned long long id,  // NOLINT(runtime/int)
-      int num_args,
-      const char** arg_names,
-      const unsigned char* arg_types,
-      const unsigned long long* arg_values,  // NOLINT(runtime/int)
-      unsigned char flags) {
-    RTC_DCHECK(traced_report_);
-    EXPECT_STREQ("webrtc_stats",
-                 reinterpret_cast<const char*>(category_enabled));
-    EXPECT_STREQ("webrtc_stats", name);
-    EXPECT_EQ(1, num_args);
-    EXPECT_STREQ("report", arg_names[0]);
-    EXPECT_EQ(TRACE_VALUE_TYPE_COPY_STRING, arg_types[0]);
-
-    traced_report_->last_trace_ = reinterpret_cast<const char*>(arg_values[0]);
-  }
-
-  static RTCStatsReportTraceListener* traced_report_;
-  std::string last_trace_;
-};
-
-RTCStatsReportTraceListener* RTCStatsReportTraceListener::traced_report_ =
-    nullptr;
-
 class RTCStatsIntegrationTest : public ::testing::Test {
  public:
   RTCStatsIntegrationTest()
       : network_thread_(new rtc::Thread(&virtual_socket_server_)),
         worker_thread_(rtc::Thread::Create()) {
-    RTCStatsReportTraceListener::SetUp();
-
     RTC_CHECK(network_thread_->Start());
     RTC_CHECK(worker_thread_->Start());
 
@@ -1054,10 +1001,6 @@
 
   rtc::scoped_refptr<const RTCStatsReport> report = GetStatsFromCaller();
   RTCStatsReportVerifier(report.get()).VerifyReport({});
-
-#if RTC_TRACE_EVENTS_ENABLED
-  EXPECT_EQ(report->ToJson(), RTCStatsReportTraceListener::last_trace());
-#endif
 }
 
 TEST_F(RTCStatsIntegrationTest, GetStatsFromCallee) {
@@ -1076,10 +1019,6 @@
   };
   EXPECT_TRUE_WAIT(GetStatsReportAndReturnTrueIfRttIsDefined(), kMaxWaitMs);
   RTCStatsReportVerifier(report.get()).VerifyReport({});
-
-#if RTC_TRACE_EVENTS_ENABLED
-  EXPECT_EQ(report->ToJson(), RTCStatsReportTraceListener::last_trace());
-#endif
 }
 
 // These tests exercise the integration of the stats selection algorithm inside
@@ -1157,10 +1096,6 @@
   // Any pending stats requests should have completed in the act of destroying
   // the peer connection.
   ASSERT_TRUE(stats_obtainer->report());
-#if RTC_TRACE_EVENTS_ENABLED
-  EXPECT_EQ(stats_obtainer->report()->ToJson(),
-            RTCStatsReportTraceListener::last_trace());
-#endif
 }
 
 TEST_F(RTCStatsIntegrationTest, GetsStatsWhileClosingPeerConnection) {
@@ -1172,10 +1107,6 @@
   caller_->pc()->Close();
 
   ASSERT_TRUE(stats_obtainer->report());
-#if RTC_TRACE_EVENTS_ENABLED
-  EXPECT_EQ(stats_obtainer->report()->ToJson(),
-            RTCStatsReportTraceListener::last_trace());
-#endif
 }
 
 // GetStatsReferencedIds() is optimized to recognize what is or isn't a
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index b3cb17c..e5ead5b 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -215,6 +215,13 @@
     "system:rtc_export",
   ]
   absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
+  if (rtc_use_perfetto) {
+    sources += [
+      "trace_categories.cc",
+      "trace_categories.h",
+    ]
+    deps += [ "..:tracing" ]
+  }
 }
 
 rtc_library("histogram_percentile_counter") {
diff --git a/rtc_base/DEPS b/rtc_base/DEPS
index 3a77b55..4ba8c92 100644
--- a/rtc_base/DEPS
+++ b/rtc_base/DEPS
@@ -12,4 +12,10 @@
   "gunit\.h": [
     "+testing/base/public/gunit.h"
   ],
+  "trace_categories\.h": [
+    "+perfetto",
+  ],
+  "event_tracer\.cc": [
+    "+perfetto",
+  ]
 }
diff --git a/rtc_base/event_tracer.cc b/rtc_base/event_tracer.cc
index 6ad8330..677b95b 100644
--- a/rtc_base/event_tracer.cc
+++ b/rtc_base/event_tracer.cc
@@ -7,11 +7,19 @@
  *  in the file PATENTS.  All contributing project authors may
  *  be found in the AUTHORS file in the root of the source tree.
  */
+
 #include "rtc_base/event_tracer.h"
 
+#include <stdio.h>
+
+#include "rtc_base/trace_event.h"
+
+#if defined(RTC_USE_PERFETTO)
+#include "perfetto/tracing/tracing.h"
+#include "rtc_base/trace_categories.h"
+#else
 #include <inttypes.h>
 #include <stdint.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <atomic>
@@ -28,21 +36,27 @@
 #include "rtc_base/synchronization/mutex.h"
 #include "rtc_base/thread_annotations.h"
 #include "rtc_base/time_utils.h"
-#include "rtc_base/trace_event.h"
-
-// This is a guesstimate that should be enough in most cases.
-static const size_t kEventLoggerArgsStrBufferInitialSize = 256;
-static const size_t kTraceArgBufferLength = 32;
+#endif
 
 namespace webrtc {
 
 namespace {
 
+#if !defined(RTC_USE_PERFETTO)
 GetCategoryEnabledPtr g_get_category_enabled_ptr = nullptr;
 AddTraceEventPtr g_add_trace_event_ptr = nullptr;
+#endif
 
 }  // namespace
 
+#if defined(RTC_USE_PERFETTO)
+void RegisterPerfettoTrackEvents() {
+  if (perfetto::Tracing::IsInitialized()) {
+    webrtc::TrackEvent::Register();
+  }
+}
+#else
+
 void SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr,
                       AddTraceEventPtr add_trace_event_ptr) {
   g_get_category_enabled_ptr = get_category_enabled_ptr;
@@ -73,9 +87,28 @@
                           arg_names, arg_types, arg_values, flags);
   }
 }
+#endif
 
 }  // namespace webrtc
 
+#if defined(RTC_USE_PERFETTO)
+// TODO(bugs.webrtc.org/15917): Implement for perfetto.
+namespace rtc::tracing {
+void SetupInternalTracer(bool enable_all_categories) {}
+bool StartInternalCapture(absl::string_view filename) {
+  return false;
+}
+void StartInternalCaptureToFile(FILE* file) {}
+void StopInternalCapture() {}
+void ShutdownInternalTracer() {}
+
+}  // namespace rtc::tracing
+#else
+
+// This is a guesstimate that should be enough in most cases.
+static const size_t kEventLoggerArgsStrBufferInitialSize = 256;
+static const size_t kTraceArgBufferLength = 32;
+
 namespace rtc {
 namespace tracing {
 namespace {
@@ -412,3 +445,5 @@
 
 }  // namespace tracing
 }  // namespace rtc
+
+#endif  // defined(RTC_USE_PERFETTO)
diff --git a/rtc_base/event_tracer.h b/rtc_base/event_tracer.h
index dc2eaed..7f8d3e6 100644
--- a/rtc_base/event_tracer.h
+++ b/rtc_base/event_tracer.h
@@ -8,6 +8,9 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
+#ifndef RTC_BASE_EVENT_TRACER_H_
+#define RTC_BASE_EVENT_TRACER_H_
+
 // This file defines the interface for event tracing in WebRTC.
 //
 // Event log handlers are set through SetupEventTracer(). User of this API will
@@ -23,9 +26,6 @@
 //
 // Parameters for the above two functions are described in trace_event.h.
 
-#ifndef RTC_BASE_EVENT_TRACER_H_
-#define RTC_BASE_EVENT_TRACER_H_
-
 #include <stdio.h>
 
 #include "absl/strings/string_view.h"
@@ -33,6 +33,9 @@
 
 namespace webrtc {
 
+#if defined(RTC_USE_PERFETTO)
+void RegisterPerfettoTrackEvents();
+#else
 typedef const unsigned char* (*GetCategoryEnabledPtr)(const char* name);
 typedef void (*AddTraceEventPtr)(char phase,
                                  const unsigned char* category_enabled,
@@ -67,19 +70,19 @@
                             const unsigned long long* arg_values,
                             unsigned char flags);
 };
+#endif
 
 }  // namespace webrtc
 
-namespace rtc {
-namespace tracing {
+namespace rtc::tracing {
 // Set up internal event tracer.
+// TODO(webrtc:15917): Implement for perfetto.
 RTC_EXPORT void SetupInternalTracer(bool enable_all_categories = true);
 RTC_EXPORT bool StartInternalCapture(absl::string_view filename);
 RTC_EXPORT void StartInternalCaptureToFile(FILE* file);
 RTC_EXPORT void StopInternalCapture();
 // Make sure we run this, this will tear down the internal tracing.
 RTC_EXPORT void ShutdownInternalTracer();
-}  // namespace tracing
-}  // namespace rtc
+}  // namespace rtc::tracing
 
 #endif  // RTC_BASE_EVENT_TRACER_H_
diff --git a/rtc_base/event_tracer_unittest.cc b/rtc_base/event_tracer_unittest.cc
index d0783c3..eae19a2 100644
--- a/rtc_base/event_tracer_unittest.cc
+++ b/rtc_base/event_tracer_unittest.cc
@@ -50,16 +50,16 @@
 namespace webrtc {
 
 TEST(EventTracerTest, EventTracerDisabled) {
-  { TRACE_EVENT0("test", "EventTracerDisabled"); }
+  { TRACE_EVENT0("webrtc-test", "EventTracerDisabled"); }
   EXPECT_FALSE(TestStatistics::Get()->Count());
   TestStatistics::Get()->Reset();
 }
 
-#if RTC_TRACE_EVENTS_ENABLED
+#if RTC_TRACE_EVENTS_ENABLED && !defined(RTC_USE_PERFETTO)
 TEST(EventTracerTest, ScopedTraceEvent) {
   SetupEventTracer(
       [](const char* /*name*/) {
-        return reinterpret_cast<const unsigned char*>("test");
+        return reinterpret_cast<const unsigned char*>("webrtc-test");
       },
       [](char /*phase*/, const unsigned char* /*category_enabled*/,
          const char* /*name*/, unsigned long long /*id*/, int /*num_args*/,
diff --git a/rtc_base/trace_categories.cc b/rtc_base/trace_categories.cc
new file mode 100644
index 0000000..2fe63e2
--- /dev/null
+++ b/rtc_base/trace_categories.cc
@@ -0,0 +1,13 @@
+/*
+ *  Copyright (c) 2024 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "rtc_base/trace_categories.h"
+
+PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE_WITH_ATTRS(webrtc, RTC_EXPORT);
diff --git a/rtc_base/trace_categories.h b/rtc_base/trace_categories.h
new file mode 100644
index 0000000..c22ce91
--- /dev/null
+++ b/rtc_base/trace_categories.h
@@ -0,0 +1,33 @@
+/*
+ *  Copyright (c) 2024 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef RTC_BASE_TRACE_CATEGORIES_H_
+#define RTC_BASE_TRACE_CATEGORIES_H_
+
+#define PERFETTO_ENABLE_LEGACY_TRACE_EVENTS 1
+
+#include "perfetto/tracing/track_event.h"  // IWYU pragma: export
+#include "perfetto/tracing/track_event_category_registry.h"
+#include "perfetto/tracing/track_event_legacy.h"  // IWYU pragma: export
+#include "rtc_base/system/rtc_export.h"
+
+PERFETTO_DEFINE_TEST_CATEGORY_PREFIXES("webrtc-test");
+
+PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS(
+    webrtc,
+    RTC_EXPORT,
+    perfetto::Category("webrtc"),
+    perfetto::Category("webrtc_stats"),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("webrtc")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("webrtc_stats")));
+
+PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(webrtc);
+
+#endif  // RTC_BASE_TRACE_CATEGORIES_H_
diff --git a/rtc_base/trace_event.h b/rtc_base/trace_event.h
index 8499983..ab62802 100644
--- a/rtc_base/trace_event.h
+++ b/rtc_base/trace_event.h
@@ -1,25 +1,40 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file under third_party_mods/chromium or at:
-// http://src.chromium.org/svn/trunk/src/LICENSE
+/*
+ *  Copyright (c) 2024 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
 
 #ifndef RTC_BASE_TRACE_EVENT_H_
 #define RTC_BASE_TRACE_EVENT_H_
 
-#include <string>
-
-#include "rtc_base/event_tracer.h"
-
-#if defined(TRACE_EVENT0)
-#error "Another copy of trace_event.h has already been included."
-#endif
-
 #if defined(RTC_DISABLE_TRACE_EVENTS)
 #define RTC_TRACE_EVENTS_ENABLED 0
 #else
 #define RTC_TRACE_EVENTS_ENABLED 1
 #endif
 
+#if defined(RTC_USE_PERFETTO)
+
+#include "rtc_base/trace_categories.h"  // IWYU pragma: export
+
+// TODO(webrtc:15917): Replace these events.
+#define TRACE_EVENT_ASYNC_STEP0(category_group, name, id, step) \
+  TRACE_EVENT_ASYNC_STEP_INTO0(category_group, name, id, step)
+#define TRACE_EVENT_ASYNC_STEP1(category_group, name, id, step, arg1_name, \
+                                arg1_val)                                  \
+  TRACE_EVENT_ASYNC_STEP_INTO1(category_group, name, id, step, arg1_name,  \
+                               arg1_val)
+
+#else
+
+#include <string>
+
+#include "rtc_base/event_tracer.h"
+
 // Type values for identifying types in the TraceValue union.
 #define TRACE_VALUE_TYPE_BOOL (static_cast<unsigned char>(1))
 #define TRACE_VALUE_TYPE_UINT (static_cast<unsigned char>(2))
@@ -29,6 +44,10 @@
 #define TRACE_VALUE_TYPE_STRING (static_cast<unsigned char>(6))
 #define TRACE_VALUE_TYPE_COPY_STRING (static_cast<unsigned char>(7))
 
+#if defined(TRACE_EVENT0)
+#error "Another copy of trace_event.h has already been included."
+#endif
+
 #if RTC_TRACE_EVENTS_ENABLED
 
 // Extracted from Chromium's src/base/debug/trace_event.h.
@@ -705,6 +724,7 @@
 
 }  // namespace trace_event_internal
 }  // namespace webrtc
+
 #else
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -779,5 +799,6 @@
 #define TRACE_EVENT_API_ADD_TRACE_EVENT RTC_NOOP()
 
 #endif  // RTC_TRACE_EVENTS_ENABLED
+#endif  // RTC_USE_PERFETTO
 
 #endif  // RTC_BASE_TRACE_EVENT_H_
diff --git a/test/BUILD.gn b/test/BUILD.gn
index d1554d7..783e94a 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -627,6 +627,9 @@
       if (is_win) {
         deps += [ "../rtc_base:win32_socket_init" ]
       }
+      if (rtc_use_perfetto) {
+        deps += [ "//third_party/perfetto/src/tracing:client_api" ]
+      }
     }
 
     rtc_library("test_main") {
diff --git a/test/DEPS b/test/DEPS
index a9e9a7b..f9f47e1 100644
--- a/test/DEPS
+++ b/test/DEPS
@@ -49,6 +49,10 @@
   ".*stun_validator_fuzzer\.cc": [
     "+p2p/base/stun.h",
   ],
+  ".*test_main_lib\.cc": [
+    "+perfetto/tracing",
+    "+protos/perfetto/config",
+  ],
   ".*test_main\.cc": [
     "+absl/debugging/failure_signal_handler.h",
     "+absl/debugging/symbolize.h",
diff --git a/test/test_main_lib.cc b/test/test_main_lib.cc
index 4c80315..76b5a16 100644
--- a/test/test_main_lib.cc
+++ b/test/test_main_lib.cc
@@ -10,7 +10,10 @@
 
 #include "test/test_main_lib.h"
 
+#include <cstddef>
+#include <cstdio>
 #include <fstream>
+#include <ios>
 #include <memory>
 #include <string>
 #include <vector>
@@ -18,6 +21,7 @@
 #include "absl/flags/flag.h"
 #include "absl/memory/memory.h"
 #include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
 #include "absl/types/optional.h"
 #include "api/test/metrics/chrome_perf_dashboard_metrics_exporter.h"
 #include "api/test/metrics/global_metrics_logger_and_exporter.h"
@@ -39,6 +43,13 @@
 #include "test/testsupport/perf_test.h"
 #include "test/testsupport/resources_dir_flag.h"
 
+#if defined(RTC_USE_PERFETTO)
+#include "perfetto/tracing/backend_type.h"
+#include "perfetto/tracing/tracing.h"
+#include "protos/perfetto/config/trace_config.gen.h"
+#include "rtc_base/event_tracer.h"
+#endif
+
 #if defined(WEBRTC_WIN)
 #include "rtc_base/win32_socket_init.h"
 #endif
@@ -92,8 +103,7 @@
 ABSL_FLAG(std::string,
           trace_event,
           "",
-          "Path to collect trace events (json file) for chrome://tracing. "
-          "If not set, events aren't captured.");
+          "Path to collect trace events. If not set, events aren't captured.");
 
 ABSL_FLAG(std::string,
           test_launcher_shard_index,
@@ -167,8 +177,7 @@
     std::string trace_event_path = absl::GetFlag(FLAGS_trace_event);
     const bool capture_events = !trace_event_path.empty();
     if (capture_events) {
-      rtc::tracing::SetupInternalTracer();
-      rtc::tracing::StartInternalCapture(trace_event_path);
+      StartTracingCapture(trace_event_path);
     }
 
     absl::optional<std::vector<std::string>> metrics_to_plot =
@@ -236,7 +245,7 @@
 #endif
 
     if (capture_events) {
-      rtc::tracing::StopInternalCapture();
+      StopTracingCapture();
     }
 
 #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) ||  \
@@ -256,6 +265,59 @@
 #if defined(WEBRTC_WIN)
   std::unique_ptr<rtc::WinsockInitializer> winsock_init_;
 #endif
+#if defined(RTC_USE_PERFETTO)
+  std::unique_ptr<perfetto::TracingSession> tracing_session_;
+  FILE* tracing_output_file_ = nullptr;
+#endif
+
+  void StartTracingCapture(absl::string_view trace_output_file) {
+#if defined(RTC_USE_PERFETTO)
+    tracing_output_file_ = std::fopen(trace_output_file.data(), "w");
+    if (!tracing_output_file_) {
+      RTC_LOG(LS_ERROR) << "Failed to open trace file \"" << trace_output_file
+                        << "\". Tracing will be disabled.";
+    }
+    perfetto::TracingInitArgs args;
+    args.backends |= perfetto::kInProcessBackend;
+    perfetto::Tracing::Initialize(args);
+    webrtc::RegisterPerfettoTrackEvents();
+
+    perfetto::TraceConfig cfg;
+    cfg.add_buffers()->set_size_kb(1024);  // Record up to 1 MiB.
+    tracing_session_ = perfetto::Tracing::NewTrace();
+    tracing_session_->Setup(cfg);
+    RTC_LOG(LS_INFO)
+        << "Starting tracing with Perfetto and outputting to file \""
+        << trace_output_file << "\"";
+    tracing_session_->StartBlocking();
+#else
+    rtc::tracing::SetupInternalTracer();
+    rtc::tracing::StartInternalCapture(trace_output_file);
+#endif
+  }
+
+  void StopTracingCapture() {
+#if defined(RTC_USE_PERFETTO)
+    if (tracing_output_file_) {
+      RTC_CHECK(tracing_session_);
+      tracing_session_->StopBlocking();
+      std::vector<char> tracing_data = tracing_session_->ReadTraceBlocking();
+      size_t count = std::fwrite(tracing_data.data(), sizeof tracing_data[0],
+                                 tracing_data.size(), tracing_output_file_);
+      if (count != tracing_data.size()) {
+        RTC_LOG(LS_ERROR) << "Expected to write " << tracing_data.size()
+                          << " bytes but only " << count << " bytes written";
+      }
+      std::fclose(tracing_output_file_);
+      tracing_output_file_ = nullptr;
+    } else {
+      RTC_LOG(LS_INFO) << "no file";
+    }
+
+#else
+    rtc::tracing::StopInternalCapture();
+#endif
+  }
 };
 
 }  // namespace
diff --git a/tools_webrtc/mb/mb_config.pyl b/tools_webrtc/mb/mb_config.pyl
index 8862950..a64b9e0 100644
--- a/tools_webrtc/mb/mb_config.pyl
+++ b/tools_webrtc/mb/mb_config.pyl
@@ -147,6 +147,7 @@
         'dummy_audio_file_devices_no_protobuf_android_arm',
         'rtti_no_sctp': 'rtti_no_sctp_android_arm',
 	'disable_trace_events': 'disable_trace_events_android_arm',
+        'perfetto': 'perfetto_android_arm',
       },
       'android_arm_rel': 'android_release_bot_arm',
       'android_compile_arm64_dbg': 'android_debug_static_bot_arm64',
@@ -185,6 +186,7 @@
         'dummy_audio_file_devices_no_protobuf_x64',
         'rtti_no_sctp': 'rtti_no_sctp_x64',
         'disable_trace_events': 'disable_trace_events_x64',
+        'perfetto': 'perfetto_x64',
       },
       'linux_msan': 'msan_clang_release_bot_x64',
       'linux_rel': 'release_bot_x64',
@@ -221,6 +223,7 @@
         'dummy_audio_file_devices_no_protobuf_x86',
         'rtti_no_sctp': 'rtti_no_sctp_no_unicode_win_x86',
         'disable_trace_events': 'disable_trace_events_x86',
+        'perfetto': 'perfetto_x86',
       },
     }
   },
@@ -353,6 +356,9 @@
     'msan_clang_release_bot_x64':
     ['msan', 'clang', 'openh264', 'pure_release_bot', 'x64', 'h265'],
     'no_h264_debug_bot_x86': ['debug_bot', 'x86'],
+    'perfetto_android_arm': [ 'android', 'arm', 'perfetto', 'release_bot'],
+    'perfetto_x64': [ 'x64', 'perfetto', 'release_bot'],
+    'perfetto_x86': [ 'x86', 'perfetto', 'release_bot'],
     'pure_release_bot_x64': ['openh264', 'pure_release_bot', 'x64', 'h265'],
     'pure_release_bot_x86': ['openh264', 'pure_release_bot', 'x86', 'h265'],
     'release_bot_arm': ['openh264', 'release_bot', 'arm', 'h265'],
@@ -513,6 +519,9 @@
       'gn_args':
       'coverage_instrumentation_input_file="//.code-coverage/files_to_instrument.txt"'
     },
+    'perfetto': {
+      'gn_args': 'rtc_use_perfetto=true',
+    },
     # The 'pure_release_bot' configuration is for release bots that are doing a
     # 100% release build without DCHECKs while 'release_bot' is a partial
     # release configs since `dcheck_always_on` is set to true.
diff --git a/webrtc.gni b/webrtc.gni
index 969cdbd..de35c21 100644
--- a/webrtc.gni
+++ b/webrtc.gni
@@ -268,6 +268,9 @@
   # WebRTC does not declare its public dependencies. See webrtc:8603. Instead
   # WebRTC is using a global common dependencies.
   rtc_common_public_deps = []  # no-presubmit-check TODO(webrtc:8603)
+
+  # When true, include the Perfetto library.
+  rtc_use_perfetto = false
 }
 
 if (!build_with_mozilla) {
@@ -385,6 +388,9 @@
     rtc_common_configs += [ "//build/config/compiler:enable_arc" ]
   }
 }
+if (rtc_use_perfetto) {
+  rtc_common_configs += [ "//third_party/perfetto/gn:public_config" ]
+}
 
 # Global public configuration that should be applied to all WebRTC targets. You
 # normally shouldn't need to include this in your target as it's automatically