Add hyp_enter and hyp_exit hypervisor events and show them in UI

Hypervisor events are similar to the ftrace evetns, however they don't
have a pid. In order to work around this issue, ftrace_parser uses the
pid 0 when processing hypervisor events.

Support for showing them in the UI is quite basic so far.
Once trace_processors sees a hyp_enter event it will start a slice on
the corresponding pKVM Hypervisor CPU track. Once it sees hyp_exit
event, it will end the slice.

Bug: 249050813
Test: ui/run-dev-server
Change-Id: Ic6393bb5e11c004e0c5d0486bbe11925a4f198d0
diff --git a/Android.bp b/Android.bp
index 82456b8..0bfc6c8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -4791,6 +4791,7 @@
         "protos/perfetto/trace/ftrace/generic.proto",
         "protos/perfetto/trace/ftrace/gpu_mem.proto",
         "protos/perfetto/trace/ftrace/gpu_scheduler.proto",
+        "protos/perfetto/trace/ftrace/hyp.proto",
         "protos/perfetto/trace/ftrace/i2c.proto",
         "protos/perfetto/trace/ftrace/ion.proto",
         "protos/perfetto/trace/ftrace/ipi.proto",
@@ -5034,6 +5035,7 @@
         "protos/perfetto/trace/ftrace/generic.proto",
         "protos/perfetto/trace/ftrace/gpu_mem.proto",
         "protos/perfetto/trace/ftrace/gpu_scheduler.proto",
+        "protos/perfetto/trace/ftrace/hyp.proto",
         "protos/perfetto/trace/ftrace/i2c.proto",
         "protos/perfetto/trace/ftrace/ion.proto",
         "protos/perfetto/trace/ftrace/ipi.proto",
@@ -5105,6 +5107,7 @@
         "external/perfetto/protos/perfetto/trace/ftrace/generic.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/gpu_mem.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/gpu_scheduler.gen.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/hyp.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/i2c.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/ion.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/ipi.gen.cc",
@@ -5176,6 +5179,7 @@
         "protos/perfetto/trace/ftrace/generic.proto",
         "protos/perfetto/trace/ftrace/gpu_mem.proto",
         "protos/perfetto/trace/ftrace/gpu_scheduler.proto",
+        "protos/perfetto/trace/ftrace/hyp.proto",
         "protos/perfetto/trace/ftrace/i2c.proto",
         "protos/perfetto/trace/ftrace/ion.proto",
         "protos/perfetto/trace/ftrace/ipi.proto",
@@ -5247,6 +5251,7 @@
         "external/perfetto/protos/perfetto/trace/ftrace/generic.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/gpu_mem.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/gpu_scheduler.gen.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/hyp.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/i2c.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/ion.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/ipi.gen.h",
@@ -5322,6 +5327,7 @@
         "protos/perfetto/trace/ftrace/generic.proto",
         "protos/perfetto/trace/ftrace/gpu_mem.proto",
         "protos/perfetto/trace/ftrace/gpu_scheduler.proto",
+        "protos/perfetto/trace/ftrace/hyp.proto",
         "protos/perfetto/trace/ftrace/i2c.proto",
         "protos/perfetto/trace/ftrace/ion.proto",
         "protos/perfetto/trace/ftrace/ipi.proto",
@@ -5392,6 +5398,7 @@
         "external/perfetto/protos/perfetto/trace/ftrace/generic.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/gpu_mem.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/gpu_scheduler.pb.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/hyp.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/i2c.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/ion.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/ipi.pb.cc",
@@ -5463,6 +5470,7 @@
         "protos/perfetto/trace/ftrace/generic.proto",
         "protos/perfetto/trace/ftrace/gpu_mem.proto",
         "protos/perfetto/trace/ftrace/gpu_scheduler.proto",
+        "protos/perfetto/trace/ftrace/hyp.proto",
         "protos/perfetto/trace/ftrace/i2c.proto",
         "protos/perfetto/trace/ftrace/ion.proto",
         "protos/perfetto/trace/ftrace/ipi.proto",
@@ -5533,6 +5541,7 @@
         "external/perfetto/protos/perfetto/trace/ftrace/generic.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/gpu_mem.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/gpu_scheduler.pb.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/hyp.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/i2c.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/ion.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/ipi.pb.h",
@@ -5608,6 +5617,7 @@
         "protos/perfetto/trace/ftrace/generic.proto",
         "protos/perfetto/trace/ftrace/gpu_mem.proto",
         "protos/perfetto/trace/ftrace/gpu_scheduler.proto",
