Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

463 lines
13 KiB
C++

// Copyright (c) Advanced Micro Devices, Inc.
// SPDX-License-Identifier: MIT
// Must be first: defines AINIC_SUPPORTED based on the installed AMD SMI version
// and ROCPROFSYS_USE_AINIC before any header gates code on that macro.
#include "backends/amd_smi/ainic_feature.hpp"
#include "library/pmc/collectors/common/collector_slice.hpp"
#include "library/pmc/collectors/common/settings.hpp"
#include "library/pmc/collectors/gpu/cache_policy.hpp"
#include "library/pmc/collectors/gpu/collector.hpp"
#include "library/pmc/collectors/gpu/perfetto_policy.hpp"
#include "library/pmc/device_providers/amd_smi/provider.hpp"
#include <cstdint>
#include <rocprofiler-sdk/version.h>
#if ROCPROFILER_VERSION >= 600
# include "library/pmc/collectors/gpu_perf_counter/collector.hpp"
# include "library/pmc/device_providers/rocprofiler_sdk/provider.hpp"
#endif
#if defined(ROCPROFSYS_BUILD_AINIC)
# include "library/pmc/collectors/nic/cache_policy.hpp"
# include "library/pmc/collectors/nic/collector.hpp"
# include "library/pmc/collectors/nic/perfetto_policy.hpp"
#endif
#include "library/pmc/collectors/cpu/cache_policy.hpp"
#include "library/pmc/collectors/cpu/collector.hpp"
#include "library/pmc/collectors/cpu/perfetto_policy.hpp"
#include "library/pmc/device_providers/procfs/provider.hpp"
#include "backends/amd_smi/backend.hpp"
#include "backends/amd_smi/device.hpp"
#include "backends/amd_smi/wrapper.hpp"
#include "backends/rocprofiler_sdk/wrapper.hpp"
#include "core/agent.hpp"
#include "core/common.hpp"
#include "core/components/fwd.hpp"
#include "core/state.hpp"
#if ROCPROFILER_VERSION >= 600
# include "backends/rocprofiler_sdk/backend.hpp"
#endif
#include "library/runtime.hpp"
#include "library/pmc/sampler.hpp"
#include "logger/debug.hpp"
#include <timemory/backends/threading.hpp>
#include <timemory/components/timing/backends.hpp>
#include <timemory/mpl/type_traits.hpp>
#include <timemory/utility/locking.hpp>
#include <atomic>
#include <cassert>
#include <cstdint>
#include <memory>
#include <mutex>
#include <new>
#include <stdexcept>
#include <sys/resource.h>
#include <type_traits>
#include <vector>
namespace rocprofsys::pmc
{
std::atomic<State>&
get_state()
{
static std::atomic<State> _v{ State::PreInit };
return _v;
}
namespace
{
bool&
is_initialized()
{
static bool _v = false;
return _v;
}
struct gpu_production_config
{
using SettingsApi = collectors::settings_policy;
using PerfettoApi = collectors::gpu::perfetto_policy;
using CacheApi = collectors::gpu::cache_policy;
};
#if defined(ROCPROFSYS_BUILD_AINIC)
struct nic_production_config
{
using SettingsApi = collectors::settings_policy;
using PerfettoApi = collectors::nic::perfetto_policy;
using CacheApi = collectors::nic::cache_policy;
};
#endif
struct cpu_production_config
{
using SettingsApi = collectors::settings_policy;
using PerfettoApi = collectors::cpu::perfetto_policy;
using CacheApi = collectors::cpu::cache_policy;
};
using provider_factory_t = device_providers::amd_smi::provider_factory<
backends::amd_smi::backend_factory<backends::amd_smi::wrapper>>;
using provider_t = provider_factory_t::provider_t;
using backend_session_t = provider_factory_t::provider_t::backend_t;
using amd_smi_device_t = backends::amd_smi::device<backend_session_t>;
using gpu_device_t = collectors::gpu::device<amd_smi_device_t>;
using gpu_collector_t =
collectors::gpu::collector<provider_t, gpu_device_t, gpu_production_config>;
#if ROCPROFILER_VERSION >= 600
using gpu_perf_counter_provider_t = device_providers::rocprofiler_sdk::provider<
backends::rocprofiler_sdk::backend_factory<::rocprofsys::rocprofiler_sdk::backend>>;
using gpu_perf_counter_collector_t =
collectors::gpu_perf_counter::collector<gpu_perf_counter_provider_t>;
#endif
#if defined(ROCPROFSYS_BUILD_AINIC)
using nic_device_t = collectors::nic::device<amd_smi_device_t>;
using nic_collector_t =
collectors::nic::collector<provider_t, nic_device_t, nic_production_config>;
#endif
using cpu_provider_factory_t =
device_providers::procfs::provider_factory<backends::procfs::backend_factory>;
using cpu_provider_t = cpu_provider_factory_t::provider_t;
using cpu_collector_t = collectors::cpu::collector<cpu_provider_t, cpu_production_config>;
std::shared_ptr<provider_t> g_device_provider;
std::unique_ptr<gpu_collector_t> g_gpu_collector;
#if ROCPROFILER_VERSION >= 600
std::shared_ptr<gpu_perf_counter_provider_t> g_gpu_perf_counter_provider;
std::unique_ptr<gpu_perf_counter_collector_t> g_gpu_perf_counter_collector;
#endif
#if defined(ROCPROFSYS_BUILD_AINIC)
std::unique_ptr<nic_collector_t> g_nic_collector;
#endif
std::shared_ptr<cpu_provider_t> g_cpu_provider;
std::unique_ptr<cpu_collector_t> g_cpu_collector;
std::vector<collectors::collector_slice> g_collector_slices;
std::atomic<bool> g_reinit_pending{ false };
// Tears down the AMD SMI collector slices and marks the sampler uninitialized.
// This is the only teardown performed on the post-fork reinit path: that reinit
// exists solely to refresh AMD SMI device handles in the child.
void
shutdown_amd_smi_collectors()
{
auto_lock_t _lk{ type_mutex<category::amd_smi>() };
if(!is_initialized())
{
return;
}
LOG_DEBUG("Shutting down AMD-SMI PMC sampler.");
try
{
for(auto& slice : g_collector_slices)
{
slice.shutdown();
}
} catch(const std::runtime_error& _e)
{
LOG_ERROR("Exception thrown when shutting down AMD-SMI PMC sampler: {}",
_e.what());
}
is_initialized() = false;
}
// Tears down the rocprofiler-sdk GPU hardware perf-counter collector. This must
// NOT run on the post-fork reinit path: the collector is registered once (from
// sdk_tool_configure) and is never recreated by setup(); its SDK contexts are
// owned by the parent and survive fork(), so tearing it down would permanently
// disable GPU hardware-counter sampling. Only the real finalize resets it.
void
shutdown_gpu_hw_collector()
{
#if ROCPROFILER_VERSION >= 600
auto_lock_t _lk{ type_mutex<category::amd_smi>() };
LOG_DEBUG("Shutting down rocprofiler-sdk GPU hardware counter collector.");
if(g_gpu_perf_counter_collector) g_gpu_perf_counter_collector->shutdown();
g_gpu_perf_counter_collector.reset();
g_gpu_perf_counter_provider.reset();
#endif
}
void
reinit_if_pending()
{
bool _expected = true;
if(!g_reinit_pending.compare_exchange_strong(_expected, false)) return;
LOG_DEBUG("Performing deferred PMC reinit after fork.");
shutdown_amd_smi_collectors();
setup();
}
} // namespace
void
set_state(State _v)
{
pmc::get_state().store(_v);
}
void
config()
{
for(auto& slice : g_collector_slices)
{
slice.config();
}
LOG_DEBUG("Setting PMC sampler state to active...");
pmc::set_state(State::Active);
}
void
sample()
{
reinit_if_pending();
auto_lock_t _lk{ type_mutex<category::amd_smi>() };
if(pmc::get_state() != State::Active)
{
return;
}
auto timestamp =
static_cast<std::int64_t>(tim::get_clock_real_now<size_t, std::nano>());
for(auto& slice : g_collector_slices)
{
slice.sample(timestamp);
}
#if ROCPROFILER_VERSION >= 600
if(g_gpu_perf_counter_collector) g_gpu_perf_counter_collector->sample(timestamp);
#endif
}
void
setup()
{
auto_lock_t _lk{ type_mutex<category::amd_smi>() };
if(is_initialized())
{
return;
}
ROCPROFSYS_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
try
{
g_collector_slices.clear();
g_cpu_provider = cpu_provider_factory_t::create();
g_cpu_collector = std::make_unique<cpu_collector_t>(g_cpu_provider);
g_collector_slices.emplace_back(*g_cpu_collector);
if(config::get_use_amd_smi())
{
// Create and inject device provider (shared between GPU and NIC collectors)
g_device_provider = provider_factory_t::create();
g_gpu_collector = std::make_unique<gpu_collector_t>(g_device_provider);
#if defined(ROCPROFSYS_BUILD_AINIC)
g_nic_collector = std::make_unique<nic_collector_t>(g_device_provider);
#endif
g_collector_slices.emplace_back(*g_gpu_collector);
#if defined(ROCPROFSYS_BUILD_AINIC)
g_collector_slices.emplace_back(*g_nic_collector);
#endif
}
for(auto& slice : g_collector_slices)
{
slice.setup();
}
is_initialized() = true;
} catch(const std::runtime_error& _e)
{
LOG_ERROR("Exception thrown when initializing PMC sampler: {}", _e.what());
}
}
void
shutdown()
{
shutdown_amd_smi_collectors();
shutdown_gpu_hw_collector();
}
void
post_process()
{
LOG_DEBUG("Post-processing PMC samples ({} slices).", g_collector_slices.size());
for(auto& slice : g_collector_slices)
{
slice.post_process();
}
g_collector_slices.clear();
#if ROCPROFILER_VERSION >= 600
if(g_gpu_perf_counter_collector) g_gpu_perf_counter_collector->post_process();
g_gpu_perf_counter_collector.reset();
g_gpu_perf_counter_provider.reset();
#endif
g_device_provider.reset();
g_cpu_provider.reset();
}
void
pause()
{
auto_lock_t _lk{ type_mutex<category::amd_smi>() };
if(pmc::get_state() != State::Active || !is_initialized())
{
return;
}
auto timestamp =
static_cast<std::int64_t>(tim::get_clock_real_now<size_t, std::nano>());
for(auto& slice : g_collector_slices)
{
slice.pause(timestamp);
}
#if ROCPROFILER_VERSION >= 600
if(g_gpu_perf_counter_collector) g_gpu_perf_counter_collector->pause(timestamp);
#endif
}
void
postfork_child_cleanup()
{
LOG_DEBUG("Disabling PMC sampling in child process after fork.");
pmc::get_state().store(State::Finalized);
for(auto& slice : g_collector_slices)
{
slice.shutdown();
}
g_collector_slices.clear();
#if ROCPROFILER_VERSION >= 600
// Do not call shutdown() or stop() — rocprofiler SDK / HSA mutexes may be
// inherited locked from the parent. The parent owns SDK context shutdown.
// gpu_perf_counter is not in g_collector_slices so the loop above is safe.
g_gpu_perf_counter_collector.reset();
g_gpu_perf_counter_provider.reset();
#endif
g_gpu_collector.reset();
#if defined(ROCPROFSYS_BUILD_AINIC)
g_nic_collector.reset();
#endif
g_cpu_collector.reset();
g_device_provider.reset();
g_cpu_provider.reset();
is_initialized() = false;
}
void
postfork_parent_reinit()
{
// Cannot call shutdown()/setup() here: setup() queries AMD SMI, which
// internally calls fork(), which would re-enter the postfork handler
// chain while glibc still holds __fork_lock. Defer to next sample().
g_reinit_pending.store(true);
}
// Intentionally a second-phase setup called after rocprofiler initializes (outside of
// setup()), because the rocprofiler context and agent list are not available at
// setup() time. shutdown() symmetrically stops and resets the provider/collector
// globals created here.
void
prefork_lock_sampler()
{
// A raw lock must be used, as we must lock in one function and unlock in another
// Thread that calls this should use postfork_parent_unlock_sampler()
// Child that inherits the locked mutex needs to use
// postfork_child_reset_sampler_lock()
type_mutex<category::amd_smi>().lock();
}
void
postfork_parent_unlock_sampler()
{
// Same kernel tid as thread that called prefork_lock_sampler(),
// so the unlock succeeds
type_mutex<category::amd_smi>().unlock();
}
void
postfork_child_reset_sampler_lock()
{
// Overwrite the existing mutex with a new one of the same type (placement-new)
// We cannot unlock it in the child as it has been locked by the parent and has a
// different kernel TID then what was used to lock it. Destructor cannot be used
// either, as on a locked mutex, it is undefined behaviour
using mutex_type =
std::remove_reference<decltype(type_mutex<category::amd_smi>())>::type;
auto& _m = type_mutex<category::amd_smi>();
::new(static_cast<void*>(&_m)) mutex_type{};
}
#if ROCPROFILER_VERSION >= 600
void
register_gpu_perf_counter_source(const std::vector<std::shared_ptr<agent>>& agent_list)
{
auto_lock_t _lk{ type_mutex<category::amd_smi>() };
try
{
auto settings =
collectors::settings_policy::get_gpu_perf_counter_enabled_metrics();
auto counters = std::move(settings.explicit_counters);
// counters without :device= are broadcast to every GPU agent
for(const auto& name : settings.broadcast_names)
{
for(const auto& gpu_agent : agent_list)
{
counters.push_back({ name, gpu_agent->device_type_index });
}
}
const auto enabled_metrics =
collectors::gpu_perf_counter::enabled_metrics{ std::move(counters) };
g_gpu_perf_counter_provider =
std::make_shared<gpu_perf_counter_provider_t>(agent_list, enabled_metrics);
g_gpu_perf_counter_collector =
std::make_unique<gpu_perf_counter_collector_t>(g_gpu_perf_counter_provider);
g_gpu_perf_counter_collector->setup();
g_gpu_perf_counter_collector->config();
LOG_DEBUG("Registered GPU Perf Counter PMC source");
} catch(const std::runtime_error& runtime_exception)
{
LOG_ERROR("Failed to register SDK PMC source: {}", runtime_exception.what());
}
}
#endif
} // namespace rocprofsys::pmc