• 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 #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