+        "protos/perfetto/trace/ftrace/hyp.proto",
         "protos/perfetto/trace/ftrace/i2c.proto",
         "protos/perfetto/trace/ftrace/ion.proto",
         "protos/perfetto/trace/ftrace/ipi.proto",
@@ -5679,6 +5689,7 @@
         "external/perfetto/protos/perfetto/trace/ftrace/generic.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/gpu_mem.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/gpu_scheduler.pbzero.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/hyp.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/i2c.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/ion.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/ipi.pbzero.cc",
@@ -5750,6 +5761,7 @@
         "protos/perfetto/trace/ftrace/generic.proto",
         "protos/perfetto/trace/ftrace/gpu_mem.proto",
         "protos/perfetto/trace/ftrace/gpu_scheduler.proto",
+        "protos/perfetto/trace/ftrace/hyp.proto",
         "protos/perfetto/trace/ftrace/i2c.proto",
         "protos/perfetto/trace/ftrace/ion.proto",
         "protos/perfetto/trace/ftrace/ipi.proto",
@@ -5821,6 +5833,7 @@
         "external/perfetto/protos/perfetto/trace/ftrace/generic.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/gpu_mem.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/gpu_scheduler.pbzero.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/hyp.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/i2c.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/ion.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/ipi.pbzero.h",
@@ -9273,6 +9286,7 @@
         "src/trace_processor/importers/ftrace/ftrace_tokenizer.cc",
         "src/trace_processor/importers/ftrace/iostat_tracker.cc",
         "src/trace_processor/importers/ftrace/mali_gpu_event_tracker.cc",
+        "src/trace_processor/importers/ftrace/pkvm_hyp_cpu_tracker.cc",
         "src/trace_processor/importers/ftrace/rss_stat_tracker.cc",
         "src/trace_processor/importers/ftrace/sched_event_tracker.cc",
         "src/trace_processor/importers/ftrace/thread_state_tracker.cc",
@@ -11103,6 +11117,7 @@
         "protos/perfetto/trace/ftrace/generic.proto",
         "protos/perfetto/trace/ftrace/gpu_mem.proto",
         "protos/perfetto/trace/ftrace/gpu_scheduler.proto",
+        "protos/perfetto/trace/ftrace/hyp.proto",
         "protos/perfetto/trace/ftrace/i2c.proto",
         "protos/perfetto/trace/ftrace/ion.proto",
         "protos/perfetto/trace/ftrace/ipi.proto",
diff --git a/BUILD b/BUILD
index 3d746e3..db70e40 100644
--- a/BUILD
+++ b/BUILD
@@ -1191,6 +1191,8 @@
         "src/trace_processor/importers/ftrace/iostat_tracker.h",
         "src/trace_processor/importers/ftrace/mali_gpu_event_tracker.cc",
         "src/trace_processor/importers/ftrace/mali_gpu_event_tracker.h",
+        "src/trace_processor/importers/ftrace/pkvm_hyp_cpu_tracker.cc",
+        "src/trace_processor/importers/ftrace/pkvm_hyp_cpu_tracker.h",
         "src/trace_processor/importers/ftrace/rss_stat_tracker.cc",
         "src/trace_processor/importers/ftrace/rss_stat_tracker.h",
         "src/trace_processor/importers/ftrace/sched_event_tracker.cc",
@@ -3727,6 +3729,7 @@
         "protos/perfetto/trace/ftrace/generic.proto",
         "protos/perfetto/trace/ftrace/gpu_mem.proto",
         "protos/perfetto/trace/ftrace/gpu_scheduler.proto",
+        "protos/perfetto/trace/ftrace/hyp.proto",
         "protos/perfetto/trace/ftrace/i2c.proto",
         "protos/perfetto/trace/ftrace/ion.proto",
         "protos/perfetto/trace/ftrace/ipi.proto",
diff --git a/protos/perfetto/trace/ftrace/all_protos.gni b/protos/perfetto/trace/ftrace/all_protos.gni
index 15af1e3..54a226c 100644
--- a/protos/perfetto/trace/ftrace/all_protos.gni
+++ b/protos/perfetto/trace/ftrace/all_protos.gni
@@ -42,6 +42,7 @@
   "g2d.proto",
   "gpu_mem.proto",
   "gpu_scheduler.proto",
+  "hyp.proto",
   "i2c.proto",
   "ion.proto",
   "ipi.proto",
