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