Files
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

1003 lines
38 KiB
C++

// Copyright (c) Advanced Micro Devices, Inc.
// SPDX-License-Identifier: MIT
#include "common/json_config.hpp"
#include <cstdint>
#include "common/env_vars.hpp"
#include <algorithm>
#include <array>
#include <charconv>
#include <fstream>
#include <iostream>
#include <set>
#include <sstream>
namespace rocprofsys
{
namespace json_config
{
namespace
{
/// Split a comma-separated string, trimming whitespace and lowercasing each token.
std::vector<std::string>
split_csv_lowercase(const std::string& input)
{
std::vector<std::string> tokens;
std::string token;
std::istringstream ss(input);
while(std::getline(ss, token, ','))
{
auto start = token.find_first_not_of(" \t");
auto end = token.find_last_not_of(" \t");
if(start != std::string::npos)
{
token = token.substr(start, end - start + 1);
for(auto& c : token)
c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
tokens.push_back(std::move(token));
}
}
return tokens;
}
std::vector<std::string>
collect_enabled_entry_names(const nlohmann::json& metrics_obj,
const std::set<std::string_view>& exclude = {})
{
std::vector<std::string> result;
for(const auto& [name, metric] : metrics_obj.items())
{
if(exclude.count(name) > 0) continue;
if(metric.is_object() && metric.contains("enabled") &&
metric["enabled"].get<bool>())
{
result.push_back(name);
}
}
return result;
}
std::string
join_with(const std::vector<std::string>& items, char sep)
{
std::string result;
for(const auto& item : items)
{
if(!result.empty()) result += sep;
result += item;
}
return result;
}
void
csv_to_json_enabled_flags(nlohmann::json& target_obj, const std::string& csv_value)
{
std::istringstream ss(csv_value);
std::string token;
while(std::getline(ss, token, ','))
{
auto start = token.find_first_not_of(" \t");
auto end = token.find_last_not_of(" \t");
if(start != std::string::npos)
{
token = token.substr(start, end - start + 1);
target_obj[token]["enabled"] = true;
}
}
}
} // namespace
std::string
json_value_to_string(const nlohmann::json& val)
{
if(val.is_string())
return val.get<std::string>();
else if(val.is_boolean())
return val.get<bool>() ? "true" : "false";
else if(val.is_number_integer())
return std::to_string(val.get<std::int64_t>());
else if(val.is_number_float())
return std::to_string(val.get<double>());
else if(val.is_array())
{
std::string result;
for(const auto& item : val)
{
if(!result.empty()) result += ',';
result += json_value_to_string(item);
}
return result;
}
return val.dump();
}
std::optional<std::string>
extract_setting_value(const nlohmann::json& obj)
{
if(obj.is_object())
{
if(obj.contains("value")) return json_value_to_string(obj["value"]);
if(obj.contains("enabled")) return obj["enabled"].get<bool>() ? "true" : "false";
}
else if(obj.is_boolean())
{
return obj.get<bool>() ? "true" : "false";
}
else if(!obj.is_null())
{
return json_value_to_string(obj);
}
return std::nullopt;
}
void
resolve_enabled(std::map<std::string, std::string>& result, const nlohmann::json& section,
std::string_view json_key, std::string_view env_var)
{
if(section.contains(json_key))
result[std::string{ env_var }] = section[json_key].get<bool>() ? "true" : "false";
}
void
resolve_value(std::map<std::string, std::string>& result, const nlohmann::json& section,
std::string_view json_key, std::string_view env_var)
{
if(section.contains(json_key))
{
if(auto val = extract_setting_value(section[json_key]))
result[std::string{ env_var }] = *val;
}
}
std::map<std::string, std::string>
resolve_schema_config(const nlohmann::json& config)
{
std::map<std::string, std::string> result;
// --- Tracing section ---
if(config.contains("tracing"))
{
const auto& tracing = config["tracing"];
resolve_enabled(result, tracing, "enabled", env_vars::TRACE);
if(tracing.contains("legacy"))
resolve_enabled(result, tracing["legacy"], "enabled", env_vars::TRACE_LEGACY);
resolve_value(result, tracing, "buffer_size_kb",
env_vars::PERFETTO_BUFFER_SIZE_KB);
resolve_value(result, tracing, "fill_policy", env_vars::PERFETTO_FILL_POLICY);
resolve_value(result, tracing, "backend", env_vars::PERFETTO_BACKEND);
resolve_value(result, tracing, "flush_period_ms",
env_vars::PERFETTO_FLUSH_PERIOD);
resolve_value(result, tracing, "region", env_vars::SELECTED_REGIONS);
}
// --- Profiling section ---
if(config.contains("profiling"))
{
const auto& profiling = config["profiling"];
resolve_enabled(result, profiling, "enabled", env_vars::PROFILE);
if(profiling.contains("flat_profile"))
{
resolve_enabled(result, profiling["flat_profile"], "enabled",
env_vars::FLAT_PROFILE);
}
}
// --- Sampling section ---
if(config.contains("sampling"))
{
const auto& sampling = config["sampling"];
resolve_enabled(result, sampling, "enabled", env_vars::USE_SAMPLING);
resolve_value(result, sampling, "timer", env_vars::SAMPLING_TIMER);
resolve_value(result, sampling, "frequency_hz", env_vars::SAMPLING_FREQ);
resolve_value(result, sampling, "delay_sec", env_vars::SAMPLING_DELAY);
resolve_value(result, sampling, "duration_sec", env_vars::SAMPLING_DURATION);
resolve_value(result, sampling, "cpus", env_vars::SAMPLING_CPUS);
resolve_value(result, sampling, "gpus", env_vars::SAMPLING_GPUS);
resolve_value(result, sampling, "ainics", env_vars::SAMPLING_AINICS);
resolve_value(result, sampling, "overflow_event",
env_vars::SAMPLING_OVERFLOW_EVENT);
}
// --- Domains section ---
if(config.contains("domains"))
{
const auto& domains = config["domains"];
// GPU domain (AMD SMI metrics)
if(domains.contains("gpu"))
{
const auto& gpu = domains["gpu"];
if(gpu.contains("enabled") && gpu["enabled"].get<bool>())
{
result[std::string{ env_vars::USE_AMD_SMI }] = "true";
result[std::string{ env_vars::USE_PROCESS_SAMPLING }] = "true";
if(gpu.contains("metrics"))
{
auto enabled = collect_enabled_entry_names(gpu["metrics"]);
if(!enabled.empty())
result[std::string{ env_vars::AMD_SMI_METRICS }] =
join_with(enabled, ',');
}
resolve_value(result, gpu, "sampling_rate_hz", env_vars::AMD_SMI_FREQ);
resolve_value(result, gpu, "process_sampling_freq",
env_vars::PROCESS_SAMPLING_FREQ);
resolve_value(result, gpu, "process_sampling_duration",
env_vars::PROCESS_SAMPLING_DURATION);
if(gpu.contains("ainic"))
resolve_enabled(result, gpu["ainic"], "enabled", env_vars::USE_AINIC);
if(gpu.contains("unified_memory_profiling"))
resolve_enabled(result, gpu["unified_memory_profiling"], "enabled",
env_vars::USE_UNIFIED_MEMORY_PROFILING);
}
}
// ROCm domain (API tracing)
if(domains.contains("rocm"))
{
const auto& rocm = domains["rocm"];
if(rocm.contains("enabled") && rocm["enabled"].get<bool>())
{
// Top-level rocm.enabled ensures tracing is on and default domains set
if(result.find(std::string{ env_vars::TRACE }) == result.end())
result[std::string{ env_vars::TRACE }] = "true";
}
if(rocm.contains("api_domains"))
{
std::vector<std::string> enabled_apis;
const auto& api_domains = rocm["api_domains"];
for(const auto& [name, api] : api_domains.items())
{
if(api.is_object() && api.contains("enabled") &&
api["enabled"].get<bool>())
{
enabled_apis.push_back(name);
}
}
if(!enabled_apis.empty())
{
std::string apis_str;
for(const auto& a : enabled_apis)
{
if(!apis_str.empty()) apis_str += ',';
apis_str += a;
}
result[std::string{ env_vars::ROCM_DOMAINS }] = apis_str;
}
}
if(rocm.contains("group_by_queue"))
{
resolve_enabled(result, rocm["group_by_queue"], "enabled",
env_vars::ROCM_GROUP_BY_QUEUE);
}
}
// CPU domain
if(domains.contains("cpu"))
{
const auto& cpu = domains["cpu"];
if(cpu.contains("cpu_freq_enabled"))
resolve_enabled(result, cpu["cpu_freq_enabled"], "enabled",
env_vars::CPU_FREQ_ENABLED);
if(cpu.contains("enabled") && cpu["enabled"].get<bool>())
{
result[std::string{ env_vars::USE_PROCESS_SAMPLING }] = "true";
if(cpu.contains("metrics"))
{
const auto& metrics = cpu["metrics"];
if(metrics.contains("freq") && metrics["freq"].contains("enabled") &&
metrics["freq"]["enabled"].get<bool>())
{
result[std::string{ env_vars::CPU_FREQ }] = "true";
}
auto enabled = collect_enabled_entry_names(metrics, { "freq" });
if(!enabled.empty())
result[std::string{ env_vars::CPU_METRICS }] =
join_with(enabled, ',');
}
}
}
// Parallel runtimes domain
if(domains.contains("parallel"))
{
const auto& parallel = domains["parallel"];
if(parallel.contains("runtimes"))
{
const auto& runtimes = parallel["runtimes"];
if(runtimes.contains("mpi") && runtimes["mpi"].contains("enabled") &&
runtimes["mpi"]["enabled"].get<bool>())
{
result[std::string{ env_vars::USE_MPIP }] = "true";
}
if(runtimes.contains("openmp") &&
runtimes["openmp"].contains("enabled") &&
runtimes["openmp"]["enabled"].get<bool>())
{
result[std::string{ env_vars::USE_OMPT }] = "true";
}
if(runtimes.contains("kokkos") &&
runtimes["kokkos"].contains("enabled") &&
runtimes["kokkos"]["enabled"].get<bool>())
{
result[std::string{ env_vars::USE_KOKKOSP }] = "true";
}
if(runtimes.contains("rccl") && runtimes["rccl"].contains("enabled") &&
runtimes["rccl"]["enabled"].get<bool>())
{
result[std::string{ env_vars::USE_RCCLP }] = "true";
}
if(runtimes.contains("shmem") && runtimes["shmem"].contains("enabled") &&
runtimes["shmem"]["enabled"].get<bool>())
{
result[std::string{ env_vars::USE_OPENSHMEM }] = "true";
}
if(runtimes.contains("ucx") && runtimes["ucx"].contains("enabled") &&
runtimes["ucx"]["enabled"].get<bool>())
{
result[std::string{ env_vars::USE_UCX }] = "true";
}
}
}
}
// --- Output section ---
if(config.contains("output"))
{
const auto& output = config["output"];
resolve_value(result, output, "path", env_vars::OUTPUT_PATH);
resolve_value(result, output, "unified_memory_output_path",
env_vars::UNIFIED_MEMORY_OUTPUT_PATH);
if(output.contains("time_output"))
resolve_enabled(result, output["time_output"], "enabled",
env_vars::TIME_OUTPUT);
if(output.contains("file_output"))
resolve_enabled(result, output["file_output"], "enabled",
env_vars::FILE_OUTPUT);
if(output.contains("rocpd_output"))
resolve_enabled(result, output["rocpd_output"], "enabled",
env_vars::USE_ROCPD);
if(output.contains("use_pid"))
resolve_enabled(result, output["use_pid"], "enabled", env_vars::USE_PID);
}
// --- Causal profiling section ---
if(config.contains("causal"))
{
const auto& causal = config["causal"];
resolve_enabled(result, causal, "enabled", env_vars::USE_CAUSAL);
resolve_value(result, causal, "mode", env_vars::CAUSAL_MODE);
resolve_value(result, causal, "backend", env_vars::CAUSAL_BACKEND);
resolve_value(result, causal, "binary_scope", env_vars::CAUSAL_BINARY_SCOPE);
resolve_value(result, causal, "binary_exclude", env_vars::CAUSAL_BINARY_EXCLUDE);
resolve_value(result, causal, "function_scope", env_vars::CAUSAL_FUNCTION_SCOPE);
resolve_value(result, causal, "function_exclude",
env_vars::CAUSAL_FUNCTION_EXCLUDE);
resolve_value(result, causal, "source_scope", env_vars::CAUSAL_SOURCE_SCOPE);
resolve_value(result, causal, "source_exclude", env_vars::CAUSAL_SOURCE_EXCLUDE);
if(causal.contains("end_to_end"))
resolve_enabled(result, causal["end_to_end"], "enabled",
env_vars::CAUSAL_END_TO_END);
resolve_value(result, causal, "delay_sec", env_vars::CAUSAL_DELAY);
resolve_value(result, causal, "duration_sec", env_vars::CAUSAL_DURATION);
resolve_value(result, causal, "random_seed", env_vars::CAUSAL_RANDOM_SEED);
}
// --- Hardware counters section ---
if(config.contains("hardware_counters"))
{
const auto& hw = config["hardware_counters"];
if(hw.contains("enabled") && hw["enabled"].get<bool>())
{
resolve_value(result, hw, "rocm_events", env_vars::ROCM_EVENTS);
resolve_value(result, hw, "papi_events", env_vars::PAPI_EVENTS);
resolve_value(result, hw, "gpu_perf_counters", env_vars::GPU_PERF_COUNTERS);
}
if(hw.contains("papi_multiplexing"))
resolve_enabled(result, hw["papi_multiplexing"], "enabled",
env_vars::PAPI_MULTIPLEXING_ENABLED);
}
// --- Advanced section ---
if(config.contains("advanced"))
{
const auto& adv = config["advanced"];
if(adv.contains("cpu_affinity"))
resolve_enabled(result, adv["cpu_affinity"], "enabled",
env_vars::CPU_AFFINITY);
if(adv.contains("collapse_threads"))
resolve_enabled(result, adv["collapse_threads"], "enabled",
env_vars::COLLAPSE_THREADS);
resolve_value(result, adv, "max_depth", env_vars::MAX_DEPTH);
resolve_value(result, adv, "trace_delay_sec", env_vars::TRACE_DELAY);
resolve_value(result, adv, "trace_duration_sec", env_vars::TRACE_DURATION);
resolve_value(result, adv, "verbose", env_vars::VERBOSE);
if(adv.contains("debug"))
resolve_enabled(result, adv["debug"], "enabled", env_vars::DEBUG_MODE);
resolve_value(result, adv, "timemory_components", env_vars::TIMEMORY_COMPONENTS);
resolve_value(result, adv, "network_interface", env_vars::NETWORK_INTERFACE);
resolve_value(result, adv, "trace_periods", env_vars::TRACE_PERIODS);
resolve_value(result, adv, "trace_period_clock_id",
env_vars::TRACE_PERIOD_CLOCK_ID);
}
return result;
}
std::map<std::string, std::string>
resolve_config(const nlohmann::json& config)
{
return resolve_schema_config(config);
}
std::optional<std::map<std::string, std::string>>
load_and_resolve(const std::string& filepath)
{
std::ifstream ifs{ filepath };
if(!ifs.is_open()) return std::nullopt;
try
{
auto config = nlohmann::json::parse(ifs);
return resolve_config(config);
} catch(const nlohmann::json::exception& e)
{
std::cerr << "[rocprof-sys] WARNING: Failed to parse config '" << filepath
<< "': " << e.what() << '\n';
return std::nullopt;
}
}
std::optional<config_metadata>
get_config_metadata(const nlohmann::json& config)
{
if(!config.contains("metadata")) return std::nullopt;
const auto& meta = config["metadata"];
config_metadata result;
if(meta.contains("name")) result.name = meta["name"].get<std::string>();
if(meta.contains("description"))
result.description = meta["description"].get<std::string>();
if(meta.contains("use_case")) result.use_case = meta["use_case"].get<std::string>();
if(meta.contains("category")) result.category = meta["category"].get<std::string>();
if(meta.contains("cli_flag")) result.cli_flag = meta["cli_flag"].get<std::string>();
return result;
}
std::optional<config_metadata>
load_config_metadata(const std::string& filepath)
{
std::ifstream ifs{ filepath };
if(!ifs.is_open()) return std::nullopt;
try
{
auto config = nlohmann::json::parse(ifs);
return get_config_metadata(config);
} catch(const nlohmann::json::exception&)
{
return std::nullopt;
}
}
std::string
expand_rocm_domain_shorthand(const std::string& shorthand)
{
using entry = std::pair<std::string_view, std::string_view>;
static constexpr std::array<entry, 13> shortcuts = { {
{ "hip", "hip_runtime_api" },
{ "hip_runtime", "hip_runtime_api" },
{ "hip_compiler", "hip_compiler_api" },
{ "hsa", "hsa_api" },
{ "kernel", "kernel_dispatch" },
{ "kernels", "kernel_dispatch" },
{ "memory", "memory_copy" },
{ "mem", "memory_copy" },
{ "scratch", "scratch_memory" },
{ "marker", "marker_api" },
{ "roctx", "marker_api" },
{ "rccl", "rccl_api" },
{ "hipfile", "hipfile_api" },
} };
auto it = std::find_if(shortcuts.begin(), shortcuts.end(),
[&](const entry& e) { return e.first == shorthand; });
if(it != shortcuts.end()) return std::string{ it->second };
return shorthand;
}
std::string
expand_rocm_domains(const std::string& domains_str)
{
auto tokens = split_csv_lowercase(domains_str);
std::string result;
for(const auto& t : tokens)
{
auto expanded = expand_rocm_domain_shorthand(t);
if(!result.empty()) result += ',';
result += expanded;
}
return result;
}
std::map<std::string, std::string>
expand_parallel_runtimes(const std::string& runtimes_str)
{
std::map<std::string, std::string> result;
// If empty or "all", enable all runtimes
if(runtimes_str.empty() || runtimes_str == "all")
{
result[std::string{ env_vars::USE_MPIP }] = "true";
result[std::string{ env_vars::USE_OMPT }] = "true";
result[std::string{ env_vars::USE_KOKKOSP }] = "true";
result[std::string{ env_vars::USE_RCCLP }] = "true";
return result;
}
using entry = std::pair<std::string_view, std::string_view>;
static constexpr std::array<entry, 9> shortcuts = { {
{ "mpi", env_vars::USE_MPIP },
{ "mpip", env_vars::USE_MPIP },
{ "openmp", env_vars::USE_OMPT },
{ "ompt", env_vars::USE_OMPT },
{ "omp", env_vars::USE_OMPT },
{ "kokkos", env_vars::USE_KOKKOSP },
{ "kokkosp", env_vars::USE_KOKKOSP },
{ "rccl", env_vars::USE_RCCLP },
{ "rcclp", env_vars::USE_RCCLP },
} };
for(const auto& token : split_csv_lowercase(runtimes_str))
{
auto it = std::find_if(shortcuts.begin(), shortcuts.end(),
[&](const entry& e) { return e.first == token; });
if(it != shortcuts.end()) result[std::string{ it->second }] = "true";
}
return result;
}
std::string
expand_gpu_metrics(const std::string& metrics_str)
{
if(metrics_str.empty()) return ""; // Use default
using entry = std::pair<std::string_view, std::string_view>;
static constexpr std::array<entry, 4> shortcuts = { {
{ "temperature", "temp" },
{ "usage", "busy" },
{ "utilization", "busy" },
{ "memory", "mem_usage" },
} };
std::string result;
for(const auto& token : split_csv_lowercase(metrics_str))
{
auto it = std::find_if(shortcuts.begin(), shortcuts.end(),
[&](const entry& e) { return e.first == token; });
if(!result.empty()) result += ',';
result += (it != shortcuts.end()) ? std::string{ it->second } : token;
}
return result;
}
std::optional<int>
safe_stoi(const std::string& s)
{
if(s.empty()) return std::nullopt;
int value = 0;
const auto result = std::from_chars(s.data(), s.data() + s.size(), value);
// Reject partial parses (e.g. "12.5" -> 12) by checking entire string was consumed
if(result.ec != std::errc{} || result.ptr != s.data() + s.size()) return std::nullopt;
return value;
}
std::optional<double>
safe_stod(const std::string& s)
{
if(s.empty()) return std::nullopt;
double value = 0.0;
#if defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L
// Prefer from_chars: locale-independent, zero-allocation
const auto result = std::from_chars(s.data(), s.data() + s.size(), value);
if(result.ec != std::errc{} || result.ptr != s.data() + s.size()) return std::nullopt;
#else
// Fallback for older compilers (GCC <11, Clang <16).
// Note: std::stod is locale-sensitive -assumes C/POSIX locale.
try
{
std::size_t pos = 0;
value = std::stod(s, &pos);
if(pos != s.size()) return std::nullopt;
} catch(const std::exception&)
{
return std::nullopt;
}
#endif
return value;
}
void
set_json_int(nlohmann::json& target, const std::string& value)
{
if(auto n = safe_stoi(value))
target = *n;
else
target = value;
}
void
set_json_double(nlohmann::json& target, const std::string& value)
{
if(auto n = safe_stod(value))
target = *n;
else
target = value;
}
bool
is_truthy(const std::string& v)
{
if(v == "1") return true;
if(v.size() < 2 || v.size() > 4) return false;
std::string lower;
lower.reserve(v.size());
for(auto c : v)
lower += static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
return lower == "true" || lower == "on" || lower == "yes";
}
void
export_enabled(nlohmann::json& config, const std::map<std::string, std::string>& env_map,
std::string_view env_var, const std::string& json_path_section,
const std::string& json_path_key)
{
auto it = env_map.find(std::string{ env_var });
if(it != env_map.end())
config[json_path_section][json_path_key]["enabled"] = is_truthy(it->second);
}
void
export_section_enabled(nlohmann::json& config,
const std::map<std::string, std::string>& env_map,
std::string_view env_var, const std::string& json_path_section)
{
auto it = env_map.find(std::string{ env_var });
if(it != env_map.end()) config[json_path_section]["enabled"] = is_truthy(it->second);
}
void
export_string_value(nlohmann::json& config,
const std::map<std::string, std::string>& env_map,
std::string_view env_var, const std::string& json_path_section,
const std::string& json_path_key)
{
auto it = env_map.find(std::string{ env_var });
if(it != env_map.end())
config[json_path_section][json_path_key]["value"] = it->second;
}
void
export_int_value(nlohmann::json& config,
const std::map<std::string, std::string>& env_map,
std::string_view env_var, const std::string& json_path_section,
const std::string& json_path_key)
{
auto it = env_map.find(std::string{ env_var });
if(it != env_map.end())
set_json_int(config[json_path_section][json_path_key]["value"], it->second);
}
void
export_double_value(nlohmann::json& config,
const std::map<std::string, std::string>& env_map,
std::string_view env_var, const std::string& json_path_section,
const std::string& json_path_key)
{
auto it = env_map.find(std::string{ env_var });
if(it != env_map.end())
set_json_double(config[json_path_section][json_path_key]["value"], it->second);
}
namespace
{
std::optional<std::string>
lookup(const std::map<std::string, std::string>& env_map, std::string_view key)
{
auto it = env_map.find(std::string{ key });
if(it != env_map.end()) return it->second;
return std::nullopt;
}
void
export_tracing_section(nlohmann::json& config,
const std::map<std::string, std::string>& env_map)
{
export_section_enabled(config, env_map, env_vars::TRACE, "tracing");
export_enabled(config, env_map, env_vars::TRACE_LEGACY, "tracing", "legacy");
export_int_value(config, env_map, env_vars::PERFETTO_BUFFER_SIZE_KB, "tracing",
"buffer_size_kb");
export_string_value(config, env_map, env_vars::PERFETTO_FILL_POLICY, "tracing",
"fill_policy");
export_string_value(config, env_map, env_vars::PERFETTO_BACKEND, "tracing",
"backend");
export_int_value(config, env_map, env_vars::PERFETTO_FLUSH_PERIOD, "tracing",
"flush_period_ms");
export_string_value(config, env_map, env_vars::SELECTED_REGIONS, "tracing", "region");
}
void
export_sampling_section(nlohmann::json& config,
const std::map<std::string, std::string>& env_map)
{
export_section_enabled(config, env_map, env_vars::USE_SAMPLING, "sampling");
export_int_value(config, env_map, env_vars::SAMPLING_FREQ, "sampling",
"frequency_hz");
export_string_value(config, env_map, env_vars::SAMPLING_TIMER, "sampling", "timer");
export_double_value(config, env_map, env_vars::SAMPLING_DELAY, "sampling",
"delay_sec");
export_double_value(config, env_map, env_vars::SAMPLING_DURATION, "sampling",
"duration_sec");
export_string_value(config, env_map, env_vars::SAMPLING_CPUS, "sampling", "cpus");
export_string_value(config, env_map, env_vars::SAMPLING_GPUS, "sampling", "gpus");
export_string_value(config, env_map, env_vars::SAMPLING_AINICS, "sampling", "ainics");
export_string_value(config, env_map, env_vars::SAMPLING_OVERFLOW_EVENT, "sampling",
"overflow_event");
}
void
export_domain_gpu(nlohmann::json& config,
const std::map<std::string, std::string>& env_map)
{
auto& gpu = config["domains"]["gpu"];
if(auto v = lookup(env_map, env_vars::USE_PROCESS_SAMPLING))
gpu["process_sampling"]["enabled"] = is_truthy(*v);
auto use_amd_smi = lookup(env_map, env_vars::USE_AMD_SMI);
if(!use_amd_smi) return;
gpu["enabled"] = is_truthy(*use_amd_smi);
if(auto metrics = lookup(env_map, env_vars::AMD_SMI_METRICS))
csv_to_json_enabled_flags(gpu["metrics"], *metrics);
if(auto freq = lookup(env_map, env_vars::AMD_SMI_FREQ))
set_json_int(gpu["sampling_rate_hz"]["value"], *freq);
if(auto freq = lookup(env_map, env_vars::PROCESS_SAMPLING_FREQ))
set_json_double(gpu["process_sampling_freq"]["value"], *freq);
if(auto dur = lookup(env_map, env_vars::PROCESS_SAMPLING_DURATION))
set_json_double(gpu["process_sampling_duration"]["value"], *dur);
if(auto v = lookup(env_map, env_vars::USE_AINIC))
gpu["ainic"]["enabled"] = is_truthy(*v);
if(auto v = lookup(env_map, env_vars::USE_UNIFIED_MEMORY_PROFILING))
gpu["unified_memory_profiling"]["enabled"] = is_truthy(*v);
}
void
export_domain_rocm(nlohmann::json& config,
const std::map<std::string, std::string>& env_map)
{
auto& rocm = config["domains"]["rocm"];
if(auto v = lookup(env_map, env_vars::ROCM_DOMAINS))
{
rocm["enabled"] = true;
csv_to_json_enabled_flags(rocm["api_domains"], *v);
}
if(auto v = lookup(env_map, env_vars::ROCM_GROUP_BY_QUEUE))
rocm["group_by_queue"]["enabled"] = is_truthy(*v);
}
void
export_domain_cpu(nlohmann::json& config,
const std::map<std::string, std::string>& env_map)
{
auto& cpu = config["domains"]["cpu"];
if(auto v = lookup(env_map, env_vars::CPU_FREQ_ENABLED))
cpu["cpu_freq_enabled"]["enabled"] = is_truthy(*v);
auto use_proc = lookup(env_map, env_vars::USE_PROCESS_SAMPLING);
auto cpu_freq = lookup(env_map, env_vars::CPU_FREQ);
if(use_proc && is_truthy(*use_proc) && cpu_freq && is_truthy(*cpu_freq))
{
cpu["enabled"] = true;
cpu["metrics"]["freq"]["enabled"] = true;
}
if(auto metrics = lookup(env_map, env_vars::CPU_METRICS))
{
cpu["enabled"] = true;
csv_to_json_enabled_flags(cpu["metrics"], *metrics);
}
}
void
export_domain_parallel(nlohmann::json& config,
const std::map<std::string, std::string>& env_map)
{
constexpr std::array<std::pair<std::string_view, std::string_view>, 6> runtimes{ {
{ env_vars::USE_MPIP, "mpi" },
{ env_vars::USE_OMPT, "openmp" },
{ env_vars::USE_KOKKOSP, "kokkos" },
{ env_vars::USE_RCCLP, "rccl" },
{ env_vars::USE_OPENSHMEM, "shmem" },
{ env_vars::USE_UCX, "ucx" },
} };
auto& runtimes_obj = config["domains"]["parallel"]["runtimes"];
for(const auto& [env_var, runtime_name] : runtimes)
{
if(auto v = lookup(env_map, env_var))
runtimes_obj[std::string{ runtime_name }]["enabled"] = is_truthy(*v);
}
}
void
export_hardware_counters(nlohmann::json& config,
const std::map<std::string, std::string>& env_map)
{
auto& hw = config["hardware_counters"];
if(auto v = lookup(env_map, env_vars::ROCM_EVENTS))
{
hw["enabled"] = true;
hw["rocm_events"]["value"] = *v;
}
if(auto v = lookup(env_map, env_vars::PAPI_EVENTS))
{
hw["enabled"] = true;
hw["papi_events"]["value"] = *v;
}
if(auto v = lookup(env_map, env_vars::GPU_PERF_COUNTERS))
{
hw["enabled"] = true;
hw["gpu_perf_counters"]["value"] = *v;
}
export_enabled(config, env_map, env_vars::PAPI_MULTIPLEXING_ENABLED,
"hardware_counters", "papi_multiplexing");
}
} // namespace
nlohmann::json
env_vars_to_json_schema(const std::map<std::string, std::string>& env_map)
{
nlohmann::json config;
export_tracing_section(config, env_map);
export_section_enabled(config, env_map, env_vars::PROFILE, "profiling");
export_enabled(config, env_map, env_vars::FLAT_PROFILE, "profiling", "flat_profile");
export_sampling_section(config, env_map);
export_domain_gpu(config, env_map);
export_domain_rocm(config, env_map);
export_domain_cpu(config, env_map);
export_domain_parallel(config, env_map);
export_string_value(config, env_map, env_vars::OUTPUT_PATH, "output", "path");
export_string_value(config, env_map, env_vars::UNIFIED_MEMORY_OUTPUT_PATH, "output",
"unified_memory_output_path");
export_enabled(config, env_map, env_vars::TIME_OUTPUT, "output", "time_output");
export_enabled(config, env_map, env_vars::FILE_OUTPUT, "output", "file_output");
export_enabled(config, env_map, env_vars::USE_ROCPD, "output", "rocpd_output");
export_enabled(config, env_map, env_vars::USE_PID, "output", "use_pid");
export_hardware_counters(config, env_map);
// --- Causal profiling ---
export_section_enabled(config, env_map, env_vars::USE_CAUSAL, "causal");
export_string_value(config, env_map, env_vars::CAUSAL_MODE, "causal", "mode");
export_string_value(config, env_map, env_vars::CAUSAL_BACKEND, "causal", "backend");
export_string_value(config, env_map, env_vars::CAUSAL_BINARY_SCOPE, "causal",
"binary_scope");
export_string_value(config, env_map, env_vars::CAUSAL_BINARY_EXCLUDE, "causal",
"binary_exclude");
export_string_value(config, env_map, env_vars::CAUSAL_FUNCTION_SCOPE, "causal",
"function_scope");
export_string_value(config, env_map, env_vars::CAUSAL_FUNCTION_EXCLUDE, "causal",
"function_exclude");
export_string_value(config, env_map, env_vars::CAUSAL_SOURCE_SCOPE, "causal",
"source_scope");
export_string_value(config, env_map, env_vars::CAUSAL_SOURCE_EXCLUDE, "causal",
"source_exclude");
export_enabled(config, env_map, env_vars::CAUSAL_END_TO_END, "causal", "end_to_end");
export_double_value(config, env_map, env_vars::CAUSAL_DELAY, "causal", "delay_sec");
export_double_value(config, env_map, env_vars::CAUSAL_DURATION, "causal",
"duration_sec");
export_int_value(config, env_map, env_vars::CAUSAL_RANDOM_SEED, "causal",
"random_seed");
// --- Advanced ---
export_int_value(config, env_map, env_vars::VERBOSE, "advanced", "verbose");
export_enabled(config, env_map, env_vars::DEBUG_MODE, "advanced", "debug");
export_int_value(config, env_map, env_vars::MAX_DEPTH, "advanced", "max_depth");
export_double_value(config, env_map, env_vars::TRACE_DELAY, "advanced",
"trace_delay_sec");
export_double_value(config, env_map, env_vars::TRACE_DURATION, "advanced",
"trace_duration_sec");
export_enabled(config, env_map, env_vars::CPU_AFFINITY, "advanced", "cpu_affinity");
export_enabled(config, env_map, env_vars::COLLAPSE_THREADS, "advanced",
"collapse_threads");
export_string_value(config, env_map, env_vars::TIMEMORY_COMPONENTS, "advanced",
"timemory_components");
export_string_value(config, env_map, env_vars::NETWORK_INTERFACE, "advanced",
"network_interface");
export_string_value(config, env_map, env_vars::TRACE_PERIODS, "advanced",
"trace_periods");
export_string_value(config, env_map, env_vars::TRACE_PERIOD_CLOCK_ID, "advanced",
"trace_period_clock_id");
// ========================================================================
// Intentionally excluded environment variables
// ========================================================================
//
// The following ROCPROFSYS_* env vars are NOT included in the JSON preset
// schema. These are internal runtime settings whose values depend on the
// specific invocation context or tooling infrastructure. Including them
// in presets would be harmful: a preset should describe *what* to profile,
// not *how* the profiler manages its own internals.
//
// Session-specific (depend on the invocation, not the profiling intent):
// ROCPROFSYS_CONFIG_FILE - Path to the user's config file; set at
// invocation time, not a profiling choice.
// ROCPROFSYS_OUTPUT_PREFIX - Per-run output prefix (e.g., test name);
// set by test harness or user per-run.
//
// Internal plumbing (implementation details users should not configure):
// ROCPROFSYS_ENABLED - Master profiler enable flag.
// Always true when running via CLI
// tools; setting to false in a
// preset would silently disable
// all profiling.
// ROCPROFSYS_SUPPRESS_CONFIG - Suppress config file loading.
// ROCPROFSYS_SUPPRESS_PARSING - Suppress config parsing.
// These are used internally by
// rocprof-sys-avail; setting them
// in a preset would break config
// file handling.
// ROCPROFSYS_CI - Internal CI mode flag.
// ROCPROFSYS_LOG_LEVEL - Diagnostic logging verbosity for
// the profiler itself; controls how
// the tool reports its own activity,
// not what/how to profile.
// ROCPROFSYS_TMPDIR - Base directory for temporary
// files; depends on the system's
// filesystem layout, not profiling
// intent.
return config;
}
std::string
export_config_as_json(const std::map<std::string, std::string>& env_vars,
const std::string& preset_name, std::string_view tool_name,
int indent)
{
auto config = env_vars_to_json_schema(env_vars);
if(!preset_name.empty())
{
config["metadata"]["name"] = preset_name;
std::string desc = "Exported configuration from rocprof-sys";
if(!tool_name.empty())
{
desc += '-';
desc += tool_name;
}
config["metadata"]["description"] = desc;
}
return config.dump(indent);
}
} // namespace json_config
} // namespace rocprofsys