diff --git a/profile/plugin/aie_dtrace/aie_dtrace_plugin.cpp b/profile/plugin/aie_dtrace/aie_dtrace_plugin.cpp index a2967496..6b38464d 100644 --- a/profile/plugin/aie_dtrace/aie_dtrace_plugin.cpp +++ b/profile/plugin/aie_dtrace/aie_dtrace_plugin.cpp @@ -15,6 +15,7 @@ #include "xdp/profile/device/utility.h" #include "xdp/profile/device/xdp_base_device.h" #include "xdp/profile/plugin/vp_base/info.h" +#include "xdp/profile/plugin/vp_base/profiling_runtime_config.h" #if defined(XDP_VE2_BUILD) #include "xdp/profile/plugin/aie_dtrace/ve2/aie_dtrace_ve2.h" @@ -65,7 +66,7 @@ namespace xdp { { xrt_core::message::send(severity_level::info, "XRT", "AIE dtrace: update device."); - if (!xrt_core::config::get_aie_dtrace()) + if (!profiling_runtime_config::aie_dtrace_enabled()) return; if (!handle) @@ -94,12 +95,12 @@ namespace xdp { auto device = util::convertToCoreDevice(handle, hw_context_flow); #if !defined(XRT_X86_BUILD) && !defined(XDP_CLIENT_BUILD) - if (1 == device->get_device_id() && xrt_core::config::get_xdp_mode() == "xdna") { + if (1 == device->get_device_id() && profiling_runtime_config::xdp_mode_effective() == "xdna") { xrt_core::message::send(severity_level::warning, "XRT", "AIE dtrace: unexpected ZOCL device with xdp_mode=xdna; skipping."); return; } - else if (0 == device->get_device_id() && xrt_core::config::get_xdp_mode() == "zocl") { + else if (0 == device->get_device_id() && profiling_runtime_config::xdp_mode_effective() == "zocl") { #ifdef XDP_VE2_ZOCL_BUILD xrt_core::message::send(severity_level::warning, "XRT", "AIE dtrace: XDNA device with xdp_mode=zocl; skipping."); @@ -189,7 +190,7 @@ namespace xdp { void AieDtracePlugin::runConstructorHook(void* run_impl_ptr, void* hwctx, uint32_t run_uid, const std::string& kernel_name, void* elf_handle) { - if (!xrt_core::config::get_aie_dtrace()) + if (!profiling_runtime_config::aie_dtrace_enabled()) return; auto itr = handleToAIEDtraceImpl.find(hwctx); @@ -204,7 +205,7 @@ namespace xdp { void AieDtracePlugin::runStartHook(void* run_impl_ptr, void* hwctx, uint32_t run_uid, const std::string& kernel_name) { - if (!xrt_core::config::get_aie_dtrace()) + if (!profiling_runtime_config::aie_dtrace_enabled()) return; (void)run_impl_ptr; @@ -216,7 +217,7 @@ namespace xdp { void AieDtracePlugin::runWaitHook(void* run_impl_ptr, void* hwctx, uint32_t run_uid, const std::string& kernel_name, int ert_cmd_state) { - if (!xrt_core::config::get_aie_dtrace()) + if (!profiling_runtime_config::aie_dtrace_enabled()) return; (void)run_impl_ptr; diff --git a/profile/plugin/aie_dtrace/ve2/aie_dtrace_ve2.cpp b/profile/plugin/aie_dtrace/ve2/aie_dtrace_ve2.cpp index b615dce5..e43e0478 100644 --- a/profile/plugin/aie_dtrace/ve2/aie_dtrace_ve2.cpp +++ b/profile/plugin/aie_dtrace/ve2/aie_dtrace_ve2.cpp @@ -17,6 +17,7 @@ #include "xdp/profile/database/static_info/aie_util.h" #include "xdp/profile/database/static_info/aie_constructs.h" #include "xdp/profile/database/static_info/pl_constructs.h" +#include "xdp/profile/plugin/vp_base/profiling_runtime_config.h" #include #include @@ -226,7 +227,7 @@ namespace xdp { const std::string& kernel_name, void* elf_handle) { - if (!xrt_core::config::get_aie_dtrace()) + if (!profiling_runtime_config::aie_dtrace_enabled()) return; auto ctx = xrt_core::hw_context_int::create_hw_context_from_implementation(hwctx); diff --git a/profile/plugin/aie_profile/aie_profile_metadata.cpp b/profile/plugin/aie_profile/aie_profile_metadata.cpp index 52eac0ff..0b625a81 100644 --- a/profile/plugin/aie_profile/aie_profile_metadata.cpp +++ b/profile/plugin/aie_profile/aie_profile_metadata.cpp @@ -27,6 +27,7 @@ #include "core/common/device.h" #include "core/common/message.h" #include "xdp/profile/database/database.h" +#include "xdp/profile/plugin/vp_base/profiling_runtime_config.h" #include "xdp/profile/plugin/vp_base/vp_base_plugin.h" #include "xdp/profile/database/parser/metrics.h" #include "xdp/profile/database/parser/json_parser.h" @@ -179,7 +180,7 @@ namespace xdp { , m_dtraceBandwidthMode(true) { xrt_core::message::send(severity_level::info, - "XRT", "Parsing AIE dtrace metadata (AIE_dtrace_settings)."); + "XRT", "Parsing AIE dtrace metadata."); VPDatabase* db = VPDatabase::Instance(); metadataReader = (db->getStaticInfo()).getAIEmetadataReader(deviceID); @@ -195,18 +196,61 @@ namespace xdp { clockFreqMhz = (db->getStaticInfo()).getClockRateMHz(deviceID, false); + // Polling interval and the "start" control always come from xrt.ini. pollingInterval = xrt_core::config::get_aie_dtrace_settings_interval_us(); setProfileStartControl(compilerOptions.graph_iterator_event, false, nullptr); + // Metric-set source precedence: + // 1. If Debug.profiling_runtime_config carries a control_instrumentation + // section, it wins. Only interface_tile is wired today; aie_tile and + // mem_tile entries are logged for a follow-up. + // 2. Otherwise fall back to the legacy AIE_dtrace_settings.* xrt.ini + // options. + const bool usingBlob = profiling_runtime_config::has_control_instrumentation(); + const auto& ci = profiling_runtime_config::control_instrumentation(); + + if (usingBlob) { + xrt_core::message::send(severity_level::info, "XRT", + "AIE dtrace: using control_instrumentation from " + "Debug.profiling_runtime_config; AIE_dtrace_settings.* will be ignored " + "for metric sets."); + + if (ci.aie_tile.has_value() && !ci.aie_tile->empty()) { + xrt_core::message::send(severity_level::info, "XRT", + "AIE dtrace: core tile metric '" + *ci.aie_tile + + "' from profiling_runtime_config will be supported in a follow-up."); + } + if (ci.mem_tile.has_value() && !ci.mem_tile->empty()) { + xrt_core::message::send(severity_level::info, "XRT", + "AIE dtrace: mem tile metric '" + *ci.mem_tile + + "' from profiling_runtime_config will be supported in a follow-up."); + } + } + for (int module = 0; module < NUM_MODULES; ++module) { if (moduleTypes[module] != module_type::shim) continue; - auto metricsSettings = - getSettingsVector(xrt_core::config::get_aie_dtrace_settings_tile_based_interface_tile_metrics()); - auto graphMetricsSettings = - getSettingsVector(xrt_core::config::get_aie_dtrace_settings_graph_based_interface_tile_metrics()); + std::vector metricsSettings; + std::vector graphMetricsSettings; + + if (usingBlob) { + if (ci.interface_tile.has_value() && !ci.interface_tile->empty()) { + // Blob carries a bare metric name (e.g. "ddr_bandwidth"). Synthesize + // an "all:" tile-based selection so the existing parser + // applies it to every interface tile. + metricsSettings = getSettingsVector("all:" + *ci.interface_tile); + } + // If interface_tile is unset/empty in the blob, the blob wins and + // leaves shim tiles unconfigured (no xrt.ini fallback for this knob). + } + else { + metricsSettings = + getSettingsVector(xrt_core::config::get_aie_dtrace_settings_tile_based_interface_tile_metrics()); + graphMetricsSettings = + getSettingsVector(xrt_core::config::get_aie_dtrace_settings_graph_based_interface_tile_metrics()); + } getConfigMetricsForInterfaceTiles(module, metricsSettings, graphMetricsSettings); } diff --git a/profile/plugin/vp_base/profiling_runtime_config.cpp b/profile/plugin/vp_base/profiling_runtime_config.cpp new file mode 100644 index 00000000..c4280b2f --- /dev/null +++ b/profile/plugin/vp_base/profiling_runtime_config.cpp @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved + +#define XDP_CORE_SOURCE + +#include +#include +#include + +#include +#include + +#include "core/common/config_reader.h" +#include "core/common/message.h" + +#include "xdp/profile/plugin/vp_base/profiling_runtime_config.h" + +namespace xdp::profiling_runtime_config { + + using severity_level = xrt_core::message::severity_level; + namespace pt = boost::property_tree; + + namespace { + + struct parsed_blob_t { + bool is_set = false; + bool has_ci = false; + control_instrumentation_t ci{}; + }; + + // These helpers are only called from inside get_parsed()'s cached static + // initializer, so every message they emit fires at most once per process. + void + warn(const std::string& msg) + { + xrt_core::message::send(severity_level::warning, "XRT", msg); + } + + void + info(const std::string& msg) + { + xrt_core::message::send(severity_level::info, "XRT", msg); + } + + // Parse the control_instrumentation subtree: copy known string keys into + // the returned struct and warn about any unknown keys. + control_instrumentation_t + parse_control_instrumentation(const pt::ptree& ci_tree) + { + static const std::set known_keys{ + "aie_tile", "mem_tile", "interface_tile" + }; + + control_instrumentation_t ci; + + for (const auto& kv : ci_tree) { + const auto& key = kv.first; + const auto value = kv.second.get_value(""); + + if (key == "aie_tile") { + ci.aie_tile = value; + if (!value.empty()) + info("profiling_runtime_config.control_instrumentation.aie_tile='" + value + "'"); + } + else if (key == "mem_tile") { + ci.mem_tile = value; + if (!value.empty()) + info("profiling_runtime_config.control_instrumentation.mem_tile='" + value + "'"); + } + else if (key == "interface_tile") { + ci.interface_tile = value; + if (!value.empty()) + info("profiling_runtime_config.control_instrumentation.interface_tile='" + value + "'"); + } + else { + std::stringstream msg; + msg << "Unknown key 'profiling_runtime_config.control_instrumentation." + << key << "' ignored. Supported keys:"; + const char* sep = " "; + for (const auto& k : known_keys) { + msg << sep << k; + sep = ", "; + } + warn(msg.str()); + } + } + + return ci; + } + + // Parse the root blob exactly once. + const parsed_blob_t& + get_parsed() + { + static const parsed_blob_t cached = [] { + parsed_blob_t out; + + const std::string raw = xrt_core::config::get_profiling_runtime_config(); + if (raw.empty()) + return out; + + try { + pt::ptree root; + std::istringstream is(raw); + pt::read_json(is, root); + + out.is_set = true; + + if (const auto ci_opt = root.get_child_optional("control_instrumentation")) { + out.ci = parse_control_instrumentation(*ci_opt); + out.has_ci = out.ci.aie_tile.has_value() + || out.ci.mem_tile.has_value() + || out.ci.interface_tile.has_value(); + } + } + catch (const std::exception& ex) { + warn(std::string("Failed to parse Debug.profiling_runtime_config " + "as JSON; ignoring runtime config. Details: ") + + ex.what()); + return parsed_blob_t{}; + } + + return out; + }(); + + return cached; + } + + } // anonymous namespace + + bool + is_set() + { + return get_parsed().is_set; + } + + bool + has_control_instrumentation() + { + return get_parsed().has_ci; + } + + const control_instrumentation_t& + control_instrumentation() + { + return get_parsed().ci; + } + + bool + aie_dtrace_enabled() + { + static bool value = xrt_core::config::get_aie_dtrace() || has_control_instrumentation(); + return value; + } + + const std::string& + xdp_mode_effective() + { + static const std::string value = [] { + // 1. An explicit [Debug] xdp_mode in xrt.ini (or set via + // xrt::ini::set before the first read) always wins. We probe the + // [Debug] section directly so we can distinguish "user did not set + // it" from "user set it to the same string as the default". + const auto& debug_section = + xrt_core::config::detail::get_ptree_value("Debug"); + if (auto v = debug_section.get_optional("xdp_mode")) + return *v; + + // 2. No explicit value: if the runtime config blob carries a + // control_instrumentation section, promote to "xdna" so the + // plugin loader and device gates select the XDNA variant. + if (has_control_instrumentation()) + return std::string("xdna"); + + // 3. Otherwise fall through to the built-in default. + return xrt_core::config::get_xdp_mode(); + }(); + return value; + } + +} // namespace xdp::profiling_runtime_config diff --git a/profile/plugin/vp_base/profiling_runtime_config.h b/profile/plugin/vp_base/profiling_runtime_config.h new file mode 100644 index 00000000..fc1a979b --- /dev/null +++ b/profile/plugin/vp_base/profiling_runtime_config.h @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved + +#ifndef PROFILING_RUNTIME_CONFIG_DOT_H +#define PROFILING_RUNTIME_CONFIG_DOT_H + +#include +#include + +#include "xdp/config.h" + +// Parser for the XRT INI option Debug.profiling_runtime_config which holds +// an inline JSON blob describing XDP runtime configuration. Only the +// control_instrumentation section is consumed by this change; other top-level +// keys (event_trace, etc.) are accepted but ignored and may be wired up in a +// follow-up. +// +// The parser lives on the XDP side (xdp_core) so this code can evolve quickly +// without churning the stable xrt_coreutil interface. core/common/xdp/profile.cpp +// keeps a minimal independent probe for the load-time gate. +// +// Example blob: +// {"control_instrumentation":{"aie_tile":"func_stalls","mem_tile":"","interface_tile":"ddr_bandwidth"},"event_trace":{}} + +namespace xdp::profiling_runtime_config { + + struct control_instrumentation_t { + std::optional aie_tile; // maps to "core" module internally + std::optional mem_tile; // maps to "mem_tile" module internally + std::optional interface_tile; // maps to "shim" module internally + }; + + // True when the xrt.ini value is non-empty and parsed successfully. + XDP_CORE_EXPORT bool is_set(); + + // True when is_set() and the blob contained a control_instrumentation object + // with at least one recognized key (aie_tile / mem_tile / interface_tile). + XDP_CORE_EXPORT bool has_control_instrumentation(); + + // Returns the cached control_instrumentation view. Safe to call even when + // has_control_instrumentation() is false (all members will be empty). + XDP_CORE_EXPORT const control_instrumentation_t& control_instrumentation(); + + // True if aie_dtrace should be active: either Debug.aie_dtrace is set in + // xrt.ini, or the runtime config blob carries a control_instrumentation + // section. Intended as the single gate for the aie_dtrace plugin guards. + XDP_CORE_EXPORT bool aie_dtrace_enabled(); + + // Effective Debug.xdp_mode value, with the following precedence: + // 1. Explicit [Debug] xdp_mode in xrt.ini (or set programmatically via + // xrt::ini::set before the first read) wins, whatever its value. + // 2. Otherwise, when has_control_instrumentation() is true, the value + // is auto-promoted to "xdna" so the XDNA loader/device gates pick + // the right variant without the user also having to set xdp_mode. + // 3. Otherwise, the built-in default from xrt_core::config::get_xdp_mode(). + // Use this in place of xrt_core::config::get_xdp_mode() at any plugin call + // site that should follow the runtime config's auto-promotion. Result is + // cached on first call. + XDP_CORE_EXPORT const std::string& xdp_mode_effective(); + +} // namespace xdp::profiling_runtime_config + +#endif