xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/ftrace/drm_tracker.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/trace_processor/importers/ftrace/drm_tracker.h"
18 #include "perfetto/ext/base/string_utils.h"
19 #include "protos/perfetto/trace/ftrace/dma_fence.pbzero.h"
20 #include "protos/perfetto/trace/ftrace/drm.pbzero.h"
21 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
22 #include "protos/perfetto/trace/ftrace/gpu_scheduler.pbzero.h"
23 #include "src/trace_processor/importers/common/flow_tracker.h"
24 #include "src/trace_processor/importers/common/process_tracker.h"
25 #include "src/trace_processor/importers/common/slice_tracker.h"
26 #include "src/trace_processor/importers/common/track_tracker.h"
27 
28 namespace perfetto {
29 namespace trace_processor {
30 
31 namespace {
32 
33 // There are meta-fences such as fence arrays or fence chains where a fence is
34 // a container of other fences.  These fences are on "unbound" timelines which
35 // are often dynamically created.  We want to ignore these timelines to avoid
36 // having tons of tracks for them.
37 constexpr char kUnboundFenceTimeline[] = "unbound";
38 
39 }  // namespace
40 
DrmTracker(TraceProcessorContext * context)41 DrmTracker::DrmTracker(TraceProcessorContext* context)
42     : context_(context),
43       vblank_slice_signal_id_(context->storage->InternString("signal")),
44       vblank_slice_deliver_id_(context->storage->InternString("deliver")),
45       vblank_arg_seqno_id_(context->storage->InternString("vblank seqno")),
46       sched_slice_schedule_id_(context->storage->InternString("drm_sched_job")),
47       sched_slice_job_id_(context->storage->InternString("job")),
48       sched_arg_ring_id_(context->storage->InternString("gpu sched ring")),
49       sched_arg_job_id_(context->storage->InternString("gpu sched job")),
50       fence_slice_fence_id_(context->storage->InternString("fence")),
51       fence_slice_wait_id_(context->storage->InternString("dma_fence_wait")),
52       fence_arg_context_id_(context->storage->InternString("fence context")),
53       fence_arg_seqno_id_(context->storage->InternString("fence seqno")) {}
54 
ParseDrm(int64_t timestamp,uint32_t field_id,uint32_t pid,protozero::ConstBytes blob)55 void DrmTracker::ParseDrm(int64_t timestamp,
56                           uint32_t field_id,
57                           uint32_t pid,
58                           protozero::ConstBytes blob) {
59   using protos::pbzero::FtraceEvent;
60 
61   switch (field_id) {
62     case FtraceEvent::kDrmVblankEventFieldNumber: {
63       protos::pbzero::DrmVblankEventFtraceEvent::Decoder evt(blob.data,
64                                                              blob.size);
65       DrmVblankEvent(timestamp, evt.crtc(), evt.seq());
66       break;
67     }
68     case FtraceEvent::kDrmVblankEventDeliveredFieldNumber: {
69       protos::pbzero::DrmVblankEventDeliveredFtraceEvent::Decoder evt(
70           blob.data, blob.size);
71       DrmVblankEventDelivered(timestamp, evt.crtc(), evt.seq());
72       break;
73     }
74 
75     case FtraceEvent::kDrmSchedJobFieldNumber: {
76       protos::pbzero::DrmSchedJobFtraceEvent::Decoder evt(blob.data, blob.size);
77       DrmSchedJob(timestamp, pid, evt.name(), evt.id());
78       break;
79     }
80     case FtraceEvent::kDrmRunJobFieldNumber: {
81       protos::pbzero::DrmRunJobFtraceEvent::Decoder evt(blob.data, blob.size);
82       DrmRunJob(timestamp, evt.name(), evt.id(), evt.fence());
83       break;
84     }
85     case FtraceEvent::kDrmSchedProcessJobFieldNumber: {
86       protos::pbzero::DrmSchedProcessJobFtraceEvent::Decoder evt(blob.data,
87                                                                  blob.size);
88       DrmSchedProcessJob(timestamp, evt.fence());
89       break;
90     }
91     case FtraceEvent::kDmaFenceInitFieldNumber: {
92       protos::pbzero::DmaFenceInitFtraceEvent::Decoder evt(blob.data,
93                                                            blob.size);
94       DmaFenceInit(timestamp, evt.timeline(), evt.context(), evt.seqno());
95       break;
96     }
97     case FtraceEvent::kDmaFenceEmitFieldNumber: {
98       protos::pbzero::DmaFenceEmitFtraceEvent::Decoder evt(blob.data,
99                                                            blob.size);
100       DmaFenceEmit(timestamp, evt.timeline(), evt.context(), evt.seqno());
101       break;
102     }
103     case FtraceEvent::kDmaFenceSignaledFieldNumber: {
104       protos::pbzero::DmaFenceSignaledFtraceEvent::Decoder evt(blob.data,
105                                                                blob.size);
106       DmaFenceSignaled(timestamp, evt.timeline(), evt.context(), evt.seqno());
107       break;
108     }
109     case FtraceEvent::kDmaFenceWaitStartFieldNumber: {
110       protos::pbzero::DmaFenceWaitStartFtraceEvent::Decoder evt(blob.data,
111                                                                 blob.size);
112       DmaFenceWaitStart(timestamp, pid, evt.context(), evt.seqno());
113       break;
114     }
115     case FtraceEvent::kDmaFenceWaitEndFieldNumber: {
116       DmaFenceWaitEnd(timestamp, pid);
117       break;
118     }
119     default:
120       PERFETTO_DFATAL("Unexpected field id");
121       break;
122   }
123 }
124 
InternVblankTrack(int32_t crtc)125 TrackId DrmTracker::InternVblankTrack(int32_t crtc) {
126   base::StackString<256> track_name("vblank-%d", crtc);
127   StringId track_name_id =
128       context_->storage->InternString(track_name.string_view());
129   return context_->track_tracker->LegacyInternGpuTrack(
130       tables::GpuTrackTable::Row(track_name_id));
131 }
132 
DrmVblankEvent(int64_t timestamp,int32_t crtc,uint32_t seqno)133 void DrmTracker::DrmVblankEvent(int64_t timestamp,
134                                 int32_t crtc,
135                                 uint32_t seqno) {
136   TrackId track_id = InternVblankTrack(crtc);
137   auto args_inserter = [this, seqno](ArgsTracker::BoundInserter* inserter) {
138     inserter->AddArg(vblank_arg_seqno_id_, Variadic::UnsignedInteger(seqno));
139   };
140 
141   context_->slice_tracker->Scoped(timestamp, track_id, kNullStringId,
142                                   vblank_slice_signal_id_, 0, args_inserter);
143 }
144 
DrmVblankEventDelivered(int64_t timestamp,int32_t crtc,uint32_t seqno)145 void DrmTracker::DrmVblankEventDelivered(int64_t timestamp,
146                                          int32_t crtc,
147                                          uint32_t seqno) {
148   TrackId track_id = InternVblankTrack(crtc);
149   auto args_inserter = [this, seqno](ArgsTracker::BoundInserter* inserter) {
150     inserter->AddArg(vblank_arg_seqno_id_, Variadic::UnsignedInteger(seqno));
151   };
152 
153   context_->slice_tracker->Scoped(timestamp, track_id, kNullStringId,
154                                   vblank_slice_deliver_id_, 0, args_inserter);
155 }
156 
GetSchedRingByName(base::StringView name)157 DrmTracker::SchedRing& DrmTracker::GetSchedRingByName(base::StringView name) {
158   auto* iter = sched_rings_.Find(name);
159   if (iter)
160     return **iter;
161 
162   // intern a gpu track
163   base::StackString<64> track_name("sched-%.*s", int(name.size()), name.data());
164   StringId track_name_id =
165       context_->storage->InternString(track_name.string_view());
166   TrackId track_id = context_->track_tracker->LegacyInternGpuTrack(
167       tables::GpuTrackTable::Row(track_name_id));
168 
169   // no std::make_unique until C++14..
170   auto ring = std::unique_ptr<SchedRing>(new SchedRing());
171   ring->track_id = track_id;
172 
173   SchedRing& ret = *ring;
174   sched_rings_.Insert(name, std::move(ring));
175 
176   return ret;
177 }
178 
BeginSchedRingSlice(int64_t timestamp,SchedRing & ring)179 void DrmTracker::BeginSchedRingSlice(int64_t timestamp, SchedRing& ring) {
180   PERFETTO_DCHECK(!ring.running_jobs.empty());
181   uint64_t job_id = ring.running_jobs.front();
182 
183   auto args_inserter = [this, job_id](ArgsTracker::BoundInserter* inserter) {
184     inserter->AddArg(sched_arg_job_id_, Variadic::UnsignedInteger(job_id));
185   };
186 
187   std::optional<SliceId> slice_id =
188       context_->slice_tracker->Begin(timestamp, ring.track_id, kNullStringId,
189                                      sched_slice_job_id_, args_inserter);
190 
191   if (slice_id) {
192     SliceId* out_slice_id = ring.out_slice_ids.Find(job_id);
193     if (out_slice_id) {
194       context_->flow_tracker->InsertFlow(*out_slice_id, *slice_id);
195       ring.out_slice_ids.Erase(job_id);
196     }
197   }
198 }
199 
DrmSchedJob(int64_t timestamp,uint32_t pid,base::StringView name,uint64_t job_id)200 void DrmTracker::DrmSchedJob(int64_t timestamp,
201                              uint32_t pid,
202                              base::StringView name,
203                              uint64_t job_id) {
204   UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
205   TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
206   StringId ring_id = context_->storage->InternString(name);
207   auto args_inserter = [this, ring_id,
208                         job_id](ArgsTracker::BoundInserter* inserter) {
209     inserter->AddArg(sched_arg_ring_id_, Variadic::String(ring_id));
210     inserter->AddArg(sched_arg_job_id_, Variadic::UnsignedInteger(job_id));
211   };
212 
213   std::optional<SliceId> slice_id = context_->slice_tracker->Scoped(
214       timestamp, track_id, kNullStringId, sched_slice_schedule_id_, 0,
215       args_inserter);
216 
217   if (slice_id) {
218     SchedRing& ring = GetSchedRingByName(name);
219     ring.out_slice_ids[job_id] = *slice_id;
220   }
221 }
222 
DrmRunJob(int64_t timestamp,base::StringView name,uint64_t job_id,uint64_t fence_id)223 void DrmTracker::DrmRunJob(int64_t timestamp,
224                            base::StringView name,
225                            uint64_t job_id,
226                            uint64_t fence_id) {
227   SchedRing& ring = GetSchedRingByName(name);
228 
229   ring.running_jobs.push_back(job_id);
230   sched_pending_fences_.Insert(fence_id, &ring);
231 
232   if (ring.running_jobs.size() == 1)
233     BeginSchedRingSlice(timestamp, ring);
234 }
235 
DrmSchedProcessJob(int64_t timestamp,uint64_t fence_id)236 void DrmTracker::DrmSchedProcessJob(int64_t timestamp, uint64_t fence_id) {
237   // look up ring using fence_id
238   auto* iter = sched_pending_fences_.Find(fence_id);
239   if (!iter)
240     return;
241   SchedRing& ring = **iter;
242   sched_pending_fences_.Erase(fence_id);
243 
244   ring.running_jobs.pop_front();
245   context_->slice_tracker->End(timestamp, ring.track_id);
246 
247   if (!ring.running_jobs.empty())
248     BeginSchedRingSlice(timestamp, ring);
249 }
250 
GetFenceTimelineByContext(uint32_t context,base::StringView name)251 DrmTracker::FenceTimeline& DrmTracker::GetFenceTimelineByContext(
252     uint32_t context,
253     base::StringView name) {
254   auto* iter = fence_timelines_.Find(context);
255   if (iter)
256     return **iter;
257 
258   // intern a gpu track
259   base::StackString<64> track_name("fence-%.*s-%u", int(name.size()),
260                                    name.data(), context);
261   StringId track_name_id =
262       context_->storage->InternString(track_name.string_view());
263   TrackId track_id = context_->track_tracker->LegacyInternGpuTrack(
264       tables::GpuTrackTable::Row(track_name_id));
265 
266   // no std::make_unique until C++14..
267   auto timeline = std::unique_ptr<FenceTimeline>(new FenceTimeline());
268   timeline->track_id = track_id;
269 
270   FenceTimeline& ret = *timeline;
271   fence_timelines_.Insert(context, std::move(timeline));
272 
273   return ret;
274 }
275 
BeginFenceTimelineSlice(int64_t timestamp,const FenceTimeline & timeline)276 void DrmTracker::BeginFenceTimelineSlice(int64_t timestamp,
277                                          const FenceTimeline& timeline) {
278   PERFETTO_DCHECK(!timeline.pending_fences.empty());
279   uint32_t seqno = timeline.pending_fences.front();
280 
281   auto args_inserter = [this, seqno](ArgsTracker::BoundInserter* inserter) {
282     inserter->AddArg(fence_arg_seqno_id_, Variadic::UnsignedInteger(seqno));
283   };
284 
285   context_->slice_tracker->Begin(timestamp, timeline.track_id, kNullStringId,
286                                  fence_slice_fence_id_, args_inserter);
287 }
288 
DmaFenceInit(int64_t timestamp,base::StringView name,uint32_t context,uint32_t seqno)289 void DrmTracker::DmaFenceInit(int64_t timestamp,
290                               base::StringView name,
291                               uint32_t context,
292                               uint32_t seqno) {
293   if (name == kUnboundFenceTimeline)
294     return;
295 
296   FenceTimeline& timeline = GetFenceTimelineByContext(context, name);
297   // ignore dma_fence_init when the timeline has dma_fence_emit
298   if (timeline.has_dma_fence_emit)
299     return;
300 
301   timeline.pending_fences.push_back(seqno);
302 
303   if (timeline.pending_fences.size() == 1)
304     BeginFenceTimelineSlice(timestamp, timeline);
305 }
306 
DmaFenceEmit(int64_t timestamp,base::StringView name,uint32_t context,uint32_t seqno)307 void DrmTracker::DmaFenceEmit(int64_t timestamp,
308                               base::StringView name,
309                               uint32_t context,
310                               uint32_t seqno) {
311   if (name == kUnboundFenceTimeline)
312     return;
313 
314   FenceTimeline& timeline = GetFenceTimelineByContext(context, name);
315 
316   // Most timelines do not have dma_fence_emit and we rely on the less
317   // accurate dma_fence_init instead.  But for those who do, we will switch to
318   // dma_fence_emit.
319   if (!timeline.has_dma_fence_emit) {
320     timeline.has_dma_fence_emit = true;
321 
322     if (!timeline.pending_fences.empty()) {
323       context_->slice_tracker->End(timestamp, timeline.track_id);
324       timeline.pending_fences.clear();
325     }
326   }
327 
328   timeline.pending_fences.push_back(seqno);
329 
330   if (timeline.pending_fences.size() == 1)
331     BeginFenceTimelineSlice(timestamp, timeline);
332 }
333 
DmaFenceSignaled(int64_t timestamp,base::StringView name,uint32_t context,uint32_t seqno)334 void DrmTracker::DmaFenceSignaled(int64_t timestamp,
335                                   base::StringView name,
336                                   uint32_t context,
337                                   uint32_t seqno) {
338   if (name == kUnboundFenceTimeline)
339     return;
340 
341   FenceTimeline& timeline = GetFenceTimelineByContext(context, name);
342   if (timeline.pending_fences.empty() ||
343       seqno < timeline.pending_fences.front()) {
344     return;
345   }
346 
347   timeline.pending_fences.pop_front();
348   context_->slice_tracker->End(timestamp, timeline.track_id);
349 
350   if (!timeline.pending_fences.empty())
351     BeginFenceTimelineSlice(timestamp, timeline);
352 }
353 
DmaFenceWaitStart(int64_t timestamp,uint32_t pid,uint32_t context,uint32_t seqno)354 void DrmTracker::DmaFenceWaitStart(int64_t timestamp,
355                                    uint32_t pid,
356                                    uint32_t context,
357                                    uint32_t seqno) {
358   UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
359   TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
360   auto args_inserter = [this, context,
361                         seqno](ArgsTracker::BoundInserter* inserter) {
362     inserter->AddArg(fence_arg_context_id_, Variadic::UnsignedInteger(context));
363     inserter->AddArg(fence_arg_seqno_id_, Variadic::UnsignedInteger(seqno));
364   };
365 
366   context_->slice_tracker->Begin(timestamp, track_id, kNullStringId,
367                                  fence_slice_wait_id_, args_inserter);
368 }
369 
DmaFenceWaitEnd(int64_t timestamp,uint32_t pid)370 void DrmTracker::DmaFenceWaitEnd(int64_t timestamp, uint32_t pid) {
371   UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
372   TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
373 
374   context_->slice_tracker->End(timestamp, track_id);
375 }
376 
377 }  // namespace trace_processor
378 }  // namespace perfetto
379