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