diff --git a/protos/perfetto/trace/ftrace/ftrace_event.proto b/protos/perfetto/trace/ftrace/ftrace_event.proto
index f32c8e1..b4f985d 100644
--- a/protos/perfetto/trace/ftrace/ftrace_event.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_event.proto
@@ -42,6 +42,7 @@
 import "protos/perfetto/trace/ftrace/g2d.proto";
 import "protos/perfetto/trace/ftrace/gpu_mem.proto";
 import "protos/perfetto/trace/ftrace/gpu_scheduler.proto";
+import "protos/perfetto/trace/ftrace/hyp.proto";
 import "protos/perfetto/trace/ftrace/i2c.proto";
 import "protos/perfetto/trace/ftrace/ion.proto";
 import "protos/perfetto/trace/ftrace/ipi.proto";
@@ -585,5 +586,7 @@
     MaliMaliKCPUFENCESIGNALFtraceEvent mali_mali_KCPU_FENCE_SIGNAL = 473;
     MaliMaliKCPUFENCEWAITSTARTFtraceEvent mali_mali_KCPU_FENCE_WAIT_START = 474;
     MaliMaliKCPUFENCEWAITENDFtraceEvent mali_mali_KCPU_FENCE_WAIT_END = 475;
+    HypEnterFtraceEvent hyp_enter = 476;
+    HypExitFtraceEvent hyp_exit = 477;
   }
 }
diff --git a/protos/perfetto/trace/ftrace/hyp.proto b/protos/perfetto/trace/ftrace/hyp.proto
new file mode 100644
index 0000000..c0f82b5
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/hyp.proto
@@ -0,0 +1,9 @@
+// Autogenerated by:
+// ../../src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message HypEnterFtraceEvent {}
+message HypExitFtraceEvent {}
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 6205554..c77c5ac 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -5912,6 +5912,13 @@
 
 // End of protos/perfetto/trace/ftrace/gpu_scheduler.proto
 
+// Begin of protos/perfetto/trace/ftrace/hyp.proto
+
+message HypEnterFtraceEvent {}
+message HypExitFtraceEvent {}
+
+// End of protos/perfetto/trace/ftrace/hyp.proto
+
 // Begin of protos/perfetto/trace/ftrace/i2c.proto
 
 message I2cReadFtraceEvent {
@@ -7997,6 +8004,8 @@
     MaliMaliKCPUFENCESIGNALFtraceEvent mali_mali_KCPU_FENCE_SIGNAL = 473;
     MaliMaliKCPUFENCEWAITSTARTFtraceEvent mali_mali_KCPU_FENCE_WAIT_START = 474;
     MaliMaliKCPUFENCEWAITENDFtraceEvent mali_mali_KCPU_FENCE_WAIT_END = 475;
+    HypEnterFtraceEvent hyp_enter = 476;
+    HypExitFtraceEvent hyp_exit = 477;
   }
 }
 
diff --git a/src/tools/ftrace_proto_gen/event_list b/src/tools/ftrace_proto_gen/event_list
index fdb8d3e..12ddeab 100644
--- a/src/tools/ftrace_proto_gen/event_list
+++ b/src/tools/ftrace_proto_gen/event_list
@@ -470,3 +470,5 @@
 mali/mali_KCPU_FENCE_SIGNAL
 mali/mali_KCPU_FENCE_WAIT_START
 mali/mali_KCPU_FENCE_WAIT_END
+hyp/hyp_enter
+hyp/hyp_exit
diff --git a/src/trace_processor/importers/ftrace/BUILD.gn b/src/trace_processor/importers/ftrace/BUILD.gn
index 86c4ddc..ef0974a 100644
--- a/src/trace_processor/importers/ftrace/BUILD.gn
+++ b/src/trace_processor/importers/ftrace/BUILD.gn
@@ -43,6 +43,8 @@
     "iostat_tracker.h",
     "mali_gpu_event_tracker.cc",
     "mali_gpu_event_tracker.h",
+    "pkvm_hyp_cpu_tracker.cc",
+    "pkvm_hyp_cpu_tracker.h",
     "rss_stat_tracker.cc",
     "rss_stat_tracker.h",
     "sched_event_tracker.cc",
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index 6e57384..ab31410 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -222,6 +222,7 @@
       iostat_tracker_(context),
       virtio_gpu_tracker_(context),
       mali_gpu_event_tracker_(context),
