Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ jobs:
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 10 --slave /usr/bin/g++ g++ /usr/bin/g++-11 --slave /usr/bin/gcov gcov /usr/bin/gcov-11
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 20 --slave /usr/bin/g++ g++ /usr/bin/g++-12 --slave /usr/bin/gcov gcov /usr/bin/gcov-12
python3 -m pip install -U --user -r requirements.txt
# Held at 3.25 rather than the 4.x that requirements.txt resolves to: this
# branch's external submodules (json, pybind11, sqlite, yaml-cpp) still declare
# cmake_minimum_required below 3.5, which CMake 4 rejects outright. This must
# run after requirements.txt so the pin is not upgraded away.
python3 -m pip install -U --user cmake==3.25.0

# Add pip packages to path
echo "$HOME/.local/bin" >> "$GITHUB_PATH"

# Add pip packages to path
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "lib/rocprofiler-sdk/kernel_replay/memory_snapshot.hpp"
#include "lib/rocprofiler-sdk/kernel_replay/replay_callbacks.hpp"
#include "lib/rocprofiler-sdk/pc_sampling/hsa_adapter.hpp"
#include "lib/rocprofiler-sdk/pc_sampling/queue_hooks.hpp"
#include "lib/rocprofiler-sdk/pc_sampling/service.hpp"
#include "lib/rocprofiler-sdk/registration.hpp"
#include "lib/rocprofiler-sdk/tracing/tracing.hpp"
Expand Down Expand Up @@ -294,6 +295,15 @@ AsyncSignalHandler(hsa_signal_value_t /*signal_v*/, void* data)
}
});

// PC sampling completion is no longer routed through the per-queue
// callback registry; invoke its hook explicitly.
pc_sampling::signal_completion_hook(&queue_info_session.queue,
packet.kernel_packet,
_session,
packet,
packet.instrumentation_packets,
dispatch_time);

