Merge "tp: Parse OEM atoms generically where possible" into main
diff --git a/src/trace_processor/importers/proto/statsd_module.cc b/src/trace_processor/importers/proto/statsd_module.cc
index e536e11..49a7300 100644
--- a/src/trace_processor/importers/proto/statsd_module.cc
+++ b/src/trace_processor/importers/proto/statsd_module.cc
@@ -141,6 +141,54 @@
   TraceStorage& storage_;
 };
 
+// If we don't know about the atom format put whatever details we
+// can. This has the following restrictions:
+// - We can't tell the difference between double, fixed64, sfixed64
+//   so those all show up as double
+// - We can't tell the difference between float, fixed32, sfixed32
+//   so those all show up as float
+// - We can't tell the difference between int32, int64 and sint32
+//   and sint64. We assume int32/int64.
+// - We only show the length of strings, nested messages, packed ints
+//   and any other length delimited fields.
+base::Status ParseGenericEvent(const protozero::ConstBytes& cb,
+                               util::ProtoToArgsParser::Delegate& delegate) {
+  protozero::ProtoDecoder decoder(cb);
+  for (auto f = decoder.ReadField(); f.valid(); f = decoder.ReadField()) {
+    switch (f.type()) {
+      case protozero::proto_utils::ProtoWireType::kLengthDelimited: {
+        base::StackString<64> name("field_%u", f.id());
+        std::string name_str = name.ToStdString();
+        util::ProtoToArgsParser::Key key{name_str, name_str};
+        delegate.AddBytes(key, f.as_bytes());
+        break;
+      }
+      case protozero::proto_utils::ProtoWireType::kVarInt: {
+        base::StackString<64> name("field_%u", f.id());
+        std::string name_str = name.ToStdString();
+        util::ProtoToArgsParser::Key key{name_str, name_str};
+        delegate.AddInteger(key, f.as_int64());
+        break;
+      }
+      case protozero::proto_utils::ProtoWireType::kFixed32: {
+        base::StackString<64> name("field_%u_assuming_float", f.id());
+        std::string name_str = name.ToStdString();
+        util::ProtoToArgsParser::Key key{name_str, name_str};
+        delegate.AddDouble(key, static_cast<double>(f.as_float()));
+        break;
+      }
+      case protozero::proto_utils::ProtoWireType::kFixed64: {
+        base::StackString<64> name("field_%u_assuming_double", f.id());
+        std::string name_str = name.ToStdString();
+        util::ProtoToArgsParser::Key key{name_str, name_str};
+        delegate.AddDouble(key, f.as_double());
+        break;
+      }
+    }
+  }
+  return base::OkStatus();
+}
+
 }  // namespace
 
 using perfetto::protos::pbzero::StatsdAtom;
@@ -247,10 +295,26 @@
   SliceId slice = opt_slice.value();
   auto inserter = context_->args_tracker->AddArgsTo(slice);
   InserterDelegate delegate(inserter, *context_->storage.get());
-  base::Status result = args_parser_.ParseMessage(
-      nested_bytes, kAtomProtoName, nullptr /* parse all fields */, delegate);
-  if (!result.ok()) {
-    PERFETTO_ELOG("%s", result.c_message());
+
+  const auto& fields = pool_.descriptor()->fields();
+  const auto& field_it = fields.find(nested_field_id);
+  base::Status status;
+
+  if (field_it == fields.end()) {
+    /// Field ids 100000 and over are OEM atoms - we can't have the
+    // descriptor for them so don't report errors. See:
+    // https://cs.android.com/android/platform/superproject/main/+/main:frameworks/proto_logging/stats/atoms.proto;l=1290;drc=a34b11bfebe897259a0340a59f1793ae2dffd762
+    if (nested_field_id < 100000) {
+      context_->storage->IncrementStats(stats::atom_unknown);
+    }
+
+    status = ParseGenericEvent(field.as_bytes(), delegate);
+  } else {
+    status = args_parser_.ParseMessage(
+        nested_bytes, kAtomProtoName, nullptr /* parse all fields */, delegate);
+  }
+
+  if (!status.ok()) {
     context_->storage->IncrementStats(stats::atom_unknown);
   }
 }
@@ -263,18 +327,18 @@
       return context_->storage->InternString("Could not load atom descriptor");
     }
 
+    StringId name_id;
     const auto& fields = pool_.descriptor()->fields();
     const auto& field_it = fields.find(atom_field_id);
     if (field_it == fields.end()) {
-      context_->storage->IncrementStats(stats::atom_unknown);
-      return context_->storage->InternString("Unknown atom");
+      base::StackString<255> name("atom_%u", atom_field_id);
+      name_id = context_->storage->InternString(name.string_view());
+    } else {
+      const FieldDescriptor& field = field_it->second;
+      name_id = context_->storage->InternString(base::StringView(field.name()));
     }
-
-    const FieldDescriptor& field = field_it->second;
-    StringId name =
-        context_->storage->InternString(base::StringView(field.name()));
-    atom_names_[atom_field_id] = name;
-    return name;
+    atom_names_[atom_field_id] = name_id;
+    return name_id;
   }
   return *cached_name;
 }
diff --git a/test/data/statsd_atoms_oem.pb.sha256 b/test/data/statsd_atoms_oem.pb.sha256
new file mode 100644
index 0000000..9ab0459
--- /dev/null
+++ b/test/data/statsd_atoms_oem.pb.sha256
@@ -0,0 +1 @@
+5c38eaf8133ca06b1e9ab800c54430ac4807c98aa4684be3da48c56175bca679
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/parser/parsing/tests.py b/test/trace_processor/diff_tests/parser/parsing/tests.py
index 5261b09..90067a7 100644
--- a/test/trace_processor/diff_tests/parser/parsing/tests.py
+++ b/test/trace_processor/diff_tests/parser/parsing/tests.py
@@ -1110,6 +1110,16 @@
         query=Path('all_atoms_test.sql'),
         out=Path('statsd_atoms_all_atoms.out'))
 
+  # Statsd Atoms
+  def test_statsd_atoms_unknown_atoms(self):
+    return DiffTestBlueprint(
+        trace=DataPath('statsd_atoms_oem.pb'),
+        query=Path('all_atoms_test.sql'),
+        out=Csv("""
+          "name","key","display_value"
+          "atom_202001","field_1","1"
+        """))
+
   # Kernel function tracing.
   def test_funcgraph_trace_funcgraph(self):
     return DiffTestBlueprint(