+      pkvm_hyp_cpu_tracker_(context),
       sched_wakeup_name_id_(context->storage->InternString("sched_wakeup")),
       sched_waking_name_id_(context->storage->InternString("sched_waking")),
       cpu_id_(context->storage->InternString("cpu")),
@@ -547,10 +548,11 @@
   PacketSequenceStateGeneration* seq_state = data.sequence_state.get();
   ProtoDecoder decoder(event.data(), event.length());
   uint64_t raw_pid = 0;
+  bool no_pid = false;
   if (auto pid_field = decoder.FindField(FtraceEvent::kPidFieldNumber)) {
     raw_pid = pid_field.as_uint64();
   } else {
-    return util::ErrStatus("Pid field not found in ftrace packet");
+    no_pid = true;
   }
   uint32_t pid = static_cast<uint32_t>(raw_pid);
 
@@ -560,6 +562,13 @@
     if (is_metadata_field)
       continue;
 
+    // pKVM hypervisor events are recorded as ftrace events, however they are
+    // not associated with any pid. The rest of trace parsing logic for
+    // hypervisor events will use the pid 0.
+    if (no_pid && !PkvmHypervisorCpuTracker::IsPkvmHypervisorEvent(fld.id())) {
+      return util::ErrStatus("Pid field not found in ftrace packet");
+    }
+
     ConstBytes fld_bytes = fld.as_bytes();
     if (fld.id() == FtraceEvent::kGenericFieldNumber) {
       ParseGenericFtrace(ts, cpu, pid, fld_bytes);
@@ -568,6 +577,10 @@
       ParseTypedFtraceToRaw(fld.id(), ts, cpu, pid, fld_bytes, seq_state);
     }
 