CHECK_NOTNULL(hsa::get_queue_controller())
->serializer(&queue_info_session.queue)
.wlock([&](auto& serializer) {
Expand Down Expand Up @@ -430,6 +440,7 @@ WriteInterceptor(const void* packets,
const bool graph_launch_active = (gls != nullptr);
const bool no_real_consumers =
(queue.get_notifiers() == 0 &&
!pc_sampling::is_configured_on_agent(queue.get_agent().get_rocp_agent()->id) &&
context::get_active_contexts(full_packet_instrumentation_context_filter).empty());

const bool has_kernel_replay = kernel_replay::has_active_replay_contexts();
Expand Down Expand Up @@ -508,8 +519,8 @@ WriteInterceptor(const void* packets,
return;
}

// these are for the services (dispatch counter collection, pc sampling, ATT) which use
// the queue/queue_controller callback mechanism
// Services that attach packet instrumentation or need dispatch correlation data. Some are
// still routed through queue-controller callbacks while migrated services use explicit hooks.
const auto queue_callback_context_filter = [](const context::context* ctx) {
return (ctx->dispatch_counter_collection || ctx->pc_sampler || ctx->dispatch_thread_trace ||
ctx->dispatch_spm);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
#
add_subdirectory(parser)

# queue_hooks provides always-linkable shims (is_configured_on_agent /
# signal_completion_hook) that the HSA queue interceptor calls unconditionally, so it must
# compile even when PC sampling HSA support is unavailable (its body is guarded by
# ROCPROFILER_SDK_HSA_PC_SAMPLING). Add it before the early return.
target_sources(rocprofiler-sdk-object-library PRIVATE queue_hooks.cpp queue_hooks.hpp)

if(hsa-runtime64_VERSION AND hsa-runtime64_VERSION VERSION_LESS 1.14.0)
return()
endif()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,20 +123,22 @@ amd_intercept_marker_handler_callback(const struct amd_aql_intercept_marker_s* p
parser->newDispatch(dispatch_pkt);
}

} // namespace

/**
* Callback called by HSA interceptor when the kernel has completed.
* Callback called by HSA interceptor when the kernel has completed. Declared in
* hsa_adapter.hpp and invoked from pc_sampling::signal_completion_hook (see
* pc_sampling/queue_hooks.cpp), so it lives outside the anonymous namespace.
*/
void
kernel_completion_cb(const rocprofiler_agent_t* rocp_agent,
rocprofiler::hsa::rocprofiler_packet& /*kernel_pkt*/,
const rocprofiler::hsa::rocprofiler_packet& /*kernel_pkt*/,
const rocprofiler::hsa::queue_info_session_t& session)
{
// No internal correlation IDs, meaning there is no need to call CID manager.
if(!session.correlation_id) return;

// Check if the PC sampling service is configured on this agent.
if(!is_pc_sample_service_configured(rocp_agent->id)) return;

// signal_completion_hook verifies that the service is configured before forwarding here.
auto* agent_session = get_agent_session(rocp_agent->id);
ROCP_FATAL_IF(agent_session == nullptr)
<< "No PC sampling sessions configured for the agent where kernel completion "
Expand All @@ -145,6 +147,8 @@ kernel_completion_cb(const rocprofiler_agent_t* rocp_agent,
agent_session->cid_manager->cid_async_activity_completed(session.correlation_id);
}

namespace
{
void
data_ready_callback(void* client_callback_data,
size_t data_size,
Expand Down Expand Up @@ -345,34 +349,11 @@ pc_sampling_service_finish_configuration(context::pc_sampling_service* service)
}
}

using external_corr_id_map_t = ::rocprofiler::hsa::queue_info_session_t::external_corr_id_map_t;

// Register callbacks for the HSA's queue interceptor.
// TODO: should we store callback ID in the service?
rocprofiler::hsa::get_queue_controller()->add_callback(
std::nullopt,
::rocprofiler::hsa::queue_callbacks_t{
.batch_packets = []() { return true; },
.write_interceptor =
[](const rocprofiler::hsa::Queue&,
const rocprofiler::hsa::rocprofiler_packet&,
rocprofiler_kernel_id_t /*kernel_id*/,
rocprofiler_dispatch_id_t /*dispatch_id*/,
rocprofiler_user_data_t*,
const external_corr_id_map_t&,
const context::correlation_id*) {
return rocprofiler::hsa::write_packet_t{nullptr, false};
},
// Completion CB
.signal_completion =
[](const rocprofiler::hsa::Queue& q,
rocprofiler::hsa::rocprofiler_packet kern_pkt,
std::shared_ptr<rocprofiler::hsa::queue_info_session_t>& session,
rocprofiler::hsa::packet_data_t& /*packet*/,
rocprofiler::hsa::inst_pkt_t&,
kernel_dispatch::profiling_time) {
kernel_completion_cb(q.get_agent().get_rocp_agent(), kern_pkt, *session);
}});
// PC sampling no longer registers a per-queue callback with the HSA queue
// controller. Kernel completion is delivered explicitly via
// pc_sampling::signal_completion_hook, called from the HSA async signal
// handler (see hsa/queue.cpp). The marker packet is still injected directly
// in the write interceptor, gated by is_pc_sample_service_configured.
}

rocprofiler_status_t
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ pc_sampling_service_stop(context::pc_sampling_service* service);
void
pc_sampling_service_finish_configuration(context::pc_sampling_service* service);

// Notifies the CID manager that the kernel's correlation ID has completed.
// Invoked from pc_sampling::signal_completion_hook (pc_sampling/queue_hooks.cpp),
// which is called by the HSA async signal handler in hsa/queue.cpp.
void
kernel_completion_cb(const rocprofiler_agent_t* rocp_agent,
const rocprofiler::hsa::rocprofiler_packet& kernel_pkt,
const rocprofiler::hsa::queue_info_session_t& session);

rocprofiler_status_t
flush_internal_agent_buffers(const PCSAgentSession* agent_session);
} // namespace hsa
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// MIT License
//
// Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "lib/rocprofiler-sdk/pc_sampling/queue_hooks.hpp"

#include "lib/rocprofiler-sdk/pc_sampling/defines.hpp"

#if ROCPROFILER_SDK_HSA_PC_SAMPLING > 0
# include "lib/rocprofiler-sdk/pc_sampling/hsa_adapter.hpp"
# include "lib/rocprofiler-sdk/pc_sampling/service.hpp"
#endif

namespace rocprofiler
{
namespace pc_sampling
{
// The compile gate cannot move into is_pc_sample_service_configured: this file is
// added to the object library before the early return in CMakeLists.txt, so it is
// always built, while service.cpp (which defines that function) is added after and
// is absent from the build when PC sampling is compiled out. The guard has to stay
// at the call site to keep the write interceptor linkable in that configuration.
bool
is_configured_on_agent(rocprofiler_agent_id_t agent_id)
{
#if ROCPROFILER_SDK_HSA_PC_SAMPLING > 0
return is_pc_sample_service_configured(agent_id);
#else
(void) agent_id;
return false;
#endif
}

void
signal_completion_hook(const ::rocprofiler::hsa::Queue* queue,
const ::rocprofiler::hsa::rocprofiler_packet& kernel_packet,
std::shared_ptr<::rocprofiler::hsa::queue_info_session_t>& session,
::rocprofiler::hsa::packet_data_t& /*packet*/,
::rocprofiler::hsa::inst_pkt_t& /*inst_pkt*/,
kernel_dispatch::profiling_time /*dispatch_time*/)
{
#if ROCPROFILER_SDK_HSA_PC_SAMPLING > 0
if(!session) return;
CHECK(queue);
const auto* agent = queue->get_agent().get_rocp_agent();
CHECK(agent);
if(!is_pc_sample_service_configured(agent->id)) return;
hsa::kernel_completion_cb(agent, kernel_packet, *session);
#else
(void) queue;
(void) kernel_packet;
(void) session;
#endif
}
} // namespace pc_sampling
} // namespace rocprofiler
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// MIT License
//
// Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#pragma once

#include "lib/rocprofiler-sdk/hsa/queue.hpp"
#include "lib/rocprofiler-sdk/hsa/queue_info_session.hpp"
#include "lib/rocprofiler-sdk/hsa/rocprofiler_packet.hpp"
#include "lib/rocprofiler-sdk/kernel_dispatch/profiling_time.hpp"

#include <rocprofiler-sdk/fwd.h>

#include <memory>

namespace rocprofiler
{
namespace pc_sampling
{
// True if the PC sampling service is configured on the given agent. Always
// linkable: returns false when PC sampling HSA support is unavailable. Used by
// the HSA write interceptor gate (in place of the per-queue callback that used
// to keep queue.get_notifiers() > 0) to decide whether a marker packet must be
// injected for this dispatch.
bool
is_configured_on_agent(rocprofiler_agent_id_t agent_id);

// Explicit replacement for the PC-sampling per-queue completion callback that
// used to be registered with the HSA queue controller. Notifies the CID manager
// that the kernel's correlation ID has completed. No-op when PC sampling HSA
// support is unavailable or when the service is not configured on the agent.
void
signal_completion_hook(const ::rocprofiler::hsa::Queue* queue,
const ::rocprofiler::hsa::rocprofiler_packet& kernel_packet,
std::shared_ptr<::rocprofiler::hsa::queue_info_session_t>& session,
::rocprofiler::hsa::packet_data_t& packet,
::rocprofiler::hsa::inst_pkt_t& inst_pkt,
kernel_dispatch::profiling_time dispatch_time);
} // namespace pc_sampling
} // namespace rocprofiler
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ set(ROCPROFILER_LIB_PC_SAMPLING_TEST_SOURCES
pc_sampling_vs_counter_collection.cpp
query_configuration.cpp
multiple_contexts_one_per_agent.cpp
queue_hooks_test.cpp
local_context.cpp)
set(ROCPROFILER_LIB_PC_SAMPLING_TEST_HEADERS pc_sampling_internals.hpp)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,6 @@ amd_intercept_marker_handler_callback(const struct amd_aql_intercept_marker_s* p
hsa_queue_t* queue,
uint64_t packet_id);

extern void
kernel_completion_cb(const std::shared_ptr<rocprofiler::counters::counter_callback_info>& info,
const rocprofiler_agent_t* rocp_agent,
rocprofiler::hsa::ClientID client_id,
const rocprofiler::hsa::rocprofiler_packet& kernel_pkt,
const rocprofiler::hsa::queue_info_session_t& session,
std::unique_ptr<rocprofiler::hsa::AQLPacket> pkt);

extern void
data_ready_callback(void* client_callback_data,
size_t data_size,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// MIT License
//
// Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include "lib/rocprofiler-sdk/pc_sampling/queue_hooks.hpp"

#include <gtest/gtest.h>

namespace
{
// is_configured_on_agent replaces the queue.get_notifiers() signal that the old
// per-queue callback registration used to provide to the HSA write interceptor
// gate. With no PC sampling service configured, any agent id must report as not
// configured. This is always linkable regardless of HSA PC sampling support and
// requires no GPU / HSA runtime.
TEST(pc_sampling_queue_hooks, is_configured_on_agent_unconfigured)
{
rocprofiler_agent_id_t agent_id;
agent_id.handle = 123456;
EXPECT_FALSE(rocprofiler::pc_sampling::is_configured_on_agent(agent_id));
}

TEST(pc_sampling_queue_hooks, signal_completion_hook_null_session_is_noop)
{
rocprofiler::hsa::rocprofiler_packet kern_pkt{};
std::shared_ptr<rocprofiler::hsa::queue_info_session_t> null_session;
rocprofiler::hsa::packet_data_t packet{};
rocprofiler::hsa::inst_pkt_t inst_pkt{};

rocprofiler::pc_sampling::signal_completion_hook(
nullptr,
kern_pkt,
null_session,
packet,
inst_pkt,
rocprofiler::kernel_dispatch::profiling_time{});

SUCCEED();
}
} // namespace
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ struct PCSAgentSession
#if ROCPROFILER_SDK_HSA_PC_SAMPLING > 0
hsa_ven_amd_pcs_t hsa_pc_sampling = {};
#endif
hsa::ClientID intercept_cb_id = -1;
// ioctl relevant information
uint32_t ioctl_pcs_id = 0;
// PC sampling parser
Expand Down
Loading