Add 'projects/rocprofiler-sdk/' from commit 'bf0fad1d5406fbc51403ba1aa9621a9d4a9bce2b'

git-subtree-dir: projects/rocprofiler-sdk
git-subtree-mainline: 50a90550e9
git-subtree-split: bf0fad1d54
This commit is contained in:
systems-assistant[bot]
2025-07-22 22:52:46 +00:00
1272 changed files with 230117 additions and 0 deletions
@@ -0,0 +1,99 @@
#
#
#
cmake_minimum_required(VERSION 3.21.0 FATAL_ERROR)
if(NOT CMAKE_HIP_COMPILER)
find_program(
amdclangpp_EXECUTABLE
NAMES amdclang++
HINTS ${ROCM_PATH} ENV ROCM_PATH /opt/rocm
PATHS ${ROCM_PATH} ENV ROCM_PATH /opt/rocm
PATH_SUFFIXES bin llvm/bin NO_CACHE)
mark_as_advanced(amdclangpp_EXECUTABLE)
if(amdclangpp_EXECUTABLE)
set(CMAKE_HIP_COMPILER "${amdclangpp_EXECUTABLE}")
endif()
endif()
project(
rocprofiler-sdk-tests-thread-trace
LANGUAGES CXX HIP
VERSION 0.0.0)
find_package(rocprofiler-sdk REQUIRED)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_HIP_STANDARD 17)
set(CMAKE_HIP_EXTENSIONS OFF)
set(CMAKE_HIP_STANDARD_REQUIRED ON)
foreach(_TYPE DEBUG MINSIZEREL RELEASE RELWITHDEBINFO)
if("${CMAKE_HIP_FLAGS_${_TYPE}}" STREQUAL "")
set(CMAKE_HIP_FLAGS_${_TYPE} "${CMAKE_CXX_FLAGS_${_TYPE}}")
endif()
endforeach()
set_source_files_properties(
kernel_branch.cpp kernel_lds.cpp
PROPERTIES LANGUAGE HIP COMPILE_FLAGS "${CMAKE_HIP_FLAGS_RELWITHDEBINFO}")
set_source_files_properties(main.cpp PROPERTIES LANGUAGE HIP)
# Single dispatch test
add_executable(thread-trace-api-single-test)
target_sources(
thread-trace-api-single-test PRIVATE main.cpp trace_callbacks.cpp single_dispatch.cpp
kernel_branch.cpp kernel_lds.cpp)
target_link_libraries(
thread-trace-api-single-test
PRIVATE rocprofiler-sdk::rocprofiler-sdk rocprofiler-sdk::tests-build-flags
rocprofiler-sdk::tests-common-library)
add_test(NAME thread-trace-api-single-test
COMMAND $<TARGET_FILE:thread-trace-api-single-test>)
set_tests_properties(
thread-trace-api-single-test
PROPERTIES TIMEOUT 10 LABELS "integration-tests" ENVIRONMENT
"${ROCPROFILER_MEMCHECK_PRELOAD_ENV}" FAIL_REGULAR_EXPRESSION
"${ROCPROFILER_DEFAULT_FAIL_REGEX}")
# Multi dispatch test
add_executable(thread-trace-api-multi-test)
target_sources(
thread-trace-api-multi-test PRIVATE main.cpp trace_callbacks.cpp multi_dispatch.cpp
kernel_branch.cpp kernel_lds.cpp)
target_link_libraries(
thread-trace-api-multi-test
PRIVATE rocprofiler-sdk::rocprofiler-sdk rocprofiler-sdk::tests-build-flags
rocprofiler-sdk::tests-common-library)
add_test(NAME thread-trace-api-multi-test
COMMAND $<TARGET_FILE:thread-trace-api-multi-test>)
set_tests_properties(
thread-trace-api-multi-test
PROPERTIES TIMEOUT 10 LABELS "integration-tests" ENVIRONMENT
"${ROCPROFILER_MEMCHECK_PRELOAD_ENV}" FAIL_REGULAR_EXPRESSION
"${ROCPROFILER_DEFAULT_FAIL_REGEX}")
# Agent profiling test
add_executable(thread-trace-api-agent-test)
target_sources(thread-trace-api-agent-test PRIVATE main.cpp trace_callbacks.cpp agent.cpp
kernel_branch.cpp kernel_lds.cpp)
target_link_libraries(
thread-trace-api-agent-test
PRIVATE rocprofiler-sdk::rocprofiler-sdk rocprofiler-sdk::tests-build-flags
rocprofiler-sdk::tests-common-library)
add_test(NAME thread-trace-api-agent-test
COMMAND $<TARGET_FILE:thread-trace-api-agent-test>)
set_tests_properties(
thread-trace-api-agent-test
PROPERTIES TIMEOUT 10 LABELS "integration-tests" ENVIRONMENT
"${ROCPROFILER_MEMCHECK_PRELOAD_ENV}" FAIL_REGULAR_EXPRESSION
"${ROCPROFILER_DEFAULT_FAIL_REGEX}")
@@ -0,0 +1,214 @@
// MIT License
//
// Copyright (c) 2024-2025 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.
//
// undefine NDEBUG so asserts are implemented
#ifdef NDEBUG
# undef NDEBUG
#endif
#include "trace_callbacks.hpp"
#include <set>
#define C_API_BEGIN \
try \
{
#define C_API_END \
} \
catch(std::exception & e) \
{ \
std::cerr << "Error in " << __FILE__ << ':' << __LINE__ << ' ' << e.what() << std::endl; \
} \
catch(...) { std::cerr << "Error in " << __FILE__ << ':' << __LINE__ << std::endl; }
namespace ATTTest
{
namespace Agent
{
rocprofiler_client_id_t* client_id = nullptr;
rocprofiler_context_id_t agent_ctx = {};
rocprofiler_context_id_t tracing_ctx = {};
void
dispatch_tracing_callback(rocprofiler_callback_tracing_record_t record,
rocprofiler_user_data_t* /* user_data */,
void* /* userdata */)
{
if(record.kind != ROCPROFILER_CALLBACK_TRACING_KERNEL_DISPATCH) return;
if(record.phase == ROCPROFILER_CALLBACK_PHASE_EXIT) return;
assert(record.payload);
auto* rdata = static_cast<rocprofiler_callback_tracing_kernel_dispatch_data_t*>(record.payload);
int dispatch_id = (int) rdata->dispatch_info.dispatch_id;
auto get_int_var = [](const char* var_name, int def) {
const char* var = getenv(var_name);
if(var) return atoi(var);
return def;
};
static int begin_dispatch = get_int_var("ROCPROFILER_THREAD_TRACE_BEGIN", 1);
static int end_dispatch = get_int_var("ROCPROFILER_THREAD_TRACE_END", 4);
static std::atomic<bool> isprofiling{false};
static std::mutex mut;
static std::set<int> captured_ids;
if(record.phase == ROCPROFILER_CALLBACK_PHASE_ENTER)
{
if(dispatch_id == begin_dispatch)
{
ROCPROFILER_CALL(rocprofiler_start_context(agent_ctx), "context start");
isprofiling.store(true);
}
if(isprofiling && dispatch_id <= end_dispatch)
{
std::unique_lock<std::mutex> lk(mut);
captured_ids.insert(dispatch_id);
}
return;
}
assert(record.phase == ROCPROFILER_CALLBACK_PHASE_NONE);
if(!isprofiling) return;
std::unique_lock<std::mutex> lk(mut);
captured_ids.erase(dispatch_id);
if(!captured_ids.empty()) return;
bool _exp = true;
if(!isprofiling.compare_exchange_strong(_exp, false, std::memory_order_relaxed)) return;
ROCPROFILER_CALL(rocprofiler_stop_context(agent_ctx), "context stop");
}
rocprofiler_status_t
query_available_agents(rocprofiler_agent_version_t /* version */,
const void** agents,
size_t num_agents,
void* user_data)
{
rocprofiler_user_data_t user{};
user.ptr = user_data;
for(size_t idx = 0; idx < num_agents; idx++)
{
const auto* agent = static_cast<const rocprofiler_agent_v0_t*>(agents[idx]);
if(agent->type != ROCPROFILER_AGENT_TYPE_GPU) continue;
std::vector<rocprofiler_thread_trace_parameter_t> parameters;
parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_TARGET_CU, 1});
parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_SIMD_SELECT, 0xF});
parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_BUFFER_SIZE, 0x6000000});
parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_SHADER_ENGINE_MASK, 0x11});
parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_SERIALIZE_ALL, 0});
ROCPROFILER_CALL(
rocprofiler_configure_device_thread_trace_service(agent_ctx,
agent->id,
parameters.data(),
parameters.size(),
Callbacks::shader_data_callback,
user),
"thread trace service configure");
}
return ROCPROFILER_STATUS_SUCCESS;
}
int
tool_init(rocprofiler_client_finalize_t fini_func, void* tool_data)
{
(void) fini_func;
ROCPROFILER_CALL(rocprofiler_create_context(&tracing_ctx), "context creation");
ROCPROFILER_CALL(rocprofiler_create_context(&agent_ctx), "context creation");
ROCPROFILER_CALL(
rocprofiler_configure_callback_tracing_service(tracing_ctx,
ROCPROFILER_CALLBACK_TRACING_CODE_OBJECT,
nullptr,
0,
Callbacks::tool_codeobj_tracing_callback,
tool_data),
"code object tracing service configure");
ROCPROFILER_CALL(
rocprofiler_configure_callback_tracing_service(tracing_ctx,
ROCPROFILER_CALLBACK_TRACING_KERNEL_DISPATCH,
nullptr,
0,
dispatch_tracing_callback,
tool_data),
"dispatch tracing service configure");
ROCPROFILER_CALL(rocprofiler_query_available_agents(ROCPROFILER_AGENT_INFO_VERSION_0,
&query_available_agents,
sizeof(rocprofiler_agent_t),
tool_data),
"Failed to find GPU agents");
int valid_ctx = 0;
ROCPROFILER_CALL(rocprofiler_context_is_valid(agent_ctx, &valid_ctx), "validity check");
assert(valid_ctx != 0);
ROCPROFILER_CALL(rocprofiler_context_is_valid(tracing_ctx, &valid_ctx), "validity check");
assert(valid_ctx != 0);
ROCPROFILER_CALL(rocprofiler_start_context(tracing_ctx), "context start");
// no errors
return 0;
}
void
tool_fini(void* tool_data)
{
Callbacks::finalize_json(tool_data);
delete static_cast<Callbacks::ToolData*>(tool_data);
}
} // namespace Agent
} // namespace ATTTest
extern "C" rocprofiler_tool_configure_result_t*
rocprofiler_configure(uint32_t /* version */,
const char* /* runtime_version */,
uint32_t priority,
rocprofiler_client_id_t* id)
{
// only activate if main tool
if(priority > 0) return nullptr;
// set the client name
id->name = "ATT_test_agent";
// store client info
ATTTest::Agent::client_id = id;
// create configure data
static auto cfg =
rocprofiler_tool_configure_result_t{sizeof(rocprofiler_tool_configure_result_t),
&ATTTest::Agent::tool_init,
&ATTTest::Agent::tool_fini,
new Callbacks::ToolData{"att_agent_test/"}};
// return pointer to configure data
return &cfg;
}
@@ -0,0 +1,39 @@
// MIT License
//
// Copyright (c) 2023-2025 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.
//
// undefine NDEBUG so asserts are implemented
#ifdef NDEBUG
# undef NDEBUG
#endif
#include <cstdint>
#include "hip/hip_runtime.h"
__global__ void
branching_kernel(float* __restrict__ a, const float* __restrict__ b, const float* __restrict__ c)
{
size_t index = blockDim.x * blockIdx.x + threadIdx.x;
if(blockIdx.x % 2 == 0)
a[index] = b[index] + c[index];
else
a[index] = b[index] * c[index] - 2.0f;
}
@@ -0,0 +1,56 @@
// MIT License
//
// Copyright (c) 2023-2025 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.
//
// undefine NDEBUG so asserts are implemented
#ifdef NDEBUG
# undef NDEBUG
#endif
#include <cstdint>
#include "hip/hip_runtime.h"
#define SHM_SIZE 64
__global__ void
looping_lds_kernel(float* __restrict__ a,
const float* __restrict__ b,
const float* __restrict__ c,
size_t size,
size_t loopcount)
{
__shared__ float interm[SHM_SIZE];
size_t index = blockDim.x * blockIdx.x + threadIdx.x;
for(size_t i = index; i < size; i += blockDim.x * gridDim.x)
interm[threadIdx.x % SHM_SIZE] = b[index] + threadIdx.x;
for(size_t it = 0; it < loopcount; it++)
{
__syncthreads();
float value = interm[(it + threadIdx.x + SHM_SIZE / 2) % SHM_SIZE];
__syncthreads();
interm[threadIdx.x % SHM_SIZE] += value;
}
a[index] = interm[threadIdx.x % SHM_SIZE] + c[index];
}
@@ -0,0 +1,96 @@
// MIT License
//
// Copyright (c) 2023-2025 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.
//
// undefine NDEBUG so asserts are implemented
#ifdef NDEBUG
# undef NDEBUG
#endif
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include "hip/hip_runtime.h"
// Two waves per SIMD on MI300
#define DATA_SIZE (304 * 64 * 4 * 2)
#define HIP_API_CALL(CALL) assert((CALL) == hipSuccess)
#define SHM_SIZE 64
#define LOOPCOUNT 4
__global__ void
branching_kernel(float* __restrict__ a, const float* __restrict__ b, const float* __restrict__ c);
__global__ void
looping_lds_kernel(float* __restrict__ a,
const float* __restrict__ b,
const float* __restrict__ c,
size_t size,
size_t loopcount);
class hipMemory
{
public:
hipMemory(size_t size)
{
HIP_API_CALL(hipMalloc(&ptr, size * sizeof(float)));
HIP_API_CALL(hipMemset(ptr, 0, size * sizeof(float)));
}
~hipMemory()
{
if(ptr) HIP_API_CALL(hipFree(ptr));
}
float* ptr = nullptr;
};
int
main(int /*argc*/, char** /*argv*/)
{
hipMemory src1(DATA_SIZE);
hipMemory src2(DATA_SIZE);
hipMemory dst(DATA_SIZE);
HIP_API_CALL(hipDeviceSynchronize());
for(size_t i = 0; i < LOOPCOUNT; i++)
{
hipLaunchKernelGGL(
branching_kernel, dim3(DATA_SIZE / 64), dim3(64), 0, 0, dst.ptr, src1.ptr, src2.ptr);
HIP_API_CALL(hipGetLastError());
hipLaunchKernelGGL(looping_lds_kernel,
dim3(DATA_SIZE / 64),
dim3(64),
0,
0,
dst.ptr,
src1.ptr,
src2.ptr,
DATA_SIZE,
LOOPCOUNT);
HIP_API_CALL(hipGetLastError());
HIP_API_CALL(hipDeviceSynchronize());
}
return 0;
}
@@ -0,0 +1,158 @@
// MIT License
//
// Copyright (c) 2023-2025 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.
//
// undefine NDEBUG so asserts are implemented
#ifdef NDEBUG
# undef NDEBUG
#endif
#include "trace_callbacks.hpp"
constexpr double WAVE_RATIO_TOLERANCE = 0.05;
constexpr size_t NUM_KERNELS = 5;
namespace ATTTest
{
namespace Multi
{
rocprofiler_client_id_t* client_id = nullptr;
rocprofiler_thread_trace_control_flags_t
dispatch_callback(rocprofiler_agent_id_t /* agent */,
rocprofiler_queue_id_t /* queue_id */,
rocprofiler_async_correlation_id_t /* correlation_id */,
rocprofiler_kernel_id_t /* kernel_id */,
rocprofiler_dispatch_id_t /* dispatch_id */,
void* userdata,
rocprofiler_user_data_t* dispatch_userdata)
{
static std::atomic<size_t> count{0};
if(count.fetch_add(1) > NUM_KERNELS) return ROCPROFILER_THREAD_TRACE_CONTROL_NONE;
assert(userdata && "Dispatch callback passed null!");
dispatch_userdata->ptr = userdata;
return ROCPROFILER_THREAD_TRACE_CONTROL_START_AND_STOP;
}
int
tool_init(rocprofiler_client_finalize_t /* fini_func */, void* tool_data)
{
static rocprofiler_context_id_t client_ctx = {0};
ROCPROFILER_CALL(rocprofiler_create_context(&client_ctx), "context creation");
ROCPROFILER_CALL(
rocprofiler_configure_callback_tracing_service(client_ctx,
ROCPROFILER_CALLBACK_TRACING_CODE_OBJECT,
nullptr,
0,
Callbacks::tool_codeobj_tracing_callback,
tool_data),
"code object tracing service configure");
std::vector<rocprofiler_thread_trace_parameter_t> params{};
params.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_SERIALIZE_ALL, 1});
std::vector<rocprofiler_agent_id_t> agents{};
ROCPROFILER_CALL(
rocprofiler_query_available_agents(
ROCPROFILER_AGENT_INFO_VERSION_0,
[](rocprofiler_agent_version_t, const void** _agents, size_t _num_agents, void* _data) {
auto* agent_v = static_cast<std::vector<rocprofiler_agent_id_t>*>(_data);
for(size_t i = 0; i < _num_agents; ++i)
{
auto* agent = static_cast<const rocprofiler_agent_v0_t*>(_agents[i]);
if(agent->type == ROCPROFILER_AGENT_TYPE_GPU) agent_v->emplace_back(agent->id);
}
return ROCPROFILER_STATUS_SUCCESS;
},
sizeof(rocprofiler_agent_v0_t),
&agents),
"Failed to iterate agents");
for(auto id : agents)
{
ROCPROFILER_CALL(
rocprofiler_configure_dispatch_thread_trace_service(client_ctx,
id,
params.data(),
params.size(),
dispatch_callback,
Callbacks::shader_data_callback,
tool_data),
"thread trace service configure");
}
int valid_ctx = 0;
ROCPROFILER_CALL(rocprofiler_context_is_valid(client_ctx, &valid_ctx),
"context validity check");
if(valid_ctx == 0)
{
// notify rocprofiler that initialization failed
// and all the contexts, buffers, etc. created
// should be ignored
return -1;
}
ROCPROFILER_CALL(rocprofiler_start_context(client_ctx), "context start");
// no errors
return 0;
}
void
tool_fini(void* tool_data)
{
Callbacks::finalize_json(tool_data);
delete static_cast<Callbacks::ToolData*>(tool_data);
}
} // namespace Multi
} // namespace ATTTest
extern "C" rocprofiler_tool_configure_result_t*
rocprofiler_configure(uint32_t /* version */,
const char* /* runtime_version */,
uint32_t priority,
rocprofiler_client_id_t* id)
{
// only activate if main tool
if(priority > 0) return nullptr;
// set the client name
id->name = "ATT_test_multi_dispatch";
// store client info
ATTTest::Multi::client_id = id;
// create configure data
static auto cfg = rocprofiler_tool_configure_result_t{
sizeof(rocprofiler_tool_configure_result_t),
&ATTTest::Multi::tool_init,
&ATTTest::Multi::tool_fini,
reinterpret_cast<void*>(new Callbacks::ToolData{"att_multi_test/"})};
// return pointer to configure data
return &cfg;
}
@@ -0,0 +1,168 @@
// MIT License
//
// Copyright (c) 2023-2025 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.
//
// undefine NDEBUG so asserts are implemented
#ifdef NDEBUG
# undef NDEBUG
#endif
#include "trace_callbacks.hpp"
constexpr double WAVE_RATIO_TOLERANCE = 0.05;
namespace ATTTest
{
namespace Single
{
rocprofiler_client_id_t* client_id = nullptr;
rocprofiler_thread_trace_control_flags_t
dispatch_callback(rocprofiler_agent_id_t /* agent */,
rocprofiler_queue_id_t /* queue_id */,
rocprofiler_async_correlation_id_t /* correlation_id */,
rocprofiler_kernel_id_t kernel_id,
rocprofiler_dispatch_id_t /* dispatch_id */,
void* userdata,
rocprofiler_user_data_t* dispatch_userdata)
{
C_API_BEGIN
assert(userdata && "Dispatch callback passed null!");
auto& tool = *reinterpret_cast<Callbacks::ToolData*>(userdata);
dispatch_userdata->ptr = userdata;
static std::string_view desired_func_name = "branching_kernel";
try
{
auto& kernel_name = tool.kernel_id_to_kernel_name.at(kernel_id);
if(kernel_name.find(desired_func_name) == std::string::npos)
return ROCPROFILER_THREAD_TRACE_CONTROL_NONE;
return ROCPROFILER_THREAD_TRACE_CONTROL_START_AND_STOP;
} catch(...)
{
std::cerr << "Could not find kernel id: " << kernel_id << std::endl;
}
C_API_END
return ROCPROFILER_THREAD_TRACE_CONTROL_NONE;
}
int
tool_init(rocprofiler_client_finalize_t /* fini_func */, void* tool_data)
{
static rocprofiler_context_id_t client_ctx = {0};
ROCPROFILER_CALL(rocprofiler_create_context(&client_ctx), "context creation");
ROCPROFILER_CALL(
rocprofiler_configure_callback_tracing_service(client_ctx,
ROCPROFILER_CALLBACK_TRACING_CODE_OBJECT,
nullptr,
0,
Callbacks::tool_codeobj_tracing_callback,
tool_data),
"code object tracing service configure");
std::vector<rocprofiler_agent_id_t> agents{};
ROCPROFILER_CALL(
rocprofiler_query_available_agents(
ROCPROFILER_AGENT_INFO_VERSION_0,
[](rocprofiler_agent_version_t, const void** _agents, size_t _num_agents, void* _data) {
auto* agent_v = static_cast<std::vector<rocprofiler_agent_id_t>*>(_data);
for(size_t i = 0; i < _num_agents; ++i)
{
auto* agent = static_cast<const rocprofiler_agent_v0_t*>(_agents[i]);
if(agent->type == ROCPROFILER_AGENT_TYPE_GPU) agent_v->emplace_back(agent->id);
}
return ROCPROFILER_STATUS_SUCCESS;
},
sizeof(rocprofiler_agent_v0_t),
&agents),
"Failed to iterate agents");
for(auto id : agents)
{
ROCPROFILER_CALL(
rocprofiler_configure_dispatch_thread_trace_service(client_ctx,
id,
nullptr,
0,
dispatch_callback,
Callbacks::shader_data_callback,
tool_data),
"thread trace service configure");
}
int valid_ctx = 0;
ROCPROFILER_CALL(rocprofiler_context_is_valid(client_ctx, &valid_ctx),
"context validity check");
if(valid_ctx == 0)
{
// notify rocprofiler that initialization failed
// and all the contexts, buffers, etc. created
// should be ignored
return -1;
}
ROCPROFILER_CALL(rocprofiler_start_context(client_ctx), "context start");
// no errors
return 0;
}
void
tool_fini(void* tool_data)
{
Callbacks::finalize_json(tool_data);
delete static_cast<Callbacks::ToolData*>(tool_data);
}
} // namespace Single
} // namespace ATTTest
extern "C" rocprofiler_tool_configure_result_t*
rocprofiler_configure(uint32_t /* version */,
const char* /* runtime_version */,
uint32_t priority,
rocprofiler_client_id_t* id)
{
// only activate if main tool
if(priority > 0) return nullptr;
// set the client name
id->name = "ATT_test_single_dispatch";
// store client info
ATTTest::Single::client_id = id;
// create configure data
static auto cfg = rocprofiler_tool_configure_result_t{
sizeof(rocprofiler_tool_configure_result_t),
&ATTTest::Single::tool_init,
&ATTTest::Single::tool_fini,
reinterpret_cast<void*>(new Callbacks::ToolData{"att_single_test/"})};
// return pointer to configure data
return &cfg;
}
@@ -0,0 +1,172 @@
// MIT License
//
// Copyright (c) 2023-2025 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.
//
// undefine NDEBUG so asserts are implemented
#ifdef NDEBUG
# undef NDEBUG
#endif
#include "trace_callbacks.hpp"
#include <rocprofiler-sdk/cxx/codeobj/code_printing.hpp>
#ifdef ENABLE_ATT_FILES
# include <nlohmann/json.hpp>
#endif
#include <unistd.h>
#include <cassert>
#include <fstream>
namespace Callbacks
{
using code_obj_load_data_t = rocprofiler_callback_tracing_code_object_load_data_t;
using kernel_symbol_data_t = rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t;
void
tool_codeobj_tracing_callback(rocprofiler_callback_tracing_record_t record,
rocprofiler_user_data_t* /* user_data */,
void* userdata)
{
C_API_BEGIN
if(record.kind != ROCPROFILER_CALLBACK_TRACING_CODE_OBJECT) return;
if(record.phase != ROCPROFILER_CALLBACK_PHASE_LOAD) return;
assert(userdata && "Dispatch callback passed null!");
auto& tool = *reinterpret_cast<Callbacks::ToolData*>(userdata);
if(record.operation == ROCPROFILER_CODE_OBJECT_DEVICE_KERNEL_SYMBOL_REGISTER)
{
auto* data = static_cast<kernel_symbol_data_t*>(record.payload);
tool.kernel_id_to_kernel_name.emplace(data->kernel_id, data->kernel_name);
}
if(record.operation != ROCPROFILER_CODE_OBJECT_LOAD) return;
auto* data = static_cast<code_obj_load_data_t*>(record.payload);
static std::atomic<int> filecnt{0};
std::string name = "codeobj_" + std::to_string(filecnt.fetch_add(1)) + ".out";
#ifdef ENABLE_ATT_FILES
if(std::string_view(data->uri).find("file:///") == 0)
{
rocprofiler::sdk::codeobj::disassembly::CodeObjectBinary binary(data->uri);
std::ofstream file(tool.out_dir + name, std::ios::binary);
assert(file.is_open() && "Could not open codeobj file for writing");
file.write((char*) binary.buffer.data(), binary.buffer.size());
}
else
{
std::ofstream file(tool.out_dir + name, std::ios::binary);
file.write((char*) data->memory_base, data->memory_size);
}
#endif
auto _lk = std::unique_lock{tool.mut};
tool.codeobjs.push_back(
{data->load_delta, data->load_size, data->code_object_id, name, data->uri});
C_API_END
}
void
shader_data_callback(rocprofiler_agent_id_t agent,
int64_t se_id,
void* se_data,
size_t data_size,
rocprofiler_user_data_t userdata)
{
C_API_BEGIN
assert(userdata.ptr && "Dispatch callback passed null!");
auto& tool = *reinterpret_cast<Callbacks::ToolData*>(userdata.ptr);
std::string name = "agent_" + std::to_string(agent.handle) + "_shader_engine_" +
std::to_string(se_id) + "_" + std::to_string(agent.handle) + ".att";
#ifdef ENABLE_ATT_FILES
{
std::ofstream file(tool.out_dir + name, std::ios::binary);
assert(file.is_open() && "Could not open ATT file for writing");
file.write((char*) se_data, data_size);
}
#endif
assert(se_data);
assert(data_size);
auto _lk = std::unique_lock{tool.mut};
tool.att_files.push_back(name);
C_API_END
}
void
finalize_json(void* userdata)
{
assert(userdata && "Dispatch callback passed null!");
auto& tool = *reinterpret_cast<Callbacks::ToolData*>(userdata);
auto _lk = std::unique_lock{tool.mut};
assert(!tool.att_files.empty());
#ifdef ENABLE_ATT_FILES
nlohmann::json att_json;
for(auto& file : tool.att_files)
att_json.push_back(file);
nlohmann::json codeobj_json;
nlohmann::json snapshot_json;
for(auto& file : tool.codeobjs)
{
nlohmann::json codeobj;
codeobj["code_object_id"] = file.id;
codeobj["load_delta"] = file.addr;
codeobj["load_size"] = file.size;
codeobj["uri"] = file.uri;
codeobj["filename"] = file.filename;
codeobj_json.push_back(codeobj);
nlohmann::json pair_json;
pair_json["key"] = file.id;
pair_json["value"] = file.filename;
snapshot_json.push_back(pair_json);
}
nlohmann::json tool_json;
tool_json["strings"]["att_files"] = att_json;
tool_json["code_objects"] = codeobj_json;
tool_json["strings"]["code_object_snapshot_files"] = snapshot_json;
nlohmann::json array;
array.push_back(tool_json);
nlohmann::json sdk_json;
sdk_json["rocprofiler-sdk-tool"] = array;
std::ofstream json_file(tool.out_dir + (std::to_string(getpid()) + "_results.json"));
assert(json_file.is_open() && "Could not open json file for writing!");
json_file << sdk_json;
#endif
}
} // namespace Callbacks
@@ -0,0 +1,112 @@
// MIT License
//
// Copyright (c) 2023-2025 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 <rocprofiler-sdk/buffer.h>
#include <rocprofiler-sdk/callback_tracing.h>
#include <rocprofiler-sdk/experimental/thread_trace.h>
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/registration.h>
#include <rocprofiler-sdk/rocprofiler.h>
#include <atomic>
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <mutex>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
#define ROCPROFILER_VAR_NAME_COMBINE(X, Y) X##Y
#define ROCPROFILER_VARIABLE(X, Y) ROCPROFILER_VAR_NAME_COMBINE(X, Y)
#define C_API_BEGIN \
try \
{
#define C_API_END \
} \
catch(std::exception & e) \
{ \
std::cerr << "Error in " << __FILE__ << ':' << __LINE__ << ' ' << e.what() << std::endl; \
} \
catch(...) { std::cerr << "Error in " << __FILE__ << ':' << __LINE__ << std::endl; }
#define ROCPROFILER_CALL(result, msg) \
{ \
rocprofiler_status_t CHECKSTATUS = result; \
if(CHECKSTATUS != ROCPROFILER_STATUS_SUCCESS) \
{ \
std::string status_msg = rocprofiler_get_status_string(CHECKSTATUS); \
std::cerr << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg \
<< " failed with error code " << CHECKSTATUS << ": " << status_msg \
<< std::endl; \
std::stringstream errmsg{}; \
errmsg << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg " failure (" \
<< status_msg << ")"; \
throw std::runtime_error(errmsg.str()); \
} \
}
namespace Callbacks
{
struct CodeobjInfo
{
int64_t addr = 0;
size_t size = 0;
size_t id = 0;
std::string filename{};
std::string uri{};
};
struct ToolData
{
ToolData(const char* out)
: out_dir(out){};
std::string out_dir{};
std::mutex mut{};
std::vector<CodeobjInfo> codeobjs{};
std::vector<std::string> att_files{};
std::unordered_map<uint64_t, std::string> kernel_id_to_kernel_name = {};
};
void
tool_codeobj_tracing_callback(rocprofiler_callback_tracing_record_t record,
rocprofiler_user_data_t*,
void* callback_data);
void
shader_data_callback(rocprofiler_agent_id_t agent,
int64_t se_id,
void* se_data,
size_t data_size,
rocprofiler_user_data_t userdata);
void
finalize_json(void* userdata);
} // namespace Callbacks