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/virtio_gpu_tracker.h"
18
19 #include <cstdint>
20
21 #include "perfetto/base/logging.h"
22 #include "perfetto/ext/base/string_utils.h"
23 #include "perfetto/ext/base/string_view.h"
24 #include "perfetto/protozero/field.h"
25 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
26 #include "protos/perfetto/trace/ftrace/virtio_gpu.pbzero.h"
27 #include "src/trace_processor/importers/common/async_track_set_tracker.h"
28 #include "src/trace_processor/importers/common/event_tracker.h"
29 #include "src/trace_processor/importers/common/slice_tracker.h"
30 #include "src/trace_processor/importers/common/track_tracker.h"
31 #include "src/trace_processor/importers/common/tracks.h"
32 #include "src/trace_processor/storage/trace_storage.h"
33
34 enum virtio_gpu_ctrl_type {
35 VIRTIO_GPU_UNDEFINED = 0,
36
37 /* 2d commands */
38 VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100,
39 VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
40 VIRTIO_GPU_CMD_RESOURCE_UNREF,
41 VIRTIO_GPU_CMD_SET_SCANOUT,
42 VIRTIO_GPU_CMD_RESOURCE_FLUSH,
43 VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
44 VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
45 VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
46 VIRTIO_GPU_CMD_GET_CAPSET_INFO,
47 VIRTIO_GPU_CMD_GET_CAPSET,
48 VIRTIO_GPU_CMD_GET_EDID,
49 VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID,
50 VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB,
51 VIRTIO_GPU_CMD_SET_SCANOUT_BLOB,
52
53 /* 3d commands */
54 VIRTIO_GPU_CMD_CTX_CREATE = 0x0200,
55 VIRTIO_GPU_CMD_CTX_DESTROY,
56 VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE,
57 VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE,
58 VIRTIO_GPU_CMD_RESOURCE_CREATE_3D,
59 VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D,
60 VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D,
61 VIRTIO_GPU_CMD_SUBMIT_3D,
62 VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB,
63 VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB,
64
65 /* cursor commands */
66 VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
67 VIRTIO_GPU_CMD_MOVE_CURSOR,
68
69 /* success responses */
70 VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
71 VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
72 VIRTIO_GPU_RESP_OK_CAPSET_INFO,
73 VIRTIO_GPU_RESP_OK_CAPSET,
74 VIRTIO_GPU_RESP_OK_EDID,
75 VIRTIO_GPU_RESP_OK_RESOURCE_UUID,
76 VIRTIO_GPU_RESP_OK_MAP_INFO,
77
78 /* error responses */
79 VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
80 VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
81 VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
82 VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
83 VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
84 VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
85 };
86
virtio_gpu_ctrl_name(uint32_t type)87 static const char* virtio_gpu_ctrl_name(uint32_t type) {
88 switch (type) {
89 #define ENUM(n) \
90 case VIRTIO_GPU_CMD_##n: \
91 return #n
92 /* 2d commands */
93 ENUM(GET_DISPLAY_INFO);
94 ENUM(RESOURCE_CREATE_2D);
95 ENUM(RESOURCE_UNREF);
96 ENUM(SET_SCANOUT);
97 ENUM(RESOURCE_FLUSH);
98 ENUM(TRANSFER_TO_HOST_2D);
99 ENUM(RESOURCE_ATTACH_BACKING);
100 ENUM(RESOURCE_DETACH_BACKING);
101 ENUM(GET_CAPSET_INFO);
102 ENUM(GET_CAPSET);
103 ENUM(GET_EDID);
104 ENUM(RESOURCE_ASSIGN_UUID);
105 ENUM(RESOURCE_CREATE_BLOB);
106 ENUM(SET_SCANOUT_BLOB);
107
108 /* 3d commands */
109 ENUM(CTX_CREATE);
110 ENUM(CTX_DESTROY);
111 ENUM(CTX_ATTACH_RESOURCE);
112 ENUM(CTX_DETACH_RESOURCE);
113 ENUM(RESOURCE_CREATE_3D);
114 ENUM(TRANSFER_TO_HOST_3D);
115 ENUM(TRANSFER_FROM_HOST_3D);
116 ENUM(SUBMIT_3D);
117 ENUM(RESOURCE_MAP_BLOB);
118 ENUM(RESOURCE_UNMAP_BLOB);
119
120 /* cursor commands */
121 ENUM(UPDATE_CURSOR);
122 ENUM(MOVE_CURSOR);
123 #undef ENUM
124 default:
125 return "";
126 }
127 }
128
129 namespace perfetto::trace_processor {
130
131 namespace {
132
133 constexpr auto kVirtgpuNameDimension =
134 tracks::StringDimensionBlueprint("virtgpu_name");
135
136 }
137
VirtioGpuTracker(TraceProcessorContext * context)138 VirtioGpuTracker::VirtioGpuTracker(TraceProcessorContext* context)
139 : virtgpu_control_queue_(context, "Control"),
140 virtgpu_cursor_queue_(context, "Cursor") {}
141
ParseVirtioGpu(int64_t timestamp,uint32_t field_id,uint32_t pid,protozero::ConstBytes blob)142 void VirtioGpuTracker::ParseVirtioGpu(int64_t timestamp,
143 uint32_t field_id,
144 uint32_t pid,
145 protozero::ConstBytes blob) {
146 using protos::pbzero::FtraceEvent;
147
148 switch (field_id) {
149 case FtraceEvent::kVirtioGpuCmdQueueFieldNumber: {
150 ParseVirtioGpuCmdQueue(timestamp, pid, blob);
151 break;
152 }
153 case FtraceEvent::kVirtioGpuCmdResponseFieldNumber: {
154 ParseVirtioGpuCmdResponse(timestamp, pid, blob);
155 break;
156 }
157 default:
158 PERFETTO_DFATAL("Unexpected field id");
159 break;
160 }
161 }
162
VirtioGpuQueue(TraceProcessorContext * context,const char * name)163 VirtioGpuTracker::VirtioGpuQueue::VirtioGpuQueue(TraceProcessorContext* context,
164 const char* name)
165 : context_(context),
166 queue_track_id_(context->storage->InternString(
167 base::StackString<255>("Virtgpu %s Queue", name).string_view())),
168 name_(name) {}
169
HandleNumFree(int64_t timestamp,uint32_t num_free)170 void VirtioGpuTracker::VirtioGpuQueue::HandleNumFree(int64_t timestamp,
171 uint32_t num_free) {
172 static constexpr auto kBlueprint = tracks::CounterBlueprint(
173 "virtgpu_num_free", tracks::UnknownUnitBlueprint(),
174 tracks::DimensionBlueprints(kVirtgpuNameDimension),
175 tracks::FnNameBlueprint([](base::StringView name) {
176 return base::StackString<255>("Virtgpu %.*s Free", int(name.size()),
177 name.data());
178 }));
179
180 TrackId track = context_->track_tracker->InternTrack(
181 kBlueprint, tracks::Dimensions(name_));
182 context_->event_tracker->PushCounter(timestamp, static_cast<double>(num_free),
183 track);
184 }
185
HandleCmdQueue(int64_t timestamp,uint32_t seqno,uint32_t type,uint64_t fence_id)186 void VirtioGpuTracker::VirtioGpuQueue::HandleCmdQueue(int64_t timestamp,
187 uint32_t seqno,
188 uint32_t type,
189 uint64_t fence_id) {
190 auto async_track =
191 context_->async_track_set_tracker->InternGlobalTrackSet(queue_track_id_);
192 TrackId start_id =
193 context_->async_track_set_tracker->Begin(async_track, seqno);
194
195 context_->slice_tracker->Begin(
196 timestamp, start_id, kNullStringId,
197 context_->storage->InternString(
198 base::StringView(virtio_gpu_ctrl_name(type))));
199
200 /* cmds with a fence do not necessarily get an immediate response from
201 * the host, so we should not use them for calculating latency:
202 */
203 if (!fence_id) {
204 start_timestamps_[seqno] = timestamp;
205 }
206 }
207
HandleCmdResponse(int64_t timestamp,uint32_t seqno)208 void VirtioGpuTracker::VirtioGpuQueue::HandleCmdResponse(int64_t timestamp,
209 uint32_t seqno) {
210 auto async_track =
211 context_->async_track_set_tracker->InternGlobalTrackSet(queue_track_id_);
212 TrackId end_id = context_->async_track_set_tracker->End(async_track, seqno);
213 context_->slice_tracker->End(timestamp, end_id);
214
215 int64_t* start_timestamp = start_timestamps_.Find(seqno);
216 if (!start_timestamp) {
217 return;
218 }
219
220 int64_t duration = timestamp - *start_timestamp;
221
222 static constexpr auto kBlueprint = tracks::CounterBlueprint(
223 "virtgpu_latency", tracks::UnknownUnitBlueprint(),
224 tracks::DimensionBlueprints(kVirtgpuNameDimension),
225 tracks::FnNameBlueprint([](base::StringView name) {
226 return base::StackString<255>("Virtgpu %.*s Latency", int(name.size()),
227 name.data());
228 }));
229
230 TrackId track = context_->track_tracker->InternTrack(
231 kBlueprint, tracks::Dimensions(name_));
232 context_->event_tracker->PushCounter(timestamp, static_cast<double>(duration),
233 track);
234 start_timestamps_.Erase(seqno);
235 }
236
ParseVirtioGpuCmdQueue(int64_t timestamp,uint32_t,protozero::ConstBytes blob)237 void VirtioGpuTracker::ParseVirtioGpuCmdQueue(int64_t timestamp,
238 uint32_t /*pid*/,
239 protozero::ConstBytes blob) {
240 protos::pbzero::VirtioGpuCmdQueueFtraceEvent::Decoder evt(blob.data,
241 blob.size);
242
243 auto name = base::StringView(evt.name());
244 if (name == "control") {
245 virtgpu_control_queue_.HandleNumFree(timestamp, evt.num_free());
246 virtgpu_control_queue_.HandleCmdQueue(timestamp, evt.seqno(), evt.type(),
247 evt.fence_id());
248 } else if (name == "cursor") {
249 virtgpu_cursor_queue_.HandleNumFree(timestamp, evt.num_free());
250 virtgpu_cursor_queue_.HandleCmdQueue(timestamp, evt.seqno(), evt.type(),
251 evt.fence_id());
252 }
253 }
254
ParseVirtioGpuCmdResponse(int64_t timestamp,uint32_t,protozero::ConstBytes blob)255 void VirtioGpuTracker::ParseVirtioGpuCmdResponse(int64_t timestamp,
256 uint32_t /*pid*/,
257 protozero::ConstBytes blob) {
258 protos::pbzero::VirtioGpuCmdResponseFtraceEvent::Decoder evt(blob.data,
259 blob.size);
260 auto name = base::StringView(evt.name());
261 if (name == "control") {
262 virtgpu_control_queue_.HandleNumFree(timestamp, evt.num_free());
263 virtgpu_control_queue_.HandleCmdResponse(timestamp, evt.seqno());
264 } else if (name == "cursor") {
265 virtgpu_cursor_queue_.HandleNumFree(timestamp, evt.num_free());
266 virtgpu_cursor_queue_.HandleCmdResponse(timestamp, evt.seqno());
267 }
268 }
269
270 } // namespace perfetto::trace_processor
271