+    if (PkvmHypervisorCpuTracker::IsPkvmHypervisorEvent(fld.id())) {
+      pkvm_hyp_cpu_tracker_.ParseHypEvent(cpu, ts, fld.id());
+    }
+
     switch (fld.id()) {
       case FtraceEvent::kSchedSwitchFieldNumber: {
         ParseSchedSwitch(cpu, ts, fld_bytes);
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index c2a40a2..41a9682 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -26,6 +26,7 @@
 #include "src/trace_processor/importers/ftrace/ftrace_descriptors.h"
 #include "src/trace_processor/importers/ftrace/iostat_tracker.h"
 #include "src/trace_processor/importers/ftrace/mali_gpu_event_tracker.h"
+#include "src/trace_processor/importers/ftrace/pkvm_hyp_cpu_tracker.h"
 #include "src/trace_processor/importers/ftrace/rss_stat_tracker.h"
 #include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
 #include "src/trace_processor/importers/ftrace/virtio_gpu_tracker.h"
@@ -278,6 +279,7 @@
   IostatTracker iostat_tracker_;
   VirtioGpuTracker virtio_gpu_tracker_;
   MaliGpuEventTracker mali_gpu_event_tracker_;
+  PkvmHypervisorCpuTracker pkvm_hyp_cpu_tracker_;
 
   const StringId sched_wakeup_name_id_;
   const StringId sched_waking_name_id_;
diff --git a/src/trace_processor/importers/ftrace/pkvm_hyp_cpu_tracker.cc b/src/trace_processor/importers/ftrace/pkvm_hyp_cpu_tracker.cc
new file mode 100644
index 0000000..05f8bbc
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/pkvm_hyp_cpu_tracker.cc
@@ -0,0 +1,91 @@
+/*
+ * 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/ftrace/pkvm_hyp_cpu_tracker.h"
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+PkvmHypervisorCpuTracker::PkvmHypervisorCpuTracker(
+    TraceProcessorContext* context)
+    : context_(context),
+      pkvm_hyp_id_(context->storage->InternString("pkvm_hyp")) {}
+
+// static
+bool PkvmHypervisorCpuTracker::IsPkvmHypervisorEvent(uint16_t event_id) {
+  using protos::pbzero::FtraceEvent;
+  switch (event_id) {
+    case FtraceEvent::kHypEnterFieldNumber:
+    case FtraceEvent::kHypExitFieldNumber:
+      return true;
+    default:
+      return false;
+  }
+}
+
+void PkvmHypervisorCpuTracker::ParseHypEvent(uint32_t cpu,
+                                             int64_t timestamp,
+                                             uint16_t event_id) {
+  using protos::pbzero::FtraceEvent;
+  switch (event_id) {
+    case FtraceEvent::kHypEnterFieldNumber:
+      ParseHypEnter(cpu, timestamp);
+      break;
+    case FtraceEvent::kHypExitFieldNumber:
+      ParseHypExit(cpu, timestamp);
+      break;
+    // TODO(b/249050813): add remaining hypervisor events
+    default:
+      PERFETTO_FATAL("Not a hypervisor event %d", event_id);
+  }
+}
+
+void PkvmHypervisorCpuTracker::ParseHypEnter(uint32_t cpu, int64_t timestamp) {
+  // TODO(b/249050813): handle bad events (e.g. 2 hyp_enter in a row)
+
+  // TODO(b/249050813): ideally we want to add here a reason for entering
+  // hypervisor (e.g. host_hcall). However, such reason comes in a separate
+  // hypevisor event, so for the time being use a very generic "in hyp" name.
+  // TODO(b/249050813): figure out the UI story once we add hyp events.
+  base::StackString<255> slice_name("in hyp");
+  StringId slice_id = context_->storage->InternString(slice_name.string_view());
+
+  StringId track_id = GetHypCpuTrackId(cpu);
+  TrackId track = context_->track_tracker->InternCpuTrack(track_id, cpu);
+  context_->slice_tracker->Begin(timestamp, track, pkvm_hyp_id_, slice_id);
+}
+
+void PkvmHypervisorCpuTracker::ParseHypExit(uint32_t cpu, int64_t timestamp) {
+  // TODO(b/249050813): handle bad events (e.g. 2 hyp_exit in a row)
+  StringId track_id = GetHypCpuTrackId(cpu);
+  TrackId track = context_->track_tracker->InternCpuTrack(track_id, cpu);
+  context_->slice_tracker->End(timestamp, track);
+}
+
+StringId PkvmHypervisorCpuTracker::GetHypCpuTrackId(uint32_t cpu) {
+  base::StackString<255> track_name("pkVM Hypervisor CPU %d", cpu);
+  return context_->storage->InternString(track_name.string_view());
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/ftrace/pkvm_hyp_cpu_tracker.h b/src/trace_processor/importers/ftrace/pkvm_hyp_cpu_tracker.h
new file mode 100644
index 0000000..1bd9a7c
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/pkvm_hyp_cpu_tracker.h
@@ -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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_PKVM_HYP_CPU_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_PKVM_HYP_CPU_TRACKER_H_
+
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+// Handles parsing and showing hypervisor events in the UI.
+// TODO(b/249050813): link to the documentation once it's available in AOSP.
+class PkvmHypervisorCpuTracker {
+ public:
+  explicit PkvmHypervisorCpuTracker(TraceProcessorContext*);
+
+  static bool IsPkvmHypervisorEvent(uint16_t);
+
+  void ParseHypEvent(uint32_t cput, int64_t timestamp, uint16_t event_id);
+
+ private:
+  void ParseHypEnter(uint32_t cpu, int64_t timestamp);
+  void ParseHypExit(uint32_t cpu, int64_t timestamp);
+
+  StringId GetHypCpuTrackId(uint32_t cpu);
+
+  TraceProcessorContext* context_;
+  const StringId pkvm_hyp_id_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_PKVM_HYP_CPU_TRACKER_H_
diff --git a/src/traced/probes/ftrace/event_info.cc b/src/traced/probes/ftrace/event_info.cc
index ea53a44..dfe6f11 100644
--- a/src/traced/probes/ftrace/event_info.cc
+++ b/src/traced/probes/ftrace/event_info.cc
@@ -4850,6 +4850,8 @@
        kUnsetFtraceId,
        413,
        kUnsetSize},
+      {"hyp_enter", "hyp", {}, kUnsetFtraceId, 476, kUnsetSize},
+      {"hyp_exit", "hyp", {}, kUnsetFtraceId, 477, kUnsetSize},
       {"i2c_read",
        "i2c",
        {
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/hyp/hyp_enter/format b/src/traced/probes/ftrace/test/data/synthetic/events/hyp/hyp_enter/format
new file mode 100644
index 0000000..5ca6fd6
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/hyp/hyp_enter/format
@@ -0,0 +1,6 @@
+name: hyp_enter
+ID: 1001
+format:
+	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
+
+print fmt: " ",
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/hyp/hyp_exit/format b/src/traced/probes/ftrace/test/data/synthetic/events/hyp/hyp_exit/format
new file mode 100644
index 0000000..aca2d7d
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/hyp/hyp_exit/format
@@ -0,0 +1,6 @@
+name: hyp_exit
+ID: 1002
+format:
+	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
+
+print fmt: " ",