[ETW] Adds the EtwTokenizer

First part of adding etw trace processor.
This CL adds a new etw_module and adds to the default modules as well as
the tokenization logic itself. There's no path to this code in this CL.

In the upcoming CLs:
- Refactoring sched_event_tracker & thread_state_tracker
- Add Etw Praser to the trace processor

Change-Id: Id6c492112f65bf30593776561613bc1756b40bdf
diff --git a/Android.bp b/Android.bp
index dd2c861..50f69fc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2267,6 +2267,7 @@
         ":perfetto_src_trace_processor_importers_common_common",
         ":perfetto_src_trace_processor_importers_common_parser_types",
         ":perfetto_src_trace_processor_importers_common_trace_parser_hdr",
+        ":perfetto_src_trace_processor_importers_etw_full",
         ":perfetto_src_trace_processor_importers_ftrace_ftrace_descriptors",
         ":perfetto_src_trace_processor_importers_ftrace_full",
         ":perfetto_src_trace_processor_importers_ftrace_minimal",
@@ -11040,6 +11041,15 @@
     ],
 }
 
+// GN: //src/trace_processor/importers/etw:full
+filegroup {
+    name: "perfetto_src_trace_processor_importers_etw_full",
+    srcs: [
+        "src/trace_processor/importers/etw/etw_module.cc",
+        "src/trace_processor/importers/etw/etw_tokenizer.cc",
+    ],
+}
+
 // GN: //src/trace_processor/importers/ftrace:ftrace_descriptors
 filegroup {
     name: "perfetto_src_trace_processor_importers_ftrace_ftrace_descriptors",
@@ -13705,6 +13715,7 @@
         ":perfetto_src_trace_processor_importers_common_parser_types",
         ":perfetto_src_trace_processor_importers_common_trace_parser_hdr",
         ":perfetto_src_trace_processor_importers_common_unittests",
+        ":perfetto_src_trace_processor_importers_etw_full",
         ":perfetto_src_trace_processor_importers_ftrace_ftrace_descriptors",
         ":perfetto_src_trace_processor_importers_ftrace_full",
         ":perfetto_src_trace_processor_importers_ftrace_minimal",
@@ -14417,6 +14428,7 @@
         ":perfetto_src_trace_processor_importers_common_common",
         ":perfetto_src_trace_processor_importers_common_parser_types",
         ":perfetto_src_trace_processor_importers_common_trace_parser_hdr",
+        ":perfetto_src_trace_processor_importers_etw_full",
         ":perfetto_src_trace_processor_importers_ftrace_ftrace_descriptors",
         ":perfetto_src_trace_processor_importers_ftrace_full",
         ":perfetto_src_trace_processor_importers_ftrace_minimal",
@@ -14650,6 +14662,7 @@
         ":perfetto_src_trace_processor_importers_common_common",
         ":perfetto_src_trace_processor_importers_common_parser_types",
         ":perfetto_src_trace_processor_importers_common_trace_parser_hdr",
+        ":perfetto_src_trace_processor_importers_etw_full",
         ":perfetto_src_trace_processor_importers_ftrace_ftrace_descriptors",
         ":perfetto_src_trace_processor_importers_ftrace_full",
         ":perfetto_src_trace_processor_importers_ftrace_minimal",
diff --git a/BUILD b/BUILD
index f8f66a2..9f7b69c 100644
--- a/BUILD
+++ b/BUILD
@@ -223,6 +223,7 @@
         ":src_trace_processor_importers_common_common",
         ":src_trace_processor_importers_common_parser_types",
         ":src_trace_processor_importers_common_trace_parser_hdr",
+        ":src_trace_processor_importers_etw_full",
         ":src_trace_processor_importers_ftrace_ftrace_descriptors",
         ":src_trace_processor_importers_ftrace_full",
         ":src_trace_processor_importers_ftrace_minimal",
@@ -1402,6 +1403,17 @@
     ],
 )
 
