diff --git a/layersvt/device_memory_report/device_memory_report_perfetto.cpp b/layersvt/device_memory_report/device_memory_report_perfetto.cpp index 01b4dc258e..eb381d07fb 100644 --- a/layersvt/device_memory_report/device_memory_report_perfetto.cpp +++ b/layersvt/device_memory_report/device_memory_report_perfetto.cpp @@ -32,63 +32,25 @@ void InitializeDeviceMemoryReportPerfetto() { }); } -namespace { -static const std::unordered_map kStaticTracks = { - // App usage tracks - {"vulkan.mem.app.usage.staging_transfer", perfetto::CounterTrack("vulkan.mem.app.usage.staging_transfer").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.app.usage.ray_tracing", perfetto::CounterTrack("vulkan.mem.app.usage.ray_tracing").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.app.usage.indirect_gpu_driven", perfetto::CounterTrack("vulkan.mem.app.usage.indirect_gpu_driven").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.app.usage.compute_storage", perfetto::CounterTrack("vulkan.mem.app.usage.compute_storage").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.app.usage.uniform_constants", perfetto::CounterTrack("vulkan.mem.app.usage.uniform_constants").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.app.usage.geometry_mesh", perfetto::CounterTrack("vulkan.mem.app.usage.geometry_mesh").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.app.usage.transform_feedback", perfetto::CounterTrack("vulkan.mem.app.usage.transform_feedback").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.app.usage.general_buffer", perfetto::CounterTrack("vulkan.mem.app.usage.general_buffer").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.app.usage.transient_memoryless", perfetto::CounterTrack("vulkan.mem.app.usage.transient_memoryless").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.app.usage.storage_compute_image", perfetto::CounterTrack("vulkan.mem.app.usage.storage_compute_image").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.app.usage.depth_stencil_target", perfetto::CounterTrack("vulkan.mem.app.usage.depth_stencil_target").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.app.usage.color_render_target", perfetto::CounterTrack("vulkan.mem.app.usage.color_render_target").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.app.usage.static_texture", perfetto::CounterTrack("vulkan.mem.app.usage.static_texture").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.app.usage.general_image", perfetto::CounterTrack("vulkan.mem.app.usage.general_image").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.app.usage.unbound_memory", perfetto::CounterTrack("vulkan.mem.app.usage.unbound_memory").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - - // Driver usage tracks - {"vulkan.mem.driver.usage.staging_transfer", perfetto::CounterTrack("vulkan.mem.driver.usage.staging_transfer").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.driver.usage.ray_tracing", perfetto::CounterTrack("vulkan.mem.driver.usage.ray_tracing").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.driver.usage.indirect_gpu_driven", perfetto::CounterTrack("vulkan.mem.driver.usage.indirect_gpu_driven").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.driver.usage.compute_storage", perfetto::CounterTrack("vulkan.mem.driver.usage.compute_storage").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.driver.usage.uniform_constants", perfetto::CounterTrack("vulkan.mem.driver.usage.uniform_constants").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.driver.usage.geometry_mesh", perfetto::CounterTrack("vulkan.mem.driver.usage.geometry_mesh").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.driver.usage.transform_feedback", perfetto::CounterTrack("vulkan.mem.driver.usage.transform_feedback").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.driver.usage.general_buffer", perfetto::CounterTrack("vulkan.mem.driver.usage.general_buffer").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.driver.usage.transient_memoryless", perfetto::CounterTrack("vulkan.mem.driver.usage.transient_memoryless").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.driver.usage.storage_compute_image", perfetto::CounterTrack("vulkan.mem.driver.usage.storage_compute_image").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.driver.usage.depth_stencil_target", perfetto::CounterTrack("vulkan.mem.driver.usage.depth_stencil_target").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.driver.usage.color_render_target", perfetto::CounterTrack("vulkan.mem.driver.usage.color_render_target").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.driver.usage.static_texture", perfetto::CounterTrack("vulkan.mem.driver.usage.static_texture").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.driver.usage.general_image", perfetto::CounterTrack("vulkan.mem.driver.usage.general_image").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, - {"vulkan.mem.driver.usage.unbound_memory", perfetto::CounterTrack("vulkan.mem.driver.usage.unbound_memory").set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES)}, -}; -} // namespace - perfetto::CounterTrack GetCounterTrack(std::string_view name) { - auto it = kStaticTracks.find(name); - if (it != kStaticTracks.end()) { + static std::mutex tracks_mutex; + // Declare track_names first so LIFO static destruction destroys tracks before the strings it views. + static std::unordered_set track_names; + static std::unordered_map tracks; + std::lock_guard lock(tracks_mutex); + + auto it = tracks.find(name); + if (it != tracks.end()) { return it->second; } - static std::mutex dynamic_mutex; - static std::unordered_map dynamic_tracks; - static std::unordered_set dynamic_names; - std::lock_guard lock(dynamic_mutex); - auto dyn_it = dynamic_tracks.find(std::string(name)); - if (dyn_it != dynamic_tracks.end()) { - return dyn_it->second; - } - auto name_it = dynamic_names.insert(std::string(name)).first; - perfetto::CounterTrack track = perfetto::CounterTrack(perfetto::DynamicString(name_it->c_str())) + + // CounterTrack captures ProcessTrack::Current().uuid at construction time; ensure tracing is + // initialized before constructing the first track so it isn't parented to the null/global track. + InitializeDeviceMemoryReportPerfetto(); + + auto name_it = track_names.emplace(name).first; + perfetto::CounterTrack track = perfetto::CounterTrack(perfetto::DynamicString(*name_it)) .set_unit(perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES); - dynamic_tracks.emplace( - std::piecewise_construct, - std::forward_as_tuple(*name_it), - std::forward_as_tuple(track)); + tracks.emplace(*name_it, track); return track; } diff --git a/layersvt/test/test_devicememoryreport.cpp b/layersvt/test/test_devicememoryreport.cpp index 5efa12044c..ae609cf22a 100644 --- a/layersvt/test/test_devicememoryreport.cpp +++ b/layersvt/test/test_devicememoryreport.cpp @@ -427,6 +427,65 @@ TEST_F(DeviceMemoryReportTests, StaticCounterTrackLookup) { EXPECT_EQ(dynamic_track_again.Serialize().counter().unit(), perfetto::protos::gen::CounterDescriptor::UNIT_SIZE_BYTES); } +TEST_F(DeviceMemoryReportTests, CounterTracksAreScopedToProcessTrack) { + TEST_DESCRIPTION( + "Test that counter tracks are parented to the process track, so Perfetto scopes them under the traced " + "application instead of emitting them as global tracks"); + + InitializeDeviceMemoryReportPerfetto(); + + // Perfetto only assigns a process uuid once tracing has been initialized. Tracks created before that (for + // example during static initialization) are permanently parented to the null/global track. + const uint64_t process_uuid = perfetto::ProcessTrack::Current().uuid; + ASSERT_NE(process_uuid, 0u) << "Perfetto tracing was not initialized"; + + const char* kTrackNames[] = { + "vulkan.mem.app.usage.color_render_target", + "vulkan.mem.app.usage.unbound_memory", + "vulkan.mem.driver.usage.static_texture", + "vulkan.mem.driver.usage.unbound_memory", + "vulkan.mem.app.usage.brand_new_category", + }; + + for (const char* name : kTrackNames) { + perfetto::CounterTrack track = GetCounterTrack(name); + + EXPECT_NE(track.uuid, 0u) << "track " << name; + EXPECT_EQ(track.parent_uuid, process_uuid) << "track " << name << " is not scoped to the process track"; + + // The uuid must match what Perfetto derives for a process-scoped track of the same name. + EXPECT_EQ(track.uuid, perfetto::CounterTrack(perfetto::DynamicString(name)).uuid) << "track " << name; + + const perfetto::protos::gen::TrackDescriptor descriptor = track.Serialize(); + EXPECT_EQ(descriptor.uuid(), track.uuid) << "track " << name; + EXPECT_EQ(descriptor.parent_uuid(), process_uuid) << "track " << name; + EXPECT_EQ(descriptor.counter().unit(), perfetto::protos::gen::CounterDescriptor::UNIT_SIZE_BYTES) + << "track " << name; + } +} + +TEST_F(DeviceMemoryReportTests, GetCounterTrackInitializesTracing) { + TEST_DESCRIPTION("Test that GetCounterTrack initializes Perfetto itself, so tracks always have process context"); + + // Force fork+execve so the child starts with an uninitialized process_uuid rather than + // inheriting static state already initialized by earlier tests in this binary. + GTEST_FLAG_SET(death_test_style, "threadsafe"); + + EXPECT_EXIT( + { + if (perfetto::ProcessTrack::Current().uuid != 0u) { + exit(1); + } + perfetto::CounterTrack track = GetCounterTrack("vulkan.mem.app.usage.general_image"); + const uint64_t process_uuid = perfetto::ProcessTrack::Current().uuid; + if (process_uuid == 0u || track.parent_uuid != process_uuid) { + exit(2); + } + exit(0); + }, + ::testing::ExitedWithCode(0), ""); +} + TEST_F(DeviceMemoryReportTests, DriverVsAppUnboundMemoryAttribution) { TEST_DESCRIPTION("Test that application unbound memory is not misclassified when object handle matches an existing resource handle");