Merge "Add an error counter for unknown extension fields"
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index 2af9a04..136c5de 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -1120,9 +1120,14 @@
     }
 
     TrackEventArgsParser args_writer(*inserter, *storage_, *sequence_state_);
+    int unknown_extensions = 0;
     log_errors(parser_->args_parser_.ParseMessage(
         blob_, ".perfetto.protos.TrackEvent", &parser_->reflect_fields_,
-        args_writer));
+        args_writer, &unknown_extensions));
+    if (unknown_extensions > 0) {
+      context_->storage->IncrementStats(stats::unknown_extension_fields,
+                                        unknown_extensions);
+    }
 
     {
       auto key = parser_->args_parser_.EnterDictionary("debug");
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index ea71ef0..4584704 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -187,9 +187,13 @@
       "processor."),                                                           \
   F(perf_guardrail_stop_ts,             kIndexed, kDataLoss, kTrace,    ""),   \
   F(sorter_push_event_out_of_order,     kSingle, kError,     kTrace,           \
-       "Trace events are out of order event after sorting. This can happen "   \
-       "due to many factors including clock sync drift, producers emitting "   \
-       "events out of order or a bug in trace processor's logic of sorting.")
+      "Trace events are out of order event after sorting. This can happen "    \
+      "due to many factors including clock sync drift, producers emitting "    \
+      "events out of order or a bug in trace processor's logic of sorting."),  \
+  F(unknown_extension_fields,           kSingle,  kError,    kTrace,           \
+      "TraceEvent had unknown extension fields, which might result in "        \
+      "missing some arguments. You may need a newer version of trace "         \
+      "processor to parse them.")
 // clang-format on
 
 enum Type {
diff --git a/src/trace_processor/util/proto_to_args_parser.cc b/src/trace_processor/util/proto_to_args_parser.cc
index a17b5dc..8e3e8d1 100644
--- a/src/trace_processor/util/proto_to_args_parser.cc
+++ b/src/trace_processor/util/proto_to_args_parser.cc
@@ -79,9 +79,11 @@
     const protozero::ConstBytes& cb,
     const std::string& type,
     const std::vector<uint16_t>* allowed_fields,
-    Delegate& delegate) {
+    Delegate& delegate,
+    int* unknown_extensions) {
   ScopedNestedKeyContext key_context(key_prefix_);
-  return ParseMessageInternal(key_context, cb, type, allowed_fields, delegate);
+  return ParseMessageInternal(key_context, cb, type, allowed_fields, delegate,
+                              unknown_extensions);
 }
 
 base::Status ProtoToArgsParser::ParseMessageInternal(
@@ -89,7 +91,8 @@
     const protozero::ConstBytes& cb,
     const std::string& type,
     const std::vector<uint16_t>* allowed_fields,
-    Delegate& delegate) {
+    Delegate& delegate,
+    int* unknown_extensions) {
   if (auto override_result =
           MaybeApplyOverrideForType(type, key_context, cb, delegate)) {
     return override_result.value();
@@ -112,6 +115,9 @@
     empty_message = false;
     auto field = descriptor.FindFieldByTag(f.id());
     if (!field) {
+      if (unknown_extensions != nullptr) {
+        (*unknown_extensions)++;
+      }
       // Unknown field, possibly an unknown extension.
       continue;
     }
@@ -127,8 +133,8 @@
       // reflected.
       continue;
     }
-    RETURN_IF_ERROR(
-        ParseField(*field, repeated_field_index[f.id()], f, delegate));
+    RETURN_IF_ERROR(ParseField(*field, repeated_field_index[f.id()], f,
+                               delegate, unknown_extensions));
     if (field->is_repeated()) {
       repeated_field_index[f.id()]++;
     }
@@ -145,7 +151,8 @@
     const FieldDescriptor& field_descriptor,
     int repeated_field_number,
     protozero::Field field,
-    Delegate& delegate) {
+    Delegate& delegate,
+    int* unknown_extensions) {
   std::string prefix_part = field_descriptor.name();
   if (field_descriptor.is_repeated()) {
     std::string number = std::to_string(repeated_field_number);
@@ -176,7 +183,7 @@
       protos::pbzero::FieldDescriptorProto::TYPE_MESSAGE) {
     return ParseMessageInternal(key_context, field.as_bytes(),
                                 field_descriptor.resolved_type_name(), nullptr,
-                                delegate);
+                                delegate, unknown_extensions);
   }
 
   return ParseSimpleField(field_descriptor, field, delegate);
diff --git a/src/trace_processor/util/proto_to_args_parser.h b/src/trace_processor/util/proto_to_args_parser.h
index 2ec4f1c..2329edb 100644
--- a/src/trace_processor/util/proto_to_args_parser.h
+++ b/src/trace_processor/util/proto_to_args_parser.h
@@ -136,7 +136,8 @@
   base::Status ParseMessage(const protozero::ConstBytes& cb,
                             const std::string& type,
                             const std::vector<uint16_t>* allowed_fields,
-                            Delegate& delegate);
+                            Delegate& delegate,
+                            int* unknown_extensions = nullptr);
 
   // This class is responsible for resetting the current key prefix to the old
   // value when deleted or reset.
@@ -237,7 +238,8 @@
   base::Status ParseField(const FieldDescriptor& field_descriptor,
                           int repeated_field_number,
                           protozero::Field field,
-                          Delegate& delegate);
+                          Delegate& delegate,
+                          int* unknown_extensions);
 
   base::Optional<base::Status> MaybeApplyOverrideForField(
       const protozero::Field&,
@@ -255,7 +257,8 @@
                                     const protozero::ConstBytes& cb,
                                     const std::string& type,
                                     const std::vector<uint16_t>* fields,
-                                    Delegate& delegate);
+                                    Delegate& delegate,
+                                    int* unknown_extensions);
 
   base::Status ParseSimpleField(const FieldDescriptor& desciptor,
                                 const protozero::Field& field,