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