diff --git a/profile/plugin/aie_profile/aie_profile_metadata.cpp b/profile/plugin/aie_profile/aie_profile_metadata.cpp index b7148ed4..ded2d669 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..5c8758fd --- /dev/null +++ b/profile/plugin/vp_base/profiling_runtime_config.cpp @@ -0,0 +1,149 @@ +// 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; + } + +} // 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..23c10bf8 --- /dev/null +++ b/profile/plugin/vp_base/profiling_runtime_config.h @@ -0,0 +1,50 @@ +// 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 it can evolve without +// churning the stable xrt_coreutil interface. Plugin enablement and xdp_mode +// selection are not derived from this blob: FlexmlRT (or the user) is +// expected to set Debug.aie_dtrace / Debug.aie_trace and Debug.xdp_mode in +// xrt.ini directly. The blob's role is to override the per-metric settings +// that the corresponding plugin would otherwise read from +// AIE_dtrace_settings.* / AIE_trace_settings.* in xrt.ini. +// +// 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(); + +} // namespace xdp::profiling_runtime_config + +#endif