blob: d4d09a2ca33bf0b701ffcdb4c0143c1057fe75c0 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/trace_processor/importers/ftrace/virtio_gpu_tracker.h"
#include "perfetto/ext/base/string_utils.h"
#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
#include "protos/perfetto/trace/ftrace/virtio_gpu.pbzero.h"
#include "src/trace_processor/importers/common/async_track_set_tracker.h"
#include "src/trace_processor/importers/common/event_tracker.h"
#include "src/trace_processor/importers/common/slice_tracker.h"
#include "src/trace_processor/importers/common/track_tracker.h"
enum virtio_gpu_ctrl_type {
VIRTIO_GPU_UNDEFINED = 0,
/* 2d commands */
VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100,
VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
VIRTIO_GPU_CMD_RESOURCE_UNREF,
VIRTIO_GPU_CMD_SET_SCANOUT,
VIRTIO_GPU_CMD_RESOURCE_FLUSH,
VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
VIRTIO_GPU_CMD_GET_CAPSET_INFO,
VIRTIO_GPU_CMD_GET_CAPSET,
VIRTIO_GPU_CMD_GET_EDID,
VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID,
VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB,
VIRTIO_GPU_CMD_SET_SCANOUT_BLOB,
/* 3d commands */
VIRTIO_GPU_CMD_CTX_CREATE = 0x0200,
VIRTIO_GPU_CMD_CTX_DESTROY,
VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE,
VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE,
VIRTIO_GPU_CMD_RESOURCE_CREATE_3D,
VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D,
VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D,
VIRTIO_GPU_CMD_SUBMIT_3D,
VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB,
VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB,
/* cursor commands */
VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
VIRTIO_GPU_CMD_MOVE_CURSOR,
/* success responses */
VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
VIRTIO_GPU_RESP_OK_CAPSET_INFO,
VIRTIO_GPU_RESP_OK_CAPSET,
VIRTIO_GPU_RESP_OK_EDID,
VIRTIO_GPU_RESP_OK_RESOURCE_UUID,
VIRTIO_GPU_RESP_OK_MAP_INFO,
/* error responses */
VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
};
static const char* virtio_gpu_ctrl_name(uint32_t type) {
switch (type) {
#define ENUM(n) \
case VIRTIO_GPU_CMD_##n: \
return #n
/* 2d commands */
ENUM(GET_DISPLAY_INFO);
ENUM(RESOURCE_CREATE_2D);
ENUM(RESOURCE_UNREF);
ENUM(SET_SCANOUT);
ENUM(RESOURCE_FLUSH);
ENUM(TRANSFER_TO_HOST_2D);
ENUM(RESOURCE_ATTACH_BACKING);
ENUM(RESOURCE_DETACH_BACKING);
ENUM(GET_CAPSET_INFO);
ENUM(GET_CAPSET);
ENUM(GET_EDID);
ENUM(RESOURCE_ASSIGN_UUID);
ENUM(RESOURCE_CREATE_BLOB);
ENUM(SET_SCANOUT_BLOB);
/* 3d commands */
ENUM(CTX_CREATE);
ENUM(CTX_DESTROY);
ENUM(CTX_ATTACH_RESOURCE);
ENUM(CTX_DETACH_RESOURCE);
ENUM(RESOURCE_CREATE_3D);
ENUM(TRANSFER_TO_HOST_3D);
ENUM(TRANSFER_FROM_HOST_3D);
ENUM(SUBMIT_3D);
ENUM(RESOURCE_MAP_BLOB);
ENUM(RESOURCE_UNMAP_BLOB);
/* cursor commands */
ENUM(UPDATE_CURSOR);
ENUM(MOVE_CURSOR);
#undef ENUM
default:
return "";
}
}
namespace perfetto {
namespace trace_processor {
VirtioGpuTracker::VirtioGpuTracker(TraceProcessorContext* context)
: virtgpu_control_queue_(context, "Control"),
virtgpu_cursor_queue_(context, "Cursor") {}
void VirtioGpuTracker::ParseVirtioGpu(int64_t timestamp,
int32_t field_id,
uint32_t pid,
protozero::ConstBytes blob) {
using protos::pbzero::FtraceEvent;
switch (field_id) {
case FtraceEvent::kVirtioGpuCmdQueueFieldNumber: {
ParseVirtioGpuCmdQueue(timestamp, pid, blob);
break;
}
case FtraceEvent::kVirtioGpuCmdResponseFieldNumber: {
ParseVirtioGpuCmdResponse(timestamp, pid, blob);
break;
}
default:
PERFETTO_DFATAL("Unexpected field id");
break;
}
}
VirtioGpuTracker::VirtioGpuQueue::VirtioGpuQueue(TraceProcessorContext* context,
const char* name)
: context_(context) {
base::StackString<255> num_free_name("Virtgpu %s Free", name);
base::StackString<255> latency_name("Virtgpu %s Latency", name);
base::StackString<255> queue_track_name("Virtgpu %s Queue", name);
num_free_id_ = context->storage->InternString(num_free_name.string_view());
latency_id_ = context->storage->InternString(latency_name.string_view());
queue_track_id_ =
context->storage->InternString(queue_track_name.string_view());
}
void VirtioGpuTracker::VirtioGpuQueue::HandleNumFree(int64_t timestamp,
uint32_t num_free) {
TrackId track =
context_->track_tracker->InternGlobalCounterTrack(num_free_id_);
context_->event_tracker->PushCounter(timestamp, static_cast<double>(num_free),
track);
}
void VirtioGpuTracker::VirtioGpuQueue::HandleCmdQueue(int64_t timestamp,
uint32_t seqno,
uint32_t type,
uint64_t fence_id) {
auto async_track =
context_->async_track_set_tracker->InternGlobalTrackSet(queue_track_id_);
TrackId start_id =
context_->async_track_set_tracker->Begin(async_track, seqno);
context_->slice_tracker->Begin(
timestamp, start_id, kNullStringId,
context_->storage->InternString(
base::StringView(virtio_gpu_ctrl_name(type))));
/* cmds with a fence do not necessarily get an immediate response from
* the host, so we should not use them for calculating latency:
*/
if (!fence_id) {
start_timestamps_[seqno] = timestamp;
}
}
void VirtioGpuTracker::VirtioGpuQueue::HandleCmdResponse(int64_t timestamp,
uint32_t seqno) {
auto async_track =
context_->async_track_set_tracker->InternGlobalTrackSet(queue_track_id_);
TrackId end_id = context_->async_track_set_tracker->End(async_track, seqno);
context_->slice_tracker->End(timestamp, end_id);
int64_t* start_timestamp = start_timestamps_.Find(seqno);
if (!start_timestamp)
return;
int64_t duration = timestamp - *start_timestamp;
TrackId track =
context_->track_tracker->InternGlobalCounterTrack(latency_id_);
context_->event_tracker->PushCounter(timestamp,
static_cast<double>(duration), track);
start_timestamps_.Erase(seqno);
}
void VirtioGpuTracker::ParseVirtioGpuCmdQueue(int64_t timestamp,
uint32_t /*pid*/,
protozero::ConstBytes blob) {
protos::pbzero::VirtioGpuCmdQueueFtraceEvent::Decoder evt(blob.data,
blob.size);
auto name = base::StringView(evt.name());
if (name == "control") {
virtgpu_control_queue_.HandleNumFree(timestamp, evt.num_free());
virtgpu_control_queue_.HandleCmdQueue(timestamp, evt.seqno(), evt.type(),
evt.fence_id());
} else if (name == "cursor") {
virtgpu_cursor_queue_.HandleNumFree(timestamp, evt.num_free());
virtgpu_cursor_queue_.HandleCmdQueue(timestamp, evt.seqno(), evt.type(),
evt.fence_id());
}
}
void VirtioGpuTracker::ParseVirtioGpuCmdResponse(int64_t timestamp,
uint32_t /*pid*/,
protozero::ConstBytes blob) {
protos::pbzero::VirtioGpuCmdResponseFtraceEvent::Decoder evt(blob.data,
blob.size);
auto name = base::StringView(evt.name());
if (name == "control") {
virtgpu_control_queue_.HandleNumFree(timestamp, evt.num_free());
virtgpu_control_queue_.HandleCmdResponse(timestamp, evt.seqno());
} else if (name == "cursor") {
virtgpu_cursor_queue_.HandleNumFree(timestamp, evt.num_free());
virtgpu_cursor_queue_.HandleCmdResponse(timestamp, evt.seqno());
}
}
} // namespace trace_processor
} // namespace perfetto