Merge "trace_processor: support drm-related events"
diff --git a/Android.bp b/Android.bp
index 5373c69..a1c55ee 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8572,6 +8572,7 @@
srcs: [
"src/trace_processor/importers/additional_modules.cc",
"src/trace_processor/importers/ftrace/binder_tracker.cc",
+ "src/trace_processor/importers/ftrace/drm_tracker.cc",
"src/trace_processor/importers/ftrace/ftrace_module_impl.cc",
"src/trace_processor/importers/ftrace/ftrace_parser.cc",
"src/trace_processor/importers/ftrace/ftrace_tokenizer.cc",
diff --git a/BUILD b/BUILD
index 8fa7874..da9ae80 100644
--- a/BUILD
+++ b/BUILD
@@ -1439,6 +1439,8 @@
"src/trace_processor/importers/additional_modules.h",
"src/trace_processor/importers/ftrace/binder_tracker.cc",
"src/trace_processor/importers/ftrace/binder_tracker.h",
+ "src/trace_processor/importers/ftrace/drm_tracker.cc",
+ "src/trace_processor/importers/ftrace/drm_tracker.h",
"src/trace_processor/importers/ftrace/ftrace_module_impl.cc",
"src/trace_processor/importers/ftrace/ftrace_module_impl.h",
"src/trace_processor/importers/ftrace/ftrace_parser.cc",
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index ec49f71..d40dcb6 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -218,6 +218,8 @@
"importers/additional_modules.h",
"importers/ftrace/binder_tracker.cc",
"importers/ftrace/binder_tracker.h",
+ "importers/ftrace/drm_tracker.cc",
+ "importers/ftrace/drm_tracker.h",
"importers/ftrace/ftrace_module_impl.cc",
"importers/ftrace/ftrace_module_impl.h",
"importers/ftrace/ftrace_parser.cc",
diff --git a/src/trace_processor/importers/ftrace/drm_tracker.cc b/src/trace_processor/importers/ftrace/drm_tracker.cc
new file mode 100644
index 0000000..ebe5b31
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/drm_tracker.cc
@@ -0,0 +1,378 @@
+/*
+ * 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.
+ */
+
+#include "src/trace_processor/importers/ftrace/drm_tracker.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "protos/perfetto/trace/ftrace/dma_fence.pbzero.h"
+#include "protos/perfetto/trace/ftrace/drm.pbzero.h"
+#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
+#include "protos/perfetto/trace/ftrace/gpu_scheduler.pbzero.h"
+#include "src/trace_processor/importers/common/flow_tracker.h"
+#include "src/trace_processor/importers/common/process_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 {
+
+namespace {
+
+// There are meta-fences such as fence arrays or fence chains where a fence is
+// a container of other fences. These fences are on "unbound" timelines which
+// are often dynamically created. We want to ignore these timelines to avoid
+// having tons of tracks for them.
+constexpr char kUnboundFenceTimeline[] = "unbound";
+
+} // namespace
+
+DrmTracker::DrmTracker(TraceProcessorContext* context)
+ : context_(context),
+ vblank_slice_signal_id_(context->storage->InternString("signal")),
+ vblank_slice_deliver_id_(context->storage->InternString("deliver")),
+ vblank_arg_seqno_id_(context->storage->InternString("vblank seqno")),
+ sched_slice_schedule_id_(context->storage->InternString("drm_sched_job")),
+ sched_slice_job_id_(context->storage->InternString("job")),
+ sched_arg_ring_id_(context->storage->InternString("gpu sched ring")),
+ sched_arg_job_id_(context->storage->InternString("gpu sched job")),
+ fence_slice_fence_id_(context->storage->InternString("fence")),
+ fence_slice_wait_id_(context->storage->InternString("dma_fence_wait")),
+ fence_arg_context_id_(context->storage->InternString("fence context")),
+ fence_arg_seqno_id_(context->storage->InternString("fence seqno")) {}
+
+void DrmTracker::ParseDrm(int64_t timestamp,
+ int32_t field_id,
+ uint32_t pid,
+ protozero::ConstBytes blob) {
+ using protos::pbzero::FtraceEvent;
+
+ switch (field_id) {
+ case FtraceEvent::kDrmVblankEventFieldNumber: {
+ protos::pbzero::DrmVblankEventFtraceEvent::Decoder evt(blob.data,
+ blob.size);
+ DrmVblankEvent(timestamp, evt.crtc(), evt.seq());
+ break;
+ }
+ case FtraceEvent::kDrmVblankEventDeliveredFieldNumber: {
+ protos::pbzero::DrmVblankEventDeliveredFtraceEvent::Decoder evt(
+ blob.data, blob.size);
+ DrmVblankEventDelivered(timestamp, evt.crtc(), evt.seq());
+ break;
+ }
+
+ case FtraceEvent::kDrmSchedJobFieldNumber: {
+ protos::pbzero::DrmSchedJobFtraceEvent::Decoder evt(blob.data, blob.size);
+ DrmSchedJob(timestamp, pid, evt.name(), evt.id());
+ break;
+ }
+ case FtraceEvent::kDrmRunJobFieldNumber: {
+ protos::pbzero::DrmRunJobFtraceEvent::Decoder evt(blob.data, blob.size);
+ DrmRunJob(timestamp, evt.name(), evt.id(), evt.fence());
+ break;
+ }
+ case FtraceEvent::kDrmSchedProcessJobFieldNumber: {
+ protos::pbzero::DrmSchedProcessJobFtraceEvent::Decoder evt(blob.data,
+ blob.size);
+ DrmSchedProcessJob(timestamp, evt.fence());
+ break;
+ }
+ case FtraceEvent::kDmaFenceInitFieldNumber: {
+ protos::pbzero::DmaFenceInitFtraceEvent::Decoder evt(blob.data,
+ blob.size);
+ DmaFenceInit(timestamp, evt.timeline(), evt.context(), evt.seqno());
+ break;
+ }
+ case FtraceEvent::kDmaFenceEmitFieldNumber: {
+ protos::pbzero::DmaFenceEmitFtraceEvent::Decoder evt(blob.data,
+ blob.size);
+ DmaFenceEmit(timestamp, evt.timeline(), evt.context(), evt.seqno());
+ break;
+ }
+ case FtraceEvent::kDmaFenceSignaledFieldNumber: {
+ protos::pbzero::DmaFenceSignaledFtraceEvent::Decoder evt(blob.data,
+ blob.size);
+ DmaFenceSignaled(timestamp, evt.timeline(), evt.context(), evt.seqno());
+ break;
+ }
+ case FtraceEvent::kDmaFenceWaitStartFieldNumber: {
+ protos::pbzero::DmaFenceWaitStartFtraceEvent::Decoder evt(blob.data,
+ blob.size);
+ DmaFenceWaitStart(timestamp, pid, evt.context(), evt.seqno());
+ break;
+ }
+ case FtraceEvent::kDmaFenceWaitEndFieldNumber: {
+ DmaFenceWaitEnd(timestamp, pid);
+ break;
+ }
+ default:
+ PERFETTO_DFATAL("Unexpected field id");
+ break;
+ }
+}
+
+TrackId DrmTracker::InternVblankTrack(int32_t crtc) {
+ base::StackString<256> track_name("vblank-%d", crtc);
+ StringId track_name_id =
+ context_->storage->InternString(track_name.string_view());
+ return context_->track_tracker->InternGpuTrack(
+ tables::GpuTrackTable::Row(track_name_id));
+}
+
+void DrmTracker::DrmVblankEvent(int64_t timestamp,
+ int32_t crtc,
+ uint32_t seqno) {
+ TrackId track_id = InternVblankTrack(crtc);
+ auto args_inserter = [this, seqno](ArgsTracker::BoundInserter* inserter) {
+ inserter->AddArg(vblank_arg_seqno_id_, Variadic::UnsignedInteger(seqno));
+ };
+
+ context_->slice_tracker->Scoped(timestamp, track_id, kNullStringId,
+ vblank_slice_signal_id_, 0, args_inserter);
+}
+
+void DrmTracker::DrmVblankEventDelivered(int64_t timestamp,
+ int32_t crtc,
+ uint32_t seqno) {
+ TrackId track_id = InternVblankTrack(crtc);
+ auto args_inserter = [this, seqno](ArgsTracker::BoundInserter* inserter) {
+ inserter->AddArg(vblank_arg_seqno_id_, Variadic::UnsignedInteger(seqno));
+ };
+
+ context_->slice_tracker->Scoped(timestamp, track_id, kNullStringId,
+ vblank_slice_deliver_id_, 0, args_inserter);
+}
+
+DrmTracker::SchedRing& DrmTracker::GetSchedRingByName(base::StringView name) {
+ auto* iter = sched_rings_.Find(name);
+ if (iter)
+ return **iter;
+
+ // intern a gpu track
+ base::StackString<64> track_name("sched-%.*s", int(name.size()), name.data());
+ StringId track_name_id =
+ context_->storage->InternString(track_name.string_view());
+ TrackId track_id = context_->track_tracker->InternGpuTrack(
+ tables::GpuTrackTable::Row(track_name_id));
+
+ // no std::make_unique until C++14..
+ auto ring = std::unique_ptr<SchedRing>(new SchedRing());
+ ring->track_id = track_id;
+
+ SchedRing& ret = *ring;
+ sched_rings_.Insert(name, std::move(ring));
+
+ return ret;
+}
+
+void DrmTracker::BeginSchedRingSlice(int64_t timestamp, SchedRing& ring) {
+ PERFETTO_DCHECK(!ring.running_jobs.empty());
+ uint64_t job_id = ring.running_jobs.front();
+
+ auto args_inserter = [this, job_id](ArgsTracker::BoundInserter* inserter) {
+ inserter->AddArg(sched_arg_job_id_, Variadic::UnsignedInteger(job_id));
+ };
+
+ base::Optional<SliceId> slice_id =
+ context_->slice_tracker->Begin(timestamp, ring.track_id, kNullStringId,
+ sched_slice_job_id_, args_inserter);
+
+ if (slice_id) {
+ SliceId* out_slice_id = ring.out_slice_ids.Find(job_id);
+ if (out_slice_id) {
+ context_->flow_tracker->InsertFlow(*out_slice_id, *slice_id);
+ ring.out_slice_ids.Erase(job_id);
+ }
+ }
+}
+
+void DrmTracker::DrmSchedJob(int64_t timestamp,
+ uint32_t pid,
+ base::StringView name,
+ uint64_t job_id) {
+ UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+ TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
+ StringId ring_id = context_->storage->InternString(name);
+ auto args_inserter = [this, ring_id,
+ job_id](ArgsTracker::BoundInserter* inserter) {
+ inserter->AddArg(sched_arg_ring_id_, Variadic::String(ring_id));
+ inserter->AddArg(sched_arg_job_id_, Variadic::UnsignedInteger(job_id));
+ };
+
+ base::Optional<SliceId> slice_id = context_->slice_tracker->Scoped(
+ timestamp, track_id, kNullStringId, sched_slice_schedule_id_, 0,
+ args_inserter);
+
+ if (slice_id) {
+ SchedRing& ring = GetSchedRingByName(name);
+ ring.out_slice_ids[job_id] = *slice_id;
+ }
+}
+
+void DrmTracker::DrmRunJob(int64_t timestamp,
+ base::StringView name,
+ uint64_t job_id,
+ uint64_t fence_id) {
+ SchedRing& ring = GetSchedRingByName(name);
+
+ ring.running_jobs.push_back(job_id);
+ sched_pending_fences_.Insert(fence_id, &ring);
+
+ if (ring.running_jobs.size() == 1)
+ BeginSchedRingSlice(timestamp, ring);
+}
+
+void DrmTracker::DrmSchedProcessJob(int64_t timestamp, uint64_t fence_id) {
+ // look up ring using fence_id
+ auto* iter = sched_pending_fences_.Find(fence_id);
+ if (!iter)
+ return;
+ SchedRing& ring = **iter;
+ sched_pending_fences_.Erase(fence_id);
+
+ ring.running_jobs.pop_front();
+ context_->slice_tracker->End(timestamp, ring.track_id);
+
+ if (!ring.running_jobs.empty())
+ BeginSchedRingSlice(timestamp, ring);
+}
+
+DrmTracker::FenceTimeline& DrmTracker::GetFenceTimelineByContext(
+ uint32_t context,
+ base::StringView name) {
+ auto* iter = fence_timelines_.Find(context);
+ if (iter)
+ return **iter;
+
+ // intern a gpu track
+ base::StackString<64> track_name("fence-%.*s-%u", int(name.size()),
+ name.data(), context);
+ StringId track_name_id =
+ context_->storage->InternString(track_name.string_view());
+ TrackId track_id = context_->track_tracker->InternGpuTrack(
+ tables::GpuTrackTable::Row(track_name_id));
+
+ // no std::make_unique until C++14..
+ auto timeline = std::unique_ptr<FenceTimeline>(new FenceTimeline());
+ timeline->track_id = track_id;
+
+ FenceTimeline& ret = *timeline;
+ fence_timelines_.Insert(context, std::move(timeline));
+
+ return ret;
+}
+
+void DrmTracker::BeginFenceTimelineSlice(int64_t timestamp,
+ const FenceTimeline& timeline) {
+ PERFETTO_DCHECK(!timeline.pending_fences.empty());
+ uint32_t seqno = timeline.pending_fences.front();
+
+ auto args_inserter = [this, seqno](ArgsTracker::BoundInserter* inserter) {
+ inserter->AddArg(fence_arg_seqno_id_, Variadic::UnsignedInteger(seqno));
+ };
+
+ context_->slice_tracker->Begin(timestamp, timeline.track_id, kNullStringId,
+ fence_slice_fence_id_, args_inserter);
+}
+
+void DrmTracker::DmaFenceInit(int64_t timestamp,
+ base::StringView name,
+ uint32_t context,
+ uint32_t seqno) {
+ if (name == kUnboundFenceTimeline)
+ return;
+
+ FenceTimeline& timeline = GetFenceTimelineByContext(context, name);
+ // ignore dma_fence_init when the timeline has dma_fence_emit
+ if (timeline.has_dma_fence_emit)
+ return;
+
+ timeline.pending_fences.push_back(seqno);
+
+ if (timeline.pending_fences.size() == 1)
+ BeginFenceTimelineSlice(timestamp, timeline);
+}
+
+void DrmTracker::DmaFenceEmit(int64_t timestamp,
+ base::StringView name,
+ uint32_t context,
+ uint32_t seqno) {
+ if (name == kUnboundFenceTimeline)
+ return;
+
+ FenceTimeline& timeline = GetFenceTimelineByContext(context, name);
+
+ // Most timelines do not have dma_fence_emit and we rely on the less
+ // accurate dma_fence_init instead. But for those who do, we will switch to
+ // dma_fence_emit.
+ if (!timeline.has_dma_fence_emit) {
+ timeline.has_dma_fence_emit = true;
+
+ if (!timeline.pending_fences.empty()) {
+ context_->slice_tracker->End(timestamp, timeline.track_id);
+ timeline.pending_fences.clear();
+ }
+ }
+
+ timeline.pending_fences.push_back(seqno);
+
+ if (timeline.pending_fences.size() == 1)
+ BeginFenceTimelineSlice(timestamp, timeline);
+}
+
+void DrmTracker::DmaFenceSignaled(int64_t timestamp,
+ base::StringView name,
+ uint32_t context,
+ uint32_t seqno) {
+ if (name == kUnboundFenceTimeline)
+ return;
+
+ FenceTimeline& timeline = GetFenceTimelineByContext(context, name);
+ if (timeline.pending_fences.empty() ||
+ seqno < timeline.pending_fences.front()) {
+ return;
+ }
+
+ timeline.pending_fences.pop_front();
+ context_->slice_tracker->End(timestamp, timeline.track_id);
+
+ if (!timeline.pending_fences.empty())
+ BeginFenceTimelineSlice(timestamp, timeline);
+}
+
+void DrmTracker::DmaFenceWaitStart(int64_t timestamp,
+ uint32_t pid,
+ uint32_t context,
+ uint32_t seqno) {
+ UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+ TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
+ auto args_inserter = [this, context,
+ seqno](ArgsTracker::BoundInserter* inserter) {
+ inserter->AddArg(fence_arg_context_id_, Variadic::UnsignedInteger(context));
+ inserter->AddArg(fence_arg_seqno_id_, Variadic::UnsignedInteger(seqno));
+ };
+
+ context_->slice_tracker->Begin(timestamp, track_id, kNullStringId,
+ fence_slice_wait_id_, args_inserter);
+}
+
+void DrmTracker::DmaFenceWaitEnd(int64_t timestamp, uint32_t pid) {
+ UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+ TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
+
+ context_->slice_tracker->End(timestamp, track_id);
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/ftrace/drm_tracker.h b/src/trace_processor/importers/ftrace/drm_tracker.h
new file mode 100644
index 0000000..3762df6
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/drm_tracker.h
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_DRM_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_DRM_TRACKER_H_
+
+#include <deque>
+#include <memory>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/protozero/field.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class DrmTracker {
+ public:
+ explicit DrmTracker(TraceProcessorContext*);
+
+ void ParseDrm(int64_t timestamp,
+ int32_t field_id,
+ uint32_t pid,
+ protozero::ConstBytes blob);
+
+ private:
+ TrackId InternVblankTrack(int32_t crtc);
+ void DrmVblankEvent(int64_t timestamp, int32_t crtc, uint32_t seqno);
+ void DrmVblankEventDelivered(int64_t timestamp, int32_t crtc, uint32_t seqno);
+
+ struct SchedRing {
+ TrackId track_id;
+ std::deque<uint64_t> running_jobs;
+
+ base::FlatHashMap<uint64_t, SliceId> out_slice_ids;
+ };
+ SchedRing& GetSchedRingByName(base::StringView name);
+ void BeginSchedRingSlice(int64_t timestamp, SchedRing& ring);
+
+ void DrmSchedJob(int64_t timestamp,
+ uint32_t pid,
+ base::StringView name,
+ uint64_t job_id);
+ void DrmRunJob(int64_t timestamp,
+ base::StringView name,
+ uint64_t job_id,
+ uint64_t fence_id);
+ void DrmSchedProcessJob(int64_t timestamp, uint64_t fence_id);
+
+ struct FenceTimeline {
+ TrackId track_id;
+ bool has_dma_fence_emit;
+ std::deque<uint32_t> pending_fences;
+ };
+ FenceTimeline& GetFenceTimelineByContext(uint32_t context,
+ base::StringView name);
+ void BeginFenceTimelineSlice(int64_t timestamp,
+ const FenceTimeline& timeline);
+
+ void DmaFenceInit(int64_t timestamp,
+ base::StringView name,
+ uint32_t context,
+ uint32_t seqno);
+ void DmaFenceEmit(int64_t timestamp,
+ base::StringView name,
+ uint32_t context,
+ uint32_t seqno);
+ void DmaFenceSignaled(int64_t timestamp,
+ base::StringView name,
+ uint32_t context,
+ uint32_t seqno);
+ void DmaFenceWaitStart(int64_t timestamp,
+ uint32_t pid,
+ uint32_t context,
+ uint32_t seqno);
+ void DmaFenceWaitEnd(int64_t timestamp, uint32_t pid);
+
+ TraceProcessorContext* const context_;
+
+ const StringId vblank_slice_signal_id_;
+ const StringId vblank_slice_deliver_id_;
+ const StringId vblank_arg_seqno_id_;
+ const StringId sched_slice_schedule_id_;
+ const StringId sched_slice_job_id_;
+ const StringId sched_arg_ring_id_;
+ const StringId sched_arg_job_id_;
+ const StringId fence_slice_fence_id_;
+ const StringId fence_slice_wait_id_;
+ const StringId fence_arg_context_id_;
+ const StringId fence_arg_seqno_id_;
+
+ base::FlatHashMap<base::StringView, std::unique_ptr<SchedRing>> sched_rings_;
+ base::FlatHashMap<uint64_t, SchedRing*> sched_pending_fences_;
+
+ base::FlatHashMap<uint32_t, std::unique_ptr<FenceTimeline>> fence_timelines_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_DRM_TRACKER_H_
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index 4078319..3861e92 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -109,6 +109,7 @@
FtraceParser::FtraceParser(TraceProcessorContext* context)
: context_(context),
rss_stat_tracker_(context),
+ drm_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")),
@@ -746,6 +747,19 @@
ParseSuspendResume(ts, data);
break;
}
+ case FtraceEvent::kDrmVblankEventFieldNumber:
+ case FtraceEvent::kDrmVblankEventDeliveredFieldNumber:
+ case FtraceEvent::kDrmSchedJobFieldNumber:
+ case FtraceEvent::kDrmRunJobFieldNumber:
+ case FtraceEvent::kDrmSchedProcessJobFieldNumber:
+ case FtraceEvent::kDmaFenceInitFieldNumber:
+ case FtraceEvent::kDmaFenceEmitFieldNumber:
+ case FtraceEvent::kDmaFenceSignaledFieldNumber:
+ case FtraceEvent::kDmaFenceWaitStartFieldNumber:
+ case FtraceEvent::kDmaFenceWaitEndFieldNumber: {
+ drm_tracker_.ParseDrm(ts, fld.id(), pid, data);
+ break;
+ }
default:
break;
}
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index e4730c6..a4ef6f3 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -19,6 +19,7 @@
#include "perfetto/trace_processor/status.h"
#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/ftrace/drm_tracker.h"
#include "src/trace_processor/importers/ftrace/ftrace_descriptors.h"
#include "src/trace_processor/importers/ftrace/rss_stat_tracker.h"
#include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
@@ -179,6 +180,7 @@
TraceProcessorContext* context_;
RssStatTracker rss_stat_tracker_;
+ DrmTracker drm_tracker_;
const StringId sched_wakeup_name_id_;
const StringId sched_waking_name_id_;
diff --git a/test/trace_processor/graphics/drm_dma_fence.textproto b/test/trace_processor/graphics/drm_dma_fence.textproto
new file mode 100644
index 0000000..d7166cc
--- /dev/null
+++ b/test/trace_processor/graphics/drm_dma_fence.textproto
@@ -0,0 +1,150 @@
+packet {
+ ftrace_events {
+ cpu: 7
+ event {
+ timestamp: 11303602488073
+ pid: 191
+ dma_fence_init {
+ context: 1
+ driver: "msm"
+ seqno: 16665
+ timeline: "gpu-ring-0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 7
+ event {
+ timestamp: 11303602500886
+ pid: 191
+ dma_fence_emit {
+ context: 1
+ driver: "msm"
+ seqno: 16665
+ timeline: "gpu-ring-0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 7
+ event {
+ timestamp: 11303604370470
+ pid: 191
+ dma_fence_init {
+ context: 1
+ driver: "msm"
+ seqno: 16666
+ timeline: "gpu-ring-0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 7
+ event {
+ timestamp: 11303604372188
+ pid: 191
+ dma_fence_emit {
+ context: 1
+ driver: "msm"
+ seqno: 16666
+ timeline: "gpu-ring-0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 0
+ event {
+ timestamp: 11303607306512
+ pid: 0
+ dma_fence_signaled {
+ context: 1
+ driver: "msm"
+ seqno: 16665
+ timeline: "gpu-ring-0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 0
+ event {
+ timestamp: 11303611157295
+ pid: 0
+ dma_fence_signaled {
+ context: 1
+ driver: "msm"
+ seqno: 16666
+ timeline: "gpu-ring-0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 3
+ event {
+ timestamp: 11303702681699
+ pid: 365
+ dma_fence_init {
+ context: 9
+ driver: "drm_sched"
+ seqno: 5065
+ timeline: "ring0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 5
+ event {
+ timestamp: 11303702851231
+ pid: 144
+ dma_fence_wait_start {
+ context: 9
+ driver: "drm_sched"
+ seqno: 5065
+ timeline: "ring0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 0
+ event {
+ timestamp: 11303707550086
+ pid: 0
+ dma_fence_signaled {
+ context: 9
+ driver: "drm_sched"
+ seqno: 5065
+ timeline: "ring0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 5
+ event {
+ timestamp: 11303707718889
+ pid: 144
+ dma_fence_wait_end {
+ context: 9
+ driver: "drm_sched"
+ seqno: 5065
+ timeline: "ring0"
+ }
+ }
+ }
+}
diff --git a/test/trace_processor/graphics/drm_dma_fence_gpu_track.out b/test/trace_processor/graphics/drm_dma_fence_gpu_track.out
new file mode 100644
index 0000000..5ec5f6d
--- /dev/null
+++ b/test/trace_processor/graphics/drm_dma_fence_gpu_track.out
@@ -0,0 +1,5 @@
+"name","ts","dur","name","flat_key","int_value","string_value"
+"fence-gpu-ring-0-1",11303602488073,12813,"fence","fence seqno",16665,"[NULL]"
+"fence-gpu-ring-0-1",11303602500886,4805626,"fence","fence seqno",16665,"[NULL]"
+"fence-gpu-ring-0-1",11303607306512,3850783,"fence","fence seqno",16666,"[NULL]"
+"fence-ring0-9",11303702681699,4868387,"fence","fence seqno",5065,"[NULL]"
diff --git a/test/trace_processor/graphics/drm_dma_fence_thread_track.out b/test/trace_processor/graphics/drm_dma_fence_thread_track.out
new file mode 100644
index 0000000..c4d5bda
--- /dev/null
+++ b/test/trace_processor/graphics/drm_dma_fence_thread_track.out
@@ -0,0 +1,3 @@
+"utid","ts","dur","name","flat_key","int_value","string_value"
+3,11303702851231,4867658,"dma_fence_wait","fence context",9,"[NULL]"
+3,11303702851231,4867658,"dma_fence_wait","fence seqno",5065,"[NULL]"
diff --git a/test/trace_processor/graphics/drm_gpu_track.sql b/test/trace_processor/graphics/drm_gpu_track.sql
new file mode 100644
index 0000000..b1554a8
--- /dev/null
+++ b/test/trace_processor/graphics/drm_gpu_track.sql
@@ -0,0 +1,15 @@
+SELECT
+ gpu_track.name,
+ ts,
+ dur,
+ slice.name,
+ flat_key,
+ int_value,
+ string_value
+FROM
+ gpu_track
+ JOIN slice
+ ON slice.track_id = gpu_track.id
+ JOIN args
+ ON slice.arg_set_id = args.arg_set_id
+ORDER BY ts
diff --git a/test/trace_processor/graphics/drm_sched.textproto b/test/trace_processor/graphics/drm_sched.textproto
new file mode 100644
index 0000000..7ce3528
--- /dev/null
+++ b/test/trace_processor/graphics/drm_sched.textproto
@@ -0,0 +1,184 @@
+packet {
+ ftrace_events {
+ cpu: 0
+ event {
+ timestamp: 9246165326050
+ pid: 365
+ drm_sched_job {
+ entity: 18446743526218327296
+ fence: 18446743526909918272
+ hw_job_count: 0
+ id: 13481
+ job_count: 0
+ name: "ring0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 7
+ event {
+ timestamp: 9246165349383
+ pid: 191
+ drm_run_job {
+ entity: 18446743526218327296
+ fence: 18446743526909918272
+ hw_job_count: 1
+ id: 13481
+ job_count: 0
+ name: "ring0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 4
+ event {
+ timestamp: 9246166957616
+ pid: 762
+ drm_sched_job {
+ entity: 18446743526854133248
+ fence: 18446743526610453312
+ hw_job_count: 1
+ id: 13482
+ job_count: 0
+ name: "ring0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 7
+ event {
+ timestamp: 9246166978033
+ pid: 191
+ drm_run_job {
+ entity: 18446743526854133248
+ fence: 18446743526610453312
+ hw_job_count: 2
+ id: 13482
+ job_count: 0
+ name: "ring0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 4
+ event {
+ timestamp: 9246167272512
+ pid: 762
+ drm_sched_job {
+ entity: 18446743526854133248
+ fence: 18446743526610452736
+ hw_job_count: 2
+ id: 13483
+ job_count: 0
+ name: "ring0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 7
+ event {
+ timestamp: 9246167288190
+ pid: 191
+ drm_run_job {
+ entity: 18446743526854133248
+ fence: 18446743526610452736
+ hw_job_count: 3
+ id: 13483
+ job_count: 0
+ name: "ring0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 0
+ event {
+ timestamp: 9246170078456
+ pid: 0
+ drm_sched_process_job {
+ fence: 18446743526909918272
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 0
+ event {
+ timestamp: 9246174020027
+ pid: 0
+ drm_sched_process_job {
+ fence: 18446743526610453312
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 0
+ event {
+ timestamp: 9246174045183
+ pid: 0
+ drm_sched_process_job {
+ fence: 18446743526610452736
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 0
+ event {
+ timestamp: 9246181907439
+ pid: 365
+ drm_sched_job {
+ entity: 18446743526218327296
+ fence: 18446743526909915584
+ hw_job_count: 0
+ id: 13484
+ job_count: 0
+ name: "ring0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 7
+ event {
+ timestamp: 9246181933273
+ pid: 191
+ drm_run_job {
+ entity: 18446743526218327296
+ fence: 18446743526909915584
+ hw_job_count: 1
+ id: 13484
+ job_count: 0
+ name: "ring0"
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 0
+ event {
+ timestamp: 9246186659585
+ pid: 0
+ drm_sched_process_job {
+ fence: 18446743526909915584
+ }
+ }
+ }
+}
diff --git a/test/trace_processor/graphics/drm_sched_gpu_track.out b/test/trace_processor/graphics/drm_sched_gpu_track.out
new file mode 100644
index 0000000..e9533da
--- /dev/null
+++ b/test/trace_processor/graphics/drm_sched_gpu_track.out
@@ -0,0 +1,5 @@
+"name","ts","dur","name","flat_key","int_value","string_value"
+"sched-ring0",9246165349383,4729073,"job","gpu sched job",13481,"[NULL]"
+"sched-ring0",9246170078456,3941571,"job","gpu sched job",13482,"[NULL]"
+"sched-ring0",9246174020027,25156,"job","gpu sched job",13483,"[NULL]"
+"sched-ring0",9246181933273,4726312,"job","gpu sched job",13484,"[NULL]"
diff --git a/test/trace_processor/graphics/drm_sched_thread_track.out b/test/trace_processor/graphics/drm_sched_thread_track.out
new file mode 100644
index 0000000..6e9e577
--- /dev/null
+++ b/test/trace_processor/graphics/drm_sched_thread_track.out
@@ -0,0 +1,9 @@
+"utid","ts","dur","name","flat_key","int_value","string_value"
+1,9246165326050,0,"drm_sched_job","gpu sched ring","[NULL]","ring0"
+1,9246165326050,0,"drm_sched_job","gpu sched job",13481,"[NULL]"
+3,9246166957616,0,"drm_sched_job","gpu sched ring","[NULL]","ring0"
+3,9246166957616,0,"drm_sched_job","gpu sched job",13482,"[NULL]"
+3,9246167272512,0,"drm_sched_job","gpu sched ring","[NULL]","ring0"
+3,9246167272512,0,"drm_sched_job","gpu sched job",13483,"[NULL]"
+1,9246181907439,0,"drm_sched_job","gpu sched ring","[NULL]","ring0"
+1,9246181907439,0,"drm_sched_job","gpu sched job",13484,"[NULL]"
diff --git a/test/trace_processor/graphics/drm_thread_track.sql b/test/trace_processor/graphics/drm_thread_track.sql
new file mode 100644
index 0000000..4c879fb
--- /dev/null
+++ b/test/trace_processor/graphics/drm_thread_track.sql
@@ -0,0 +1,15 @@
+SELECT
+ utid,
+ ts,
+ dur,
+ slice.name,
+ flat_key,
+ int_value,
+ string_value
+FROM
+ thread_track
+ JOIN slice
+ ON slice.track_id = thread_track.id
+ JOIN args
+ ON slice.arg_set_id = args.arg_set_id
+ORDER BY ts
diff --git a/test/trace_processor/graphics/drm_vblank.textproto b/test/trace_processor/graphics/drm_vblank.textproto
new file mode 100644
index 0000000..71817fc
--- /dev/null
+++ b/test/trace_processor/graphics/drm_vblank.textproto
@@ -0,0 +1,29 @@
+packet {
+ ftrace_events {
+ cpu: 0
+ event {
+ timestamp: 6159770881976
+ pid: 0
+ drm_vblank_event {
+ crtc: 0
+ high_prec: 1
+ seq: 3551
+ time: 6159771267407
+ }
+ }
+ }
+}
+packet {
+ ftrace_events {
+ cpu: 4
+ event {
+ timestamp: 6159770993376
+ pid: 144
+ drm_vblank_event_delivered {
+ crtc: 0
+ file: 18446743526216291840
+ seq: 3551
+ }
+ }
+ }
+}
diff --git a/test/trace_processor/graphics/drm_vblank_gpu_track.out b/test/trace_processor/graphics/drm_vblank_gpu_track.out
new file mode 100644
index 0000000..1578c8e
--- /dev/null
+++ b/test/trace_processor/graphics/drm_vblank_gpu_track.out
@@ -0,0 +1,3 @@
+"name","ts","dur","name","flat_key","int_value","string_value"
+"vblank-0",6159770881976,0,"signal","vblank seqno",3551,"[NULL]"
+"vblank-0",6159770993376,0,"deliver","vblank seqno",3551,"[NULL]"
diff --git a/test/trace_processor/graphics/index b/test/trace_processor/graphics/index
index 34cc136..603db06 100644
--- a/test/trace_processor/graphics/index
+++ b/test/trace_processor/graphics/index
@@ -52,3 +52,10 @@
# DPU vote clock and bandwidth
dpu_vote_clock_bw.textproto android_hwcomposer dpu_vote_clock_bw.out
+
+# DRM-related ftrace events
+drm_vblank.textproto drm_gpu_track.sql drm_vblank_gpu_track.out
+drm_sched.textproto drm_gpu_track.sql drm_sched_gpu_track.out
+drm_sched.textproto drm_thread_track.sql drm_sched_thread_track.out
+drm_dma_fence.textproto drm_gpu_track.sql drm_dma_fence_gpu_track.out
+drm_dma_fence.textproto drm_thread_track.sql drm_dma_fence_thread_track.out