xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/ftrace/virtio_gpu_tracker.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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