Merge "Use Chrome's TrackEvent extensions in trace processor"
diff --git a/Android.bp b/Android.bp
index a8f0d8c..d2eefc3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6324,6 +6324,44 @@
   ],
 }
 
+// GN: //protos/third_party/chromium:chrome_track_event_descriptor
+genrule {
+  name: "perfetto_protos_third_party_chromium_chrome_track_event_descriptor",
+  srcs: [
+    "protos/perfetto/trace/track_event/chrome_application_state_info.proto",
+    "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
+    "protos/perfetto/trace/track_event/chrome_frame_reporter.proto",
+    "protos/perfetto/trace/track_event/chrome_histogram_sample.proto",
+    "protos/perfetto/trace/track_event/chrome_keyed_service.proto",
+    "protos/perfetto/trace/track_event/chrome_latency_info.proto",
+    "protos/perfetto/trace/track_event/chrome_legacy_ipc.proto",
+    "protos/perfetto/trace/track_event/chrome_message_pump.proto",
+    "protos/perfetto/trace/track_event/chrome_mojo_event_info.proto",
+    "protos/perfetto/trace/track_event/chrome_process_descriptor.proto",
+    "protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.proto",
+    "protos/perfetto/trace/track_event/chrome_thread_descriptor.proto",
+    "protos/perfetto/trace/track_event/chrome_user_event.proto",
+    "protos/perfetto/trace/track_event/chrome_window_handle_event_info.proto",
+    "protos/perfetto/trace/track_event/counter_descriptor.proto",
+    "protos/perfetto/trace/track_event/debug_annotation.proto",
+    "protos/perfetto/trace/track_event/log_message.proto",
+    "protos/perfetto/trace/track_event/process_descriptor.proto",
+    "protos/perfetto/trace/track_event/source_location.proto",
+    "protos/perfetto/trace/track_event/task_execution.proto",
+    "protos/perfetto/trace/track_event/thread_descriptor.proto",
+    "protos/perfetto/trace/track_event/track_descriptor.proto",
+    "protos/perfetto/trace/track_event/track_event.proto",
+    "protos/third_party/chromium/chrome_track_event.proto",
+  ],
+  tools: [
+    "aprotoc",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --descriptor_set_out=$(out) $(in)",
+  out: [
+    "perfetto_protos_third_party_chromium_chrome_track_event_descriptor.bin",
+  ],
+}
+
 // GN: //protos/third_party/pprof:zero
 genrule {
   name: "perfetto_protos_third_party_pprof_zero_gen",
@@ -7288,6 +7326,21 @@
   ],
 }
 
+// GN: //src/trace_processor/importers:gen_cc_chrome_track_event_descriptor
+genrule {
+  name: "perfetto_src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
+  srcs: [
+    ":perfetto_protos_third_party_chromium_chrome_track_event_descriptor",
+  ],
+  cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
+  out: [
+    "src/trace_processor/importers/chrome_track_event.descriptor.h",
+  ],
+  tool_files: [
+    "tools/gen_cc_proto_descriptor.py",
+  ],
+}
+
 // GN: //src/trace_processor/importers:gen_cc_config_descriptor
 genrule {
   name: "perfetto_src_trace_processor_importers_gen_cc_config_descriptor",
@@ -8753,6 +8806,7 @@
     "perfetto_src_protozero_testing_messages_cpp_gen_headers",
     "perfetto_src_protozero_testing_messages_lite_gen_headers",
     "perfetto_src_protozero_testing_messages_zero_gen_headers",
+    "perfetto_src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
     "perfetto_src_trace_processor_importers_gen_cc_config_descriptor",
     "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
     "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
@@ -8907,6 +8961,7 @@
     "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
     "perfetto_src_base_version_gen_h",
+    "perfetto_src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
     "perfetto_src_trace_processor_importers_gen_cc_config_descriptor",
     "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
     "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
@@ -9061,6 +9116,7 @@
     "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
     "perfetto_protos_third_party_pprof_zero_gen_headers",
     "perfetto_src_base_version_gen_h",
+    "perfetto_src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
     "perfetto_src_trace_processor_importers_gen_cc_config_descriptor",
     "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
     "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
diff --git a/BUILD b/BUILD
index 71a5ef7..20db6b0 100644
--- a/BUILD
+++ b/BUILD
@@ -825,6 +825,16 @@
 )
 
 perfetto_cc_proto_descriptor(
+    name = "src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
+    deps = [
+        ":protos_third_party_chromium_chrome_track_event_descriptor",
+    ],
+    outs = [
+        "src/trace_processor/importers/chrome_track_event.descriptor.h",
+    ],
+)
+
+perfetto_cc_proto_descriptor(
     name = "src_trace_processor_importers_gen_cc_config_descriptor",
     deps = [
         ":protos_perfetto_config_descriptor",
@@ -2967,6 +2977,31 @@
     ],
 )
 
