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