Merge "tp: extract the event id in the ftrace tokenizer" into main
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
index c699ab9..711415f 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
@@ -53,6 +53,47 @@
   return context->clock_tracker->ToTraceTime(clock_id, ts);
 }
 
+// Fast path for parsing the event id of an ftrace event.
+// Speculate on the fact that, if the timestamp was found, the common pid
+// will appear immediately after and the event id immediately after that.
+uint64_t TryFastParseFtraceEventId(const uint8_t* start, const uint8_t* end) {
+  constexpr auto kPidFieldNumber = protos::pbzero::FtraceEvent::kPidFieldNumber;
+  constexpr auto kPidFieldTag = MakeTagVarInt(kPidFieldNumber);
+
+  // If the next byte is not the common pid's tag, just skip the field.
+  constexpr uint32_t kMaxPidLength = 5;
+  if (PERFETTO_UNLIKELY(static_cast<uint32_t>(end - start) <= kMaxPidLength ||
+                        start[0] != kPidFieldTag)) {
+    return 0;
+  }
+
+  // Skip the common pid.
+  uint64_t common_pid = 0;
+  const uint8_t* common_pid_end = ParseVarInt(start + 1, end, &common_pid);
+  if (PERFETTO_UNLIKELY(common_pid_end == start + 1)) {
+    return 0;
+  }
+
+  // Read the next varint: this should be the event id tag.
+  uint64_t event_tag = 0;
+  const uint8_t* event_id_end = ParseVarInt(common_pid_end, end, &event_tag);
+  if (event_id_end == common_pid_end) {
+    return 0;
+  }
+
+  constexpr uint8_t kFieldTypeNumBits = 3;
+  constexpr uint64_t kFieldTypeMask =
+      (1 << kFieldTypeNumBits) - 1;  // 0000 0111;
+
+  // The event wire type should be length delimited.
+  auto wire_type = static_cast<protozero::proto_utils::ProtoWireType>(
+      event_tag & kFieldTypeMask);
+  if (wire_type != protozero::proto_utils::ProtoWireType::kLengthDelimited) {
+    return 0;
+  }
+  return event_tag >> kFieldTypeNumBits;
+}
+
 }  // namespace
 
 PERFETTO_ALWAYS_INLINE
@@ -125,29 +166,53 @@
 
   const uint8_t* data = event.data();
   const size_t length = event.length();
-  ProtoDecoder decoder(data, length);
 
-  // Speculate on the fact that the timestamp is often the 1st field of the
-  // event.
+  // Speculate on the following sequence of varints
+  //  - timestamp tag
+  //  - timestamp (64 bit)
+  //  - common pid tag
+  //  - common pid (32 bit)
+  //  - event tag
   uint64_t raw_timestamp = 0;
   bool timestamp_found = false;
+  uint64_t event_id = 0;
   if (PERFETTO_LIKELY(length > 10 && data[0] == kTimestampFieldTag)) {
     // Fastpath.
-    const uint8_t* next = ParseVarInt(data + 1, data + 11, &raw_timestamp);
-    timestamp_found = next != data + 1;
-    decoder.Reset(next);
-  } else {
-    // Slowpath.
+    const uint8_t* ts_end = ParseVarInt(data + 1, data + 11, &raw_timestamp);
+    timestamp_found = ts_end != data + 1;
+    if (PERFETTO_LIKELY(timestamp_found)) {
+      event_id = TryFastParseFtraceEventId(ts_end, data + length);
+    }
+  }
+
+  // Slowpath for finding the timestamp.
+  if (PERFETTO_UNLIKELY(!timestamp_found)) {
+    ProtoDecoder decoder(data, length);
     if (auto ts_field = decoder.FindField(kTimestampFieldNumber)) {
       timestamp_found = true;
       raw_timestamp = ts_field.as_uint64();
     }
+    if (PERFETTO_UNLIKELY(!timestamp_found)) {
+      context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
+      return;
+    }
   }
 
-  if (PERFETTO_UNLIKELY(!timestamp_found)) {
-    PERFETTO_ELOG("Timestamp field not found in FtraceEvent");
-    context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
-    return;
+  // Slowpath for finding the event id.
+  if (PERFETTO_UNLIKELY(event_id == 0)) {
+    ProtoDecoder decoder(data, length);
+    for (auto f = decoder.ReadField(); f.valid(); f = decoder.ReadField()) {
+      // Find the first length-delimited tag as this corresponds to the ftrace
+      // event.
+      if (f.type() == protozero::proto_utils::ProtoWireType::kLengthDelimited) {
+        event_id = f.id();
+        break;
+      }
+    }
+    if (PERFETTO_UNLIKELY(!event_id)) {
+      context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
+      return;
+    }
   }
 
   // ClockTracker will increment some error stats if it failed to convert the