+# GN target: //protos/third_party/chromium:chrome_track_event_descriptor
+perfetto_proto_descriptor(
+    name = "protos_third_party_chromium_chrome_track_event_descriptor",
+    deps = [
+        ":protos_third_party_chromium_chrome_track_event_protos",
+    ],
+    outs = [
+        "protos_third_party_chromium_chrome_track_event_descriptor.bin",
+    ],
+)
+
+# GN target: //protos/third_party/chromium:chrome_track_event_descriptor
+perfetto_proto_library(
+    name = "protos_third_party_chromium_chrome_track_event_protos",
+    srcs = [
+        "protos/third_party/chromium/chrome_track_event.proto",
+    ],
+    visibility = [
+        PERFETTO_CONFIG.proto_library_visibility,
+    ],
+    deps = [
+        ":protos_perfetto_trace_track_event_protos",
+    ],
+)
+
 # GN target: //protos/third_party/pprof:zero
 perfetto_proto_library(
     name = "protos_third_party_pprof_protos",
@@ -3241,6 +3276,7 @@
                ":protos_perfetto_trace_track_event_zero",
                ":protozero",
                ":src_base_base",
+               ":src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
                ":src_trace_processor_importers_gen_cc_config_descriptor",
                ":src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
                ":src_trace_processor_metrics_gen_cc_metrics_descriptor",
@@ -3331,6 +3367,7 @@
                ":protozero",
                ":src_base_base",
                ":src_base_unix_socket",
+               ":src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
                ":src_trace_processor_importers_gen_cc_config_descriptor",
                ":src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
                ":src_trace_processor_metrics_gen_cc_metrics_descriptor",
@@ -3508,6 +3545,7 @@
                ":protos_third_party_pprof_zero",
                ":protozero",
                ":src_base_base",
+               ":src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
                ":src_trace_processor_importers_gen_cc_config_descriptor",
                ":src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
                ":src_trace_processor_metrics_gen_cc_metrics_descriptor",
diff --git a/protos/third_party/chromium/BUILD.gn b/protos/third_party/chromium/BUILD.gn
new file mode 100644
index 0000000..6f9b5ab
--- /dev/null
+++ b/protos/third_party/chromium/BUILD.gn
@@ -0,0 +1,9 @@
+import("../../../gn/perfetto.gni")
+import("../../../gn/proto_library.gni")
+
+perfetto_proto_library("chrome_track_event_@TYPE@") {
+  proto_generators = [ "descriptor" ]
+  sources = [ "chrome_track_event.proto" ]
+  generate_descriptor = "chrome_track_event.descriptor"
+  deps = [ "../../perfetto/trace/track_event:source_set" ]
+}
diff --git a/protos/third_party/chromium/chrome_track_event.proto b/protos/third_party/chromium/chrome_track_event.proto
index 31cd53a..158715f 100644
--- a/protos/third_party/chromium/chrome_track_event.proto
+++ b/protos/third_party/chromium/chrome_track_event.proto
@@ -4,7 +4,7 @@
 
 syntax = "proto2";
 
-import "third_party/perfetto/protos/perfetto/trace/track_event/track_event.proto";
+import public "protos/perfetto/trace/track_event/track_event.proto";
 
 package perfetto.protos;
 
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 51a21ff..e0c00c4 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -145,6 +145,7 @@
     "../protozero",
     "containers",
     "importers:common",
+    "importers:gen_cc_chrome_track_event_descriptor",
     "importers/memory_tracker:graph_processor",
     "storage",
     "tables",
diff --git a/src/trace_processor/importers/BUILD.gn b/src/trace_processor/importers/BUILD.gn
index f01421f..7d728ae 100644
--- a/src/trace_processor/importers/BUILD.gn
+++ b/src/trace_processor/importers/BUILD.gn
@@ -95,3 +95,24 @@
   outputs = [ generated_header ]
   public_configs = [ ":gen_config" ]
 }
+
+action("gen_cc_chrome_track_event_descriptor") {
+  descriptor_target =
+      "../../../protos/third_party/chromium:chrome_track_event_descriptor"
+  generated_header = "${target_gen_dir}/chrome_track_event.descriptor.h"
+
+  descriptor_file_path = get_label_info(descriptor_target, "target_gen_dir") +
+                         "/chrome_track_event.descriptor"
+
+  script = "../../../tools/gen_cc_proto_descriptor.py"
+  deps = [ descriptor_target ]
+  args = [
+    "--gen_dir",
+    rebase_path(root_gen_dir, root_build_dir),
+    "--cpp_out",
+    rebase_path(generated_header, root_build_dir),
+    rebase_path(descriptor_file_path, root_build_dir),
+  ]
+  inputs = [ descriptor_file_path ]
+  outputs = [ generated_header ]
+}
diff --git a/src/trace_processor/importers/proto/args_table_utils.cc b/src/trace_processor/importers/proto/args_table_utils.cc
index 3db5899..d7e365c 100644
--- a/src/trace_processor/importers/proto/args_table_utils.cc
+++ b/src/trace_processor/importers/proto/args_table_utils.cc
@@ -36,9 +36,11 @@
 
 util::Status ProtoToArgsTable::AddProtoFileDescriptor(
     const uint8_t* proto_descriptor_array,
-    size_t proto_descriptor_array_size) {
+    size_t proto_descriptor_array_size,
+    bool merge_existing_messages) {
   return pool_.AddFromFileDescriptorSet(proto_descriptor_array,
-                                        proto_descriptor_array_size);
+                                        proto_descriptor_array_size,
+                                        merge_existing_messages);
 }
 
 util::Status ProtoToArgsTable::InternProtoFieldsIntoArgsTable(
@@ -59,17 +61,16 @@
   protozero::ProtoDecoder decoder(cb);
   for (protozero::Field f = decoder.ReadField(); f.valid();
        f = decoder.ReadField()) {
-    auto field_idx = descriptor.FindFieldIdxByTag(f.id());
-    if (!field_idx) {
+    auto field = descriptor.FindFieldByTag(f.id());
+    if (!field) {
       // Unknown field, possibly an unknown extension.
       continue;
     }
-    auto field = descriptor.fields()[*field_idx];
 
     // If allowlist is not provided, reflect all fields. Otherwise, check if the
     // current field either an extension or is in allowlist.
     bool is_allowed =
-        field.is_extension() || fields == nullptr ||
+        field->is_extension() || !fields ||
         std::find(fields->begin(), fields->end(), f.id()) != fields->end();
 
     if (!is_allowed) {
@@ -79,8 +80,8 @@
     }
     ParsingOverrideState state{context_, sequence_state};
     RETURN_IF_ERROR(InternFieldIntoArgsTable(
-        field, repeated_field_index[f.id()], state, inserter, f));
-    if (field.is_repeated()) {
+        *field, repeated_field_index[f.id()], state, inserter, f));
+    if (field->is_repeated()) {
       repeated_field_index[f.id()]++;
     }
   }
@@ -150,29 +151,25 @@
   }
   auto proto_descriptor = pool_.descriptors()[*opt_proto_descriptor_idx];
 