+# GN target: //src/trace_processor/importers/etw:full
+perfetto_filegroup(
+    name = "src_trace_processor_importers_etw_full",
+    srcs = [
+        "src/trace_processor/importers/etw/etw_module.cc",
+        "src/trace_processor/importers/etw/etw_module.h",
+        "src/trace_processor/importers/etw/etw_tokenizer.cc",
+        "src/trace_processor/importers/etw/etw_tokenizer.h",
+    ],
+)
+
 # GN target: //src/trace_processor/importers/ftrace:ftrace_descriptors
 perfetto_filegroup(
     name = "src_trace_processor_importers_ftrace_ftrace_descriptors",
@@ -5287,6 +5299,7 @@
         ":src_trace_processor_importers_common_common",
         ":src_trace_processor_importers_common_parser_types",
         ":src_trace_processor_importers_common_trace_parser_hdr",
+        ":src_trace_processor_importers_etw_full",
         ":src_trace_processor_importers_ftrace_ftrace_descriptors",
         ":src_trace_processor_importers_ftrace_full",
         ":src_trace_processor_importers_ftrace_minimal",
@@ -5451,6 +5464,7 @@
         ":src_trace_processor_importers_common_common",
         ":src_trace_processor_importers_common_parser_types",
         ":src_trace_processor_importers_common_trace_parser_hdr",
+        ":src_trace_processor_importers_etw_full",
         ":src_trace_processor_importers_ftrace_ftrace_descriptors",
         ":src_trace_processor_importers_ftrace_full",
         ":src_trace_processor_importers_ftrace_minimal",
@@ -5673,6 +5687,7 @@
         ":src_trace_processor_importers_common_common",
         ":src_trace_processor_importers_common_parser_types",
         ":src_trace_processor_importers_common_trace_parser_hdr",
+        ":src_trace_processor_importers_etw_full",
         ":src_trace_processor_importers_ftrace_ftrace_descriptors",
         ":src_trace_processor_importers_ftrace_full",
         ":src_trace_processor_importers_ftrace_minimal",
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index c5be430..c20ba5e 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -158,6 +158,7 @@
       "db",
       "importers/android_bugreport",
       "importers/common",
+      "importers/etw:full",
       "importers/ftrace:full",
       "importers/fuchsia:full",
       "importers/gzip:full",
diff --git a/src/trace_processor/importers/etw/BUILD.gn b/src/trace_processor/importers/etw/BUILD.gn
new file mode 100644
index 0000000..d8985b1
--- /dev/null
+++ b/src/trace_processor/importers/etw/BUILD.gn
@@ -0,0 +1,40 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../../gn/test.gni")
+
+source_set("full") {
+  sources = [
+    "etw_module.cc",
+    "etw_module.h",
+    "etw_tokenizer.cc",
+    "etw_tokenizer.h",
+  ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../../../../protos/perfetto/common:zero",
+    "../../../../protos/perfetto/trace:zero",
+    "../../../../protos/perfetto/trace/etw:zero",
+    "../../../../protos/perfetto/trace/interned_data:zero",
+    "../../../protozero",
+    "../../sorter",
+    "../../storage",
+    "../../types",
+    "../common",
+    "../common:parser_types",
+    "../i2c:full",
+    "../proto:minimal",
+    "../syscalls:full",
+  ]
+}
diff --git a/src/trace_processor/importers/etw/etw_module.cc b/src/trace_processor/importers/etw/etw_module.cc
new file mode 100644
index 0000000..508d2ca
--- /dev/null
+++ b/src/trace_processor/importers/etw/etw_module.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/importers/etw/etw_module.h"
+#include "perfetto/base/build_config.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/etw/etw_tokenizer.h"
+
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+using perfetto::protos::pbzero::TracePacket;
+
+EtwModule::EtwModule(TraceProcessorContext* context) : tokenizer_(context) {
+  RegisterForField(TracePacket::kEtwEventsFieldNumber, context);
+}
+
+ModuleResult EtwModule::TokenizePacket(
+    const protos::pbzero::TracePacket::Decoder& decoder,
+    TraceBlobView* packet,
+    int64_t /*packet_timestamp*/,
+    PacketSequenceState* seq_state,
+    uint32_t field_id) {
+  switch (field_id) {
+    case TracePacket::kEtwEventsFieldNumber: {
+      auto etw_field = decoder.etw_events();
+      tokenizer_.TokenizeEtwBundle(
+          packet->slice(etw_field.data, etw_field.size), seq_state);
+      return ModuleResult::Handled();
+    }
+  }
+  return ModuleResult::Ignored();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/etw/etw_module.h b/src/trace_processor/importers/etw/etw_module.h
new file mode 100644
index 0000000..1624af1
--- /dev/null
+++ b/src/trace_processor/importers/etw/etw_module.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_ETW_ETW_MODULE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_ETW_ETW_MODULE_H_
+
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/importers/common/trace_parser.h"
+#include "src/trace_processor/importers/etw/etw_module.h"
+#include "src/trace_processor/importers/etw/etw_tokenizer.h"
+#include "src/trace_processor/importers/proto/proto_importer_module.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class EtwModule : public ProtoImporterModule {
+ public:
+  explicit EtwModule(TraceProcessorContext* context);
+
+  ModuleResult TokenizePacket(
+      const protos::pbzero::TracePacket::Decoder& decoder,
+      TraceBlobView* packet,
+      int64_t packet_timestamp,
+      PacketSequenceState* state,
+      uint32_t field_id) override;
+
+ private:
+  EtwTokenizer tokenizer_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_ETW_ETW_MODULE_H_
diff --git a/src/trace_processor/importers/etw/etw_tokenizer.cc b/src/trace_processor/importers/etw/etw_tokenizer.cc
new file mode 100644
index 0000000..f07d74c
--- /dev/null
+++ b/src/trace_processor/importers/etw/etw_tokenizer.cc
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <optional>
+
+#include "src/trace_processor/importers/etw/etw_tokenizer.h"
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/status_or.h"
+#include "perfetto/protozero/proto_decoder.h"
+#include "perfetto/protozero/proto_utils.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+#include "protos/perfetto/common/builtin_clock.pbzero.h"
+#include "protos/perfetto/trace/etw/etw_event.pbzero.h"
+#include "protos/perfetto/trace/etw/etw_event_bundle.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+using protozero::ProtoDecoder;
+using protozero::proto_utils::MakeTagVarInt;
+using protozero::proto_utils::ParseVarInt;
+
+using protos::pbzero::BuiltinClock;
+using protos::pbzero::EtwTraceEventBundle;
+
+PERFETTO_ALWAYS_INLINE
+base::Status EtwTokenizer::TokenizeEtwBundle(TraceBlobView bundle,
+                                             PacketSequenceState* state) {
+  protos::pbzero::EtwTraceEventBundle::Decoder decoder(bundle.data(),
+                                                       bundle.length());
+  // Cpu id can either be in the etw bundle or inside the individual
+  // EtwTraceEvent. If present at this level, we pass it to the TokenizeEtwEvent
+  // in case the EtwTraceEvent does not contain the cpu.
+  std::optional<uint32_t> bundle_cpu =
+      decoder.has_cpu() ? std::make_optional(decoder.cpu()) : std::nullopt;
+  auto it = decoder.event();
+  return TokenizeEtwEvent(bundle_cpu, bundle.slice(it->data(), it->size()),
+                          state);
+}
+
+PERFETTO_ALWAYS_INLINE
+base::Status EtwTokenizer::TokenizeEtwEvent(
+    std::optional<uint32_t> fallback_cpu,
+    TraceBlobView event,
+    PacketSequenceState* state) {
+  const uint8_t* data = event.data();
+  const size_t length = event.length();
+  ProtoDecoder decoder(data, length);
+
+  protos::pbzero::EtwTraceEvent::Decoder etw_decoder(data, length);
+  // Some ETW events lack CPU info; in that case, the bundle may
+  // provide it.
+  uint32_t cpu;
+  if (etw_decoder.has_cpu()) {
+    cpu = etw_decoder.cpu();
+  } else {
+    if (!fallback_cpu.has_value()) {
+      return base::ErrStatus(
+          "CPU field not found in EtwEvent and/or EtwEventBundle");
+    }
+    cpu = fallback_cpu.value();
+  }
+
+  static constexpr uint32_t kMaxCpuCount = 1024;
+  if (PERFETTO_UNLIKELY(cpu >= kMaxCpuCount)) {
+    return base::ErrStatus(
+        "CPU %u is greater than maximum allowed of %u. This is likely because "
+        "of trace corruption",
+        cpu, kMaxCpuCount);
+  }
+
+  uint64_t raw_timestamp = 0;
+  if (etw_decoder.has_timestamp()) {
+    raw_timestamp = etw_decoder.timestamp();
+  } else {
+    return base::ErrStatus("Timestamp field not found in EtwEvent");
+  }
+
+  base::StatusOr<int64_t> timestamp = static_cast<int64_t>(raw_timestamp);
+
+  // ClockTracker will increment some error stats if it failed to convert the
+  // timestamp so just return.
+  if (!timestamp.ok()) {
+    return timestamp.status();
+  }
+
+  context_->sorter->PushEtwEvent(cpu, *timestamp, std::move(event),
+                                 state->current_generation());
+
+  return base::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/etw/etw_tokenizer.h b/src/trace_processor/importers/etw/etw_tokenizer.h
new file mode 100644
index 0000000..1d93a14
--- /dev/null
+++ b/src/trace_processor/importers/etw/etw_tokenizer.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_ETW_ETW_TOKENIZER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_ETW_ETW_TOKENIZER_H_
+
+#include <optional>
+
+#include "perfetto/base/status.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+#include "protos/perfetto/trace/etw/etw_event_bundle.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class PacketSequenceState;
+
+class EtwTokenizer {
+ public:
+  explicit EtwTokenizer(TraceProcessorContext* context) : context_(context) {}
+
+  base::Status TokenizeEtwBundle(TraceBlobView bundle,
+                                 PacketSequenceState* state);
+
+ private:
+  base::Status TokenizeEtwEvent(std::optional<uint32_t> fallback_cpu,
+                                TraceBlobView event,
+                                PacketSequenceState* state);
+
+  TraceProcessorContext* context_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_ETW_ETW_TOKENIZER_H_
diff --git a/src/trace_processor/importers/proto/BUILD.gn b/src/trace_processor/importers/proto/BUILD.gn
index 7f8d3e0..a901ae1 100644
--- a/src/trace_processor/importers/proto/BUILD.gn
+++ b/src/trace_processor/importers/proto/BUILD.gn
@@ -75,6 +75,7 @@
     "../../../../protos/perfetto/trace:zero",
     "../../../../protos/perfetto/trace/android:zero",
     "../../../../protos/perfetto/trace/chrome:zero",
+    "../../../../protos/perfetto/trace/etw:zero",
     "../../../../protos/perfetto/trace/ftrace:zero",
     "../../../../protos/perfetto/trace/interned_data:zero",
     "../../../../protos/perfetto/trace/perfetto:zero",
@@ -181,6 +182,7 @@
     "../../util:proto_to_args_parser",
     "../common",
     "../common:parser_types",
+    "../etw:full",
     "../ftrace:full",
     "../syscalls:full",
     "winscope:full",
diff --git a/src/trace_processor/importers/proto/additional_modules.cc b/src/trace_processor/importers/proto/additional_modules.cc
index 341d6d7..6bd58ee 100644
--- a/src/trace_processor/importers/proto/additional_modules.cc
+++ b/src/trace_processor/importers/proto/additional_modules.cc
@@ -15,6 +15,7 @@
  */
 
 #include "src/trace_processor/importers/proto/additional_modules.h"
+#include "src/trace_processor/importers/etw/etw_module.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module_impl.h"
 #include "src/trace_processor/importers/proto/android_camera_event_module.h"
 #include "src/trace_processor/importers/proto/android_probes_module.h"
@@ -43,6 +44,7 @@
   context->modules.emplace_back(new MetadataModule(context));
   context->modules.emplace_back(new V8Module(context));
   context->modules.emplace_back(new WinscopeModule(context));
+  context->modules.emplace_back(new EtwModule(context));
 
   // Ftrace module is special, because it has one extra method for parsing
   // ftrace packets. So we need to store a pointer to it separately.
diff --git a/src/trace_processor/importers/proto/default_modules.cc b/src/trace_processor/importers/proto/default_modules.cc
index c24b0f0..ca18beb 100644
--- a/src/trace_processor/importers/proto/default_modules.cc
+++ b/src/trace_processor/importers/proto/default_modules.cc
@@ -32,6 +32,7 @@
   // ftrace packets. So we need to store a pointer to it separately.
   context->ftrace_module =
       static_cast<FtraceModule*>(context->modules.back().get());
+
   context->modules.emplace_back(new TrackEventModule(context));
   context->track_module =
       static_cast<TrackEventModule*>(context->modules.back().get());
diff --git a/src/trace_processor/sorter/trace_sorter.cc b/src/trace_processor/sorter/trace_sorter.cc
index 708def9..0a09224 100644
--- a/src/trace_processor/sorter/trace_sorter.cc
+++ b/src/trace_processor/sorter/trace_sorter.cc
@@ -204,6 +204,26 @@
     case TimestampedEvent::Type::kInlineSchedSwitch:
     case TimestampedEvent::Type::kInlineSchedWaking:
     case TimestampedEvent::Type::kFtraceEvent:
+    case TimestampedEvent::Type::kEtwEvent:
+      PERFETTO_FATAL("Invalid event type");
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+void TraceSorter::ParseEtwPacket(uint32_t /*cpu*/,
+                                 const TimestampedEvent& event) {
+  switch (static_cast<TimestampedEvent::Type>(event.event_type)) {
+    case TimestampedEvent::Type::kEtwEvent:
+      return;
+    case TimestampedEvent::Type::kInlineSchedSwitch:
+    case TimestampedEvent::Type::kInlineSchedWaking:
+    case TimestampedEvent::Type::kFtraceEvent:
+    case TimestampedEvent::Type::kTrackEvent:
+    case TimestampedEvent::Type::kSystraceLine:
+    case TimestampedEvent::Type::kTracePacket:
+    case TimestampedEvent::Type::kTraceBlobView:
+    case TimestampedEvent::Type::kJsonValue:
+    case TimestampedEvent::Type::kFuchsiaRecord:
       PERFETTO_FATAL("Invalid event type");
   }
   PERFETTO_FATAL("For GCC");
@@ -225,6 +245,7 @@
       parser_->ParseFtraceEvent(cpu, event.ts,
                                 token_buffer_.Extract<TracePacketData>(id));
       return;
+    case TimestampedEvent::Type::kEtwEvent:
     case TimestampedEvent::Type::kTrackEvent:
     case TimestampedEvent::Type::kSystraceLine:
     case TimestampedEvent::Type::kTracePacket:
@@ -267,6 +288,9 @@
     case TimestampedEvent::Type::kFtraceEvent:
       base::ignore_result(token_buffer_.Extract<TracePacketData>(id));
       return;
+    case TimestampedEvent::Type::kEtwEvent:
+      base::ignore_result(token_buffer_.Extract<TracePacketData>(id));
+      return;
   }
   PERFETTO_FATAL("For GCC");
 }
@@ -291,7 +315,13 @@
   } else {
     // Ftrace queues start at offset 1. So queues_[1] = cpu[0] and so on.
     uint32_t cpu = static_cast<uint32_t>(queue_idx - 1);
-    ParseFtracePacket(cpu, event);
+    auto event_type = static_cast<TimestampedEvent::Type>(event.event_type);
+
+    if (event_type == TimestampedEvent::Type::kEtwEvent) {
+      ParseEtwPacket(static_cast<uint32_t>(cpu), event);
+    } else {
+      ParseFtracePacket(cpu, event);
+    }
   }
 }
 
diff --git a/src/trace_processor/sorter/trace_sorter.h b/src/trace_processor/sorter/trace_sorter.h
index ef1ec92..fc4bac8 100644
--- a/src/trace_processor/sorter/trace_sorter.h
+++ b/src/trace_processor/sorter/trace_sorter.h
@@ -136,6 +136,17 @@
     AppendNonFtraceEvent(timestamp, TimestampedEvent::Type::kTrackEvent, id);
   }
 
