1 /*
2 * Copyright (C) 2019 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/proto/gpu_event_parser.h"
18
19 #include <array>
20 #include <cinttypes>
21 #include <cstddef>
22 #include <cstdint>
23 #include <limits>
24 #include <optional>
25 #include <string>
26 #include <vector>
27
28 #include "perfetto/base/logging.h"
29 #include "perfetto/ext/base/string_utils.h"
30 #include "perfetto/ext/base/string_view.h"
31 #include "perfetto/ext/base/string_writer.h"
32 #include "perfetto/protozero/field.h"
33 #include "protos/perfetto/trace/android/gpu_mem_event.pbzero.h"
34 #include "src/trace_processor/importers/common/args_tracker.h"
35 #include "src/trace_processor/importers/common/event_tracker.h"
36 #include "src/trace_processor/importers/common/process_tracker.h"
37 #include "src/trace_processor/importers/common/slice_tracker.h"
38 #include "src/trace_processor/importers/common/track_tracker.h"
39 #include "src/trace_processor/importers/common/tracks.h"
40 #include "src/trace_processor/importers/common/tracks_common.h"
41 #include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
42 #include "src/trace_processor/importers/proto/vulkan_memory_tracker.h"
43 #include "src/trace_processor/storage/stats.h"
44 #include "src/trace_processor/storage/trace_storage.h"
45 #include "src/trace_processor/tables/profiler_tables_py.h"
46 #include "src/trace_processor/tables/slice_tables_py.h"
47 #include "src/trace_processor/tables/track_tables_py.h"
48 #include "src/trace_processor/types/trace_processor_context.h"
49 #include "src/trace_processor/types/variadic.h"
50
51 #include "protos/perfetto/common/gpu_counter_descriptor.pbzero.h"
52 #include "protos/perfetto/trace/gpu/gpu_counter_event.pbzero.h"
53 #include "protos/perfetto/trace/gpu/gpu_log.pbzero.h"
54 #include "protos/perfetto/trace/gpu/gpu_render_stage_event.pbzero.h"
55 #include "protos/perfetto/trace/gpu/vulkan_api_event.pbzero.h"
56 #include "protos/perfetto/trace/gpu/vulkan_memory_event.pbzero.h"
57 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
58
59 namespace perfetto::trace_processor {
60
61 namespace {
62
63 // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkObjectType.html
64 enum VkObjectType {
65 VK_OBJECT_TYPE_UNKNOWN = 0,
66 VK_OBJECT_TYPE_INSTANCE = 1,
67 VK_OBJECT_TYPE_PHYSICAL_DEVICE = 2,
68 VK_OBJECT_TYPE_DEVICE = 3,
69 VK_OBJECT_TYPE_QUEUE = 4,
70 VK_OBJECT_TYPE_SEMAPHORE = 5,
71 VK_OBJECT_TYPE_COMMAND_BUFFER = 6,
72 VK_OBJECT_TYPE_FENCE = 7,
73 VK_OBJECT_TYPE_DEVICE_MEMORY = 8,
74 VK_OBJECT_TYPE_BUFFER = 9,
75 VK_OBJECT_TYPE_IMAGE = 10,
76 VK_OBJECT_TYPE_EVENT = 11,
77 VK_OBJECT_TYPE_QUERY_POOL = 12,
78 VK_OBJECT_TYPE_BUFFER_VIEW = 13,
79 VK_OBJECT_TYPE_IMAGE_VIEW = 14,
80 VK_OBJECT_TYPE_SHADER_MODULE = 15,
81 VK_OBJECT_TYPE_PIPELINE_CACHE = 16,
82 VK_OBJECT_TYPE_PIPELINE_LAYOUT = 17,
83 VK_OBJECT_TYPE_RENDER_PASS = 18,
84 VK_OBJECT_TYPE_PIPELINE = 19,
85 VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT = 20,
86 VK_OBJECT_TYPE_SAMPLER = 21,
87 VK_OBJECT_TYPE_DESCRIPTOR_POOL = 22,
88 VK_OBJECT_TYPE_DESCRIPTOR_SET = 23,
89 VK_OBJECT_TYPE_FRAMEBUFFER = 24,
90 VK_OBJECT_TYPE_COMMAND_POOL = 25,
91 VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION = 1000156000,
92 VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE = 1000085000,
93 VK_OBJECT_TYPE_SURFACE_KHR = 1000000000,
94 VK_OBJECT_TYPE_SWAPCHAIN_KHR = 1000001000,
95 VK_OBJECT_TYPE_DISPLAY_KHR = 1000002000,
96 VK_OBJECT_TYPE_DISPLAY_MODE_KHR = 1000002001,
97 VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT = 1000011000,
98 VK_OBJECT_TYPE_OBJECT_TABLE_NVX = 1000086000,
99 VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX = 1000086001,
100 VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000128000,
101 VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000,
102 VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV = 1000165000,
103 VK_OBJECT_TYPE_PERFORMANCE_CONFIGURATION_INTEL = 1000210000,
104 VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR =
105 VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE,
106 VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR =
107 VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION,
108 VK_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF
109 };
110
111 using protos::pbzero::VulkanMemoryEvent;
112
113 } // anonymous namespace
114
GpuEventParser(TraceProcessorContext * context)115 GpuEventParser::GpuEventParser(TraceProcessorContext* context)
116 : context_(context),
117 vulkan_memory_tracker_(context),
118 description_id_(context->storage->InternString("description")),
119 gpu_render_stage_scope_id_(
120 context->storage->InternString("gpu_render_stage")),
121 gpu_log_track_name_id_(context_->storage->InternString("GPU Log")),
122 gpu_log_scope_id_(context_->storage->InternString("gpu_log")),
123 tag_id_(context_->storage->InternString("tag")),
124 log_message_id_(context->storage->InternString("message")),
125 log_severity_ids_{{context_->storage->InternString("UNSPECIFIED"),
126 context_->storage->InternString("VERBOSE"),
127 context_->storage->InternString("DEBUG"),
128 context_->storage->InternString("INFO"),
129 context_->storage->InternString("WARNING"),
130 context_->storage->InternString("ERROR"),
131 context_->storage->InternString(
132 "UNKNOWN_SEVERITY") /* must be last */}},
133 vk_event_track_id_(context->storage->InternString("Vulkan Events")),
134 vk_event_scope_id_(context->storage->InternString("vulkan_events")),
135 vk_queue_submit_id_(context->storage->InternString("vkQueueSubmit")) {}
136
ParseGpuCounterEvent(int64_t ts,ConstBytes blob)137 void GpuEventParser::ParseGpuCounterEvent(int64_t ts, ConstBytes blob) {
138 protos::pbzero::GpuCounterEvent::Decoder event(blob.data, blob.size);
139
140 protos::pbzero::GpuCounterDescriptor::Decoder descriptor(
141 event.counter_descriptor());
142 // Add counter spec to ID map.
143 for (auto it = descriptor.specs(); it; ++it) {
144 protos::pbzero::GpuCounterDescriptor_GpuCounterSpec::Decoder spec(*it);
145 if (!spec.has_counter_id()) {
146 PERFETTO_ELOG("Counter spec missing counter id");
147 context_->storage->IncrementStats(stats::gpu_counters_invalid_spec);
148 continue;
149 }
150 if (!spec.has_name()) {
151 context_->storage->IncrementStats(stats::gpu_counters_invalid_spec);
152 continue;
153 }
154
155 auto counter_id = spec.counter_id();
156 auto name = spec.name();
157 if (gpu_counter_track_ids_.find(counter_id) ==
158 gpu_counter_track_ids_.end()) {
159 auto desc = spec.description();
160
161 StringId unit_id = kNullStringId;
162 if (spec.has_numerator_units() || spec.has_denominator_units()) {
163 char buffer[1024];
164 base::StringWriter unit(buffer, sizeof(buffer));
165 for (auto numer = spec.numerator_units(); numer; ++numer) {
166 if (unit.pos()) {
167 unit.AppendChar(':');
168 }
169 unit.AppendInt(*numer);
170 }
171 char sep = '/';
172 for (auto denom = spec.denominator_units(); denom; ++denom) {
173 unit.AppendChar(sep);
174 unit.AppendInt(*denom);
175 sep = ':';
176 }
177 unit_id = context_->storage->InternString(unit.GetStringView());
178 }
179
180 auto name_id = context_->storage->InternString(name);
181 auto desc_id = context_->storage->InternString(desc);
182 auto track_id = context_->track_tracker->InternTrack(
183 tracks::kGpuCounterBlueprint,
184 tracks::Dimensions(0 /* gpu_id */, name),
185 tracks::DynamicName(name_id),
186 [&, this](ArgsTracker::BoundInserter& inserter) {
187 inserter.AddArg(description_id_, Variadic::String(desc_id));
188 },
189 tracks::DynamicUnit(unit_id));
190 gpu_counter_track_ids_.emplace(counter_id, track_id);
191 if (spec.has_groups()) {
192 for (auto group = spec.groups(); group; ++group) {
193 tables::GpuCounterGroupTable::Row row;
194 row.group_id = *group;
195 row.track_id = track_id;
196 context_->storage->mutable_gpu_counter_group_table()->Insert(row);
197 }
198 } else {
199 tables::GpuCounterGroupTable::Row row;
200 row.group_id = protos::pbzero::GpuCounterDescriptor::UNCLASSIFIED;
201 row.track_id = track_id;
202 context_->storage->mutable_gpu_counter_group_table()->Insert(row);
203 }
204 } else {
205 // Either counter spec was repeated or it came after counter data.
206 PERFETTO_ELOG("Duplicated counter spec found. (counter_id=%d, name=%s)",
207 counter_id, name.ToStdString().c_str());
208 context_->storage->IncrementStats(stats::gpu_counters_invalid_spec);
209 }
210 }
211
212 for (auto it = event.counters(); it; ++it) {
213 protos::pbzero::GpuCounterEvent_GpuCounter::Decoder counter(*it);
214 if (counter.has_counter_id() &&
215 (counter.has_int_value() || counter.has_double_value())) {
216 auto counter_id = counter.counter_id();
217 // Check missing counter_id
218 if (gpu_counter_track_ids_.find(counter_id) ==
219 gpu_counter_track_ids_.end()) {
220 continue;
221 }
222 double counter_val = counter.has_int_value()
223 ? static_cast<double>(counter.int_value())
224 : counter.double_value();
225 context_->event_tracker->PushCounter(ts, counter_val,
226 gpu_counter_track_ids_[counter_id]);
227 }
228 }
229 }
230
GetFullStageName(PacketSequenceStateGeneration * sequence_state,const protos::pbzero::GpuRenderStageEvent_Decoder & event) const231 StringId GpuEventParser::GetFullStageName(
232 PacketSequenceStateGeneration* sequence_state,
233 const protos::pbzero::GpuRenderStageEvent_Decoder& event) const {
234 StringId stage_name;
235 if (event.has_stage_iid()) {
236 auto stage_iid = event.stage_iid();
237 auto* decoder = sequence_state->LookupInternedMessage<
238 protos::pbzero::InternedData::kGpuSpecificationsFieldNumber,
239 protos::pbzero::InternedGpuRenderStageSpecification>(stage_iid);
240 if (!decoder) {
241 return kNullStringId;
242 }
243 stage_name = context_->storage->InternString(decoder->name());
244 } else {
245 auto stage_id = static_cast<uint64_t>(event.stage_id());
246 if (stage_id < gpu_render_stage_ids_.size()) {
247 stage_name = gpu_render_stage_ids_[static_cast<size_t>(stage_id)].first;
248 } else {
249 base::StackString<64> name("render stage(%" PRIu64 ")", stage_id);
250 stage_name = context_->storage->InternString(name.string_view());
251 }
252 }
253 return stage_name;
254 }
255
256 /**
257 * Create a GPU render stage track based
258 * GpuRenderStageEvent.Specifications.Description.
259 */
InsertGpuTrack(const protos::pbzero::GpuRenderStageEvent_Specifications_Description_Decoder & hw_queue)260 void GpuEventParser::InsertGpuTrack(
261 const protos::pbzero::
262 GpuRenderStageEvent_Specifications_Description_Decoder& hw_queue) {
263 StringId track_name = context_->storage->InternString(hw_queue.name());
264 if (gpu_hw_queue_counter_ >= gpu_hw_queue_ids_.size() ||
265 !gpu_hw_queue_ids_[gpu_hw_queue_counter_].has_value()) {
266 tables::GpuTrackTable::Row track(track_name);
267 track.scope = gpu_render_stage_scope_id_;
268 track.description = context_->storage->InternString(hw_queue.description());
269 if (gpu_hw_queue_counter_ >= gpu_hw_queue_ids_.size()) {
270 gpu_hw_queue_ids_.emplace_back(
271 context_->track_tracker->LegacyInternGpuTrack(track));
272 } else {
273 // If a gpu_render_stage_event is received before the specification, it is
274 // possible that the slot has already been allocated.
275 gpu_hw_queue_ids_[gpu_hw_queue_counter_] =
276 context_->track_tracker->LegacyInternGpuTrack(track);
277 }
278 } else {
279 // If a gpu_render_stage_event is received before the specification, a track
280 // will be automatically generated. In that case, update the name and
281 // description.
282 auto track_id = gpu_hw_queue_ids_[gpu_hw_queue_counter_];
283 if (track_id.has_value()) {
284 auto rr = *context_->storage->mutable_gpu_track_table()->FindById(
285 track_id.value());
286 rr.set_name(track_name);
287 rr.set_description(
288 context_->storage->InternString(hw_queue.description()));
289 } else {
290 tables::GpuTrackTable::Row track(track_name);
291 track.scope = gpu_render_stage_scope_id_;
292 track.description =
293 context_->storage->InternString(hw_queue.description());
294 }
295 }
296 ++gpu_hw_queue_counter_;
297 }
FindDebugName(int32_t vk_object_type,uint64_t vk_handle) const298 std::optional<std::string> GpuEventParser::FindDebugName(
299 int32_t vk_object_type,
300 uint64_t vk_handle) const {
301 auto map = debug_marker_names_.find(vk_object_type);
302 if (map == debug_marker_names_.end()) {
303 return std::nullopt;
304 }
305
306 auto name = map->second.find(vk_handle);
307 if (name == map->second.end()) {
308 return std::nullopt;
309 }
310 return name->second;
311 }
312
ParseRenderSubpasses(const protos::pbzero::GpuRenderStageEvent_Decoder & event) const313 StringId GpuEventParser::ParseRenderSubpasses(
314 const protos::pbzero::GpuRenderStageEvent_Decoder& event) const {
315 if (!event.has_render_subpass_index_mask()) {
316 return kNullStringId;
317 }
318 char buf[256];
319 base::StringWriter writer(buf, sizeof(buf));
320 uint32_t bit_index = 0;
321 bool first = true;
322 for (auto it = event.render_subpass_index_mask(); it; ++it) {
323 auto subpasses_bits = *it;
324 do {
325 if ((subpasses_bits & 1) != 0) {
326 if (!first) {
327 writer.AppendChar(',');
328 }
329 first = false;
330 writer.AppendUnsignedInt(bit_index);
331 }
332 subpasses_bits >>= 1;
333 ++bit_index;
334 } while (subpasses_bits != 0);
335 // Round up to the next multiple of 64.
336 bit_index = ((bit_index - 1) / 64 + 1) * 64;
337 }
338 return context_->storage->InternString(writer.GetStringView());
339 }
340
ParseGpuRenderStageEvent(int64_t ts,PacketSequenceStateGeneration * sequence_state,ConstBytes blob)341 void GpuEventParser::ParseGpuRenderStageEvent(
342 int64_t ts,
343 PacketSequenceStateGeneration* sequence_state,
344 ConstBytes blob) {
345 protos::pbzero::GpuRenderStageEvent::Decoder event(blob.data, blob.size);
346
347 int32_t pid = 0;
348 if (event.has_specifications()) {
349 protos::pbzero::GpuRenderStageEvent_Specifications::Decoder spec(
350 event.specifications().data, event.specifications().size);
351 for (auto it = spec.hw_queue(); it; ++it) {
352 protos::pbzero::GpuRenderStageEvent_Specifications_Description::Decoder
353 hw_queue(*it);
354 if (hw_queue.has_name()) {
355 InsertGpuTrack(hw_queue);
356 }
357 }
358 for (auto it = spec.stage(); it; ++it) {
359 protos::pbzero::GpuRenderStageEvent_Specifications_Description::Decoder
360 stage(*it);
361 if (stage.has_name()) {
362 gpu_render_stage_ids_.emplace_back(
363 context_->storage->InternString(stage.name()),
364 context_->storage->InternString(stage.description()));
365 }
366 }
367 if (spec.has_context_spec()) {
368 protos::pbzero::GpuRenderStageEvent_Specifications_ContextSpec::Decoder
369 context_spec(spec.context_spec());
370 if (context_spec.has_pid()) {
371 pid = context_spec.pid();
372 }
373 }
374 }
375
376 if (event.has_context()) {
377 uint64_t context_id = event.context();
378 auto* decoder = sequence_state->LookupInternedMessage<
379 protos::pbzero::InternedData::kGraphicsContextsFieldNumber,
380 protos::pbzero::InternedGraphicsContext>(context_id);
381 if (decoder) {
382 pid = decoder->pid();
383 }
384 }
385
386 auto args_callback = [this, &event,
387 sequence_state](ArgsTracker::BoundInserter* inserter) {
388 if (event.has_stage_iid()) {
389 size_t stage_iid = static_cast<size_t>(event.stage_iid());
390 auto* decoder = sequence_state->LookupInternedMessage<
391 protos::pbzero::InternedData::kGpuSpecificationsFieldNumber,
392 protos::pbzero::InternedGpuRenderStageSpecification>(stage_iid);
393 if (decoder) {
394 // TODO: Add RenderStageCategory to gpu_slice table.
395 inserter->AddArg(description_id_,
396 Variadic::String(context_->storage->InternString(
397 decoder->description())));
398 }
399 } else if (event.has_stage_id()) {
400 size_t stage_id = static_cast<size_t>(event.stage_id());
401 if (stage_id < gpu_render_stage_ids_.size()) {
402 auto description = gpu_render_stage_ids_[stage_id].second;
403 if (description != kNullStringId) {
404 inserter->AddArg(description_id_, Variadic::String(description));
405 }
406 }
407 }
408 for (auto it = event.extra_data(); it; ++it) {
409 protos::pbzero::GpuRenderStageEvent_ExtraData_Decoder datum(*it);
410 StringId name_id = context_->storage->InternString(datum.name());
411 StringId value = context_->storage->InternString(
412 datum.has_value() ? datum.value() : base::StringView());
413 inserter->AddArg(name_id, Variadic::String(value));
414 }
415 };
416
417 if (event.has_event_id()) {
418 TrackId track_id;
419 uint64_t hw_queue_id = 0;
420 if (event.has_hw_queue_iid()) {
421 hw_queue_id = event.hw_queue_iid();
422 auto* decoder = sequence_state->LookupInternedMessage<
423 protos::pbzero::InternedData::kGpuSpecificationsFieldNumber,
424 protos::pbzero::InternedGpuRenderStageSpecification>(hw_queue_id);
425 if (!decoder) {
426 // Skip
427 return;
428 }
429 // TODO: Add RenderStageCategory to gpu_track table.
430 tables::GpuTrackTable::Row track(
431 context_->storage->InternString(decoder->name()));
432 track.scope = gpu_render_stage_scope_id_;
433 track.description =
434 context_->storage->InternString(decoder->description());
435 track_id = context_->track_tracker->LegacyInternGpuTrack(track);
436 } else {
437 uint32_t id = static_cast<uint32_t>(event.hw_queue_id());
438 if (id < gpu_hw_queue_ids_.size() && gpu_hw_queue_ids_[id].has_value()) {
439 track_id = gpu_hw_queue_ids_[id].value();
440 } else {
441 // If the event has a hw_queue_id that does not have a Specification,
442 // create a new track for it.
443 char buf[128];
444 base::StringWriter writer(buf, sizeof(buf));
445 writer.AppendLiteral("Unknown GPU Queue ");
446 if (id > 1024) {
447 // We don't expect this to happen, but just in case there is a corrupt
448 // packet, make sure we don't allocate a ridiculous amount of memory.
449 id = 1024;
450 context_->storage->IncrementStats(
451 stats::gpu_render_stage_parser_errors);
452 PERFETTO_ELOG("Invalid hw_queue_id.");
453 } else {
454 writer.AppendInt(event.hw_queue_id());
455 }
456 StringId track_name =
457 context_->storage->InternString(writer.GetStringView());
458 tables::GpuTrackTable::Row track(track_name);
459 track.scope = gpu_render_stage_scope_id_;
460 track_id = context_->track_tracker->LegacyInternGpuTrack(track);
461 gpu_hw_queue_ids_.resize(id + 1);
462 gpu_hw_queue_ids_[id] = track_id;
463 }
464 hw_queue_id = id;
465 }
466
467 auto render_target_name =
468 FindDebugName(VK_OBJECT_TYPE_FRAMEBUFFER, event.render_target_handle());
469 auto render_target_name_id = render_target_name.has_value()
470 ? context_->storage->InternString(
471 render_target_name.value().c_str())
472 : kNullStringId;
473 auto render_pass_name =
474 FindDebugName(VK_OBJECT_TYPE_RENDER_PASS, event.render_pass_handle());
475 auto render_pass_name_id =
476 render_pass_name.has_value()
477 ? context_->storage->InternString(render_pass_name.value().c_str())
478 : kNullStringId;
479 auto command_buffer_name = FindDebugName(VK_OBJECT_TYPE_COMMAND_BUFFER,
480 event.command_buffer_handle());
481 auto command_buffer_name_id = command_buffer_name.has_value()
482 ? context_->storage->InternString(
483 command_buffer_name.value().c_str())
484 : kNullStringId;
485
486 tables::GpuSliceTable::Row row;
487 row.ts = ts;
488 row.track_id = track_id;
489 row.name = GetFullStageName(sequence_state, event);
490 row.dur = static_cast<int64_t>(event.duration());
491 // TODO: Create table for graphics context and lookup
492 // InternedGraphicsContext.
493 row.context_id = static_cast<int64_t>(event.context());
494 row.render_target = static_cast<int64_t>(event.render_target_handle());
495 row.render_target_name = render_target_name_id;
496 row.render_pass = static_cast<int64_t>(event.render_pass_handle());
497 row.render_pass_name = render_pass_name_id;
498 row.render_subpasses = ParseRenderSubpasses(event);
499 row.command_buffer = static_cast<int64_t>(event.command_buffer_handle());
500 row.command_buffer_name = command_buffer_name_id;
501 row.submission_id = event.submission_id();
502 row.hw_queue_id = static_cast<int64_t>(hw_queue_id);
503 row.upid = context_->process_tracker->GetOrCreateProcess(
504 static_cast<uint32_t>(pid));
505 context_->slice_tracker->ScopedTyped(
506 context_->storage->mutable_gpu_slice_table(), row, args_callback);
507 }
508 }
509
UpdateVulkanMemoryAllocationCounters(UniquePid upid,const VulkanMemoryEvent::Decoder & event)510 void GpuEventParser::UpdateVulkanMemoryAllocationCounters(
511 UniquePid upid,
512 const VulkanMemoryEvent::Decoder& event) {
513 switch (event.source()) {
514 case VulkanMemoryEvent::SOURCE_DRIVER: {
515 auto allocation_scope = static_cast<VulkanMemoryEvent::AllocationScope>(
516 event.allocation_scope());
517 if (allocation_scope == VulkanMemoryEvent::SCOPE_UNSPECIFIED) {
518 return;
519 }
520 switch (event.operation()) {
521 case VulkanMemoryEvent::OP_CREATE:
522 vulkan_driver_memory_counters_[allocation_scope] +=
523 event.memory_size();
524 break;
525 case VulkanMemoryEvent::OP_DESTROY:
526 vulkan_driver_memory_counters_[allocation_scope] -=
527 event.memory_size();
528 break;
529 case VulkanMemoryEvent::OP_UNSPECIFIED:
530 case VulkanMemoryEvent::OP_BIND:
531 case VulkanMemoryEvent::OP_DESTROY_BOUND:
532 case VulkanMemoryEvent::OP_ANNOTATIONS:
533 return;
534 }
535 static constexpr auto kBlueprint = tracks::CounterBlueprint(
536 "vulkan_driver_mem", tracks::UnknownUnitBlueprint(),
537 tracks::DimensionBlueprints(
538 tracks::kProcessDimensionBlueprint,
539 tracks::StringDimensionBlueprint("vulkan_allocation_scope")),
540 tracks::FnNameBlueprint([](UniquePid, base::StringView scope) {
541 return base::StackString<1024>("vulkan.mem.driver.scope.%.*s",
542 int(scope.size()), scope.data());
543 }));
544 static constexpr std::array kEventScopes = {
545 "UNSPECIFIED", "COMMAND", "OBJECT", "CACHE", "DEVICE", "INSTANCE",
546 };
547 TrackId track = context_->track_tracker->InternTrack(
548 kBlueprint,
549 tracks::Dimensions(upid, kEventScopes[uint32_t(allocation_scope)]));
550 context_->event_tracker->PushCounter(
551 event.timestamp(),
552 static_cast<double>(vulkan_driver_memory_counters_[allocation_scope]),
553 track);
554 break;
555 }
556 case VulkanMemoryEvent::SOURCE_DEVICE_MEMORY: {
557 auto memory_type = static_cast<uint32_t>(event.memory_type());
558 switch (event.operation()) {
559 case VulkanMemoryEvent::OP_CREATE:
560 vulkan_device_memory_counters_allocate_[memory_type] +=
561 event.memory_size();
562 break;
563 case VulkanMemoryEvent::OP_DESTROY:
564 vulkan_device_memory_counters_allocate_[memory_type] -=
565 event.memory_size();
566 break;
567 case VulkanMemoryEvent::OP_UNSPECIFIED:
568 case VulkanMemoryEvent::OP_BIND:
569 case VulkanMemoryEvent::OP_DESTROY_BOUND:
570 case VulkanMemoryEvent::OP_ANNOTATIONS:
571 return;
572 }
573 static constexpr auto kBlueprint = tracks::CounterBlueprint(
574 "vulkan_device_mem_allocation", tracks::UnknownUnitBlueprint(),
575 tracks::DimensionBlueprints(
576 tracks::kProcessDimensionBlueprint,
577 tracks::UintDimensionBlueprint("vulkan_memory_type")),
578 tracks::FnNameBlueprint([](UniquePid, uint32_t type) {
579 return base::StackString<1024>(
580 "vulkan.mem.device.memory.type.%u.allocation", type);
581 }));
582 TrackId track = context_->track_tracker->InternTrack(
583 kBlueprint, tracks::Dimensions(upid, memory_type));
584 context_->event_tracker->PushCounter(
585 event.timestamp(),
586 static_cast<double>(
587 vulkan_device_memory_counters_allocate_[memory_type]),
588 track);
589 break;
590 }
591 case VulkanMemoryEvent::SOURCE_BUFFER:
592 case VulkanMemoryEvent::SOURCE_IMAGE: {
593 auto memory_type = static_cast<uint32_t>(event.memory_type());
594 switch (event.operation()) {
595 case VulkanMemoryEvent::OP_BIND:
596 vulkan_device_memory_counters_bind_[memory_type] +=
597 event.memory_size();
598 break;
599 case VulkanMemoryEvent::OP_DESTROY_BOUND:
600 vulkan_device_memory_counters_bind_[memory_type] -=
601 event.memory_size();
602 break;
603 case VulkanMemoryEvent::OP_UNSPECIFIED:
604 case VulkanMemoryEvent::OP_CREATE:
605 case VulkanMemoryEvent::OP_DESTROY:
606 case VulkanMemoryEvent::OP_ANNOTATIONS:
607 return;
608 }
609 static constexpr auto kBlueprint = tracks::CounterBlueprint(
610 "vulkan_device_mem_bind", tracks::UnknownUnitBlueprint(),
611 tracks::DimensionBlueprints(
612 tracks::kProcessDimensionBlueprint,
613 tracks::UintDimensionBlueprint("vulkan_memory_type")),
614 tracks::FnNameBlueprint([](UniquePid, uint32_t type) {
615 return base::StackString<1024>(
616 "vulkan.mem.device.memory.type.%u.bind", type);
617 }));
618 TrackId track = context_->track_tracker->InternTrack(
619 kBlueprint, tracks::Dimensions(upid, memory_type));
620 context_->event_tracker->PushCounter(
621 event.timestamp(),
622 static_cast<double>(vulkan_device_memory_counters_bind_[memory_type]),
623 track);
624 break;
625 }
626 case VulkanMemoryEvent::SOURCE_UNSPECIFIED:
627 case VulkanMemoryEvent::SOURCE_DEVICE:
628 return;
629 }
630 }
631
ParseVulkanMemoryEvent(PacketSequenceStateGeneration * sequence_state,ConstBytes blob)632 void GpuEventParser::ParseVulkanMemoryEvent(
633 PacketSequenceStateGeneration* sequence_state,
634 ConstBytes blob) {
635 using protos::pbzero::InternedData;
636 VulkanMemoryEvent::Decoder vulkan_memory_event(blob.data, blob.size);
637 tables::VulkanMemoryAllocationsTable::Row vulkan_memory_event_row;
638 vulkan_memory_event_row.source = vulkan_memory_tracker_.FindSourceString(
639 static_cast<VulkanMemoryEvent::Source>(vulkan_memory_event.source()));
640 vulkan_memory_event_row.operation =
641 vulkan_memory_tracker_.FindOperationString(
642 static_cast<VulkanMemoryEvent::Operation>(
643 vulkan_memory_event.operation()));
644 vulkan_memory_event_row.timestamp = vulkan_memory_event.timestamp();
645 vulkan_memory_event_row.upid =
646 context_->process_tracker->GetOrCreateProcess(vulkan_memory_event.pid());
647 if (vulkan_memory_event.has_device()) {
648 vulkan_memory_event_row.device =
649 static_cast<int64_t>(vulkan_memory_event.device());
650 }
651 if (vulkan_memory_event.has_device_memory()) {
652 vulkan_memory_event_row.device_memory =
653 static_cast<int64_t>(vulkan_memory_event.device_memory());
654 }
655 if (vulkan_memory_event.has_heap()) {
656 vulkan_memory_event_row.heap = vulkan_memory_event.heap();
657 }
658 if (vulkan_memory_event.has_memory_type()) {
659 vulkan_memory_event_row.memory_type = vulkan_memory_event.memory_type();
660 }
661 if (vulkan_memory_event.has_caller_iid()) {
662 vulkan_memory_event_row.function_name =
663 vulkan_memory_tracker_
664 .GetInternedString<InternedData::kFunctionNamesFieldNumber>(
665 sequence_state,
666 static_cast<uint64_t>(vulkan_memory_event.caller_iid()));
667 }
668 if (vulkan_memory_event.has_object_handle()) {
669 vulkan_memory_event_row.object_handle =
670 static_cast<int64_t>(vulkan_memory_event.object_handle());
671 }
672 if (vulkan_memory_event.has_memory_address()) {
673 vulkan_memory_event_row.memory_address =
674 static_cast<int64_t>(vulkan_memory_event.memory_address());
675 }
676 if (vulkan_memory_event.has_memory_size()) {
677 vulkan_memory_event_row.memory_size =
678 static_cast<int64_t>(vulkan_memory_event.memory_size());
679 }
680 if (vulkan_memory_event.has_allocation_scope()) {
681 vulkan_memory_event_row.scope =
682 vulkan_memory_tracker_.FindAllocationScopeString(
683 static_cast<VulkanMemoryEvent::AllocationScope>(
684 vulkan_memory_event.allocation_scope()));
685 }
686
687 UpdateVulkanMemoryAllocationCounters(vulkan_memory_event_row.upid.value(),
688 vulkan_memory_event);
689
690 auto* allocs = context_->storage->mutable_vulkan_memory_allocations_table();
691 VulkanAllocId id = allocs->Insert(vulkan_memory_event_row).id;
692
693 if (vulkan_memory_event.has_annotations()) {
694 auto inserter = context_->args_tracker->AddArgsTo(id);
695
696 for (auto it = vulkan_memory_event.annotations(); it; ++it) {
697 protos::pbzero::VulkanMemoryEventAnnotation::Decoder annotation(*it);
698
699 auto key_id =
700 vulkan_memory_tracker_
701 .GetInternedString<InternedData::kVulkanMemoryKeysFieldNumber>(
702 sequence_state, static_cast<uint64_t>(annotation.key_iid()));
703
704 if (annotation.has_int_value()) {
705 inserter.AddArg(key_id, Variadic::Integer(annotation.int_value()));
706 } else if (annotation.has_double_value()) {
707 inserter.AddArg(key_id, Variadic::Real(annotation.double_value()));
708 } else if (annotation.has_string_iid()) {
709 auto string_id =
710 vulkan_memory_tracker_
711 .GetInternedString<InternedData::kVulkanMemoryKeysFieldNumber>(
712 sequence_state,
713 static_cast<uint64_t>(annotation.string_iid()));
714
715 inserter.AddArg(key_id, Variadic::String(string_id));
716 }
717 }
718 }
719 }
720
ParseGpuLog(int64_t ts,ConstBytes blob)721 void GpuEventParser::ParseGpuLog(int64_t ts, ConstBytes blob) {
722 protos::pbzero::GpuLog::Decoder event(blob.data, blob.size);
723
724 tables::GpuTrackTable::Row track(gpu_log_track_name_id_);
725 track.scope = gpu_log_scope_id_;
726 TrackId track_id = context_->track_tracker->LegacyInternGpuTrack(track);
727
728 auto args_callback = [this, &event](ArgsTracker::BoundInserter* inserter) {
729 if (event.has_tag()) {
730 inserter->AddArg(
731 tag_id_,
732 Variadic::String(context_->storage->InternString(event.tag())));
733 }
734 if (event.has_log_message()) {
735 inserter->AddArg(log_message_id_,
736 Variadic::String(context_->storage->InternString(
737 event.log_message())));
738 }
739 };
740
741 auto severity = static_cast<size_t>(event.severity());
742 StringId severity_id =
743 severity < log_severity_ids_.size()
744 ? log_severity_ids_[static_cast<size_t>(event.severity())]
745 : log_severity_ids_[log_severity_ids_.size() - 1];
746
747 tables::GpuSliceTable::Row row;
748 row.ts = ts;
749 row.track_id = track_id;
750 row.name = severity_id;
751 row.dur = 0;
752 context_->slice_tracker->ScopedTyped(
753 context_->storage->mutable_gpu_slice_table(), row, args_callback);
754 }
755
ParseVulkanApiEvent(int64_t ts,ConstBytes blob)756 void GpuEventParser::ParseVulkanApiEvent(int64_t ts, ConstBytes blob) {
757 protos::pbzero::VulkanApiEvent::Decoder vk_event(blob.data, blob.size);
758 if (vk_event.has_vk_debug_utils_object_name()) {
759 protos::pbzero::VulkanApiEvent_VkDebugUtilsObjectName::Decoder event(
760 vk_event.vk_debug_utils_object_name());
761 debug_marker_names_[event.object_type()][event.object()] =
762 event.object_name().ToStdString();
763 }
764 if (vk_event.has_vk_queue_submit()) {
765 protos::pbzero::VulkanApiEvent_VkQueueSubmit::Decoder event(
766 vk_event.vk_queue_submit());
767 // Once flow table is implemented, we can create a nice UI that link the
768 // vkQueueSubmit to GpuRenderStageEvent. For now, just add it as in a GPU
769 // track so that they can appear close to the render stage slices.
770 tables::GpuTrackTable::Row track(vk_event_track_id_);
771 track.scope = vk_event_scope_id_;
772 TrackId track_id = context_->track_tracker->LegacyInternGpuTrack(track);
773 tables::GpuSliceTable::Row row;
774 row.ts = ts;
775 row.dur = static_cast<int64_t>(event.duration_ns());
776 row.track_id = track_id;
777 row.name = vk_queue_submit_id_;
778 if (event.has_vk_command_buffers()) {
779 row.command_buffer = static_cast<int64_t>(*event.vk_command_buffers());
780 }
781 row.submission_id = event.submission_id();
782 auto args_callback = [this, &event](ArgsTracker::BoundInserter* inserter) {
783 inserter->AddArg(context_->storage->InternString("pid"),
784 Variadic::Integer(event.pid()));
785 inserter->AddArg(context_->storage->InternString("tid"),
786 Variadic::Integer(event.tid()));
787 };
788 context_->slice_tracker->ScopedTyped(
789 context_->storage->mutable_gpu_slice_table(), row, args_callback);
790 }
791 }
792
ParseGpuMemTotalEvent(int64_t ts,ConstBytes blob)793 void GpuEventParser::ParseGpuMemTotalEvent(int64_t ts, ConstBytes blob) {
794 protos::pbzero::GpuMemTotalEvent::Decoder gpu_mem_total(blob);
795
796 TrackId track = kInvalidTrackId;
797 const uint32_t pid = gpu_mem_total.pid();
798 if (pid == 0) {
799 // Pid 0 is used to indicate the global total
800 track =
801 context_->track_tracker->InternTrack(tracks::kGlobalGpuMemoryBlueprint);
802 } else {
803 // Process emitting the packet can be different from the pid in the event.
804 UniqueTid utid = context_->process_tracker->UpdateThread(pid, pid);
805 UniquePid upid = context_->storage->thread_table()[utid].upid().value_or(0);
806 track = context_->track_tracker->InternTrack(
807 tracks::kProcessGpuMemoryBlueprint, tracks::Dimensions(upid));
808 }
809 context_->event_tracker->PushCounter(
810 ts, static_cast<double>(gpu_mem_total.size()), track);
811 }
812
813 } // namespace perfetto::trace_processor
814