-  // For repeated fields, contains mapping from field descriptor index to
-  // current count of how many fields have been serialized with this field.
+  // For repeated fields, contains mapping from numeric field ID to
+  // current count of how many values have been serialized with this field.
   std::unordered_map<size_t, int> repeated_field_index;
 
   // Parse this message field by field until there are no bytes left.
   protozero::ProtoDecoder decoder(cb.data, cb.size);
   for (auto field = decoder.ReadField(); field.valid();
        field = decoder.ReadField()) {
-    auto opt_field_descriptor_idx =
-        proto_descriptor.FindFieldIdxByTag(field.id());
-    if (!opt_field_descriptor_idx) {
+    auto field_descriptor = proto_descriptor.FindFieldByTag(field.id());
+    if (!field_descriptor) {
       // Since the binary descriptor is compiled in it is possible we're seeing
       // a new message that our descriptors don't have. Just skip the field.
       continue;
     }
-    const auto& field_descriptor =
-        proto_descriptor.fields()[*opt_field_descriptor_idx];
-
-    InternFieldIntoArgsTable(field_descriptor,
-                             repeated_field_index[*opt_field_descriptor_idx],
-                             state, inserter, field);
-    if (field_descriptor.is_repeated()) {
-      repeated_field_index[*opt_field_descriptor_idx]++;
+    InternFieldIntoArgsTable(*field_descriptor,
+                             repeated_field_index[field.id()], state, inserter,
+                             field);
+    if (field_descriptor->is_repeated()) {
+      repeated_field_index[field.id()]++;
     }
   }
   PERFETTO_DCHECK(decoder.bytes_left() == 0);
diff --git a/src/trace_processor/importers/proto/args_table_utils.h b/src/trace_processor/importers/proto/args_table_utils.h
index 88e00b2..10b6cd1 100644
--- a/src/trace_processor/importers/proto/args_table_utils.h
+++ b/src/trace_processor/importers/proto/args_table_utils.h
@@ -110,7 +110,8 @@
   // listed in the event_list file. You can then find your variable inside the
   // header location specified inside that python script.
   util::Status AddProtoFileDescriptor(const uint8_t* proto_descriptor_array,
-                                      size_t proto_descriptor_array_size);
+                                      size_t proto_descriptor_array_size,
+                                      bool merge_existing_messages = false);
 
   // Given a view of bytes that represent a serialized protozero message of
   // |type| we will parse each field into the Args table using RowId |row|.
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.cc b/src/trace_processor/importers/proto/proto_trace_reader.cc
index e2b1cde..1fbd37b 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.cc
+++ b/src/trace_processor/importers/proto/proto_trace_reader.cc
@@ -67,8 +67,9 @@
                                                        descriptor.size);
 
   auto extension = decoder.extension_set();
