| /* |
| * 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/importers/proto/gpu_event_parser.h" |
| |
| #include <cinttypes> |
| |
| #include "perfetto/ext/base/utils.h" |
| #include "perfetto/protozero/field.h" |
| #include "src/trace_processor/importers/common/args_tracker.h" |
| #include "src/trace_processor/importers/common/event_tracker.h" |
| #include "src/trace_processor/importers/common/process_tracker.h" |
| #include "src/trace_processor/importers/common/slice_tracker.h" |
| #include "src/trace_processor/importers/common/track_tracker.h" |
| #include "src/trace_processor/storage/trace_storage.h" |
| #include "src/trace_processor/types/trace_processor_context.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_api_event.pbzero.h" |
| #include "protos/perfetto/trace/gpu/vulkan_memory_event.pbzero.h" |
| #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h" |
| |
| namespace perfetto { |
| namespace trace_processor { |
| |
| namespace { |
| |
| // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkObjectType.html |
| typedef enum VkObjectType { |
| VK_OBJECT_TYPE_UNKNOWN = 0, |
| VK_OBJECT_TYPE_INSTANCE = 1, |
| VK_OBJECT_TYPE_PHYSICAL_DEVICE = 2, |
| VK_OBJECT_TYPE_DEVICE = 3, |
| VK_OBJECT_TYPE_QUEUE = 4, |
| VK_OBJECT_TYPE_SEMAPHORE = 5, |
| VK_OBJECT_TYPE_COMMAND_BUFFER = 6, |
| VK_OBJECT_TYPE_FENCE = 7, |
| VK_OBJECT_TYPE_DEVICE_MEMORY = 8, |
| VK_OBJECT_TYPE_BUFFER = 9, |
| VK_OBJECT_TYPE_IMAGE = 10, |
| VK_OBJECT_TYPE_EVENT = 11, |
| VK_OBJECT_TYPE_QUERY_POOL = 12, |
| VK_OBJECT_TYPE_BUFFER_VIEW = 13, |
| VK_OBJECT_TYPE_IMAGE_VIEW = 14, |
| VK_OBJECT_TYPE_SHADER_MODULE = 15, |
| VK_OBJECT_TYPE_PIPELINE_CACHE = 16, |
| VK_OBJECT_TYPE_PIPELINE_LAYOUT = 17, |
| VK_OBJECT_TYPE_RENDER_PASS = 18, |
| VK_OBJECT_TYPE_PIPELINE = 19, |
| VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT = 20, |
| VK_OBJECT_TYPE_SAMPLER = 21, |
| VK_OBJECT_TYPE_DESCRIPTOR_POOL = 22, |
| VK_OBJECT_TYPE_DESCRIPTOR_SET = 23, |
| VK_OBJECT_TYPE_FRAMEBUFFER = 24, |
| VK_OBJECT_TYPE_COMMAND_POOL = 25, |
| VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION = 1000156000, |
| VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE = 1000085000, |
| VK_OBJECT_TYPE_SURFACE_KHR = 1000000000, |
| VK_OBJECT_TYPE_SWAPCHAIN_KHR = 1000001000, |
| VK_OBJECT_TYPE_DISPLAY_KHR = 1000002000, |
| VK_OBJECT_TYPE_DISPLAY_MODE_KHR = 1000002001, |
| VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT = 1000011000, |
| VK_OBJECT_TYPE_OBJECT_TABLE_NVX = 1000086000, |
| VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX = 1000086001, |
| VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000128000, |
| VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000, |
| VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV = 1000165000, |
| VK_OBJECT_TYPE_PERFORMANCE_CONFIGURATION_INTEL = 1000210000, |
| VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR = |
| VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE, |
| VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR = |
| VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION, |
| VK_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF |
| } VkObjectType; |
| |
| } // anonymous namespace |
| |
| GpuEventParser::GpuEventParser(TraceProcessorContext* context) |
| : context_(context), |
| vulkan_memory_tracker_(context), |
| description_id_(context->storage->InternString("description")), |
| gpu_render_stage_scope_id_( |
| context->storage->InternString("gpu_render_stage")), |
| 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 */}}, |
| vk_event_track_id_(context->storage->InternString("Vulkan Events")), |
| vk_event_scope_id_(context->storage->InternString("vulkan_events")), |
| vk_queue_submit_id_(context->storage->InternString("vkQueueSubmit")), |
| gpu_mem_total_name_id_(context->storage->InternString("GPU Memory")), |
| gpu_mem_total_unit_id_(context->storage->InternString( |
| std::to_string(protos::pbzero::GpuCounterDescriptor::BYTE).c_str())), |
| gpu_mem_total_global_desc_id_(context->storage->InternString( |
| "Total GPU memory used by the entire system")), |
| gpu_mem_total_proc_desc_id_(context->storage->InternString( |
| "Total GPU memory used by this process")) {} |
| |
| void GpuEventParser::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_track_ids_.find(counter_id) == |
| gpu_counter_track_ids_.end()) { |
| auto desc = spec.description(); |
| |
| StringId unit_id = kNullStringId; |
| 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 track_id = context_->track_tracker->CreateGpuCounterTrack( |
| name_id, 0 /* gpu_id */, desc_id, unit_id); |
| gpu_counter_track_ids_.emplace(counter_id, track_id); |
| if (spec.has_groups()) { |
| for (auto group = spec.groups(); group; ++group) { |
| tables::GpuCounterGroupTable::Row row; |
| row.group_id = *group; |
| row.track_id = track_id; |
| context_->storage->mutable_gpu_counter_group_table()->Insert(row); |
| } |
| } else { |
| tables::GpuCounterGroupTable::Row row; |
| row.group_id = protos::pbzero::GpuCounterDescriptor::UNCLASSIFIED; |
| row.track_id = track_id; |
| context_->storage->mutable_gpu_counter_group_table()->Insert(row); |
| } |
| } 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_track_ids_.find(counter_id) == |
| gpu_counter_track_ids_.end()) { |
| continue; |
| } |
| double counter_val = counter.has_int_value() |
| ? static_cast<double>(counter.int_value()) |
| : counter.double_value(); |
| context_->event_tracker->PushCounter(ts, counter_val, |
| gpu_counter_track_ids_[counter_id]); |
| } |
| } |
| } |
| |
| const StringId GpuEventParser::GetFullStageName( |
| PacketSequenceStateGeneration* sequence_state, |
| const protos::pbzero::GpuRenderStageEvent_Decoder& event) const { |
| StringId stage_name; |
| if (event.has_stage_iid()) { |
| auto stage_iid = event.stage_iid(); |
| auto* decoder = sequence_state->LookupInternedMessage< |
| protos::pbzero::InternedData::kGpuSpecificationsFieldNumber, |
| protos::pbzero::InternedGpuRenderStageSpecification>(stage_iid); |
| if (!decoder) { |
| return kNullStringId; |
| } |
| stage_name = context_->storage->InternString(decoder->name()); |
| } else { |
| uint64_t stage_id = static_cast<uint64_t>(event.stage_id()); |
| |
| if (stage_id < gpu_render_stage_ids_.size()) { |
| stage_name = gpu_render_stage_ids_[static_cast<size_t>(stage_id)].first; |
| } else { |
| base::StackString<64> name("render stage(%" PRIu64 ")", stage_id); |
| stage_name = context_->storage->InternString(name.string_view()); |
| } |
| } |
| return stage_name; |
| } |
| |
| /** |
| * Create a GPU render stage track based |
| * GpuRenderStageEvent.Specifications.Description. |
| */ |
| void GpuEventParser::InsertGpuTrack( |
| const protos::pbzero:: |
| GpuRenderStageEvent_Specifications_Description_Decoder& hw_queue) { |
| StringId track_name = context_->storage->InternString(hw_queue.name()); |
| if (gpu_hw_queue_counter_ >= gpu_hw_queue_ids_.size() || |
| !gpu_hw_queue_ids_[gpu_hw_queue_counter_].has_value()) { |
| tables::GpuTrackTable::Row track(track_name); |
| track.scope = gpu_render_stage_scope_id_; |
| track.description = context_->storage->InternString(hw_queue.description()); |
| if (gpu_hw_queue_counter_ >= gpu_hw_queue_ids_.size()) { |
| gpu_hw_queue_ids_.emplace_back( |
| context_->track_tracker->InternGpuTrack(track)); |
| } else { |
| // If a gpu_render_stage_event is received before the specification, it is |
| // possible that the slot has already been allocated. |
| gpu_hw_queue_ids_[gpu_hw_queue_counter_] = |
| context_->track_tracker->InternGpuTrack(track); |
| } |
| } else { |
| // If a gpu_render_stage_event is received before the specification, a track |
| // will be automatically generated. In that case, update the name and |
| // description. |
| auto track_id = gpu_hw_queue_ids_[gpu_hw_queue_counter_]; |
| if (track_id.has_value()) { |
| auto row = context_->storage->mutable_gpu_track_table() |
| ->id() |
| .IndexOf(track_id.value()) |
| .value(); |
| context_->storage->mutable_gpu_track_table()->mutable_name()->Set( |
| row, track_name); |
| context_->storage->mutable_gpu_track_table()->mutable_description()->Set( |
| row, context_->storage->InternString(hw_queue.description())); |
| } else { |
| tables::GpuTrackTable::Row track(track_name); |
| track.scope = gpu_render_stage_scope_id_; |
| track.description = |
| context_->storage->InternString(hw_queue.description()); |
| } |
| } |
| ++gpu_hw_queue_counter_; |
| } |
| std::optional<std::string> GpuEventParser::FindDebugName( |
| int32_t vk_object_type, |
| uint64_t vk_handle) const { |
| auto map = debug_marker_names_.find(vk_object_type); |
| if (map == debug_marker_names_.end()) { |
| return std::nullopt; |
| } |
| |
| auto name = map->second.find(vk_handle); |
| if (name == map->second.end()) { |
| return std::nullopt; |
| } else { |
| return name->second; |
| } |
| } |
| |
| const StringId GpuEventParser::ParseRenderSubpasses( |
| const protos::pbzero::GpuRenderStageEvent_Decoder& event) const { |
| if (!event.has_render_subpass_index_mask()) { |
| return kNullStringId; |
| } |
| char buf[256]; |
| base::StringWriter writer(buf, sizeof(buf)); |
| uint32_t bit_index = 0; |
| bool first = true; |
| for (auto it = event.render_subpass_index_mask(); it; ++it) { |
| auto subpasses_bits = *it; |
| do { |
| if ((subpasses_bits & 1) != 0) { |
| if (!first) { |
| writer.AppendChar(','); |
| } |
| first = false; |
| writer.AppendUnsignedInt(bit_index); |
| } |
| subpasses_bits >>= 1; |
| ++bit_index; |
| } while (subpasses_bits != 0); |
| // Round up to the next multiple of 64. |
| bit_index = ((bit_index - 1) / 64 + 1) * 64; |
| } |
| return context_->storage->InternString(writer.GetStringView()); |
| } |
| |
| void GpuEventParser::ParseGpuRenderStageEvent( |
| int64_t ts, |
| PacketSequenceStateGeneration* sequence_state, |
| 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()) { |
| InsertGpuTrack(hw_queue); |
| } |
| } |
| 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(std::make_pair( |
| context_->storage->InternString(stage.name()), |
| context_->storage->InternString(stage.description()))); |
| } |
| } |
| } |
| |
| auto args_callback = [this, &event, |
| sequence_state](ArgsTracker::BoundInserter* inserter) { |
| if (event.has_stage_iid()) { |
| size_t stage_iid = static_cast<size_t>(event.stage_iid()); |
| auto* decoder = sequence_state->LookupInternedMessage< |
| protos::pbzero::InternedData::kGpuSpecificationsFieldNumber, |
| protos::pbzero::InternedGpuRenderStageSpecification>(stage_iid); |
| if (decoder) { |
| // TODO: Add RenderStageCategory to gpu_slice table. |
| inserter->AddArg(description_id_, |
| Variadic::String(context_->storage->InternString( |
| decoder->description()))); |
| } |
| } else if (event.has_stage_id()) { |
| size_t stage_id = static_cast<size_t>(event.stage_id()); |
| if (stage_id < gpu_render_stage_ids_.size()) { |
| auto description = gpu_render_stage_ids_[stage_id].second; |
| if (description != kNullStringId) { |
| inserter->AddArg(description_id_, Variadic::String(description)); |
| } |
| } |
| } |
| 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()); |
| inserter->AddArg(name_id, Variadic::String(value)); |
| } |
| }; |
| |
| if (event.has_event_id()) { |
| TrackId track_id; |
| uint64_t hw_queue_id = 0; |
| if (event.has_hw_queue_iid()) { |
| hw_queue_id = event.hw_queue_iid(); |
| auto* decoder = sequence_state->LookupInternedMessage< |
| protos::pbzero::InternedData::kGpuSpecificationsFieldNumber, |
| protos::pbzero::InternedGpuRenderStageSpecification>(hw_queue_id); |
| if (!decoder) { |
| // Skip |
| return; |
| } |
| // TODO: Add RenderStageCategory to gpu_track table. |
| tables::GpuTrackTable::Row track( |
| context_->storage->InternString(decoder->name())); |
| track.scope = gpu_render_stage_scope_id_; |
| track.description = |
| context_->storage->InternString(decoder->description()); |
| track_id = context_->track_tracker->InternGpuTrack(track); |
| } else { |
| uint32_t id = static_cast<uint32_t>(event.hw_queue_id()); |
| if (id < gpu_hw_queue_ids_.size() && gpu_hw_queue_ids_[id].has_value()) { |
| track_id = gpu_hw_queue_ids_[id].value(); |
| } else { |
| // If the event has a hw_queue_id that does not have a Specification, |
| // create a new track for it. |
| char buf[128]; |
| base::StringWriter writer(buf, sizeof(buf)); |
| writer.AppendLiteral("Unknown GPU Queue "); |
| if (id > 1024) { |
| // We don't expect this to happen, but just in case there is a corrupt |
| // packet, make sure we don't allocate a ridiculous amount of memory. |
| id = 1024; |
| context_->storage->IncrementStats( |
| stats::gpu_render_stage_parser_errors); |
| PERFETTO_ELOG("Invalid hw_queue_id."); |
| } else { |
| writer.AppendInt(event.hw_queue_id()); |
| } |
| StringId track_name = |
| context_->storage->InternString(writer.GetStringView()); |
| tables::GpuTrackTable::Row track(track_name); |
| track.scope = gpu_render_stage_scope_id_; |
| track_id = context_->track_tracker->InternGpuTrack(track); |
| gpu_hw_queue_ids_.resize(id + 1); |
| gpu_hw_queue_ids_[id] = track_id; |
| } |
| hw_queue_id = id; |
| } |
| |
| auto render_target_name = |
| FindDebugName(VK_OBJECT_TYPE_FRAMEBUFFER, event.render_target_handle()); |
| auto render_target_name_id = render_target_name.has_value() |
| ? context_->storage->InternString( |
| render_target_name.value().c_str()) |
| : kNullStringId; |
| auto render_pass_name = |
| FindDebugName(VK_OBJECT_TYPE_RENDER_PASS, event.render_pass_handle()); |
| auto render_pass_name_id = |
| render_pass_name.has_value() |
| ? context_->storage->InternString(render_pass_name.value().c_str()) |
| : kNullStringId; |
| auto command_buffer_name = FindDebugName(VK_OBJECT_TYPE_COMMAND_BUFFER, |
| event.command_buffer_handle()); |
| auto command_buffer_name_id = command_buffer_name.has_value() |
| ? context_->storage->InternString( |
| command_buffer_name.value().c_str()) |
| : kNullStringId; |
| |
| tables::GpuSliceTable::Row row; |
| row.ts = ts; |
| row.track_id = track_id; |
| row.name = GetFullStageName(sequence_state, event); |
| row.dur = static_cast<int64_t>(event.duration()); |
| // TODO: Create table for graphics context and lookup |
| // InternedGraphicsContext. |
| row.context_id = static_cast<int64_t>(event.context()); |
| row.render_target = static_cast<int64_t>(event.render_target_handle()); |
| row.render_target_name = render_target_name_id; |
| row.render_pass = static_cast<int64_t>(event.render_pass_handle()); |
| row.render_pass_name = render_pass_name_id; |
| row.render_subpasses = ParseRenderSubpasses(event); |
| row.command_buffer = static_cast<int64_t>(event.command_buffer_handle()); |
| row.command_buffer_name = command_buffer_name_id; |
| row.submission_id = event.submission_id(); |
| row.hw_queue_id = static_cast<int64_t>(hw_queue_id); |
| |
| context_->slice_tracker->ScopedTyped( |
| context_->storage->mutable_gpu_slice_table(), row, args_callback); |
| } |
| } |
| |
| void GpuEventParser::UpdateVulkanMemoryAllocationCounters( |
| UniquePid upid, |
| const VulkanMemoryEvent::Decoder& event) { |
| StringId track_str_id = kNullStringId; |
| TrackId track = kInvalidTrackId; |
| auto allocation_scope = VulkanMemoryEvent::SCOPE_UNSPECIFIED; |
| uint32_t memory_type = std::numeric_limits<uint32_t>::max(); |
| switch (event.source()) { |
| case VulkanMemoryEvent::SOURCE_DRIVER: |
| allocation_scope = static_cast<VulkanMemoryEvent::AllocationScope>( |
| event.allocation_scope()); |
| if (allocation_scope == VulkanMemoryEvent::SCOPE_UNSPECIFIED) |
| return; |
| switch (event.operation()) { |
| case VulkanMemoryEvent::OP_CREATE: |
| vulkan_driver_memory_counters_[allocation_scope] += |
| event.memory_size(); |
| break; |
| case VulkanMemoryEvent::OP_DESTROY: |
| vulkan_driver_memory_counters_[allocation_scope] -= |
| event.memory_size(); |
| break; |
| case VulkanMemoryEvent::OP_UNSPECIFIED: |
| case VulkanMemoryEvent::OP_BIND: |
| case VulkanMemoryEvent::OP_DESTROY_BOUND: |
| case VulkanMemoryEvent::OP_ANNOTATIONS: |
| return; |
| } |
| track_str_id = vulkan_memory_tracker_.FindAllocationScopeCounterString( |
| allocation_scope); |
| track = context_->track_tracker->InternProcessCounterTrack(track_str_id, |
| upid); |
| context_->event_tracker->PushCounter( |
| event.timestamp(), |
| static_cast<double>(vulkan_driver_memory_counters_[allocation_scope]), |
| track); |
| break; |
| |
| case VulkanMemoryEvent::SOURCE_DEVICE_MEMORY: |
| memory_type = static_cast<uint32_t>(event.memory_type()); |
| switch (event.operation()) { |
| case VulkanMemoryEvent::OP_CREATE: |
| vulkan_device_memory_counters_allocate_[memory_type] += |
| event.memory_size(); |
| break; |
| case VulkanMemoryEvent::OP_DESTROY: |
| vulkan_device_memory_counters_allocate_[memory_type] -= |
| event.memory_size(); |
| break; |
| case VulkanMemoryEvent::OP_UNSPECIFIED: |
| case VulkanMemoryEvent::OP_BIND: |
| case VulkanMemoryEvent::OP_DESTROY_BOUND: |
| case VulkanMemoryEvent::OP_ANNOTATIONS: |
| return; |
| } |
| track_str_id = vulkan_memory_tracker_.FindMemoryTypeCounterString( |
| memory_type, |
| VulkanMemoryTracker::DeviceCounterType::kAllocationCounter); |
| track = context_->track_tracker->InternProcessCounterTrack(track_str_id, |
| upid); |
| context_->event_tracker->PushCounter( |
| event.timestamp(), |
| static_cast<double>( |
| vulkan_device_memory_counters_allocate_[memory_type]), |
| track); |
| break; |
| |
| case VulkanMemoryEvent::SOURCE_BUFFER: |
| case VulkanMemoryEvent::SOURCE_IMAGE: |
| memory_type = static_cast<uint32_t>(event.memory_type()); |
| switch (event.operation()) { |
| case VulkanMemoryEvent::OP_BIND: |
| vulkan_device_memory_counters_bind_[memory_type] += |
| event.memory_size(); |
| break; |
| case VulkanMemoryEvent::OP_DESTROY_BOUND: |
| vulkan_device_memory_counters_bind_[memory_type] -= |
| event.memory_size(); |
| break; |
| case VulkanMemoryEvent::OP_UNSPECIFIED: |
| case VulkanMemoryEvent::OP_CREATE: |
| case VulkanMemoryEvent::OP_DESTROY: |
| case VulkanMemoryEvent::OP_ANNOTATIONS: |
| return; |
| } |
| track_str_id = vulkan_memory_tracker_.FindMemoryTypeCounterString( |
| memory_type, VulkanMemoryTracker::DeviceCounterType::kBindCounter); |
| track = context_->track_tracker->InternProcessCounterTrack(track_str_id, |
| upid); |
| context_->event_tracker->PushCounter( |
| event.timestamp(), |
| static_cast<double>(vulkan_device_memory_counters_bind_[memory_type]), |
| track); |
| break; |
| case VulkanMemoryEvent::SOURCE_UNSPECIFIED: |
| case VulkanMemoryEvent::SOURCE_DEVICE: |
| return; |
| } |
| } |
| |
| void GpuEventParser::ParseVulkanMemoryEvent( |
| PacketSequenceStateGeneration* sequence_state, |
| ConstBytes blob) { |
| using protos::pbzero::InternedData; |
| VulkanMemoryEvent::Decoder vulkan_memory_event(blob.data, blob.size); |
| tables::VulkanMemoryAllocationsTable::Row vulkan_memory_event_row; |
| vulkan_memory_event_row.source = vulkan_memory_tracker_.FindSourceString( |
| static_cast<VulkanMemoryEvent::Source>(vulkan_memory_event.source())); |
| vulkan_memory_event_row.operation = |
| vulkan_memory_tracker_.FindOperationString( |
| static_cast<VulkanMemoryEvent::Operation>( |
| vulkan_memory_event.operation())); |
| vulkan_memory_event_row.timestamp = vulkan_memory_event.timestamp(); |
| 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_memory_type()) |
| vulkan_memory_event_row.memory_type = vulkan_memory_event.memory_type(); |
| if (vulkan_memory_event.has_caller_iid()) { |
| vulkan_memory_event_row.function_name = |
| vulkan_memory_tracker_ |
| .GetInternedString<InternedData::kFunctionNamesFieldNumber>( |
| sequence_state, |
| 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()); |
| if (vulkan_memory_event.has_allocation_scope()) |
| vulkan_memory_event_row.scope = |
| vulkan_memory_tracker_.FindAllocationScopeString( |
| static_cast<VulkanMemoryEvent::AllocationScope>( |
| vulkan_memory_event.allocation_scope())); |
| |
| UpdateVulkanMemoryAllocationCounters(vulkan_memory_event_row.upid.value(), |
| vulkan_memory_event); |
| |
| auto* allocs = context_->storage->mutable_vulkan_memory_allocations_table(); |
| VulkanAllocId id = allocs->Insert(vulkan_memory_event_row).id; |
| |
| if (vulkan_memory_event.has_annotations()) { |
| auto inserter = context_->args_tracker->AddArgsTo(id); |
| |
| for (auto it = vulkan_memory_event.annotations(); it; ++it) { |
| protos::pbzero::VulkanMemoryEventAnnotation::Decoder annotation(*it); |
| |
| auto key_id = |
| vulkan_memory_tracker_ |
| .GetInternedString<InternedData::kVulkanMemoryKeysFieldNumber>( |
| sequence_state, static_cast<uint64_t>(annotation.key_iid())); |
| |
| if (annotation.has_int_value()) { |
| inserter.AddArg(key_id, Variadic::Integer(annotation.int_value())); |
| } else if (annotation.has_double_value()) { |
| inserter.AddArg(key_id, Variadic::Real(annotation.double_value())); |
| } else if (annotation.has_string_iid()) { |
| auto string_id = |
| vulkan_memory_tracker_ |
| .GetInternedString<InternedData::kVulkanMemoryKeysFieldNumber>( |
| sequence_state, |
| static_cast<uint64_t>(annotation.string_iid())); |
| |
| inserter.AddArg(key_id, Variadic::String(string_id)); |
| } |
| } |
| } |
| } |
| |
| void GpuEventParser::ParseGpuLog(int64_t ts, ConstBytes blob) { |
| protos::pbzero::GpuLog::Decoder event(blob.data, blob.size); |
| |
| tables::GpuTrackTable::Row track(gpu_log_track_name_id_); |
| track.scope = gpu_log_scope_id_; |
| TrackId track_id = context_->track_tracker->InternGpuTrack(track); |
| |
| auto args_callback = [this, &event](ArgsTracker::BoundInserter* inserter) { |
| if (event.has_tag()) { |
| inserter->AddArg( |
| tag_id_, |
| Variadic::String(context_->storage->InternString(event.tag()))); |
| } |
| if (event.has_log_message()) { |
| inserter->AddArg(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]; |
| |
| tables::GpuSliceTable::Row row; |
| row.ts = ts; |
| row.track_id = track_id; |
| row.name = severity_id; |
| row.dur = 0; |
| context_->slice_tracker->ScopedTyped( |
| context_->storage->mutable_gpu_slice_table(), row, args_callback); |
| } |
| |
| void GpuEventParser::ParseVulkanApiEvent(int64_t ts, ConstBytes blob) { |
| protos::pbzero::VulkanApiEvent::Decoder vk_event(blob.data, blob.size); |
| if (vk_event.has_vk_debug_utils_object_name()) { |
| protos::pbzero::VulkanApiEvent_VkDebugUtilsObjectName::Decoder event( |
| vk_event.vk_debug_utils_object_name()); |
| debug_marker_names_[event.object_type()][event.object()] = |
| event.object_name().ToStdString(); |
| } |
| if (vk_event.has_vk_queue_submit()) { |
| protos::pbzero::VulkanApiEvent_VkQueueSubmit::Decoder event( |
| vk_event.vk_queue_submit()); |
| // Once flow table is implemented, we can create a nice UI that link the |
| // vkQueueSubmit to GpuRenderStageEvent. For now, just add it as in a GPU |
| // track so that they can appear close to the render stage slices. |
| tables::GpuTrackTable::Row track(vk_event_track_id_); |
| track.scope = vk_event_scope_id_; |
| TrackId track_id = context_->track_tracker->InternGpuTrack(track); |
| tables::GpuSliceTable::Row row; |
| row.ts = ts; |
| row.dur = static_cast<int64_t>(event.duration_ns()); |
| row.track_id = track_id; |
| row.name = vk_queue_submit_id_; |
| if (event.has_vk_command_buffers()) { |
| row.command_buffer = static_cast<int64_t>(*event.vk_command_buffers()); |
| } |
| row.submission_id = event.submission_id(); |
| auto args_callback = [this, &event](ArgsTracker::BoundInserter* inserter) { |
| inserter->AddArg(context_->storage->InternString("pid"), |
| Variadic::Integer(event.pid())); |
| inserter->AddArg(context_->storage->InternString("tid"), |
| Variadic::Integer(event.tid())); |
| }; |
| context_->slice_tracker->ScopedTyped( |
| context_->storage->mutable_gpu_slice_table(), row, args_callback); |
| } |
| } |
| |
| void GpuEventParser::ParseGpuMemTotalEvent(int64_t ts, ConstBytes blob) { |
| protos::pbzero::GpuMemTotalEvent::Decoder gpu_mem_total(blob.data, blob.size); |
| |
| TrackId track = kInvalidTrackId; |
| const uint32_t pid = gpu_mem_total.pid(); |
| if (pid == 0) { |
| // Pid 0 is used to indicate the global total |
| track = context_->track_tracker->InternGlobalCounterTrack( |
| gpu_mem_total_name_id_, {}, gpu_mem_total_unit_id_, |
| gpu_mem_total_global_desc_id_); |
| } else { |
| // Process emitting the packet can be different from the pid in the event. |
| UniqueTid utid = context_->process_tracker->UpdateThread(pid, pid); |
| UniquePid upid = context_->storage->thread_table().upid()[utid].value_or(0); |
| track = context_->track_tracker->InternProcessCounterTrack( |
| gpu_mem_total_name_id_, upid, gpu_mem_total_unit_id_, |
| gpu_mem_total_proc_desc_id_); |
| } |
| context_->event_tracker->PushCounter( |
| ts, static_cast<double>(gpu_mem_total.size()), track); |
| } |
| |
| } // namespace trace_processor |
| } // namespace perfetto |