attach: Formalize ROCAttach API (#1653)
* attach: Formalize ROCAttach API - Make ROCAttach public with public headers - Change detach to take a PID - attach and detach are now reentrant - Cleanup of states and signal handling in ptrace session - Fixes mixed up definition of ROCPROF_ATTACH_TOOL_LIBRARY - ROCPROF_ATTACH_TOOL_LIBRARY now always means the tool library loaded by the attachment target - ROCPROF_ATTACH_LIBRARY refers to the library used to perform attachment - Add direct call of rocprof-attach - Fix python library call of rocprof-attach - Function now named attach(), changed from main() * attach: rocprof-compute ROCAttach updates - Update to new library names - Correct usage of C lib detach * attach: add test for rocattach - Disable ASan, TSan, and UBSan for the new parallel-attach test - Lower log level for LSan tests, existing behavior from other tests --------- Co-authored-by: Ammar ELWazir <aelwazir@amd.com>
Αυτή η υποβολή περιλαμβάνεται σε:
+332
-131
@@ -26,77 +26,121 @@
|
||||
#include "lib/common/logging.hpp"
|
||||
#include "lib/common/static_object.hpp"
|
||||
|
||||
#include <rocprofiler-sdk/defines.h>
|
||||
#include <rocprofiler-sdk-rocattach/defines.h>
|
||||
#include <rocprofiler-sdk-rocattach/rocattach.h>
|
||||
#include <rocprofiler-sdk-rocattach/types.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
extern char** environ;
|
||||
|
||||
namespace common = ::rocprofiler::common;
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
namespace rocattach
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::unique_ptr<rocprofiler::attach::PTraceSession> ptrace_session;
|
||||
std::thread ptrace_thread;
|
||||
std::atomic<bool> finished_setup(false);
|
||||
} // namespace
|
||||
using session_t = rocprofiler::rocattach::PTraceSession;
|
||||
using session_list_t = std::map<int, session_t>;
|
||||
|
||||
ROCPROFILER_EXTERN_C_INIT
|
||||
int
|
||||
attach(uint32_t pid) ROCPROFILER_EXPORT;
|
||||
#define ROCATTACH_STATUS_STRING(CODE, MSG) \
|
||||
template <> \
|
||||
struct status_string<CODE> \
|
||||
{ \
|
||||
static constexpr auto name = #CODE; \
|
||||
static constexpr auto value = MSG; \
|
||||
};
|
||||
|
||||
int
|
||||
detach() ROCPROFILER_EXPORT;
|
||||
ROCPROFILER_EXTERN_C_FINI
|
||||
template <size_t Idx>
|
||||
struct status_string;
|
||||
|
||||
ROCATTACH_STATUS_STRING(ROCATTACH_STATUS_SUCCESS, "Success")
|
||||
ROCATTACH_STATUS_STRING(ROCATTACH_STATUS_ERROR, "General error")
|
||||
ROCATTACH_STATUS_STRING(ROCATTACH_STATUS_ERROR_INVALID_ARGUMENT, "Invalid function argument")
|
||||
ROCATTACH_STATUS_STRING(ROCATTACH_STATUS_ERROR_NOT_SUPPORTED,
|
||||
"Attachment not supported on this platform")
|
||||
ROCATTACH_STATUS_STRING(ROCATTACH_STATUS_ERROR_PTRACE_ERROR, "General ptrace error")
|
||||
ROCATTACH_STATUS_STRING(ROCATTACH_STATUS_ERROR_PTRACE_OPERATION_NOT_PERMITTED,
|
||||
"ptrace returned EPERM, operation not permitted")
|
||||
ROCATTACH_STATUS_STRING(ROCATTACH_STATUS_ERROR_PTRACE_PROCESS_NOT_FOUND,
|
||||
"ptrace returned ESRCH, no such process")
|
||||
|
||||
template <size_t Idx, size_t... Tail>
|
||||
const char*
|
||||
get_status_name(rocattach_status_t status, std::index_sequence<Idx, Tail...>)
|
||||
{
|
||||
if(status == Idx) return status_string<Idx>::name;
|
||||
// recursion until tail empty
|
||||
if constexpr(sizeof...(Tail) > 0)
|
||||
return get_status_name(status, std::index_sequence<Tail...>{});
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <size_t Idx, size_t... Tail>
|
||||
const char*
|
||||
get_status_string(rocattach_status_t status, std::index_sequence<Idx, Tail...>)
|
||||
{
|
||||
if(status == Idx) return status_string<Idx>::value;
|
||||
// recursion until tail empty
|
||||
if constexpr(sizeof...(Tail) > 0)
|
||||
return get_status_string(status, std::index_sequence<Tail...>{});
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
initialize_logging()
|
||||
{
|
||||
auto logging_cfg = rocprofiler::common::logging_config{.install_failure_handler = true};
|
||||
common::init_logging("ROCPROF", logging_cfg);
|
||||
common::init_logging("ROCATTACH", logging_cfg);
|
||||
FLAGS_colorlogtostderr = true;
|
||||
}
|
||||
|
||||
namespace
|
||||
session_list_t*
|
||||
get_sessions()
|
||||
{
|
||||
static auto*& session_list = rocprofiler::common::static_object<session_list_t>::construct();
|
||||
return session_list;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex>
|
||||
get_sessions_lock_guard()
|
||||
{
|
||||
static auto*& m = rocprofiler::common::static_object<std::mutex>::construct();
|
||||
return std::lock_guard(*CHECK_NOTNULL(m));
|
||||
}
|
||||
|
||||
// Helper function to allocate memory in target process and write data
|
||||
bool
|
||||
write_data_to_target(const std::string& description,
|
||||
rocattach_status_t
|
||||
write_data_to_target(session_t& session,
|
||||
const std::string& description,
|
||||
const std::vector<uint8_t>& data,
|
||||
void*& allocated_addr)
|
||||
{
|
||||
// Allocate memory in target process
|
||||
if(!ptrace_session->simple_mmap(allocated_addr, data.size()))
|
||||
auto status = ROCATTACH_STATUS_SUCCESS;
|
||||
status = session.simple_mmap(allocated_addr, data.size());
|
||||
if(status != ROCATTACH_STATUS_SUCCESS)
|
||||
{
|
||||
ROCP_ERROR << "Failed to allocate memory for " << description << " in target process";
|
||||
return false;
|
||||
}
|
||||
ROCP_TRACE << "Allocated memory for " << description << " at " << allocated_addr;
|
||||
|
||||
// Stop target process for writing
|
||||
if(!ptrace_session->stop())
|
||||
{
|
||||
ROCP_ERROR << "Failed to stop target process for " << description << " writing";
|
||||
return false;
|
||||
ROCP_ERROR << "[rocprofiler-sdk-rocattach] Failed to allocate memory for " << description
|
||||
<< " in target process pid " << session.get_pid();
|
||||
return status;
|
||||
}
|
||||
ROCP_TRACE << "[rocprofiler-sdk-rocattach] Allocated memory for " << description << " at "
|
||||
<< allocated_addr << " in target process pid " << session.get_pid();
|
||||
|
||||
// Write data to target process memory
|
||||
if(!ptrace_session->write(reinterpret_cast<size_t>(allocated_addr), data, data.size()))
|
||||
status = session.write(reinterpret_cast<size_t>(allocated_addr), data, data.size());
|
||||
if(status != ROCATTACH_STATUS_SUCCESS)
|
||||
{
|
||||
ROCP_ERROR << "Failed to write " << description << " to target process";
|
||||
return false;
|
||||
ROCP_ERROR << "[rocprofiler-sdk-rocattach] Failed to write " << description
|
||||
<< " to target process pid " << session.get_pid();
|
||||
return status;
|
||||
}
|
||||
|
||||
// Continue target process
|
||||
if(!ptrace_session->cont())
|
||||
{
|
||||
ROCP_ERROR << "Failed to continue target process after " << description << " writing";
|
||||
return false;
|
||||
}
|
||||
|
||||
ROCP_TRACE << "Wrote " << description << " to target process";
|
||||
return true;
|
||||
ROCP_TRACE << "[rocprofiler-sdk-rocattach] Wrote " << description << " to target process pid "
|
||||
<< session.get_pid();
|
||||
return status;
|
||||
}
|
||||
|
||||
// Helper function to build environment buffer
|
||||
@@ -112,11 +156,12 @@ build_environment_buffer()
|
||||
const char* var = *invars;
|
||||
if(strncmp("ROCP", var, 4) != 0)
|
||||
{
|
||||
// only take envvars starting with ROCP
|
||||
continue;
|
||||
}
|
||||
|
||||
var_count++;
|
||||
ROCP_TRACE << "Adding to environment buffer: " << var;
|
||||
ROCP_TRACE << "[rocprofiler-sdk-rocattach] Adding to environment buffer: " << var;
|
||||
|
||||
// Add variable name
|
||||
while(*var != '=')
|
||||
@@ -140,119 +185,275 @@ build_environment_buffer()
|
||||
|
||||
return environment_buffer;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
ROCPROFILER_EXTERN_C_INIT
|
||||
|
||||
void
|
||||
handle_ptrace_operations(uint32_t pid)
|
||||
rocattach_status_t
|
||||
setup(int pid)
|
||||
{
|
||||
// Setup attachement for rocprofiler
|
||||
ROCP_TRACE << "Attachment library called for pid " << pid;
|
||||
ptrace_session = std::make_unique<rocprofiler::attach::PTraceSession>(pid);
|
||||
ROCP_TRACE << "Attempting attachment to pid " << pid;
|
||||
if(!ptrace_session->attach())
|
||||
ROCP_TRACE << "[rocprofiler-sdk-rocattach] Attachment library rocattach_attach function called "
|
||||
"for pid "
|
||||
<< pid;
|
||||
|
||||
auto* sessions = CHECK_NOTNULL(get_sessions());
|
||||
session_t* session;
|
||||
{
|
||||
ROCP_ERROR << "Attachment failed to pid " << pid;
|
||||
ptrace_session->m_setup_status.store(ROCPROFILER_STATUS_ERROR_INVALID_ARGUMENT);
|
||||
finished_setup.store(true);
|
||||
return;
|
||||
auto lg = get_sessions_lock_guard();
|
||||
if(sessions->count(pid) > 0)
|
||||
{
|
||||
ROCP_ERROR << "[rocprofiler-sdk-rocattach] rocattach_attach called for pid " << pid
|
||||
<< ", which already has an active "
|
||||
"attachment session.";
|
||||
return ROCATTACH_STATUS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
sessions->emplace(pid, pid);
|
||||
session = &(sessions->at(pid));
|
||||
}
|
||||
ROCP_TRACE << "Attachment success to pid " << pid;
|
||||
auto status = ROCATTACH_STATUS_SUCCESS;
|
||||
|
||||
ROCP_TRACE << "[rocprofiler-sdk-rocattach] Attempting attachment to pid " << pid;
|
||||
status = session->attach();
|
||||
if(status != ROCATTACH_STATUS_SUCCESS)
|
||||
{
|
||||
ROCP_ERROR << "[rocprofiler-sdk-rocattach] Attachment failed to pid " << pid
|
||||
<< " with status code " << status;
|
||||
return status;
|
||||
}
|
||||
ROCP_TRACE << "[rocprofiler-sdk-rocattach] Attachment success to pid " << pid;
|
||||
|
||||
// Build and write environment buffer to target process
|
||||
auto environment_buffer = build_environment_buffer();
|
||||
void* environment_buffer_addr = nullptr;
|
||||
if(!write_data_to_target("environment buffer", environment_buffer, environment_buffer_addr))
|
||||
status = write_data_to_target(
|
||||
*session, "environment buffer", environment_buffer, environment_buffer_addr);
|
||||
if(status != ROCATTACH_STATUS_SUCCESS)
|
||||
{
|
||||
ptrace_session->m_setup_status.store(ROCPROFILER_STATUS_ERROR);
|
||||
finished_setup.store(true);
|
||||
return;
|
||||
return status;
|
||||
}
|
||||
|
||||
// Build and write tool library path to target process
|
||||
auto tool_lib_path_env =
|
||||
rocprofiler::common::get_env("ROCPROF_ATTACH_TOOL_LIBRARY", "librocprofiler-sdk-tool.so");
|
||||
const char* tool_lib_path = tool_lib_path_env.c_str();
|
||||
ROCP_TRACE << "Tool library path: " << tool_lib_path;
|
||||
ROCP_TRACE << "[rocprofiler-sdk-rocattach] Tool library path: " << tool_lib_path;
|
||||
|
||||
size_t tool_lib_path_len = strlen(tool_lib_path) + 1;
|
||||
std::vector<uint8_t> tool_lib_buffer(tool_lib_path, tool_lib_path + tool_lib_path_len);
|
||||
|
||||
void* tool_lib_path_addr = nullptr;
|
||||
if(!write_data_to_target("tool library path", tool_lib_buffer, tool_lib_path_addr))
|
||||
status =
|
||||
write_data_to_target(*session, "tool library path", tool_lib_buffer, tool_lib_path_addr);
|
||||
if(status != ROCATTACH_STATUS_SUCCESS)
|
||||
{
|
||||
ptrace_session->m_setup_status.store(ROCPROFILER_STATUS_ERROR);
|
||||
finished_setup.store(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute the attach function with both parameters
|
||||
if(!ptrace_session->call_function("librocprofiler-register.so",
|
||||
"rocprofiler_register_attach",
|
||||
environment_buffer_addr,
|
||||
tool_lib_path_addr))
|
||||
{
|
||||
ROCP_ERROR << "Failed to call attach function in target process " << pid;
|
||||
ptrace_session->m_setup_status.store(ROCPROFILER_STATUS_ERROR);
|
||||
finished_setup.store(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up - free the tool library path memory in target process
|
||||
if(!ptrace_session->simple_munmap(tool_lib_path_addr, tool_lib_path_len))
|
||||
{
|
||||
ROCP_ERROR << "Failed to free tool library path memory in target process";
|
||||
// Continue anyway since the main operation succeeded
|
||||
}
|
||||
ROCP_TRACE << "Cleaned up tool library path memory in target process";
|
||||
|
||||
// Allow main thread to continue
|
||||
finished_setup.store(true);
|
||||
if(!ptrace_session->handle_signals())
|
||||
{
|
||||
ROCP_ERROR << "Signal handling loop terminated unexepectedly for pid " << pid;
|
||||
// don't return, try to detach anyways
|
||||
}
|
||||
// Detach rocprofiler
|
||||
ROCP_TRACE << "Detaching rocprofiler from pid " << pid;
|
||||
if(!ptrace_session->call_function("librocprofiler-register.so", "rocprofiler_register_detach"))
|
||||
{
|
||||
ROCP_ERROR << "Failed to call detach function in target process";
|
||||
// don't return, try to detach anyways
|
||||
}
|
||||
ptrace_session->stop();
|
||||
ptrace_session->detach();
|
||||
ptrace_session.reset();
|
||||
}
|
||||
|
||||
int
|
||||
attach(uint32_t pid)
|
||||
{
|
||||
initialize_logging();
|
||||
ptrace_thread = std::thread(handle_ptrace_operations, pid);
|
||||
// Wait for ptrace thread to finish setting up
|
||||
while(!finished_setup.load())
|
||||
std::this_thread::yield();
|
||||
|
||||
auto status = ptrace_session->m_setup_status.load();
|
||||
if(status != ROCPROFILER_STATUS_SUCCESS)
|
||||
{
|
||||
ROCP_ERROR << "ptrace session failed with error code " << ptrace_session->m_setup_status;
|
||||
ptrace_thread.join();
|
||||
finished_setup.store(false);
|
||||
return status;
|
||||
}
|
||||
return ROCPROFILER_STATUS_SUCCESS;
|
||||
|
||||
uint64_t retval = 0;
|
||||
// Execute the attach function with both parameters
|
||||
status = session->call_function("librocprofiler-register.so",
|
||||
"rocprofiler_register_attach",
|
||||
retval,
|
||||
environment_buffer_addr,
|
||||
tool_lib_path_addr);
|
||||
if(status != ROCATTACH_STATUS_SUCCESS)
|
||||
{
|
||||
ROCP_ERROR
|
||||
<< "[rocprofiler-sdk-rocattach] Failed to call "
|
||||
"rocprofiler-register::rocprofiler_register_attach function in target process "
|
||||
<< pid << ". status: " << status;
|
||||
return status;
|
||||
}
|
||||
else if(retval != 0)
|
||||
{
|
||||
ROCP_ERROR
|
||||
<< "[rocprofiler-sdk-rocattach] rocprofiler-register::rocprofiler_register_attach "
|
||||
"function returned non-zero status in target process "
|
||||
<< pid << ". return: " << retval;
|
||||
return ROCATTACH_STATUS_ERROR;
|
||||
}
|
||||
|
||||
// Clean up - free the environment buffer and tool library path memory in target process
|
||||
status = session->simple_munmap(environment_buffer_addr, environment_buffer.size());
|
||||
if(status != ROCATTACH_STATUS_SUCCESS)
|
||||
{
|
||||
ROCP_ERROR << "[rocprofiler-sdk-rocattach] Failed to free environment buffer memory in "
|
||||
"target process "
|
||||
<< pid << ", continuing...";
|
||||
// Continue anyway since the main operation succeeded
|
||||
}
|
||||
ROCP_TRACE
|
||||
<< "[rocprofiler-sdk-rocattach] Cleaned up tool environment memory in target process "
|
||||
<< pid;
|
||||
|
||||
status = session->simple_munmap(tool_lib_path_addr, tool_lib_path_len);
|
||||
if(status != ROCATTACH_STATUS_SUCCESS)
|
||||
{
|
||||
ROCP_ERROR << "[rocprofiler-sdk-rocattach] Failed to free tool library path memory in "
|
||||
"target process "
|
||||
<< pid << ", continuing...";
|
||||
// Continue anyway since the main operation succeeded
|
||||
}
|
||||
ROCP_TRACE
|
||||
<< "[rocprofiler-sdk-rocattach] Cleaned up tool library path memory in target process "
|
||||
<< pid;
|
||||
return ROCATTACH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
detach()
|
||||
rocattach_status_t
|
||||
teardown(int pid)
|
||||
{
|
||||
ptrace_session->detach_ptrace_session();
|
||||
ptrace_thread.join();
|
||||
finished_setup.store(false);
|
||||
return ROCPROFILER_STATUS_SUCCESS;
|
||||
// Setup attachement for rocprofiler
|
||||
ROCP_TRACE << "[rocprofiler-sdk-rocattach] Attachment library rocattach_detach function called "
|
||||
"for pid "
|
||||
<< pid;
|
||||
|
||||
auto* sessions = CHECK_NOTNULL(get_sessions());
|
||||
session_t* session;
|
||||
{
|
||||
auto lg = get_sessions_lock_guard();
|
||||
if(sessions->count(pid) == 0)
|
||||
{
|
||||
ROCP_ERROR << "[rocprofiler-sdk-rocattach] rocattach_detach called for pid " << pid
|
||||
<< ", which has no active "
|
||||
"attachment session.";
|
||||
return ROCATTACH_STATUS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
session = &(sessions->at(pid));
|
||||
}
|
||||
auto status = ROCATTACH_STATUS_SUCCESS;
|
||||
|
||||
uint64_t retval = 0;
|
||||
// Execute the attach function with both parameters
|
||||
status =
|
||||
session->call_function("librocprofiler-register.so", "rocprofiler_register_detach", retval);
|
||||
if(status != ROCATTACH_STATUS_SUCCESS)
|
||||
{
|
||||
ROCP_ERROR
|
||||
<< "[rocprofiler-sdk-rocattach] Failed to call "
|
||||
"rocprofiler-register::rocprofiler_register_detach function in target process "
|
||||
<< pid << ". status: " << status;
|
||||
// continue to detach anyways
|
||||
}
|
||||
else if(retval != 0)
|
||||
{
|
||||
ROCP_ERROR
|
||||
<< "[rocprofiler-sdk-rocattach] rocprofiler-register::rocprofiler_register_detach "
|
||||
"function returned non-zero status in target process "
|
||||
<< pid << ". return: " << retval;
|
||||
// continue to detach anyways
|
||||
}
|
||||
|
||||
ROCP_TRACE << "[rocprofiler-sdk-rocattach] Attempting detachment to pid " << pid;
|
||||
status = session->detach();
|
||||
if(status != ROCATTACH_STATUS_SUCCESS)
|
||||
{
|
||||
ROCP_ERROR << "[rocprofiler-sdk-rocattach] Detachment failed from pid " << pid;
|
||||
return status;
|
||||
}
|
||||
ROCP_TRACE << "[rocprofiler-sdk-rocattach] Detachment success from pid " << pid;
|
||||
|
||||
{
|
||||
auto lg = get_sessions_lock_guard();
|
||||
sessions->erase(pid);
|
||||
}
|
||||
|
||||
return ROCATTACH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
ROCPROFILER_EXTERN_C_FINI
|
||||
} // namespace
|
||||
} // namespace rocattach
|
||||
} // namespace rocprofiler
|
||||
|
||||
ROCATTACH_EXTERN_C_INIT
|
||||
|
||||
rocattach_status_t
|
||||
rocattach_attach(int pid)
|
||||
{
|
||||
rocprofiler::rocattach::initialize_logging();
|
||||
|
||||
if(!rocprofiler::rocattach::PTraceSession::is_supported())
|
||||
{
|
||||
ROCP_ERROR << "[rocprofiler-sdk-attach] rocattach is not supported on this platform.";
|
||||
return ROCATTACH_STATUS_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
auto status = rocprofiler::rocattach::setup(pid);
|
||||
if(status != ROCATTACH_STATUS_SUCCESS)
|
||||
{
|
||||
ROCP_ERROR << "[rocprofiler-sdk-rocattach] rocattach_attach failed with error code "
|
||||
<< status;
|
||||
return status;
|
||||
}
|
||||
return ROCATTACH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
rocattach_status_t
|
||||
rocattach_detach(int pid)
|
||||
{
|
||||
rocprofiler::rocattach::initialize_logging();
|
||||
if(pid != 0)
|
||||
{
|
||||
auto status = rocprofiler::rocattach::teardown(pid);
|
||||
if(status != ROCATTACH_STATUS_SUCCESS)
|
||||
{
|
||||
ROCP_ERROR << "[rocprofiler-sdk-rocattach] rocattach_detach failed with error code "
|
||||
<< status;
|
||||
return status;
|
||||
}
|
||||
return ROCATTACH_STATUS_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
ROCP_INFO << "[rocprofiler-sdk-rocattach] rocattach_detach received pid=0, detaching from "
|
||||
"ALL sessions";
|
||||
std::vector<int> pids;
|
||||
{
|
||||
auto lg = rocprofiler::rocattach::get_sessions_lock_guard();
|
||||
for(auto& pair_itr : *(CHECK_NOTNULL(rocprofiler::rocattach::get_sessions())))
|
||||
{
|
||||
pids.emplace_back(pair_itr.first);
|
||||
}
|
||||
}
|
||||
|
||||
for(int pid_itr : pids)
|
||||
{
|
||||
rocprofiler::rocattach::teardown(pid_itr);
|
||||
}
|
||||
return ROCATTACH_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
rocattach_status_t
|
||||
rocattach_get_version(uint32_t* major, uint32_t* minor, uint32_t* patch)
|
||||
{
|
||||
*CHECK_NOTNULL(major) = ROCATTACH_VERSION_MAJOR;
|
||||
*CHECK_NOTNULL(minor) = ROCATTACH_VERSION_MINOR;
|
||||
*CHECK_NOTNULL(patch) = ROCATTACH_VERSION_PATCH;
|
||||
return ROCATTACH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
rocattach_status_t
|
||||
rocattach_get_version_triplet(rocattach_version_triplet_t* info)
|
||||
{
|
||||
*CHECK_NOTNULL(info) = {.major = ROCATTACH_VERSION_MAJOR,
|
||||
.minor = ROCATTACH_VERSION_MINOR,
|
||||
.patch = ROCATTACH_VERSION_PATCH};
|
||||
return ROCATTACH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
const char*
|
||||
rocattach_get_status_name(rocattach_status_t status)
|
||||
{
|
||||
return rocprofiler::rocattach::get_status_name(
|
||||
status, std::make_index_sequence<ROCATTACH_STATUS_LAST>{});
|
||||
}
|
||||
|
||||
const char*
|
||||
rocattach_get_status_string(rocattach_status_t status)
|
||||
{
|
||||
return rocprofiler::rocattach::get_status_string(
|
||||
status, std::make_index_sequence<ROCATTACH_STATUS_LAST>{});
|
||||
}
|
||||
|
||||
ROCATTACH_EXTERN_C_FINI
|
||||
|
||||
Αναφορά σε νέο ζήτημα
Block a user