+  inline void PushEtwEvent(uint32_t cpu,
+                           int64_t timestamp,
+                           TraceBlobView tbv,
+                           RefPtr<PacketSequenceStateGeneration> state) {
+    TraceTokenBuffer::Id id =
+        token_buffer_.Append(TracePacketData{std::move(tbv), std::move(state)});
+    auto* queue = GetQueue(cpu + 1);
+    queue->Append(timestamp, TimestampedEvent::Type::kEtwEvent, id);
+    UpdateAppendMaxTs(queue);
+  }
+
   inline void PushFtraceEvent(uint32_t cpu,
                               int64_t timestamp,
                               TraceBlobView tbv,
@@ -213,7 +224,8 @@
       kFuchsiaRecord,
       kTrackEvent,
       kSystraceLine,
-      kMax = kSystraceLine,
+      kEtwEvent,
+      kMax = kEtwEvent,
     };
 
     // Number of bits required to store the max element in |Type|.
@@ -327,6 +339,7 @@
 
   void ParseTracePacket(const TimestampedEvent&);
   void ParseFtracePacket(uint32_t cpu, const TimestampedEvent&);
+  void ParseEtwPacket(uint32_t cpu, const TimestampedEvent&);
 
   void MaybeExtractEvent(size_t queue_idx, const TimestampedEvent&);
   void ExtractAndDiscardTokenizedObject(const TimestampedEvent& event);
diff --git a/src/trace_processor/types/trace_processor_context.h b/src/trace_processor/types/trace_processor_context.h
index 8d770d0..25f129b 100644
--- a/src/trace_processor/types/trace_processor_context.h
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -47,6 +47,7 @@
 class ClockTracker;
 class ClockConverter;
 class DeobfuscationMappingTable;
+class EtwModule;
 class EventTracker;
 class ForwardingTraceParser;
 class FtraceModule;
@@ -157,6 +158,7 @@
   // all fields.
   std::vector<ProtoImporterModule*> modules_for_all_fields;
   FtraceModule* ftrace_module = nullptr;
+  EtwModule* etw_module = nullptr;
   TrackEventModule* track_module = nullptr;
 
   // Marks whether the uuid was read from the trace.