• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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