samples/api_callback_tracing/client.cpp

samples/api_callback_tracing/client.cpp#

Rocprofiler SDK Developer API: samples/api_callback_tracing/client.cpp
Rocprofiler SDK Developer API 0.5.0
ROCm Profiling API and tools
samples/api_callback_tracing/client.cpp

Iterates over all the arguments for the traced function (when available). This is particularly useful when tools want to annotate traces with the function arguments. See for a usage example.

Iterates over all the arguments for the traced function (when available). This is particularly useful when tools want to annotate traces with the function arguments. See for a usage example. It is recommended to use this function when the record phase is ROCPROFILER_CALLBACK_PHASE_EXIT or ROCPROFILER_CALLBACK_PHASE_NONE. When the phase is ROCPROFILER_CALLBACK_PHASE_ENTER, the function may have output parameters which have not set. In the case of an output parameter with one level of indirection, e.g. int* output_len, this is considered safe since the output parameter is either null or, in the worst case scenario, pointing to an uninitialized value which will result in garbage values to be stringified. However, if the output parameter has more than one level of indirection, e.g. const char** output_name, this can result in a segmentation fault because the dereferenced output parameter may be uninitialized and point to an invalid address. E.g.:

struct dim3
{
int x;
int y;
int z;
};
static dim3 default_dims = {.x = 1, .y = 1, .z = 1};
void set_dim_x(int val, dim3* output_dims) { output_dims->x = val; }
void get_default_dims(dim3** output_dims) { *output_dims = default_dims; }
int main()
{
dim3 my_dims; // uninitialized value. x, y, and z may be set to random values
dim3* current_dims; // uninitialized pointer. May be set to invalid address
set_dim_x(3, &my_dims); // if rocprofiler-sdk wrapped this function and tried to stringify
// in the enter phase, dereferencing my_dims is not problematic
// since there is an actual dim3 allocation
get_default_dims(&current_dims); // if rocprofiler-sdk wrapped this function,
// and tried to stringify in the enter phase,
// current_dims may point to an address outside
// of the address space of this process and
// cause a segfault
}
int main(int argc, char *argv[])
Parameters
[in]recordRecord provided by service callback
[in]callbackThe callback function which will be invoked for each argument
[in]max_dereference_countIn the callback enter phase, certain arguments may be output parameters which have not been set. When the output parameter has multiple levels of indirection, it may be invalid to dereference the output parameter more than once and doing so may result in a segmentation fault. Thus, it is recommended to set this parameter to a maximum value of 1 when the phase is ROCPROFILER_CALLBACK_PHASE_ENTER to ensure that output parameters which point to uninitialized pointers do not cause segmentation faults.
[in]user_dataData to be passed to each invocation of the callback
// MIT License
//
// Copyright (c) 2023 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
/**
* @file samples/api_callback_tracing/client.cpp
*
* @brief Example rocprofiler client (tool)
*/
#include "client.hpp"
#include "common/call_stack.hpp"
#include "common/defines.hpp"
#include "common/filesystem.hpp"
#include "common/name_info.hpp"
#include <cassert>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <mutex>
#include <ratio>
#include <string>
#include <string_view>
#include <unordered_set>
#include <vector>
namespace client
{
namespace
{
using common::call_stack_t;
using common::callback_name_info;
using common::source_location;
rocprofiler_client_id_t* client_id = nullptr;
rocprofiler_client_finalize_t client_fini_func = nullptr;
rocprofiler_context_id_t client_ctx = {};
void
print_call_stack(const call_stack_t& _call_stack)
{
common::print_call_stack("api_callback_trace.log", _call_stack);
}
void
tool_tracing_ctrl_callback(rocprofiler_callback_tracing_record_t record,
void* client_data)
{
auto* ctx = static_cast<rocprofiler_context_id_t*>(client_data);
{
ROCPROFILER_CALL(rocprofiler_stop_context(*ctx), "pausing client context");
}
{
ROCPROFILER_CALL(rocprofiler_start_context(*ctx), "resuming client context");
}
}
void
tool_tracing_callback(rocprofiler_callback_tracing_record_t record,
void* callback_data)
{
assert(callback_data != nullptr);
auto now = std::chrono::steady_clock::now().time_since_epoch().count();
uint64_t dt = 0;
user_data->value = now;
else
dt = (now - user_data->value);
auto info = std::stringstream{};
info << std::left << "tid=" << record.thread_id << ", cid=" << std::setw(3)
<< record.correlation_id.internal << ", kind=" << record.kind
<< ", operation=" << std::setw(3) << record.operation << ", phase=" << record.phase
<< ", dt_nsec=" << std::setw(6) << dt;
auto info_data_cb = [](rocprofiler_callback_tracing_kind_t,
uint32_t arg_num,
const void* const arg_value_addr,
int32_t indirection_count,
const char* arg_type,
const char* arg_name,
const char* arg_value_str,
int32_t dereference_count,
void* cb_data) -> int {
auto& dss = *static_cast<std::stringstream*>(cb_data);
dss << ((arg_num == 0) ? "(" : ", ");
dss << arg_num << ": " << arg_name << "=" << arg_value_str;
(void) arg_value_addr;
(void) arg_type;
(void) indirection_count;
(void) dereference_count;
return 0;
};
int32_t max_deref = (record.phase == ROCPROFILER_CALLBACK_PHASE_ENTER) ? 1 : 2;
auto info_data = std::stringstream{};
record, info_data_cb, max_deref, static_cast<void*>(&info_data)),
"Failure iterating trace operation args");
auto info_data_str = info_data.str();
if(!info_data_str.empty()) info << " " << info_data_str << ")";
static auto _mutex = std::mutex{};
_mutex.lock();
static_cast<call_stack_t*>(callback_data)
->emplace_back(source_location{__FUNCTION__, __FILE__, __LINE__, info.str()});
_mutex.unlock();
}
void
tool_control_init(rocprofiler_context_id_t& primary_ctx)
{
// Create a specialized (throw-away) context for handling ROCTx profiler pause and resume.
// A separate context is used because if the context that is associated with roctxProfilerPause
// disabled that same context, a call to roctxProfilerResume would be ignored because the
// context that enables the callback for that API call is disabled.
auto cntrl_ctx = rocprofiler_context_id_t{};
ROCPROFILER_CALL(rocprofiler_create_context(&cntrl_ctx), "control context creation failed");
// enable callback marker tracing with only the pause/resume operations
cntrl_ctx,
nullptr,
0,
tool_tracing_ctrl_callback,
&primary_ctx),
"callback tracing service failed to configure");
// start the context so that it is always active
ROCPROFILER_CALL(rocprofiler_start_context(cntrl_ctx), "start of control context");
}
int
tool_init(rocprofiler_client_finalize_t fini_func, void* tool_data)
{
assert(tool_data != nullptr);
auto* call_stack_v = static_cast<call_stack_t*>(tool_data);
call_stack_v->emplace_back(source_location{__FUNCTION__, __FILE__, __LINE__, ""});
callback_name_info name_info = common::get_callback_id_names();
for(const auto& itr : name_info)
{
auto name_idx = std::stringstream{};
name_idx << " [" << std::setw(3) << itr.value << "]";
call_stack_v->emplace_back(
source_location{"rocprofiler_callback_tracing_kind_names " + name_idx.str(),
__FILE__,
__LINE__,
std::string{itr.name}});
for(auto [didx, ditr] : itr.items())
{
auto operation_idx = std::stringstream{};
operation_idx << " [" << std::setw(3) << didx << "]";
call_stack_v->emplace_back(source_location{
"rocprofiler_callback_tracing_kind_operation_names" + operation_idx.str(),
__FILE__,
__LINE__,
std::string{"- "} + std::string{*ditr}});
}
}
client_fini_func = fini_func;
ROCPROFILER_CALL(rocprofiler_create_context(&client_ctx), "context creation failed");
// enable the control
tool_control_init(client_ctx);
{
client_ctx, itr, nullptr, 0, tool_tracing_callback, tool_data),
"callback tracing service failed to configure");
}
ROCPROFILER_CALL(
nullptr,
0,
tool_tracing_callback,
tool_data),
"callback tracing service failed to configure");
ROCPROFILER_CALL(
nullptr,
0,
tool_tracing_callback,
tool_data),
"callback tracing service failed to configure");
ROCPROFILER_CALL(
nullptr,
0,
tool_tracing_callback,
tool_data),
"callback tracing service failed to configure");
int valid_ctx = 0;
ROCPROFILER_CALL(rocprofiler_context_is_valid(client_ctx, &valid_ctx),
"failure checking context validity");
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), "rocprofiler context start failed");
// no errors
return 0;
}
void
tool_fini(void* tool_data)
{
assert(tool_data != nullptr);
auto* _call_stack = static_cast<call_stack_t*>(tool_data);
_call_stack->emplace_back(source_location{__FUNCTION__, __FILE__, __LINE__, ""});
print_call_stack(*_call_stack);
delete _call_stack;
}
} // namespace
void
setup()
{}
void
shutdown()
{
if(client_id) client_fini_func(*client_id);
}
void
start()
{
ROCPROFILER_CALL(rocprofiler_start_context(client_ctx), "rocprofiler context start failed");
}
void
stop()
{
int status = 0;
ROCPROFILER_CALL(rocprofiler_is_initialized(&status), "failed to retrieve init status");
if(status != 0)
{
ROCPROFILER_CALL(rocprofiler_stop_context(client_ctx), "rocprofiler context stop failed");
}
}
} // namespace client
rocprofiler_configure(uint32_t version,
const char* runtime_version,
uint32_t priority,
{
// set the client name
id->name = "ExampleTool";
// store client info
client::client_id = id;
// compute major/minor/patch version info
uint32_t major = version / 10000;
uint32_t minor = (version % 10000) / 100;
uint32_t patch = version % 100;
// generate info string
auto info = std::stringstream{};
info << id->name << " (priority=" << priority << ") is using rocprofiler-sdk v" << major << "."
<< minor << "." << patch << " (" << runtime_version << ")";
std::clog << info.str() << std::endl;
// demonstration of alternative way to get the version info
{
auto version_info = std::array<uint32_t, 3>{};
ROCPROFILER_CALL(
rocprofiler_get_version(&version_info.at(0), &version_info.at(1), &version_info.at(2)),
"failed to get version info");
if(std::array<uint32_t, 3>{major, minor, patch} != version_info)
{
throw std::runtime_error{"version info mismatch"};
}
}
// data passed around all the callbacks
auto* client_tool_data = new std::vector<client::source_location>{};
// add first entry
client_tool_data->emplace_back(
client::source_location{__FUNCTION__, __FILE__, __LINE__, info.str()});
// create configure data
static auto cfg =
&client::tool_init,
&client::tool_fini,
static_cast<void*>(client_tool_data)};
// return pointer to configure data
return &cfg;
}
uint64_t value
usage example: set to process id, thread id, etc.
Definition fwd.h:488
rocprofiler_callback_phase_t phase
Definition fwd.h:583
rocprofiler_callback_tracing_kind_t kind
Definition fwd.h:581
rocprofiler_thread_id_t thread_id
Definition fwd.h:579
rocprofiler_tracing_operation_t operation
Definition fwd.h:582
rocprofiler_correlation_id_t correlation_id
Definition fwd.h:580
int32_t rocprofiler_tracing_operation_t
Tracing Operation ID. Depending on the kind, operations can be determined. If the value is equal to z...
Definition fwd.h:448
rocprofiler_callback_tracing_kind_t
Service Callback Tracing Kind.
Definition fwd.h:155
@ ROCPROFILER_CALLBACK_PHASE_EXIT
Callback invoked after to function execution.
Definition fwd.h:145
@ ROCPROFILER_CALLBACK_PHASE_ENTER
Callback invoked prior to function execution.
Definition fwd.h:142
@ ROCPROFILER_CALLBACK_TRACING_MARKER_CONTROL_API
Definition fwd.h:165
@ ROCPROFILER_CALLBACK_TRACING_HSA_AMD_EXT_API
Definition fwd.h:158
@ ROCPROFILER_CALLBACK_TRACING_MARKER_NAME_API
Definition fwd.h:167
@ ROCPROFILER_CALLBACK_TRACING_HSA_CORE_API
Definition fwd.h:157
@ ROCPROFILER_CALLBACK_TRACING_HIP_RUNTIME_API
Definition fwd.h:162
@ ROCPROFILER_CALLBACK_TRACING_HSA_IMAGE_EXT_API
Definition fwd.h:159
@ ROCPROFILER_CALLBACK_TRACING_MARKER_CORE_API
Definition fwd.h:164
@ ROCPROFILER_CALLBACK_TRACING_HSA_FINALIZE_EXT_API
Definition fwd.h:160
Context ID.
Definition fwd.h:502
User-assignable data type.
Definition fwd.h:487
rocprofiler_status_t rocprofiler_iterate_callback_tracing_kind_operation_args(rocprofiler_callback_tracing_record_t record, rocprofiler_callback_tracing_operation_args_cb_t callback, int32_t max_dereference_count, void *user_data)
rocprofiler_status_t rocprofiler_configure_callback_tracing_service(rocprofiler_context_id_t context_id, rocprofiler_callback_tracing_kind_t kind, const rocprofiler_tracing_operation_t *operations, unsigned long operations_count, rocprofiler_callback_tracing_cb_t callback, void *callback_args)
Configure Callback Tracing Service. The callback tracing service provides two synchronous callbacks a...
rocprofiler_status_t rocprofiler_start_context(rocprofiler_context_id_t context_id)
Start context.
rocprofiler_status_t rocprofiler_create_context(rocprofiler_context_id_t *context_id)
Create context.
rocprofiler_status_t rocprofiler_context_is_valid(rocprofiler_context_id_t context_id, int *status)
Query whether the context is valid.
rocprofiler_status_t rocprofiler_stop_context(rocprofiler_context_id_t context_id)
Stop context.
const char * name
clients should set this value for debugging
void(* rocprofiler_client_finalize_t)(rocprofiler_client_id_t)
Prototype for the function pointer provided to tool in rocprofiler_tool_initialize_t....
rocprofiler_tool_configure_result_t * rocprofiler_configure(uint32_t version, const char *runtime_version, uint32_t priority, rocprofiler_client_id_t *client_id)
This is the special function that tools define to enable rocprofiler support. The tool should return ...
rocprofiler_status_t rocprofiler_is_initialized(int *status)
Query whether rocprofiler has already scanned the binary for all the instances of rocprofiler_configu...
A client refers to an individual or entity engaged in the configuration of ROCprofiler services....
Data structure containing a initialization, finalization, and data.
rocprofiler_status_t rocprofiler_get_version(uint32_t *major, uint32_t *minor, uint32_t *patch)
Query the version of the installed library.
@ ROCPROFILER_MARKER_CONTROL_API_ID_roctxProfilerPause
Definition api_id.h:43
@ ROCPROFILER_MARKER_CONTROL_API_ID_roctxProfilerResume
Definition api_id.h:44