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/event_tracker.h"
28 #include "src/trace_processor/importers/common/slice_tracker.h"
29 #include "src/trace_processor/importers/common/track_compressor.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 constexpr auto kQueueBlueprint = TrackCompressor::SliceBlueprint(
137 "virtgpu_queue_event",
138 tracks::Dimensions(kVirtgpuNameDimension),
__anon16d485120202(base::StringView name) 139 tracks::FnNameBlueprint([](base::StringView name) {
140 return base::StackString<255>("Virtgpu %.*s Queue", int(name.size()),
141 name.data());
142 }));
143
144 } // namespace
145
VirtioGpuTracker(TraceProcessorContext * context)146 VirtioGpuTracker::VirtioGpuTracker(TraceProcessorContext* context)
147 : virtgpu_control_queue_(context, "Control"),
148 virtgpu_cursor_queue_(context, "Cursor") {}
149
ParseVirtioGpu(int64_t timestamp,uint32_t field_id,uint32_t pid,protozero::ConstBytes blob)150 void VirtioGpuTracker::ParseVirtioGpu(int64_t timestamp,
151 uint32_t field_id,
152 uint32_t pid,
153 protozero::ConstBytes blob) {
154 using protos::pbzero::FtraceEvent;
155
156 switch (field_id) {
157 case FtraceEvent::kVirtioGpuCmdQueueFieldNumber: {
158 ParseVirtioGpuCmdQueue(timestamp, pid, blob);
159 break;
160 }
161 case FtraceEvent::kVirtioGpuCmdResponseFieldNumber: {
162 ParseVirtioGpuCmdResponse(timestamp, pid, blob);
163 break;
164 }
165 default:
166 PERFETTO_DFATAL("Unexpected field id");
167 break;
168 }
169 }
170
VirtioGpuQueue(TraceProcessorContext * context,const char * name)171 VirtioGpuTracker::VirtioGpuQueue::VirtioGpuQueue(TraceProcessorContext* context,
172 const char* name)
173 : context_(context), name_(name) {}
174
HandleNumFree(int64_t timestamp,uint32_t num_free)175 void VirtioGpuTracker::VirtioGpuQueue::HandleNumFree(int64_t timestamp,
176 uint32_t num_free) {
177 static constexpr auto kBlueprint = tracks::CounterBlueprint(
178 "virtgpu_num_free", tracks::UnknownUnitBlueprint(),
179 tracks::DimensionBlueprints(kVirtgpuNameDimension),
180 tracks::FnNameBlueprint([](base::StringView name) {
181 return base::StackString<255>("Virtgpu %.*s Free", int(name.size()),
182 name.data());
183 }));
184
185 TrackId track = context_->track_tracker->InternTrack(
186 kBlueprint, tracks::Dimensions(name_));
187 context_->event_tracker->PushCounter(timestamp, static_cast<double>(num_free),
188 track);
189 }
190
HandleCmdQueue(int64_t timestamp,uint32_t seqno,uint32_t type,uint64_t fence_id)191 void VirtioGpuTracker::VirtioGpuQueue::HandleCmdQueue(int64_t timestamp,
192 uint32_t seqno,
193 uint32_t type,
194 uint64_t fence_id) {
195 TrackId start_id = context_->track_compressor->InternBegin(
196 kQueueBlueprint, tracks::Dimensions(name_), seqno);
197 context_->slice_tracker->Begin(
198 timestamp, start_id, kNullStringId,
199 context_->storage->InternString(
200 base::StringView(virtio_gpu_ctrl_name(type))));
201
202 /* cmds with a fence do not necessarily get an immediate response from
203 * the host, so we should not use them for calculating latency:
204 */
205 if (!fence_id) {
206 start_timestamps_[seqno] = timestamp;
207 }
208 }
209
HandleCmdResponse(int64_t timestamp,uint32_t seqno)210 void VirtioGpuTracker::VirtioGpuQueue::HandleCmdResponse(int64_t timestamp,
211 uint32_t seqno) {
212 TrackId end_id = context_->track_compressor->InternEnd(
213 kQueueBlueprint, tracks::Dimensions(name_), seqno);
214 context_->slice_tracker->End(timestamp, end_id);
215
216 int64_t* start_timestamp = start_timestamps_.Find(seqno);
217 if (!start_timestamp) {
218 return;
219 }
220
221 int64_t duration = timestamp - *start_timestamp;
222
223 static constexpr auto kBlueprint = tracks::CounterBlueprint(
224 "virtgpu_latency", tracks::UnknownUnitBlueprint(),
225 tracks::DimensionBlueprints(kVirtgpuNameDimension),
226 tracks::FnNameBlueprint([](base::StringView name) {
227 return base::StackString<255>("Virtgpu %.*s Latency", int(name.size()),
228 name.data());
229 }));
230
231 TrackId track = context_->track_tracker->InternTrack(
232 kBlueprint, tracks::Dimensions(name_));
233 context_->event_tracker->PushCounter(timestamp, static_cast<double>(duration),
234 track);
235 start_timestamps_.Erase(seqno);
236 }
237
ParseVirtioGpuCmdQueue(int64_t timestamp,uint32_t,protozero::ConstBytes blob)238 void VirtioGpuTracker::ParseVirtioGpuCmdQueue(int64_t timestamp,
239 uint32_t /*pid*/,
240 protozero::ConstBytes blob) {
241 protos::pbzero::VirtioGpuCmdQueueFtraceEvent::Decoder evt(blob);
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);
259 auto name = base::StringView(evt.name());
260 if (name == "control") {
261 virtgpu_control_queue_.HandleNumFree(timestamp, evt.num_free());
262 virtgpu_control_queue_.HandleCmdResponse(timestamp, evt.seqno());
263 } else if (name == "cursor") {
264 virtgpu_cursor_queue_.HandleNumFree(timestamp, evt.num_free());
265 virtgpu_cursor_queue_.HandleCmdResponse(timestamp, evt.seqno());
266 }
267 }
268
269 } // namespace perfetto::trace_processor
270