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