-  return context_->proto_to_args_table_->AddProtoFileDescriptor(extension.data,
-                                                                extension.size);
+  return context_->proto_to_args_table_->AddProtoFileDescriptor(
+      extension.data, extension.size,
+      /*merge_existing_messages=*/true);
 }
 
 util::Status ProtoTraceReader::ParsePacket(TraceBlobView packet) {
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index 019d878..89ca206 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -22,6 +22,7 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_writer.h"
 #include "perfetto/trace_processor/status.h"
+#include "src/trace_processor/importers/chrome_track_event.descriptor.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
 #include "src/trace_processor/importers/common/event_tracker.h"
 #include "src/trace_processor/importers/common/flow_tracker.h"
@@ -1325,6 +1326,11 @@
 
   PERFETTO_DCHECK(status.ok());
 
+  status = context_->proto_to_args_table_->AddProtoFileDescriptor(
+      kChromeTrackEventDescriptor.data(), kChromeTrackEventDescriptor.size());
+
+  PERFETTO_DCHECK(status.ok());
+
   // Switch |source_location_iid| into its interned data variant.
   context_->proto_to_args_table_->AddParsingOverride(
       "begin_impl_frame_args.current_args.source_location_iid",
diff --git a/src/trace_processor/metrics/metrics.cc b/src/trace_processor/metrics/metrics.cc
index 3e7ac8f..be21ada 100644
--- a/src/trace_processor/metrics/metrics.cc
+++ b/src/trace_processor/metrics/metrics.cc
@@ -97,43 +97,43 @@
 util::Status ProtoBuilder::AppendLong(const std::string& field_name,
                                       int64_t value,
                                       bool is_inside_repeated) {
-  auto field_idx = descriptor_->FindFieldIdxByName(field_name);
-  if (!field_idx.has_value()) {
+  auto field = descriptor_->FindFieldByName(field_name);
+  if (!field) {
     return util::ErrStatus("Field with name %s not found in proto type %s",
                            field_name.c_str(),
                            descriptor_->full_name().c_str());
   }
 
   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
-  const auto& field = descriptor_->fields()[field_idx.value()];
-  if (field.is_repeated() && !is_inside_repeated) {
+  if (field->is_repeated() && !is_inside_repeated) {
     return util::ErrStatus(
         "Unexpected long value for repeated field %s in proto type %s",
         field_name.c_str(), descriptor_->full_name().c_str());
   }
 
-  switch (field.type()) {
+  switch (field->type()) {
     case FieldDescriptorProto::TYPE_INT32:
     case FieldDescriptorProto::TYPE_INT64:
     case FieldDescriptorProto::TYPE_UINT32:
     case FieldDescriptorProto::TYPE_BOOL:
-      message_->AppendVarInt(field.number(), value);
+      message_->AppendVarInt(field->number(), value);
       break;
     case FieldDescriptorProto::TYPE_SINT32:
     case FieldDescriptorProto::TYPE_SINT64:
-      message_->AppendSignedVarInt(field.number(), value);
+      message_->AppendSignedVarInt(field->number(), value);
       break;
     case FieldDescriptorProto::TYPE_FIXED32:
     case FieldDescriptorProto::TYPE_SFIXED32:
     case FieldDescriptorProto::TYPE_FIXED64:
     case FieldDescriptorProto::TYPE_SFIXED64:
-      message_->AppendFixed(field.number(), value);
+      message_->AppendFixed(field->number(), value);
       break;
     default: {
       return util::ErrStatus(
           "Tried to write value of type long into field %s (in proto type %s) "
           "which has type %d",
-          field.name().c_str(), descriptor_->full_name().c_str(), field.type());
+          field->name().c_str(), descriptor_->full_name().c_str(),
+          field->type());
     }
   }
   return util::OkStatus();
@@ -142,28 +142,27 @@
 util::Status ProtoBuilder::AppendDouble(const std::string& field_name,
                                         double value,
                                         bool is_inside_repeated) {
-  auto field_idx = descriptor_->FindFieldIdxByName(field_name);
-  if (!field_idx.has_value()) {
+  auto field = descriptor_->FindFieldByName(field_name);
+  if (!field) {
     return util::ErrStatus("Field with name %s not found in proto type %s",
                            field_name.c_str(),
                            descriptor_->full_name().c_str());
   }
 
   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
-  const auto& field = descriptor_->fields()[field_idx.value()];
-  if (field.is_repeated() && !is_inside_repeated) {
+  if (field->is_repeated() && !is_inside_repeated) {
     return util::ErrStatus(
         "Unexpected double value for repeated field %s in proto type %s",
         field_name.c_str(), descriptor_->full_name().c_str());
   }
 
-  switch (field.type()) {
+  switch (field->type()) {
     case FieldDescriptorProto::TYPE_FLOAT:
     case FieldDescriptorProto::TYPE_DOUBLE: {
-      if (field.type() == FieldDescriptorProto::TYPE_FLOAT) {
-        message_->AppendFixed(field.number(), static_cast<float>(value));
+      if (field->type() == FieldDescriptorProto::TYPE_FLOAT) {
+        message_->AppendFixed(field->number(), static_cast<float>(value));
       } else {
-        message_->AppendFixed(field.number(), value);
+        message_->AppendFixed(field->number(), value);
       }
       break;
     }
@@ -171,7 +170,8 @@
       return util::ErrStatus(
           "Tried to write value of type double into field %s (in proto type "
           "%s) which has type %d",
-          field.name().c_str(), descriptor_->full_name().c_str(), field.type());
+          field->name().c_str(), descriptor_->full_name().c_str(),
+          field->type());
     }
   }
   return util::OkStatus();
@@ -180,31 +180,31 @@
 util::Status ProtoBuilder::AppendString(const std::string& field_name,
                                         base::StringView data,
                                         bool is_inside_repeated) {
-  auto field_idx = descriptor_->FindFieldIdxByName(field_name);
-  if (!field_idx.has_value()) {
+  const FieldDescriptor* field = descriptor_->FindFieldByName(field_name);
+  if (!field) {
     return util::ErrStatus("Field with name %s not found in proto type %s",
                            field_name.c_str(),
                            descriptor_->full_name().c_str());
   }
 
   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
-  const auto& field = descriptor_->fields()[field_idx.value()];
-  if (field.is_repeated() && !is_inside_repeated) {
+  if (field->is_repeated() && !is_inside_repeated) {
     return util::ErrStatus(
         "Unexpected string value for repeated field %s in proto type %s",
         field_name.c_str(), descriptor_->full_name().c_str());
   }
 
-  switch (field.type()) {
+  switch (field->type()) {
     case FieldDescriptorProto::TYPE_STRING: {
-      message_->AppendBytes(field.number(), data.data(), data.size());
+      message_->AppendBytes(field->number(), data.data(), data.size());
       break;
     }
     default: {
       return util::ErrStatus(
           "Tried to write value of type string into field %s (in proto type "
           "%s) which has type %d",
-          field.name().c_str(), descriptor_->full_name().c_str(), field.type());
+          field->name().c_str(), descriptor_->full_name().c_str(),
+          field->type());
     }
   }
   return util::OkStatus();
@@ -214,20 +214,19 @@
                                        const uint8_t* ptr,
                                        size_t size,
                                        bool is_inside_repeated) {
-  auto field_idx = descriptor_->FindFieldIdxByName(field_name);
-  if (!field_idx.has_value()) {
+  const FieldDescriptor* field = descriptor_->FindFieldByName(field_name);
+  if (!field) {
     return util::ErrStatus("Field with name %s not found in proto type %s",
                            field_name.c_str(),
                            descriptor_->full_name().c_str());
   }
 
   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
-  const auto& field = descriptor_->fields()[field_idx.value()];
-  if (field.is_repeated() && !is_inside_repeated)
-    return AppendRepeated(field, ptr, size);
+  if (field->is_repeated() && !is_inside_repeated)
+    return AppendRepeated(*field, ptr, size);
 
-  if (field.type() == FieldDescriptorProto::TYPE_MESSAGE)
-    return AppendSingleMessage(field, ptr, size);
+  if (field->type() == FieldDescriptorProto::TYPE_MESSAGE)
+    return AppendSingleMessage(*field, ptr, size);
 
   if (size == 0) {
     return util::ErrStatus(
@@ -235,13 +234,13 @@
         "Nulls are only supported for message protos; all other types should"
         "ensure that nulls are not passed to proto builder functions by using"
         "the SQLite IFNULL/COALESCE functions.",
-        field.name().c_str(), descriptor_->full_name().c_str());
+        field->name().c_str(), descriptor_->full_name().c_str());
   }
 
   return util::ErrStatus(
       "Tried to write value of type bytes into field %s (in proto type %s) "
       "which has type %d",
-      field.name().c_str(), descriptor_->full_name().c_str(), field.type());
+      field->name().c_str(), descriptor_->full_name().c_str(), field->type());
 }
 
 util::Status ProtoBuilder::AppendSingleMessage(const FieldDescriptor& field,
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 1fc79f6..567aa7e 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -930,9 +930,8 @@
       pool_.FindDescriptorIdx(".perfetto.protos.TraceMetrics");
   if (!desc_idx.has_value())
     return false;
-  base::Optional<uint32_t> field_idx =
-      pool_.descriptors()[*desc_idx].FindFieldIdxByName(metric_name);
-  return field_idx.has_value();
+  auto field_idx = pool_.descriptors()[*desc_idx].FindFieldByName(metric_name);
+  return field_idx != nullptr;
 }
 
 util::Status TraceProcessorImpl::RegisterMetric(const std::string& path,
diff --git a/src/trace_processor/util/BUILD.gn b/src/trace_processor/util/BUILD.gn
index efd3557..f497b43 100644
--- a/src/trace_processor/util/BUILD.gn
+++ b/src/trace_processor/util/BUILD.gn
@@ -44,6 +44,7 @@
     "descriptors.h",
   ]
   deps = [
+    ":util",
     "..:track_event_descriptor",
     "../../../gn:default_deps",
     "../../../include/perfetto/trace_processor",
diff --git a/src/trace_processor/util/descriptors.cc b/src/trace_processor/util/descriptors.cc
index 55bc525..d99b0e2 100644
--- a/src/trace_processor/util/descriptors.cc
+++ b/src/trace_processor/util/descriptors.cc
@@ -20,6 +20,7 @@
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "protos/perfetto/common/descriptor.pbzero.h"
 #include "protos/perfetto/trace_processor/trace_processor.pbzero.h"
+#include "src/trace_processor/util/status_macros.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -86,26 +87,13 @@
   return util::OkStatus();
 }
 
-void DescriptorPool::CheckPreviousDefinition(
-    const std::string& file_name,
-    const std::string& descriptor_name) {
-  auto prev_idx = FindDescriptorIdx(descriptor_name);
-  if (prev_idx.has_value()) {
-    auto prev_file = descriptors_[*prev_idx].file_name();
-    // We should already make sure we process each file once, so if we're
-    // hitting this path, it means the same message was defined in multiple
-    // files.
-    PERFETTO_FATAL("%s: %s was already defined in file %s", file_name.c_str(),
-                   descriptor_name.c_str(), prev_file.c_str());
-  }
-}
-
-void DescriptorPool::AddNestedProtoDescriptors(
+util::Status DescriptorPool::AddNestedProtoDescriptors(
     const std::string& file_name,
     const std::string& package_name,
     base::Optional<uint32_t> parent_idx,
     protozero::ConstBytes descriptor_proto,
-    std::vector<ExtensionInfo>* extensions) {
+    std::vector<ExtensionInfo>* extensions,
+    bool merge_existing_messages) {
   protos::pbzero::DescriptorProto::Decoder decoder(descriptor_proto);
 
   auto parent_name =
@@ -113,34 +101,70 @@
   auto full_name =
       parent_name + "." + base::StringView(decoder.name()).ToStdString();
 
-  CheckPreviousDefinition(file_name, full_name);
+  auto prev_idx = FindDescriptorIdx(full_name);
+  if (prev_idx.has_value() && !merge_existing_messages) {
+    const auto& existing_descriptor = descriptors_[*prev_idx];
+    return util::ErrStatus("%s: %s was already defined in file %s",
+                           file_name.c_str(), full_name.c_str(),
+                           existing_descriptor.file_name().c_str());
+  }
+  if (!prev_idx.has_value()) {
+    prev_idx = static_cast<unsigned int>(descriptors_.size());
+    ProtoDescriptor proto_descriptor(file_name, package_name, full_name,
+                                     ProtoDescriptor::Type::kMessage,
+                                     parent_idx);
+    descriptors_.emplace_back(std::move(proto_descriptor));
+  }
+  ProtoDescriptor& proto_descriptor = descriptors_[*prev_idx];
+  if (proto_descriptor.type() != ProtoDescriptor::Type::kMessage) {
+    return util::ErrStatus("%s was enum, redefined as message",
+                           full_name.c_str());
+  }
 
   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
-  ProtoDescriptor proto_descriptor(file_name, package_name, full_name,
-                                   ProtoDescriptor::Type::kMessage, parent_idx);
   for (auto it = decoder.field(); it; ++it) {
     FieldDescriptorProto::Decoder f_decoder(*it);
-    proto_descriptor.AddField(CreateFieldFromDecoder(f_decoder, false));
+    auto field = CreateFieldFromDecoder(f_decoder, /*is_extension=*/false);
+    auto existing_field = proto_descriptor.FindFieldByTag(field.number());
+    if (!existing_field) {
+      proto_descriptor.AddField(std::move(field));
+    } else {
+      if (field.type() != existing_field->type()) {
+        return util::ErrStatus("Field %s is re-introduced with different type",
+                               field.name().c_str());
+      }
+      if (field.type() == FieldDescriptorProto::TYPE_MESSAGE &&
+          field.resolved_type_name() != existing_field->resolved_type_name()) {
+        return util::ErrStatus(
+            "Field %s is re-introduced with different type %s (was %s)",
+            field.name().c_str(), field.resolved_type_name().c_str(),
+            existing_field->resolved_type_name().c_str());
+      }
+    }
   }
-  descriptors_.emplace_back(std::move(proto_descriptor));
 
   auto idx = static_cast<uint32_t>(descriptors_.size()) - 1;
   for (auto it = decoder.enum_type(); it; ++it) {
-    AddEnumProtoDescriptors(file_name, package_name, idx, *it);
+    AddEnumProtoDescriptors(file_name, package_name, idx, *it,
+                            merge_existing_messages);
   }
   for (auto it = decoder.nested_type(); it; ++it) {
-    AddNestedProtoDescriptors(file_name, package_name, idx, *it, extensions);
+    RETURN_IF_ERROR(AddNestedProtoDescriptors(file_name, package_name, idx, *it,
+                                              extensions,
+                                              merge_existing_messages));
   }
   for (auto ext_it = decoder.extension(); ext_it; ++ext_it) {
     extensions->emplace_back(package_name, *ext_it);
   }
+  return util::OkStatus();
 }
 
-void DescriptorPool::AddEnumProtoDescriptors(
+util::Status DescriptorPool::AddEnumProtoDescriptors(
     const std::string& file_name,
     const std::string& package_name,
     base::Optional<uint32_t> parent_idx,
-    protozero::ConstBytes descriptor_proto) {
+    protozero::ConstBytes descriptor_proto,
+    bool merge_existing_messages) {
   protos::pbzero::EnumDescriptorProto::Decoder decoder(descriptor_proto);
 
   auto parent_name =
@@ -148,22 +172,40 @@
   auto full_name =
       parent_name + "." + base::StringView(decoder.name()).ToStdString();
 
-  CheckPreviousDefinition(file_name, full_name);
+  auto prev_idx = FindDescriptorIdx(full_name);
+  if (prev_idx.has_value() && !merge_existing_messages) {
+    const auto& existing_descriptor = descriptors_[*prev_idx];
+    return util::ErrStatus("%s: %s was already defined in file %s",
+                           file_name.c_str(), full_name.c_str(),
+                           existing_descriptor.file_name().c_str());
+  }
+  if (!prev_idx.has_value()) {
+    prev_idx = static_cast<unsigned int>(descriptors_.size());
+    ProtoDescriptor proto_descriptor(file_name, package_name, full_name,
+                                     ProtoDescriptor::Type::kEnum,
+                                     base::nullopt);
+    descriptors_.emplace_back(std::move(proto_descriptor));
+  }
+  ProtoDescriptor& proto_descriptor = descriptors_[*prev_idx];
+  if (proto_descriptor.type() != ProtoDescriptor::Type::kEnum) {
+    return util::ErrStatus("%s was message, redefined as enum",
+                           full_name.c_str());
+  }
 
-  ProtoDescriptor proto_descriptor(file_name, package_name, full_name,
-                                   ProtoDescriptor::Type::kEnum, base::nullopt);
   for (auto it = decoder.value(); it; ++it) {
     protos::pbzero::EnumValueDescriptorProto::Decoder enum_value(it->data(),
                                                                  it->size());
     proto_descriptor.AddEnumValue(enum_value.number(),
                                   enum_value.name().ToStdString());
   }
-  descriptors_.emplace_back(std::move(proto_descriptor));
+
+  return util::OkStatus();
 }
 
 util::Status DescriptorPool::AddFromFileDescriptorSet(
     const uint8_t* file_descriptor_set_proto,
-    size_t size) {
+    size_t size,
+    bool merge_existing_messages) {
   // First pass: extract all the message descriptors from the file and add them
   // to the pool.
   protos::pbzero::FileDescriptorSet::Decoder proto(file_descriptor_set_proto,
@@ -179,11 +221,13 @@
     processed_files_.insert(file_name);
     std::string package = "." + base::StringView(file.package()).ToStdString();
     for (auto message_it = file.message_type(); message_it; ++message_it) {
-      AddNestedProtoDescriptors(file_name, package, base::nullopt, *message_it,
-                                &extensions);
+      RETURN_IF_ERROR(AddNestedProtoDescriptors(
+          file_name, package, base::nullopt, *message_it, &extensions,
+          merge_existing_messages));
     }
     for (auto enum_it = file.enum_type(); enum_it; ++enum_it) {
-      AddEnumProtoDescriptors(file_name, package, base::nullopt, *enum_it);
+      AddEnumProtoDescriptors(file_name, package, base::nullopt, *enum_it,
+                              merge_existing_messages);
     }
     for (auto ext_it = file.extension(); ext_it; ++ext_it) {
       extensions.emplace_back(package, *ext_it);
@@ -200,7 +244,8 @@
   // Third pass: resolve the types of all the fields to the correct indiices.
   using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
   for (auto& descriptor : descriptors_) {
-    for (auto& field : *descriptor.mutable_fields()) {
+    for (auto& entry : *descriptor.mutable_fields()) {
+      auto& field = entry.second;
       if (!field.resolved_type_name().empty())
         continue;
 
@@ -238,7 +283,8 @@
     protos::pbzero::DescriptorProto* proto_descriptor =
         descs->add_descriptors();
     proto_descriptor->set_name(desc.full_name());
-    for (auto& field : desc.fields()) {
+    for (auto& entry : desc.fields()) {
+      auto& field = entry.second;
       protos::pbzero::FieldDescriptorProto* field_descriptor =
           proto_descriptor->add_field();
       field_descriptor->set_name(field.name());
diff --git a/src/trace_processor/util/descriptors.h b/src/trace_processor/util/descriptors.h
index aac5d47..54a9ab1 100644
--- a/src/trace_processor/util/descriptors.h
+++ b/src/trace_processor/util/descriptors.h
@@ -20,6 +20,7 @@
 #include <algorithm>
 #include <set>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "perfetto/ext/base/optional.h"
@@ -81,42 +82,40 @@
 
   void AddField(FieldDescriptor descriptor) {
     PERFETTO_DCHECK(type_ == Type::kMessage);
-    fields_.emplace_back(std::move(descriptor));
+    fields_.emplace(descriptor.number(), std::move(descriptor));
   }
 
   void AddEnumValue(int32_t integer_representation,
                     std::string string_representation) {
     PERFETTO_DCHECK(type_ == Type::kEnum);
-    enum_values_.emplace_back(integer_representation,
-                              std::move(string_representation));
+    enum_values_[integer_representation] = std::move(string_representation);
   }
 
-  base::Optional<uint32_t> FindFieldIdxByName(const std::string& name) const {
+  const FieldDescriptor* FindFieldByName(const std::string& name) const {
     PERFETTO_DCHECK(type_ == Type::kMessage);
-    auto it = std::find_if(
-        fields_.begin(), fields_.end(),
-        [name](const FieldDescriptor& desc) { return desc.name() == name; });
-    auto idx = static_cast<uint32_t>(std::distance(fields_.begin(), it));
-    return idx < fields_.size() ? base::Optional<uint32_t>(idx) : base::nullopt;
+    auto it =
+        std::find_if(fields_.begin(), fields_.end(),
+                     [name](std::pair<int32_t, const FieldDescriptor&> p) {
+                       return p.second.name() == name;
+                     });
+    if (it == fields_.end()) {
+      return nullptr;
+    }
+    return &it->second;
   }
 
-  base::Optional<uint32_t> FindFieldIdxByTag(const uint16_t tag_number) const {
+  const FieldDescriptor* FindFieldByTag(const uint32_t tag_number) const {
     PERFETTO_DCHECK(type_ == Type::kMessage);
-    auto it = std::find_if(fields_.begin(), fields_.end(),
-                           [tag_number](const FieldDescriptor& desc) {
-                             return desc.number() == tag_number;
-                           });
-    auto idx = static_cast<uint32_t>(std::distance(fields_.begin(), it));
-    return idx < fields_.size() ? base::Optional<uint32_t>(idx) : base::nullopt;
+    auto it = fields_.find(tag_number);
+    if (it == fields_.end()) {
+      return nullptr;
+    }
+    return &it->second;
   }
 
   base::Optional<std::string> FindEnumString(const int32_t value) const {
     PERFETTO_DCHECK(type_ == Type::kEnum);
-    auto it =
-        std::find_if(enum_values_.begin(), enum_values_.end(),
-                     [value](const std::pair<int32_t, std::string>& enum_val) {
-                       return enum_val.first == value;
-                     });
+    auto it = enum_values_.find(value);
     return it == enum_values_.end() ? base::nullopt
                                     : base::Optional<std::string>(it->second);
   }
@@ -127,8 +126,14 @@
 
   const std::string& full_name() const { return full_name_; }
 
-  const std::vector<FieldDescriptor>& fields() const { return fields_; }
-  std::vector<FieldDescriptor>* mutable_fields() { return &fields_; }
+  Type type() const { return type_; }
+
+  const std::unordered_map<uint32_t, FieldDescriptor>& fields() const {
+    return fields_;
+  }
+  std::unordered_map<uint32_t, FieldDescriptor>* mutable_fields() {
+    return &fields_;
+  }
 
  private:
   std::string file_name_;  // File in which descriptor was originally defined.
@@ -136,8 +141,8 @@
   std::string full_name_;
   const Type type_;
   base::Optional<uint32_t> parent_id_;
-  std::vector<FieldDescriptor> fields_;
-  std::vector<std::pair<int32_t, std::string>> enum_values_;
+  std::unordered_map<uint32_t, FieldDescriptor> fields_;
+  std::unordered_map<int32_t, std::string> enum_values_;
 };
 
 using ExtensionInfo = std::pair<std::string, protozero::ConstBytes>;
@@ -146,7 +151,8 @@
  public:
   util::Status AddFromFileDescriptorSet(
       const uint8_t* file_descriptor_set_proto,
-      size_t size);
+      size_t size,
+      bool merge_existing_messages = false);
 
   base::Optional<uint32_t> FindDescriptorIdx(
       const std::string& full_name) const;
@@ -158,18 +164,17 @@
   std::vector<uint8_t> SerializeAsDescriptorSet();
 
  private:
-  void AddNestedProtoDescriptors(const std::string& file_name,
-                                 const std::string& package_name,
-                                 base::Optional<uint32_t> parent_idx,
-                                 protozero::ConstBytes descriptor_proto,
-                                 std::vector<ExtensionInfo>* extensions);
-  void AddEnumProtoDescriptors(const std::string& file_name,
-                               const std::string& package_name,
-                               base::Optional<uint32_t> parent_idx,
-                               protozero::ConstBytes descriptor_proto);
-
-  void CheckPreviousDefinition(const std::string& file_name,
-                               const std::string& descriptor_name);
+  util::Status AddNestedProtoDescriptors(const std::string& file_name,
+                                         const std::string& package_name,
+                                         base::Optional<uint32_t> parent_idx,
+                                         protozero::ConstBytes descriptor_proto,
+                                         std::vector<ExtensionInfo>* extensions,
+                                         bool merge_existing_messages);
+  util::Status AddEnumProtoDescriptors(const std::string& file_name,
+                                       const std::string& package_name,
+                                       base::Optional<uint32_t> parent_idx,
+                                       protozero::ConstBytes descriptor_proto,
+                                       bool merge_existing_messages);
 
   util::Status AddExtensionField(const std::string& package_name,
                                  protozero::ConstBytes field_desc_proto);
diff --git a/src/trace_processor/util/protozero_to_text.cc b/src/trace_processor/util/protozero_to_text.cc
index df7deb8..47702ed 100644
--- a/src/trace_processor/util/protozero_to_text.cc
+++ b/src/trace_processor/util/protozero_to_text.cc
@@ -172,16 +172,14 @@
   protozero::ProtoDecoder decoder(protobytes.data, protobytes.size);
   for (auto field = decoder.ReadField(); field.valid();
        field = decoder.ReadField()) {
-    auto opt_field_descriptor_idx =
-        proto_descriptor.FindFieldIdxByTag(field.id());
-    if (!opt_field_descriptor_idx) {
+    auto opt_field_descriptor = proto_descriptor.FindFieldByTag(field.id());
+    if (!opt_field_descriptor) {
       StrAppend(
           output, output->empty() ? "" : "\n", *indents,
           "# Ignoring unknown field with id: ", std::to_string(field.id()));
       continue;
     }
-    const auto& field_descriptor =
-        proto_descriptor.fields()[*opt_field_descriptor_idx];
+    const auto& field_descriptor = *opt_field_descriptor;
 
     if (field_descriptor.type() ==
         protos::pbzero::FieldDescriptorProto::TYPE_MESSAGE) {
diff --git a/tools/gen_bazel b/tools/gen_bazel
index c6b7698..34a427a 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -146,6 +146,8 @@
       gen_cc_metrics_descriptor,
     '//src/trace_processor/importers:gen_cc_config_descriptor':
       gen_cc_metrics_descriptor,
+    '//src/trace_processor/importers:gen_cc_chrome_track_event_descriptor':
+      gen_cc_metrics_descriptor,
 }
 
 # ------------------------------------------------------------------------------