Files
TheRock/rocm-systems/projects/rocprofiler-systems/source/bin/rocprof-sys-attach/rocprof-sys-attach.cpp
T
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

285 lines
7.8 KiB
C++

// Copyright (c) Advanced Micro Devices, Inc.
// SPDX-License-Identifier: MIT
#include "common/env_vars.hpp"
#include "common/path.hpp"
#include "logger/debug.hpp"
#include <spdlog/fmt/ranges.h>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
#include <vector>
#include <rocprofiler-sdk-rocattach/rocattach.h>
namespace
{
struct attach_options
{
int pid = -1;
std::string output_path = {};
std::vector<std::string> profile_format = {};
};
void
print_usage(const char* prog_name)
{
std::cout << "Usage: " << prog_name << " -p <pid> [OPTIONS]\n"
<< "\n"
<< "Attach to a running process for profiling.\n"
<< "\n"
<< "Options:\n"
<< " -p <pid> Process ID to attach to (required)\n"
<< " -o, --output PATH Output path for profiling results\n"
<< " -F, --format FORMAT[,FORMAT,...]\n"
<< " Output format(s): perfetto, rocpd\n"
<< " -h, --help Show this help message\n"
<< "\n"
<< "Environment variables:\n"
<< " ROCPROFSYS_OUTPUT_PATH Output directory for profiling data\n"
<< " ROCPROFSYS_TRACE Enable perfetto trace output\n"
<< " ROCPROFSYS_USE_ROCPD Enable rocpd database output\n"
<< " ROCPROF_ATTACH_TOOL_LIBRARY Path to the tool library\n"
<< "\n"
<< "Once attached, press ENTER to detach from the process.\n";
}
void
setup_tool_library_env()
{
const auto* attach_tool_library_env_name = "ROCPROF_ATTACH_TOOL_LIBRARY";
const auto* rocp_tool_libraries_env_name = "ROCP_TOOL_LIBRARIES";
const auto* output_use_current_time_env_name =
rocprofsys::env_vars::OUTPUT_USE_CURRENT_TIME;
const auto* reattach_add_session_id_env_name =
rocprofsys::env_vars::REATTACH_ADD_SESSION_ID;
// enable the use of the current time for the output path
setenv(output_use_current_time_env_name, "true", 1);
// enable the re-attach to add a session ID to the output path
setenv(reattach_add_session_id_env_name, "true", 1);
const auto* existing = std::getenv(attach_tool_library_env_name);
if(existing != nullptr)
{
setenv(rocp_tool_libraries_env_name, existing, 0);
LOG_INFO("Using tool library: {}", existing);
return;
}
const auto path =
rocprofsys::common::path::get_internal_libpath("librocprof-sys-dl.so");
if(!path.empty())
{
setenv(attach_tool_library_env_name, path.c_str(), 0);
setenv(rocp_tool_libraries_env_name, path.c_str(), 0);
LOG_INFO("Using tool library: {}", path);
}
}
void
setup_output_env(const std::string& output_path)
{
const auto* existing_output_path = getenv(rocprofsys::env_vars::OUTPUT_PATH);
if(output_path.empty() && existing_output_path != nullptr)
{
LOG_INFO("Output path: {}", existing_output_path);
return;
}
const auto* const pwd = getenv("PWD");
const auto output =
output_path.empty() ? fmt::format("{}/rocprof-sys-output", pwd) : output_path;
setenv(rocprofsys::env_vars::OUTPUT_PATH, output.c_str(), 1);
LOG_INFO("Output path: {}", output);
}
void
setup_output_format_env(const std::vector<std::string>& formats)
{
if(formats.empty()) return;
auto has_format = [&formats](const std::string& fmt) {
return std::find(formats.begin(), formats.end(), fmt) != formats.end();
};
// setenv("ROCPROFSYS_PROFILE", "false", 1);
if(has_format("perfetto") || has_format("rocpd"))
{
setenv(rocprofsys::env_vars::TRACE, has_format("perfetto") ? "true" : "false", 1);
setenv(rocprofsys::env_vars::USE_ROCPD, has_format("rocpd") ? "true" : "false",
1);
}
LOG_INFO("Output format: {}", fmt::join(formats, " "));
}
bool
is_option(const char* arg, const char* short_opt, const char* long_opt)
{
return std::strcmp(arg, short_opt) == 0 || std::strcmp(arg, long_opt) == 0;
}
const char*
consume_arg(int& i, int argc, char* argv[], const char* opt_name)
{
if(i + 1 >= argc)
{
LOG_ERROR("{} requires an argument.", opt_name);
std::exit(EXIT_FAILURE);
}
return argv[++i];
}
void
parse_pid(attach_options& opts, const char* arg)
{
try
{
opts.pid = std::stoi(arg);
if(opts.pid <= 0)
{
LOG_ERROR("PID must be a positive integer.");
std::exit(EXIT_FAILURE);
}
} catch(const std::exception&)
{
LOG_ERROR("Invalid PID '{}'.", arg);
std::exit(EXIT_FAILURE);
}
}
void
parse_formats(attach_options& opts, const char* arg)
{
std::string token;
std::stringstream ss(arg);
while(std::getline(ss, token, ','))
{
if(token == "perfetto" || token == "rocpd")
{
opts.profile_format.push_back(token);
}
else
{
LOG_ERROR("Invalid format '{}'. Valid options: perfetto, rocpd", token);
std::exit(EXIT_FAILURE);
}
}
}
attach_options
parse_args(int argc, char* argv[])
{
attach_options opts;
for(int i = 1; i < argc; ++i)
{
const char* arg = argv[i];
if(is_option(arg, "-h", "--help"))
{
print_usage(argv[0]);
std::exit(EXIT_SUCCESS);
}
if(is_option(arg, "-p", "--pid"))
{
parse_pid(opts, consume_arg(i, argc, argv, "-p"));
continue;
}
if(is_option(arg, "-o", "--output"))
{
opts.output_path = consume_arg(i, argc, argv, "-o/--output");
continue;
}
if(is_option(arg, "-F", "--format"))
{
parse_formats(opts, consume_arg(i, argc, argv, "-F/--format"));
continue;
}
LOG_ERROR("Unknown option '{}'.", arg);
print_usage(argv[0]);
std::exit(EXIT_FAILURE);
}
return opts;
}
void
print_banner()
{
std::cout << R"(
____ ___ ____ __ __ ______ ______ _____ _____ __ __ ____ _ _____ _____ _ ____ _ _
| _ \ / _ \ / ___| \/ | / ___\ \ / / ___|_ _| ____| \/ / ___| / \|_ _|_ _|/ \ / ___| | | |
| |_) | | | | | | |\/| | \___ \\ V /\___ \ | | | _| | |\/| \___ \ / _ \ | | | | / _ \| | | |_| |
| _ <| |_| | |___| | | | ___) || | ___) || | | |___| | | |___) | / ___ \| | | |/ ___ \ |___| _ |
|_| \_\\___/ \____|_| |_| |____/ |_| |____/ |_| |_____|_| |_|____/ /_/ \_\_| |_/_/ \_\____|_| |_|
)" << "\n";
}
} // namespace
int
main(int argc, char* argv[])
{
print_banner();
if(argc < 2)
{
print_usage(argv[0]);
return EXIT_FAILURE;
}
auto opts = parse_args(argc, argv);
if(opts.pid < 0)
{
LOG_ERROR("-p <pid> is required.");
print_usage(argv[0]);
return EXIT_FAILURE;
}
setup_tool_library_env();
setup_output_env(opts.output_path);
setup_output_format_env(opts.profile_format);
const auto pid = opts.pid;
LOG_INFO("Trying to attach to process {}", pid);
auto result = rocattach_attach(pid);
if(result != ROCATTACH_STATUS_SUCCESS)
{
LOG_ERROR("Failed to attach to process {}", pid);
return EXIT_FAILURE;
}
LOG_INFO("Attached to process {}. Press ENTER to detach.", pid);
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
result = rocattach_detach(pid);
if(result != ROCATTACH_STATUS_SUCCESS)
{
LOG_ERROR("Failed to detach from process {}", pid);
return EXIT_FAILURE;
}
LOG_INFO("Detached from process {}", pid);
return EXIT_SUCCESS;
}