// Copyright (c) Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT #include "common/json_config.hpp" #include #include "common/env_vars.hpp" #include #include #include #include #include #include #include namespace rocprofsys { namespace json_config { namespace { /// Split a comma-separated string, trimming whitespace and lowercasing each token. std::vector split_csv_lowercase(const std::string& input) { std::vector 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(std::tolower(static_cast(c))); tokens.push_back(std::move(token)); } } return tokens; } std::vector collect_enabled_entry_names(const nlohmann::json& metrics_obj, const std::set& exclude = {}) { std::vector 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()) { result.push_back(name); } } return result; } std::string join_with(const std::vector& 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(); else if(val.is_boolean()) return val.get() ? "true" : "false"; else if(val.is_number_integer()) return std::to_string(val.get()); else if(val.is_number_float()) return std::to_string(val.get()); 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 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() ? "true" : "false"; } else if(obj.is_boolean()) { return obj.get() ? "true" : "false"; } else if(!obj.is_null()) { return json_value_to_string(obj); } return std::nullopt; } void resolve_enabled(std::map& 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() ? "true" : "false"; } void resolve_value(std::map& 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 resolve_schema_config(const nlohmann::json& config) { std::map 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()) { 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()) { // 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 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()) { 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()) { 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()) { 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()) { result[std::string{ env_vars::USE_MPIP }] = "true"; } if(runtimes.contains("openmp") && runtimes["openmp"].contains("enabled") && runtimes["openmp"]["enabled"].get()) { result[std::string{ env_vars::USE_OMPT }] = "true"; } if(runtimes.contains("kokkos") && runtimes["kokkos"].contains("enabled") && runtimes["kokkos"]["enabled"].get()) { result[std::string{ env_vars::USE_KOKKOSP }] = "true"; } if(runtimes.contains("rccl") && runtimes["rccl"].contains("enabled") && runtimes["rccl"]["enabled"].get()) { result[std::string{ env_vars::USE_RCCLP }] = "true"; } if(runtimes.contains("shmem") && runtimes["shmem"].contains("enabled") && runtimes["shmem"]["enabled"].get()) { result[std::string{ env_vars::USE_OPENSHMEM }] = "true"; } if(runtimes.contains("ucx") && runtimes["ucx"].contains("enabled") && runtimes["ucx"]["enabled"].get()) { 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()) { 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 resolve_config(const nlohmann::json& config) { return resolve_schema_config(config); } std::optional> 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 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(); if(meta.contains("description")) result.description = meta["description"].get(); if(meta.contains("use_case")) result.use_case = meta["use_case"].get(); if(meta.contains("category")) result.category = meta["category"].get(); if(meta.contains("cli_flag")) result.cli_flag = meta["cli_flag"].get(); return result; } std::optional 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; static constexpr std::array 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 expand_parallel_runtimes(const std::string& runtimes_str) { std::map 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; static constexpr std::array 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; static constexpr std::array 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 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 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(std::tolower(static_cast(c))); return lower == "true" || lower == "on" || lower == "yes"; } void export_enabled(nlohmann::json& config, const std::map& 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& 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& 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& 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& 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 lookup(const std::map& 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& 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& 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& 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& 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& 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& env_map) { constexpr std::array, 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& 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& 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& 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