Record incident details into the trace

This patch records incident details from statsd (config id, config uid
and alert id) into the trace (together with the full trace
configuration) so they can be cross-correlated.

Bug: 73627502
Change-Id: Ib552a8b5977bc39f0b9e37dc79011d4d26eab65e
diff --git a/Android.bp b/Android.bp
index da39ea9..de7759d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -20,6 +20,7 @@
   srcs: [
     ":perfetto_protos_perfetto_common_common_gen",
     ":perfetto_protos_perfetto_config_config_gen",
+    ":perfetto_protos_perfetto_config_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
     ":perfetto_protos_perfetto_trace_chrome_lite_gen",
     ":perfetto_protos_perfetto_trace_chrome_zero_gen",
@@ -98,6 +99,7 @@
   generated_headers: [
     "perfetto_protos_perfetto_common_common_gen_headers",
     "perfetto_protos_perfetto_config_config_gen_headers",
+    "perfetto_protos_perfetto_config_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
@@ -126,6 +128,7 @@
   srcs: [
     ":perfetto_protos_perfetto_common_common_gen",
     ":perfetto_protos_perfetto_config_config_gen",
+    ":perfetto_protos_perfetto_config_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
     ":perfetto_protos_perfetto_trace_chrome_lite_gen",
     ":perfetto_protos_perfetto_trace_chrome_zero_gen",
@@ -193,6 +196,7 @@
   generated_headers: [
     "perfetto_protos_perfetto_common_common_gen_headers",
     "perfetto_protos_perfetto_config_config_gen_headers",
+    "perfetto_protos_perfetto_config_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
@@ -243,6 +247,7 @@
   srcs: [
     ":perfetto_protos_perfetto_common_common_gen",
     ":perfetto_protos_perfetto_config_config_gen",
+    ":perfetto_protos_perfetto_config_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
     ":perfetto_protos_perfetto_trace_chrome_lite_gen",
     ":perfetto_protos_perfetto_trace_chrome_zero_gen",
@@ -333,6 +338,7 @@
   generated_headers: [
     "perfetto_protos_perfetto_common_common_gen_headers",
     "perfetto_protos_perfetto_config_config_gen_headers",
+    "perfetto_protos_perfetto_config_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
@@ -447,6 +453,61 @@
   ],
 }
 
+// GN target: //protos/perfetto/config:config_zero_gen
+genrule {
+  name: "perfetto_protos_perfetto_config_config_zero_gen",
+  srcs: [
+    "protos/perfetto/config/chrome/chrome_config.proto",
+    "protos/perfetto/config/data_source_config.proto",
+    "protos/perfetto/config/data_source_descriptor.proto",
+    "protos/perfetto/config/ftrace/ftrace_config.proto",
+    "protos/perfetto/config/test_config.proto",
+    "protos/perfetto/config/trace_config.proto",
+  ],
+  tools: [
+    "aprotoc",
+    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  out: [
+    "external/perfetto/protos/perfetto/config/chrome/chrome_config.pbzero.cc",
+    "external/perfetto/protos/perfetto/config/data_source_config.pbzero.cc",
+    "external/perfetto/protos/perfetto/config/data_source_descriptor.pbzero.cc",
+    "external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pbzero.cc",
+    "external/perfetto/protos/perfetto/config/test_config.pbzero.cc",
+    "external/perfetto/protos/perfetto/config/trace_config.pbzero.cc",
+  ],
+}
+
+// GN target: //protos/perfetto/config:config_zero_gen
+genrule {
+  name: "perfetto_protos_perfetto_config_config_zero_gen_headers",
+  srcs: [
+    "protos/perfetto/config/chrome/chrome_config.proto",
+    "protos/perfetto/config/data_source_config.proto",
+    "protos/perfetto/config/data_source_descriptor.proto",
+    "protos/perfetto/config/ftrace/ftrace_config.proto",
+    "protos/perfetto/config/test_config.proto",
+    "protos/perfetto/config/trace_config.proto",
+  ],
+  tools: [
+    "aprotoc",
+    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  out: [
+    "external/perfetto/protos/perfetto/config/chrome/chrome_config.pbzero.h",
+    "external/perfetto/protos/perfetto/config/data_source_config.pbzero.h",
+    "external/perfetto/protos/perfetto/config/data_source_descriptor.pbzero.h",
+    "external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pbzero.h",
+    "external/perfetto/protos/perfetto/config/test_config.pbzero.h",
+    "external/perfetto/protos/perfetto/config/trace_config.pbzero.h",
+  ],
+  export_include_dirs: [
+    "protos",
+  ],
+}
+
 // GN target: //protos/perfetto/ipc:ipc_gen
 genrule {
   name: "perfetto_protos_perfetto_ipc_ipc_gen",
@@ -3029,6 +3090,7 @@
   srcs: [
     ":perfetto_protos_perfetto_common_common_gen",
     ":perfetto_protos_perfetto_config_config_gen",
+    ":perfetto_protos_perfetto_config_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
     ":perfetto_protos_perfetto_trace_chrome_lite_gen",
     ":perfetto_protos_perfetto_trace_chrome_zero_gen",
@@ -3094,6 +3156,7 @@
   generated_headers: [
     "perfetto_protos_perfetto_common_common_gen_headers",
     "perfetto_protos_perfetto_config_config_gen_headers",
+    "perfetto_protos_perfetto_config_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
@@ -3110,6 +3173,7 @@
   export_generated_headers: [
     "perfetto_protos_perfetto_common_common_gen_headers",
     "perfetto_protos_perfetto_config_config_gen_headers",
+    "perfetto_protos_perfetto_config_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
@@ -3136,6 +3200,7 @@
 cc_library_static {
   name: "perfetto_trace_protos",
   srcs: [
+    ":perfetto_protos_perfetto_config_config_gen",
     ":perfetto_protos_perfetto_trace_chrome_lite_gen",
     ":perfetto_protos_perfetto_trace_filesystem_lite_gen",
     ":perfetto_protos_perfetto_trace_ftrace_lite_gen",
@@ -3151,6 +3216,7 @@
     "include",
   ],
   generated_headers: [
+    "perfetto_protos_perfetto_config_config_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_filesystem_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ftrace_lite_gen_headers",
@@ -3158,6 +3224,7 @@
     "perfetto_protos_perfetto_trace_ps_lite_gen_headers",
   ],
   export_generated_headers: [
+    "perfetto_protos_perfetto_config_config_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_filesystem_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ftrace_lite_gen_headers",
@@ -3179,6 +3246,7 @@
   srcs: [
     ":perfetto_protos_perfetto_common_common_gen",
     ":perfetto_protos_perfetto_config_config_gen",
+    ":perfetto_protos_perfetto_config_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
     ":perfetto_protos_perfetto_trace_chrome_lite_gen",
     ":perfetto_protos_perfetto_trace_chrome_zero_gen",
@@ -3327,6 +3395,7 @@
   generated_headers: [
     "perfetto_protos_perfetto_common_common_gen_headers",
     "perfetto_protos_perfetto_config_config_gen_headers",
+    "perfetto_protos_perfetto_config_config_zero_gen_headers",
     "perfetto_protos_perfetto_ipc_ipc_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
diff --git a/include/perfetto/tracing/core/trace_config.h b/include/perfetto/tracing/core/trace_config.h
index 8a3584b..2b9f8f2 100644
--- a/include/perfetto/tracing/core/trace_config.h
+++ b/include/perfetto/tracing/core/trace_config.h
@@ -48,6 +48,7 @@
 class ChromeConfig;
 class TestConfig;
 class TraceConfig_ProducerConfig;
+class TraceConfig_StatsdMetadata;
 }  // namespace protos
 }  // namespace perfetto
 
@@ -169,6 +170,44 @@
     std::string unknown_fields_;
   };
 
+  class StatsdMetadata {
+   public:
+    StatsdMetadata();
+    ~StatsdMetadata();
+    StatsdMetadata(StatsdMetadata&&) noexcept;
+    StatsdMetadata& operator=(StatsdMetadata&&);
+    StatsdMetadata(const StatsdMetadata&);
+    StatsdMetadata& operator=(const StatsdMetadata&);
+
+    // Conversion methods from/to the corresponding protobuf types.
+    void FromProto(const perfetto::protos::TraceConfig_StatsdMetadata&);
+    void ToProto(perfetto::protos::TraceConfig_StatsdMetadata*) const;
+
+    int64_t triggering_alert_id() const { return triggering_alert_id_; }
+    void set_triggering_alert_id(int64_t value) {
+      triggering_alert_id_ = value;
+    }
+
+    int32_t triggering_config_uid() const { return triggering_config_uid_; }
+    void set_triggering_config_uid(int32_t value) {
+      triggering_config_uid_ = value;
+    }
+
+    int64_t triggering_config_id() const { return triggering_config_id_; }
+    void set_triggering_config_id(int64_t value) {
+      triggering_config_id_ = value;
+    }
+
+   private:
+    int64_t triggering_alert_id_ = {};
+    int32_t triggering_config_uid_ = {};
+    int64_t triggering_config_id_ = {};
+
+    // Allows to preserve unknown protobuf fields for compatibility
+    // with future versions of .proto files.
+    std::string unknown_fields_;
+  };
+
   TraceConfig();
   ~TraceConfig();
   TraceConfig(TraceConfig&&) noexcept;
@@ -216,6 +255,9 @@
     return &producers_.back();
   }
 
+  const StatsdMetadata& statsd_metadata() const { return statsd_metadata_; }
+  StatsdMetadata* mutable_statsd_metadata() { return &statsd_metadata_; }
+
  private:
   std::vector<BufferConfig> buffers_;
   std::vector<DataSource> data_sources_;
@@ -223,6 +265,7 @@
   bool enable_extra_guardrails_ = {};
   LockdownModeOperation lockdown_mode_ = {};
   std::vector<ProducerConfig> producers_;
+  StatsdMetadata statsd_metadata_ = {};
 
   // Allows to preserve unknown protobuf fields for compatibility
   // with future versions of .proto files.
diff --git a/protos/perfetto/config/BUILD.gn b/protos/perfetto/config/BUILD.gn
index 517ba1a..77dc8613 100644
--- a/protos/perfetto/config/BUILD.gn
+++ b/protos/perfetto/config/BUILD.gn
@@ -14,6 +14,7 @@
 
 import("../../../gn/perfetto.gni")
 import("../../../gn/proto_library.gni")
+import("../../../src/protozero/protozero_library.gni")
 
 proto_library("config") {
   generate_python = false
@@ -29,6 +30,20 @@
   ]
 }
 
+protozero_library("config_zero") {
+  proto_in_dir = "$perfetto_root_path/protos"
+  proto_out_dir = "$perfetto_root_path/protos"
+  sources = [
+    "chrome/chrome_config.proto",
+    "data_source_config.proto",
+    "data_source_descriptor.proto",
+    "ftrace/ftrace_config.proto",
+    "test_config.proto",
+    "trace_config.proto",
+  ]
+  generator_plugin_options = "wrapper_namespace=pbzero"
+}
+
 # This target is not used in the tree and is built only to guarantee that the
 # autogenerated merged proto has a valid syntax.
 proto_library("merged_config") {
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index 9bdb449..e1a5e0b 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -106,6 +106,8 @@
 // ProducerPort::StartTracing().
 // It contains the general config for the logging buffer(s) and the configs for
 // all the data source being enabled.
+//
+// Next id: 8.
 message TraceConfig {
   message BufferConfig {
     optional uint32 size_kb = 1;
@@ -199,6 +201,19 @@
   }
 
   repeated ProducerConfig producers = 6;
+
+  // Contains statsd-specific metadata about an alert associated with the trace.
+  message StatsdMetadata {
+    // The identifier of the alert which triggered this trace.
+    optional int64 triggering_alert_id = 1;
+    // The uid which registered the triggering configuration with statsd.
+    optional int32 triggering_config_uid = 2;
+    // The identifier of the config which triggered the alert.
+    optional int64 triggering_config_id = 3;
+  }
+
+  // Statsd-specific metadata.
+  optional StatsdMetadata statsd_metadata = 7;
 }
 
 // End of protos/perfetto/config/trace_config.proto
diff --git a/protos/perfetto/config/trace_config.proto b/protos/perfetto/config/trace_config.proto
index e8cf042..c6cd9ca 100644
--- a/protos/perfetto/config/trace_config.proto
+++ b/protos/perfetto/config/trace_config.proto
@@ -28,6 +28,8 @@
 // ProducerPort::StartTracing().
 // It contains the general config for the logging buffer(s) and the configs for
 // all the data source being enabled.
+//
+// Next id: 8.
 message TraceConfig {
   message BufferConfig {
     optional uint32 size_kb = 1;
@@ -121,4 +123,17 @@
   }
 
   repeated ProducerConfig producers = 6;
+
+  // Contains statsd-specific metadata about an alert associated with the trace.
+  message StatsdMetadata {
+    // The identifier of the alert which triggered this trace.
+    optional int64 triggering_alert_id = 1;
+    // The uid which registered the triggering configuration with statsd.
+    optional int32 triggering_config_uid = 2;
+    // The identifier of the config which triggered the alert.
+    optional int64 triggering_config_id = 3;
+  }
+
+  // Statsd-specific metadata.
+  optional StatsdMetadata statsd_metadata = 7;
 }
diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn
index a081c71..5298f3c 100644
--- a/protos/perfetto/trace/BUILD.gn
+++ b/protos/perfetto/trace/BUILD.gn
@@ -27,6 +27,7 @@
 # Protozero generated stubs, for writers.
 protozero_library("zero") {
   deps = [
+    "../config:config_zero",
     "chrome:zero",
     "filesystem:zero",
     "ftrace:zero",
@@ -42,6 +43,7 @@
 proto_library("lite") {
   generate_python = false
   deps = [
+    "../config:config",
     "chrome:lite",
     "filesystem:lite",
     "ftrace:lite",
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index df06282..9c38a8b 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -17,6 +17,7 @@
 syntax = "proto2";
 option optimize_for = LITE_RUNTIME;
 
+import "perfetto/config/trace_config.proto";
 import "perfetto/trace/chrome/chrome_trace_event.proto";
 import "perfetto/trace/clock_snapshot.proto";
 import "perfetto/trace/filesystem/inode_file_map.proto";
@@ -38,6 +39,10 @@
     ChromeEventBundle chrome_events = 5;
     ClockSnapshot clock_snapshot = 6;
 
+    // IDs up to 32 are reserved for events that are quite frequent because they
+    // take only one byte to encode their preamble.
+    TraceConfig trace_config = 33;
+
     // This field is only used for testing.
     TestEvent for_testing = 536870911;  // 2^29 - 1, max field id for protos.
   }
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index 0b10e31..b2ff451 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -83,12 +83,22 @@
   --dropbox        -d TAG : Upload trace into DropBox using tag TAG (default: %s)
   --no-guardrails  -n     : Ignore guardrails triggered when using --dropbox (for testing).
   --help           -h
+
+statsd-specific flags:
+  --alert-id           : ID of the alert that triggered this trace.
+  --config-id          : ID of the triggering config.
+  --config-uid         : UID of app which registered the config.
 )",
                 argv0, kDefaultDropBoxTag);
   return 1;
 }
 
 int PerfettoCmd::Main(int argc, char** argv) {
+  enum LongOption {
+    OPT_ALERT_ID = 1000,
+    OPT_CONFIG_ID,
+    OPT_CONFIG_UID,
+  };
   static const struct option long_options[] = {
       // |option_index| relies on the order of options, don't reshuffle them.
       {"help", required_argument, 0, 'h'},
@@ -97,12 +107,16 @@
       {"background", no_argument, 0, 'b'},
       {"dropbox", optional_argument, 0, 'd'},
       {"no-guardrails", optional_argument, 0, 'n'},
+      {"alert-id", required_argument, 0, OPT_ALERT_ID},
+      {"config-id", required_argument, 0, OPT_CONFIG_ID},
+      {"config-uid", required_argument, 0, OPT_CONFIG_UID},
       {nullptr, 0, nullptr, 0}};
 
   int option_index = 0;
   std::string trace_config_raw;
   bool background = false;
   bool ignore_guardrails = false;
+  perfetto::protos::TraceConfig::StatsdMetadata statsd_metadata;
   for (;;) {
     int option =
         getopt_long(argc, argv, "c:o:bd::n", long_options, &option_index);
@@ -160,6 +174,21 @@
       continue;
     }
 
+    if (option == OPT_ALERT_ID) {
+      statsd_metadata.set_triggering_alert_id(atoll(optarg));
+      continue;
+    }
+
+    if (option == OPT_CONFIG_ID) {
+      statsd_metadata.set_triggering_config_id(atoll(optarg));
+      continue;
+    }
+
+    if (option == OPT_CONFIG_UID) {
+      statsd_metadata.set_triggering_config_uid(atol(optarg));
+      continue;
+    }
+
     return PrintUsage(argv[0]);
   }
 
@@ -186,6 +215,7 @@
     PERFETTO_ELOG("Could not parse TraceConfig proto from stdin");
     return 1;
   }
+  *trace_config_proto.mutable_statsd_metadata() = std::move(statsd_metadata);
   trace_config_.reset(new TraceConfig());
   trace_config_->FromProto(trace_config_proto);
   trace_config_raw.clear();
diff --git a/src/tracing/core/service_impl.cc b/src/tracing/core/service_impl.cc
index 6249001..3c3a871 100644
--- a/src/tracing/core/service_impl.cc
+++ b/src/tracing/core/service_impl.cc
@@ -341,6 +341,7 @@
   std::vector<TracePacket> packets;
   size_t packets_bytes = 0;  // SUM(slice.size() for each slice in |packets|).
   MaybeSnapshotClocks(tracing_session, &packets);
+  MaybeEmitTraceConfig(tracing_session, &packets);
 
   // This is a rough threshold to determine how to split packets within each
   // IPC. This is not an upper bound, we just stop accumulating packets and send
@@ -740,6 +741,21 @@
     c->set_type(clock.type);
     c->set_timestamp(base::FromPosixTimespec(clock.ts).count());
   }
+  packet.set_trusted_uid(getuid());
+  Slice slice = Slice::Allocate(packet.ByteSize());
+  PERFETTO_CHECK(packet.SerializeWithCachedSizesToArray(slice.own_data()));
+  packets->emplace_back();
+  packets->back().AddSlice(std::move(slice));
+}
+
+void ServiceImpl::MaybeEmitTraceConfig(TracingSession* tracing_session,
+                                       std::vector<TracePacket>* packets) {
+  if (tracing_session->did_emit_config)
+    return;
+  tracing_session->did_emit_config = true;
+  protos::TracePacket packet;
+  tracing_session->config.ToProto(packet.mutable_trace_config());
+  packet.set_trusted_uid(getuid());
   Slice slice = Slice::Allocate(packet.ByteSize());
   PERFETTO_CHECK(packet.SerializeWithCachedSizesToArray(slice.own_data()));
   packets->emplace_back();
diff --git a/src/tracing/core/service_impl.h b/src/tracing/core/service_impl.h
index 1596877..ef01232 100644
--- a/src/tracing/core/service_impl.h
+++ b/src/tracing/core/service_impl.h
@@ -204,6 +204,9 @@
 
     // When the last clock snapshot was emitted into the output stream.
     base::TimeMillis last_clock_snapshot = {};
+
+    // Whether we mirrored the trace config back to the trace output yet.
+    bool did_emit_config = false;
   };
 
   ServiceImpl(const ServiceImpl&) = delete;
@@ -225,6 +228,7 @@
   void UpdateMemoryGuardrail();
 
   void MaybeSnapshotClocks(TracingSession*, std::vector<TracePacket>*);
+  void MaybeEmitTraceConfig(TracingSession*, std::vector<TracePacket>*);
 
   TraceBuffez* GetBufferByID(BufferID);
 
diff --git a/src/tracing/core/trace_config.cc b/src/tracing/core/trace_config.cc
index 0205d8d..54d409e 100644
--- a/src/tracing/core/trace_config.cc
+++ b/src/tracing/core/trace_config.cc
@@ -71,6 +71,8 @@
     producers_.emplace_back();
     producers_.back().FromProto(field);
   }
+
+  statsd_metadata_.FromProto(proto.statsd_metadata());
   unknown_fields_ = proto.unknown_fields();
 }
 
@@ -108,6 +110,8 @@
     auto* entry = proto->add_producers();
     it.ToProto(entry);
   }
+
+  statsd_metadata_.ToProto(proto->mutable_statsd_metadata());
   *(proto->mutable_unknown_fields()) = unknown_fields_;
 }
 
@@ -244,4 +248,64 @@
   *(proto->mutable_unknown_fields()) = unknown_fields_;
 }
 
+TraceConfig::StatsdMetadata::StatsdMetadata() = default;
+TraceConfig::StatsdMetadata::~StatsdMetadata() = default;
+TraceConfig::StatsdMetadata::StatsdMetadata(
+    const TraceConfig::StatsdMetadata&) = default;
+TraceConfig::StatsdMetadata& TraceConfig::StatsdMetadata::operator=(
+    const TraceConfig::StatsdMetadata&) = default;
+TraceConfig::StatsdMetadata::StatsdMetadata(
+    TraceConfig::StatsdMetadata&&) noexcept = default;
+TraceConfig::StatsdMetadata& TraceConfig::StatsdMetadata::operator=(
+    TraceConfig::StatsdMetadata&&) = default;
+
+void TraceConfig::StatsdMetadata::FromProto(
+    const perfetto::protos::TraceConfig_StatsdMetadata& proto) {
+  static_assert(
+      sizeof(triggering_alert_id_) == sizeof(proto.triggering_alert_id()),
+      "size mismatch");
+  triggering_alert_id_ =
+      static_cast<decltype(triggering_alert_id_)>(proto.triggering_alert_id());
+
+  static_assert(
+      sizeof(triggering_config_uid_) == sizeof(proto.triggering_config_uid()),
+      "size mismatch");
+  triggering_config_uid_ = static_cast<decltype(triggering_config_uid_)>(
+      proto.triggering_config_uid());
+
+  static_assert(
+      sizeof(triggering_config_id_) == sizeof(proto.triggering_config_id()),
+      "size mismatch");
+  triggering_config_id_ = static_cast<decltype(triggering_config_id_)>(
+      proto.triggering_config_id());
+  unknown_fields_ = proto.unknown_fields();
+}
+
+void TraceConfig::StatsdMetadata::ToProto(
+    perfetto::protos::TraceConfig_StatsdMetadata* proto) const {
+  proto->Clear();
+
+  static_assert(
+      sizeof(triggering_alert_id_) == sizeof(proto->triggering_alert_id()),
+      "size mismatch");
+  proto->set_triggering_alert_id(
+      static_cast<decltype(proto->triggering_alert_id())>(
+          triggering_alert_id_));
+
+  static_assert(
+      sizeof(triggering_config_uid_) == sizeof(proto->triggering_config_uid()),
+      "size mismatch");
+  proto->set_triggering_config_uid(
+      static_cast<decltype(proto->triggering_config_uid())>(
+          triggering_config_uid_));
+
+  static_assert(
+      sizeof(triggering_config_id_) == sizeof(proto->triggering_config_id()),
+      "size mismatch");
+  proto->set_triggering_config_id(
+      static_cast<decltype(proto->triggering_config_id())>(
+          triggering_config_id_));
+  *(proto->mutable_unknown_fields()) = unknown_fields_;
+}
+
 }  // namespace perfetto
diff --git a/src/tracing/test/tracing_integration_test.cc b/src/tracing/test/tracing_integration_test.cc
index b30e5d7..1ee5e1e 100644
--- a/src/tracing/test/tracing_integration_test.cc
+++ b/src/tracing/test/tracing_integration_test.cc
@@ -31,6 +31,7 @@
 #include "src/base/test/test_task_runner.h"
 #include "src/ipc/test/test_socket.h"
 
+#include "perfetto/config/trace_config.pb.h"
 #include "perfetto/trace/test_event.pbzero.h"
 #include "perfetto/trace/trace_packet.pb.h"
 #include "perfetto/trace/trace_packet.pbzero.h"
@@ -190,10 +191,12 @@
   consumer_endpoint->ReadBuffers();
   size_t num_pack_rx = 0;
   bool saw_clock_snapshot = false;
+  bool saw_trace_config = false;
   auto all_packets_rx = task_runner_->CreateCheckpoint("all_packets_rx");
   EXPECT_CALL(consumer, OnTracePackets(_, _))
       .WillRepeatedly(
-          Invoke([&num_pack_rx, all_packets_rx, &saw_clock_snapshot](
+          Invoke([&num_pack_rx, all_packets_rx, &trace_config,
+                  &saw_clock_snapshot, &saw_trace_config](
                      std::vector<TracePacket>* packets, bool has_more) {
             for (auto& packet : *packets) {
               ASSERT_TRUE(packet.Decode());
@@ -204,6 +207,23 @@
               } else if (packet->has_clock_snapshot()) {
                 EXPECT_GE(packet->clock_snapshot().clocks_size(), 8);
                 saw_clock_snapshot = true;
+              } else if (packet->has_trace_config()) {
+                protos::TraceConfig config_proto;
+                trace_config.ToProto(&config_proto);
+                Slice expected_slice = Slice::Allocate(config_proto.ByteSize());
+                config_proto.SerializeWithCachedSizesToArray(
+                    expected_slice.own_data());
+                Slice actual_slice =
+                    Slice::Allocate(packet->trace_config().ByteSize());
+                packet->trace_config().SerializeWithCachedSizesToArray(
+                    actual_slice.own_data());
+                EXPECT_EQ(std::string(reinterpret_cast<const char*>(
+                                          expected_slice.own_data()),
+                                      expected_slice.size),
+                          std::string(reinterpret_cast<const char*>(
+                                          actual_slice.own_data()),
+                                      actual_slice.size));
+                saw_trace_config = true;
               }
             }
             if (!has_more)
@@ -212,6 +232,7 @@
   task_runner_->RunUntilCheckpoint("all_packets_rx");
   ASSERT_EQ(kNumPackets, num_pack_rx);
   EXPECT_TRUE(saw_clock_snapshot);
+  EXPECT_TRUE(saw_trace_config);
 
   // TODO(primiano): cover FreeBuffers.
 
diff --git a/test/cts/end_to_end_integrationtest_cts.cc b/test/cts/end_to_end_integrationtest_cts.cc
index 37bca1f..d6531ec 100644
--- a/test/cts/end_to_end_integrationtest_cts.cc
+++ b/test/cts/end_to_end_integrationtest_cts.cc
@@ -61,8 +61,9 @@
                         std::vector<TracePacket> packets, bool has_more) {
       for (auto& packet : packets) {
         ASSERT_TRUE(packet.Decode());
-        ASSERT_TRUE(packet->has_for_testing() || packet->has_clock_snapshot());
-        if (packet->has_clock_snapshot()) {
+        ASSERT_TRUE(packet->has_for_testing() || packet->has_clock_snapshot() ||
+                    packet->has_trace_config());
+        if (packet->has_clock_snapshot() || packet->has_trace_config()) {
           continue;
         }
         ASSERT_EQ(protos::TracePacket::kTrustedUid,
@@ -72,8 +73,8 @@
       total += packets.size();
 
       if (!has_more) {
-        // One extra packet for the clock snapshot.
-        ASSERT_EQ(total, kEventCount + 1);
+        // Extra packets for the clock snapshot and trace config.
+        ASSERT_EQ(total, kEventCount + 2);
         on_readback_complete();
       }
     };
diff --git a/test/end_to_end_integrationtest.cc b/test/end_to_end_integrationtest.cc
index 1c969b4..b939147 100644
--- a/test/end_to_end_integrationtest.cc
+++ b/test/end_to_end_integrationtest.cc
@@ -101,8 +101,9 @@
                               std::vector<TracePacket> packets, bool has_more) {
     for (auto& packet : packets) {
       ASSERT_TRUE(packet.Decode());
-      ASSERT_TRUE(packet->has_ftrace_events() || packet->has_clock_snapshot());
-      if (packet->has_clock_snapshot())
+      ASSERT_TRUE(packet->has_ftrace_events() || packet->has_clock_snapshot() ||
+                  packet->has_trace_config());
+      if (packet->has_clock_snapshot() || packet->has_trace_config())
         continue;
       for (int ev = 0; ev < packet->ftrace_events().event_size(); ev++) {
         ASSERT_TRUE(packet->ftrace_events().event(ev).has_sched_switch());
@@ -182,7 +183,7 @@
                               std::vector<TracePacket> packets, bool has_more) {
     for (auto& packet : packets) {
       ASSERT_TRUE(packet.Decode());
-      if (packet->has_clock_snapshot())
+      if (packet->has_clock_snapshot() || packet->has_trace_config())
         continue;
       ASSERT_TRUE(packet->has_for_testing());
       ASSERT_EQ(protos::TracePacket::kTrustedUid,
@@ -192,8 +193,9 @@
     total += packets.size();
 
     if (!has_more) {
-      // One extra packet for the clock snapshot.
-      ASSERT_EQ(total, kEventCount + 1);
+      // One extra packet for the clock snapshot and another for the trace
+      // config.
+      ASSERT_EQ(total, kEventCount + 2);
       on_readback_complete();
     }
   };