blob: 3ab5deee0958c5db8c3fc19c3f518a2f2cb2f5ec [file] [log] [blame]
/*
* Copyright (C) 2019 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/graphics_event_parser.h"
#include "perfetto/protozero/field.h"
#include "src/trace_processor/args_tracker.h"
#include "src/trace_processor/event_tracker.h"
#include "src/trace_processor/process_tracker.h"
#include "src/trace_processor/slice_tracker.h"
#include "src/trace_processor/trace_processor_context.h"
#include "src/trace_processor/track_tracker.h"
#include "src/trace_processor/vulkan_memory_tracker.h"
#include "protos/perfetto/common/gpu_counter_descriptor.pbzero.h"
#include "protos/perfetto/trace/android/graphics_frame_event.pbzero.h"
#include "protos/perfetto/trace/gpu/gpu_counter_event.pbzero.h"
#include "protos/perfetto/trace/gpu/gpu_log.pbzero.h"
#include "protos/perfetto/trace/gpu/gpu_render_stage_event.pbzero.h"
#include "protos/perfetto/trace/gpu/vulkan_memory_event.pbzero.h"
namespace perfetto {
namespace trace_processor {
GraphicsEventParser::~GraphicsEventParser() = default;
GraphicsEventParser::GraphicsEventParser(TraceProcessorContext* context)
: context_(context),
gpu_render_stage_scope_id_(
context->storage->InternString("gpu_render_stage")),
graphics_event_scope_id_(
context->storage->InternString("graphics_frame_event")),
unknown_event_name_id_(context->storage->InternString("unknown_event")),
no_layer_name_name_id_(context->storage->InternString("no_layer_name")),
layer_name_key_id_(context->storage->InternString("layer_name")),
event_type_name_ids_{
{context->storage->InternString(
"unspecified_event") /* UNSPECIFIED */,
context->storage->InternString("Dequeue") /* DEQUEUE */,
context->storage->InternString("Queue") /* QUEUE */,
context->storage->InternString("Post") /* POST */,
context->storage->InternString(
"AcquireFenceSignaled") /* ACQUIRE_FENCE */,
context->storage->InternString("Latch") /* LATCH */,
context->storage->InternString(
"HWCCompositionQueued") /* HWC_COMPOSITION_QUEUED */,
context->storage->InternString(
"FallbackComposition") /* FALLBACK_COMPOSITION */,
context->storage->InternString(
"PresentFenceSignaled") /* PRESENT_FENCE */,
context->storage->InternString(
"ReleaseFenceSignaled") /* RELEASE_FENCE */,
context->storage->InternString("Modify") /* MODIFY */}},
vulkan_allocated_host_memory_id_(
context->storage->InternString("vulkan.host.memory")),
vulkan_allocated_gpu_memory_id_(
context->storage->InternString("vulkan.gpu.memory")),
vulkan_live_image_objects_id_(
context->storage->InternString("vulkan.gpu.images")),
vulkan_live_buffer_objects_id_(
context->storage->InternString("vulkan.gpu.buffers")),
vulkan_bound_image_objects_id_(
context->storage->InternString("vulkan.gpu.bound_images")),
vulkan_bound_buffer_objects_id_(
context->storage->InternString("vulkan.gpu.bound_buffers")),
vulkan_allocated_host_memory_(0),
vulkan_allocated_gpu_memory_(0),
vulkan_live_image_objects_(0),
vulkan_live_buffer_objects_(0),
vulkan_bound_image_objects_(0),
vulkan_bound_buffer_objects_(0),
gpu_log_track_name_id_(context_->storage->InternString("GPU Log")),
gpu_log_scope_id_(context_->storage->InternString("gpu_log")),
tag_id_(context_->storage->InternString("tag")),
log_message_id_(context->storage->InternString("message")),
log_severity_ids_{{context_->storage->InternString("UNSPECIFIED"),
context_->storage->InternString("VERBOSE"),
context_->storage->InternString("DEBUG"),
context_->storage->InternString("INFO"),
context_->storage->InternString("WARNING"),
context_->storage->InternString("ERROR"),
context_->storage->InternString(
"UNKNOWN_SEVERITY") /* must be last */}} {}
void GraphicsEventParser::ParseGpuCounterEvent(int64_t ts, ConstBytes blob) {
protos::pbzero::GpuCounterEvent::Decoder event(blob.data, blob.size);
protos::pbzero::GpuCounterDescriptor::Decoder descriptor(
event.counter_descriptor());
// Add counter spec to ID map.
for (auto it = descriptor.specs(); it; ++it) {
protos::pbzero::GpuCounterDescriptor_GpuCounterSpec::Decoder spec(*it);
if (!spec.has_counter_id()) {
PERFETTO_ELOG("Counter spec missing counter id");
context_->storage->IncrementStats(stats::gpu_counters_invalid_spec);
continue;
}
if (!spec.has_name()) {
context_->storage->IncrementStats(stats::gpu_counters_invalid_spec);
continue;
}
auto counter_id = spec.counter_id();
auto name = spec.name();
if (gpu_counter_ids_.find(counter_id) == gpu_counter_ids_.end()) {
auto desc = spec.description();
StringId unit_id = 0;
if (spec.has_numerator_units() || spec.has_denominator_units()) {
char buffer[1024];
base::StringWriter unit(buffer, sizeof(buffer));
for (auto numer = spec.numerator_units(); numer; ++numer) {
if (unit.pos()) {
unit.AppendChar(':');
}
unit.AppendInt(*numer);
}
char sep = '/';
for (auto denom = spec.denominator_units(); denom; ++denom) {
unit.AppendChar(sep);
unit.AppendInt(*denom);
sep = ':';
}
unit_id = context_->storage->InternString(unit.GetStringView());
}
auto name_id = context_->storage->InternString(name);
auto desc_id = context_->storage->InternString(desc);
auto* definitions = context_->storage->mutable_counter_definitions();
auto defn_id = definitions->AddCounterDefinition(
name_id, 0, RefType::kRefGpuId, desc_id, unit_id);
gpu_counter_ids_.emplace(counter_id, defn_id);
} else {
// Either counter spec was repeated or it came after counter data.
PERFETTO_ELOG("Duplicated counter spec found. (counter_id=%d, name=%s)",
counter_id, name.ToStdString().c_str());
context_->storage->IncrementStats(stats::gpu_counters_invalid_spec);
}
}
for (auto it = event.counters(); it; ++it) {
protos::pbzero::GpuCounterEvent_GpuCounter::Decoder counter(*it);
if (counter.has_counter_id() &&
(counter.has_int_value() || counter.has_double_value())) {
auto counter_id = counter.counter_id();
// Check missing counter_id
if (gpu_counter_ids_.find(counter_id) == gpu_counter_ids_.end()) {
char buffer[64];
base::StringWriter writer(buffer, sizeof(buffer));
writer.AppendString("gpu_counter(");
writer.AppendUnsignedInt(counter_id);
writer.AppendString(")");
auto name_id = context_->storage->InternString(writer.GetStringView());
auto* definitions = context_->storage->mutable_counter_definitions();
auto defn_id =
definitions->AddCounterDefinition(name_id, 0, RefType::kRefGpuId);
gpu_counter_ids_.emplace(counter_id, defn_id);
context_->storage->IncrementStats(stats::gpu_counters_missing_spec);
}
if (counter.has_int_value()) {
context_->event_tracker->PushCounter(ts, counter.int_value(),
gpu_counter_ids_[counter_id]);
} else {
context_->event_tracker->PushCounter(ts, counter.double_value(),
gpu_counter_ids_[counter_id]);
}
}
}
}
void GraphicsEventParser::ParseGpuRenderStageEvent(int64_t ts,
ConstBytes blob) {
protos::pbzero::GpuRenderStageEvent::Decoder event(blob.data, blob.size);
if (event.has_specifications()) {
protos::pbzero::GpuRenderStageEvent_Specifications::Decoder spec(
event.specifications().data, event.specifications().size);
for (auto it = spec.hw_queue(); it; ++it) {
protos::pbzero::GpuRenderStageEvent_Specifications_Description::Decoder
hw_queue(*it);
if (hw_queue.has_name()) {
StringId track_name = context_->storage->InternString(hw_queue.name());
tables::GpuTrackTable::Row track(track_name.id);
track.scope = gpu_render_stage_scope_id_;
gpu_hw_queue_ids_.emplace_back(
context_->track_tracker->InternGpuTrack(track));
}
}
for (auto it = spec.stage(); it; ++it) {
protos::pbzero::GpuRenderStageEvent_Specifications_Description::Decoder
stage(*it);
if (stage.has_name()) {
gpu_render_stage_ids_.emplace_back(
context_->storage->InternString(stage.name()));
}
}
}
auto args_callback = [this, &event](ArgsTracker* args_tracker, RowId row_id) {
for (auto it = event.extra_data(); it; ++it) {
protos::pbzero::GpuRenderStageEvent_ExtraData_Decoder datum(*it);
StringId name_id = context_->storage->InternString(datum.name());
StringId value = context_->storage->InternString(
datum.has_value() ? datum.value() : base::StringView());
args_tracker->AddArg(row_id, name_id, name_id, Variadic::String(value));
}
};
if (event.has_event_id()) {
size_t stage_id = static_cast<size_t>(event.stage_id());
StringId stage_name;
if (stage_id < gpu_render_stage_ids_.size()) {
stage_name = gpu_render_stage_ids_[stage_id];
} else {
char buffer[64];
snprintf(buffer, 64, "render stage(%zu)", stage_id);
stage_name = context_->storage->InternString(buffer);
}
TrackId track_id =
gpu_hw_queue_ids_[static_cast<size_t>(event.hw_queue_id())];
const auto slice_id = context_->slice_tracker->Scoped(
ts, track_id, track_id, RefType::kRefTrack, 0 /* cat */, stage_name,
static_cast<int64_t>(event.duration()), args_callback);
context_->storage->mutable_gpu_slice_table()->Insert(
tables::GpuSliceTable::Row(
slice_id.value(), static_cast<int64_t>(event.context()),
static_cast<int64_t>(event.render_target_handle()),
base::nullopt /*frame_id*/, event.submission_id(),
static_cast<uint32_t>(event.hw_queue_id())));
}
}
void GraphicsEventParser::ParseGraphicsFrameEvent(int64_t timestamp,
ConstBytes blob) {
protos::pbzero::GraphicsFrameEvent_Decoder frame_event(blob.data, blob.size);
if (!frame_event.has_buffer_event()) {
return;
}
ConstBytes bufferBlob = frame_event.buffer_event();
protos::pbzero::GraphicsFrameEvent_BufferEvent_Decoder event(bufferBlob.data,
bufferBlob.size);
if (!event.has_buffer_id()) {
context_->storage->IncrementStats(
stats::graphics_frame_event_parser_errors);
PERFETTO_ELOG("GraphicsFrameEvent with missing buffer id field.");
return;
}
StringId event_name_id = unknown_event_name_id_;
if (event.has_type()) {
const auto type = static_cast<size_t>(event.type());
if (type < event_type_name_ids_.size()) {
event_name_id = event_type_name_ids_[type];
} else {
context_->storage->IncrementStats(
stats::graphics_frame_event_parser_errors);
PERFETTO_ELOG("GraphicsFrameEvent with unknown type %zu.", type);
}
} else {
context_->storage->IncrementStats(
stats::graphics_frame_event_parser_errors);
PERFETTO_ELOG("GraphicsFrameEvent with missing type field.");
}
const uint32_t buffer_id = event.buffer_id();
StringId layer_name_id;
char buffer[4096];
const size_t layerNameMaxLength = 4000;
base::StringWriter track_name(buffer, sizeof(buffer));
if (event.has_layer_name()) {
const base::StringView layer_name(event.layer_name());
layer_name_id = context_->storage->InternString(layer_name);
track_name.AppendString(layer_name.substr(0, layerNameMaxLength));
} else {
layer_name_id = no_layer_name_name_id_;
track_name.AppendLiteral("unknown_layer");
}
track_name.AppendLiteral("[buffer:");
track_name.AppendUnsignedInt(buffer_id);
track_name.AppendChar(']');
const StringId track_name_id =
context_->storage->InternString(track_name.GetStringView());
const int64_t duration =
event.has_duration_ns() ? static_cast<int64_t>(event.duration_ns()) : 0;
const uint32_t frame_number =
event.has_frame_number() ? event.frame_number() : 0;
tables::GpuTrackTable::Row track(track_name_id.id);
track.scope = graphics_event_scope_id_;
TrackId track_id = context_->track_tracker->InternGpuTrack(track);
const auto slice_id = context_->slice_tracker->Scoped(
timestamp, track_id, track_id, RefType::kRefTrack, 0 /* cat */,
event_name_id, duration,
[this, layer_name_id](ArgsTracker* args_tracker, RowId row_id) {
args_tracker->AddArg(row_id, layer_name_key_id_, layer_name_key_id_,
Variadic::String(layer_name_id));
});
if (slice_id) {
tables::GpuSliceTable::Row row;
row.slice_id = slice_id.value();
row.frame_id = frame_number;
context_->storage->mutable_gpu_slice_table()->Insert(row);
}
}
void GraphicsEventParser::UpdateVulkanMemoryAllocationCounters(
const tables::VulkanMemoryAllocationsTable::Row* row) {
auto ts = row->timestamp;
if (row->source_iid == protos::pbzero::VulkanMemoryEvent_Source_HOST) {
if (row->type_iid == protos::pbzero::VulkanMemoryEvent_Type_CREATE) {
vulkan_allocated_host_memory_ += row->memory_size.value();
} else if (row->type_iid ==
protos::pbzero::VulkanMemoryEvent_Type_DESTROY) {
vulkan_allocated_host_memory_ -= row->memory_size.value();
}
context_->event_tracker->PushCounter(ts, vulkan_allocated_host_memory_,
vulkan_allocated_host_memory_id_, 0,
RefType::kRefNoRef);
} else if (row->source_iid ==
protos::pbzero::VulkanMemoryEvent_Source_GPU_DEVICE_MEMORY) {
if (row->type_iid == protos::pbzero::VulkanMemoryEvent_Type_CREATE) {
vulkan_allocated_gpu_memory_ += row->memory_size.value();
} else if (row->type_iid ==
protos::pbzero::VulkanMemoryEvent_Type_DESTROY) {
vulkan_allocated_gpu_memory_ -= row->memory_size.value();
}
context_->event_tracker->PushCounter(ts, vulkan_allocated_gpu_memory_,
vulkan_allocated_gpu_memory_id_, 0,
RefType::kRefNoRef);
} else if (row->source_iid ==
protos::pbzero::VulkanMemoryEvent_Source_GPU_BUFFER) {
if (row->type_iid == protos::pbzero::VulkanMemoryEvent_Type_CREATE) {
vulkan_live_buffer_objects_ += 1;
context_->event_tracker->PushCounter(ts, vulkan_live_buffer_objects_,
vulkan_live_buffer_objects_id_, 0,
RefType::kRefNoRef);
} else if (row->type_iid ==
protos::pbzero::VulkanMemoryEvent_Type_DESTROY) {
vulkan_live_buffer_objects_ -= 1;
context_->event_tracker->PushCounter(ts, vulkan_live_buffer_objects_,
vulkan_live_buffer_objects_id_, 0,
RefType::kRefNoRef);
} else if (row->type_iid == protos::pbzero::VulkanMemoryEvent_Type_BIND) {
vulkan_bound_buffer_objects_ += 1;
context_->event_tracker->PushCounter(ts, vulkan_bound_buffer_objects_,
vulkan_bound_buffer_objects_id_, 0,
RefType::kRefNoRef);
} else if (row->type_iid ==
protos::pbzero::VulkanMemoryEvent_Type_DESTROY_BOUND) {
vulkan_bound_buffer_objects_ -= 1;
context_->event_tracker->PushCounter(ts, vulkan_bound_buffer_objects_,
vulkan_bound_buffer_objects_id_, 0,
RefType::kRefNoRef);
}
} else if (row->source_iid ==
protos::pbzero::VulkanMemoryEvent_Source_GPU_IMAGE) {
if (row->type_iid == protos::pbzero::VulkanMemoryEvent_Type_CREATE) {
vulkan_live_image_objects_ += 1;
context_->event_tracker->PushCounter(ts, vulkan_live_image_objects_,
vulkan_live_image_objects_id_, 0,
RefType::kRefNoRef);
} else if (row->type_iid ==
protos::pbzero::VulkanMemoryEvent_Type_DESTROY) {
vulkan_live_image_objects_ -= 1;
context_->event_tracker->PushCounter(ts, vulkan_live_image_objects_,
vulkan_live_image_objects_id_, 0,
RefType::kRefNoRef);
} else if (row->type_iid == protos::pbzero::VulkanMemoryEvent_Type_BIND) {
vulkan_bound_image_objects_ += 1;
context_->event_tracker->PushCounter(ts, vulkan_bound_image_objects_,
vulkan_bound_image_objects_id_, 0,
RefType::kRefNoRef);
} else if (row->type_iid ==
protos::pbzero::VulkanMemoryEvent_Type_DESTROY_BOUND) {
vulkan_bound_image_objects_ -= 1;
context_->event_tracker->PushCounter(ts, vulkan_bound_image_objects_,
vulkan_bound_image_objects_id_, 0,
RefType::kRefNoRef);
}
}
}
void GraphicsEventParser::ParseVulkanMemoryEvent(ConstBytes blob) {
protos::pbzero::VulkanMemoryEvent::Decoder vulkan_memory_event(blob.data,
blob.size);
tables::VulkanMemoryAllocationsTable::Row vulkan_memory_event_row;
if (vulkan_memory_event.has_source()) {
vulkan_memory_event_row.source_iid =
*(context_->vulkan_memory_tracker->FindSourceString(
static_cast<uint64_t>(vulkan_memory_event.source())));
}
if (vulkan_memory_event.has_type()) {
vulkan_memory_event_row.type_iid =
*(context_->vulkan_memory_tracker->FindTypeString(
static_cast<uint64_t>(vulkan_memory_event.type())));
}
if (vulkan_memory_event.has_timestamp())
vulkan_memory_event_row.timestamp = vulkan_memory_event.timestamp();
if (vulkan_memory_event.has_pid()) {
vulkan_memory_event_row.upid =
context_->process_tracker->GetOrCreateProcess(
vulkan_memory_event.pid());
}
if (vulkan_memory_event.has_device())
vulkan_memory_event_row.device =
static_cast<int64_t>(vulkan_memory_event.device());
if (vulkan_memory_event.has_device_memory())
vulkan_memory_event_row.device_memory =
static_cast<int64_t>(vulkan_memory_event.device_memory());
if (vulkan_memory_event.has_heap())
vulkan_memory_event_row.heap = vulkan_memory_event.heap();
if (vulkan_memory_event.has_caller_iid()) {
vulkan_memory_event_row.caller_iid =
*(context_->vulkan_memory_tracker->FindString(
static_cast<uint64_t>(vulkan_memory_event.caller_iid())));
}
if (vulkan_memory_event.has_object_handle())
vulkan_memory_event_row.object_handle =
static_cast<int64_t>(vulkan_memory_event.object_handle());
if (vulkan_memory_event.has_memory_address())
vulkan_memory_event_row.memory_address =
static_cast<int64_t>(vulkan_memory_event.memory_address());
if (vulkan_memory_event.has_memory_size())
vulkan_memory_event_row.memory_size =
static_cast<int64_t>(vulkan_memory_event.memory_size());
UpdateVulkanMemoryAllocationCounters(&vulkan_memory_event_row);
auto row_id =
context_->storage->mutable_vulkan_memory_allocations_table()->Insert(
vulkan_memory_event_row);
if (vulkan_memory_event.has_annotations()) {
auto global_row_id =
TraceStorage::CreateRowId(TableId::kVulkanMemoryAllocation, row_id);
for (auto itt = vulkan_memory_event.annotations(); itt; ++itt) {
protos::pbzero::VulkanMemoryEventAnnotation::Decoder annotation(*itt);
auto annotation_id =
*(context_->vulkan_memory_tracker->FindString(annotation.key_iid()));
if (annotation.has_int_value()) {
context_->args_tracker->AddArg(
global_row_id, annotation_id, annotation_id,
Variadic::Integer(annotation.int_value()));
} else if (annotation.has_double_value()) {
context_->args_tracker->AddArg(
global_row_id, annotation_id, annotation_id,
Variadic::Real(annotation.double_value()));
} else if (annotation.has_string_iid()) {
context_->args_tracker->AddArg(
global_row_id, annotation_id, annotation_id,
Variadic::String(*(context_->vulkan_memory_tracker->FindString(
annotation.string_iid()))));
}
}
}
}
void GraphicsEventParser::ParseGpuLog(int64_t ts, ConstBytes blob) {
protos::pbzero::GpuLog::Decoder event(blob.data, blob.size);
tables::GpuTrackTable::Row track(gpu_log_track_name_id_.id);
track.scope = gpu_log_scope_id_;
TrackId track_id = context_->track_tracker->InternGpuTrack(track);
auto args_callback = [this, &event](ArgsTracker* args_tracker, RowId row_id) {
if (event.has_tag()) {
args_tracker->AddArg(
row_id, tag_id_, tag_id_,
Variadic::String(context_->storage->InternString(event.tag())));
}
if (event.has_log_message()) {
args_tracker->AddArg(row_id, log_message_id_, log_message_id_,
Variadic::String(context_->storage->InternString(
event.log_message())));
}
};
auto severity = static_cast<size_t>(event.severity());
StringId severity_id =
severity < log_severity_ids_.size()
? log_severity_ids_[static_cast<size_t>(event.severity())]
: log_severity_ids_[log_severity_ids_.size() - 1];
const auto slice_id = context_->slice_tracker->Scoped(
ts, track_id, track_id, RefType::kRefTrack, 0 /* cat */, severity_id,
0 /* duration */, args_callback);
tables::GpuSliceTable::Row row;
row.slice_id = slice_id.value();
context_->storage->mutable_gpu_slice_table()->Insert(row);
}
} // namespace trace_processor
} // namespace perfetto