From 5cc665a69c56b1e88ca89caddd3467b5c12230b8 Mon Sep 17 00:00:00 2001 From: garimad Date: Wed, 24 Jun 2026 16:44:41 +0530 Subject: [PATCH 1/3] Load AIE dtrace plugin for XDP_CLIENT_BUILD and NPU3 Wire up the AIE dtrace plugin in the client build path so it is loaded when Debug.aie_dtrace is enabled. dtrace::load() now selects the loader based on build type (sdk_loader on client Windows, module_loader otherwise) and picks the NPU3-specific plugin name via AMD_XDP_NPU3, matching the existing aie_profile/aie_trace/aie_halt plugins. --- src/runtime_src/core/common/xdp/profile.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/runtime_src/core/common/xdp/profile.cpp b/src/runtime_src/core/common/xdp/profile.cpp index f1600cecc5e..e88b665a78c 100644 --- a/src/runtime_src/core/common/xdp/profile.cpp +++ b/src/runtime_src/core/common/xdp/profile.cpp @@ -185,7 +185,17 @@ load_xdna() void load() { - load_xdna(); +#if defined(XDP_CLIENT_BUILD) && defined(_WIN32) + static xrt_core::sdk_loader + xdp_aie_dtrace_loader(std::getenv("AMD_XDP_NPU3") ? "xdp_aie_dtrace_plugin_npu3" : "xdp_aie_dtrace_plugin", + register_callbacks, + warning_callbacks_empty); +#else + static xrt_core::module_loader + xdp_aie_dtrace_loader(std::getenv("AMD_XDP_NPU3") ? "xdp_aie_dtrace_plugin_npu3" : "xdp_aie_dtrace_plugin", + register_callbacks, + warning_callbacks_empty); +#endif } void @@ -687,6 +697,14 @@ update_device(void* handle, bool hw_context_flow) "Failed to setup for AIE Debug. Caught exception ", handle); + load_once_and_update(xrt_core::config::get_aie_dtrace, + xrt_core::xdp::aie::dtrace::load, + xrt_core::xdp::aie::dtrace::update_device, + "Failed to load AIE dtrace library. Caught exception ", + "Failed to setup for AIE dtrace. Caught exception ", + handle, + hw_context_flow); + #elif defined(XDP_VE2_BUILD) load_once_and_update(xrt_core::config::get_ml_timeline, From 8ae5be9b22812e5b99eb525b21ee223c496b979d Mon Sep 17 00:00:00 2001 From: garimad Date: Fri, 26 Jun 2026 08:28:09 +0530 Subject: [PATCH 2/3] Fix dtrace run hooks polling an unsubmitted command get_xdp_kernel_data() called run_impl->state(), which polls the hwqueue. The XDP dtrace run_constructor/run_start hooks fire from the run_impl constructor and immediately before submit, when the command has no fence id yet. Polling it threw 'Cannot poll a command that has not been submitted', breaking HW-context/runner creation (e.g. FlexMLRT) when aie_dtrace was enabled. Read the cached ERT packet state directly instead of polling. --- src/runtime_src/core/common/api/kernel_int.h | 8 +++- .../core/common/api/xrt_kernel.cpp | 10 +++- src/runtime_src/core/common/xdp/profile.cpp | 47 +++++++++++++++++-- 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/runtime_src/core/common/api/kernel_int.h b/src/runtime_src/core/common/api/kernel_int.h index e69f414d857..31d67d83a85 100644 --- a/src/runtime_src/core/common/api/kernel_int.h +++ b/src/runtime_src/core/common/api/kernel_int.h @@ -124,10 +124,14 @@ get_kernel_from_impl(const xrt::kernel_impl* kernel_impl); xrt::run get_run_from_impl(const xrt::run_impl*); -// Fill XDP kernel data from a run_impl pointer for profiling hooks +// Fill XDP kernel data from a run_impl pointer for profiling hooks. +// include_state controls whether run_impl->state() is queried; that polls the +// command and is only valid after submission, so callers that run before the +// command is submitted (run_constructor/run_start hooks) must leave it false. XRT_CORE_COMMON_EXPORT void -get_xdp_kernel_data(const xrt::run_impl* run_impl, xrt_core::xdp::xrt_kernel_data* data); +get_xdp_kernel_data(const xrt::run_impl* run_impl, xrt_core::xdp::xrt_kernel_data* data, + bool include_state = false); // Set dtrace control file on a run_impl handle // This is used by XDP profiling to set the CT file without requiring xrt::run diff --git a/src/runtime_src/core/common/api/xrt_kernel.cpp b/src/runtime_src/core/common/api/xrt_kernel.cpp index 0457de53e89..8dc81381c8a 100644 --- a/src/runtime_src/core/common/api/xrt_kernel.cpp +++ b/src/runtime_src/core/common/api/xrt_kernel.cpp @@ -4580,14 +4580,20 @@ get_run_from_impl(const xrt::run_impl* run_impl) } void -get_xdp_kernel_data(const xrt::run_impl* run_impl, xrt_core::xdp::xrt_kernel_data* data) +get_xdp_kernel_data(const xrt::run_impl* run_impl, xrt_core::xdp::xrt_kernel_data* data, + bool include_state) { auto kernel = run_impl->get_kernel(); data->uid = run_impl->get_uid(); data->name = kernel->get_name(); data->hwctx = kernel->get_hw_context(); data->mod = kernel->get_module(); - data->ert_state = static_cast(run_impl->state()); + // run_impl->state() polls the command's ERT packet. That is only valid once + // the command has been submitted; calling it earlier (e.g. from the + // run_constructor / run_start hooks, which fire before submission) throws + // "Cannot poll a command that has not been submitted". Only the run_wait + // hook actually consumes ert_state, so query it only when explicitly asked. + data->ert_state = include_state ? static_cast(run_impl->state()) : 0; } void diff --git a/src/runtime_src/core/common/xdp/profile.cpp b/src/runtime_src/core/common/xdp/profile.cpp index e88b665a78c..1be9fe134a1 100644 --- a/src/runtime_src/core/common/xdp/profile.cpp +++ b/src/runtime_src/core/common/xdp/profile.cpp @@ -215,7 +215,14 @@ end_poll(void* handle) void run_constructor(xrt::run_impl* run_impl) { - if (run_constructor_cb) { + if (!run_constructor_cb) + return; + + // The dtrace run hooks are best-effort profiling instrumentation. They must + // never affect the run lifecycle: an exception escaping here would propagate + // out of the run_impl constructor and corrupt the caller's run object (e.g. + // leaving FlexMLRT polling a command that was never submitted). + try { xrt_kernel_data data{}; xrt_core::kernel_int::get_xdp_kernel_data(run_impl, &data); auto elf_hdl = data.mod ? xrt_core::module_int::get_elf_handle(data.mod) : nullptr; @@ -225,31 +232,63 @@ run_constructor(xrt::run_impl* run_impl) data.name.c_str(), elf_hdl.get()); } + catch (const std::exception& e) { + xrt_core::message::send(xrt_core::message::severity_level::debug, "XRT", + std::string{"AIE dtrace run_constructor hook failed (ignored): "} + e.what()); + } + catch (...) { + xrt_core::message::send(xrt_core::message::severity_level::debug, "XRT", + "AIE dtrace run_constructor hook failed (ignored): unknown exception"); + } } void run_start(const xrt::run_impl* run_impl) { - if (run_start_cb) { + if (!run_start_cb) + return; + + try { xrt_kernel_data data{}; xrt_core::kernel_int::get_xdp_kernel_data(run_impl, &data); run_start_cb(nullptr, data.hwctx.get_handle().get(), data.uid, data.name.c_str()); } + catch (const std::exception& e) { + xrt_core::message::send(xrt_core::message::severity_level::debug, "XRT", + std::string{"AIE dtrace run_start hook failed (ignored): "} + e.what()); + } + catch (...) { + xrt_core::message::send(xrt_core::message::severity_level::debug, "XRT", + "AIE dtrace run_start hook failed (ignored): unknown exception"); + } } void run_wait(const xrt::run_impl* run_impl) { - if (run_wait_cb) { + if (!run_wait_cb) + return; + + try { xrt_kernel_data data{}; - xrt_core::kernel_int::get_xdp_kernel_data(run_impl, &data); + // run_wait fires after the command has completed, so polling state() here + // is valid and ert_state is the value this hook needs. + xrt_core::kernel_int::get_xdp_kernel_data(run_impl, &data, true); run_wait_cb(nullptr, data.hwctx.get_handle().get(), data.uid, data.name.c_str(), data.ert_state); } + catch (const std::exception& e) { + xrt_core::message::send(xrt_core::message::severity_level::debug, "XRT", + std::string{"AIE dtrace run_wait hook failed (ignored): "} + e.what()); + } + catch (...) { + xrt_core::message::send(xrt_core::message::severity_level::debug, "XRT", + "AIE dtrace run_wait hook failed (ignored): unknown exception"); + } } } // end namespace xrt_core::xdp::aie::dtrace From 514862c0947982f918adf8de793afa407a6ce421 Mon Sep 17 00:00:00 2001 From: garimad Date: Fri, 3 Jul 2026 12:40:06 +0530 Subject: [PATCH 3/3] Use xrt_core::utils getenv/is_env instead of std::getenv in xdp profile Address review feedback: std::getenv is not permitted; switch to the xrt_core::utils env helpers (is_env for boolean checks, getenv for value comparison) and drop the now-unneeded cstring/cstdlib includes and the getenv MSVC warning suppression. Signed-off-by: Garima Dhaked Co-authored-by: Cursor --- src/runtime_src/core/common/xdp/profile.cpp | 36 +++++++++------------ 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/runtime_src/core/common/xdp/profile.cpp b/src/runtime_src/core/common/xdp/profile.cpp index 1be9fe134a1..d259bcdd772 100644 --- a/src/runtime_src/core/common/xdp/profile.cpp +++ b/src/runtime_src/core/common/xdp/profile.cpp @@ -10,16 +10,11 @@ #include "core/common/dlfcn.h" #include "core/common/module_loader.h" #include "core/common/message.h" +#include "core/common/utils.h" #include "core/include/xrt/xrt_kernel.h" -#include #include #include -#include - -#ifdef _WIN32 -#pragma warning( disable : 4996 ) /* Disable warning for getenv */ -#endif // An anonymous namespace to hold a common set of blank functions // for all modules that don't require specialization and the common @@ -64,8 +59,7 @@ namespace { #if !defined(XDP_CLIENT_BUILD) && !defined(XDP_VE2_BUILD) static bool is_hw_emulation() { - static auto xem = std::getenv("XCL_EMULATION_MODE"); - static bool hwem = xem ? (std::strcmp(xem, "hw_emu") == 0) : false; + static bool hwem = (xrt_core::utils::getenv("XCL_EMULATION_MODE") == "hw_emu"); return hwem; } #endif @@ -79,7 +73,7 @@ namespace xrt_core::xdp::core { void load_core() { - if (std::getenv("AMD_XDP_NPU3")) { + if (xrt_core::utils::is_env("AMD_XDP_NPU3")) { static xrt_core::sdk_loader xdp_core_loader("xdp_core_npu3", register_callbacks_empty, @@ -114,12 +108,12 @@ load() { #if defined(XDP_CLIENT_BUILD) && defined(_WIN32) static xrt_core::sdk_loader - xdp_aie_loader(std::getenv("AMD_XDP_NPU3") ? "xdp_aie_profile_plugin_npu3" : "xdp_aie_profile_plugin", + xdp_aie_loader(xrt_core::utils::is_env("AMD_XDP_NPU3") ? "xdp_aie_profile_plugin_npu3" : "xdp_aie_profile_plugin", register_callbacks, warning_callbacks_empty); #else static xrt_core::module_loader - xdp_aie_loader(std::getenv("AMD_XDP_NPU3") ? "xdp_aie_profile_plugin_npu3" : "xdp_aie_profile_plugin", + xdp_aie_loader(xrt_core::utils::is_env("AMD_XDP_NPU3") ? "xdp_aie_profile_plugin_npu3" : "xdp_aie_profile_plugin", register_callbacks, warning_callbacks_empty); #endif @@ -187,12 +181,12 @@ load() { #if defined(XDP_CLIENT_BUILD) && defined(_WIN32) static xrt_core::sdk_loader - xdp_aie_dtrace_loader(std::getenv("AMD_XDP_NPU3") ? "xdp_aie_dtrace_plugin_npu3" : "xdp_aie_dtrace_plugin", + xdp_aie_dtrace_loader(xrt_core::utils::is_env("AMD_XDP_NPU3") ? "xdp_aie_dtrace_plugin_npu3" : "xdp_aie_dtrace_plugin", register_callbacks, warning_callbacks_empty); #else static xrt_core::module_loader - xdp_aie_dtrace_loader(std::getenv("AMD_XDP_NPU3") ? "xdp_aie_dtrace_plugin_npu3" : "xdp_aie_dtrace_plugin", + xdp_aie_dtrace_loader(xrt_core::utils::is_env("AMD_XDP_NPU3") ? "xdp_aie_dtrace_plugin_npu3" : "xdp_aie_dtrace_plugin", register_callbacks, warning_callbacks_empty); #endif @@ -316,7 +310,7 @@ void load() { static xrt_core::module_loader - xdp_aie_debug_loader(std::getenv("AMD_XDP_NPU3") ? "xdp_aie_debug_plugin_npu3" : "xdp_aie_debug_plugin", + xdp_aie_debug_loader(xrt_core::utils::is_env("AMD_XDP_NPU3") ? "xdp_aie_debug_plugin_npu3" : "xdp_aie_debug_plugin", register_callbacks, warning_callbacks_empty); } @@ -404,7 +398,7 @@ void load() { static xrt_core::module_loader - xdp_ml_timeline_loader(std::getenv("AMD_XDP_NPU3") ? "xdp_ml_timeline_plugin_npu3" : "xdp_ml_timeline_plugin", + xdp_ml_timeline_loader(xrt_core::utils::is_env("AMD_XDP_NPU3") ? "xdp_ml_timeline_plugin_npu3" : "xdp_ml_timeline_plugin", register_callbacks, warning_callbacks_empty); } @@ -492,12 +486,12 @@ load() { #if defined(XDP_CLIENT_BUILD) && defined(_WIN32) static xrt_core::sdk_loader - xdp_aie_trace_loader(std::getenv("AMD_XDP_NPU3") ? "xdp_aie_trace_plugin_npu3" : "xdp_aie_trace_plugin", + xdp_aie_trace_loader(xrt_core::utils::is_env("AMD_XDP_NPU3") ? "xdp_aie_trace_plugin_npu3" : "xdp_aie_trace_plugin", register_callbacks, warning_callbacks_empty); #else static xrt_core::module_loader - xdp_aie_trace_loader(std::getenv("AMD_XDP_NPU3") ? "xdp_aie_trace_plugin_npu3" : "xdp_aie_trace_plugin", + xdp_aie_trace_loader(xrt_core::utils::is_env("AMD_XDP_NPU3") ? "xdp_aie_trace_plugin_npu3" : "xdp_aie_trace_plugin", register_callbacks, warning_callbacks_empty); #endif @@ -551,12 +545,12 @@ load() { #if defined(XDP_CLIENT_BUILD) && defined(_WIN32) static xrt_core::sdk_loader - xdp_aie_halt_loader(std::getenv("AMD_XDP_NPU3") ? "xdp_aie_halt_plugin_npu3" : "xdp_aie_halt_plugin", + xdp_aie_halt_loader(xrt_core::utils::is_env("AMD_XDP_NPU3") ? "xdp_aie_halt_plugin_npu3" : "xdp_aie_halt_plugin", register_callbacks, warning_callbacks_empty); #else static xrt_core::module_loader - xdp_aie_halt_loader(std::getenv("AMD_XDP_NPU3") ? "xdp_aie_halt_plugin_npu3" : "xdp_aie_halt_plugin", + xdp_aie_halt_loader(xrt_core::utils::is_env("AMD_XDP_NPU3") ? "xdp_aie_halt_plugin_npu3" : "xdp_aie_halt_plugin", register_callbacks, warning_callbacks_empty); #endif @@ -830,7 +824,7 @@ update_device(void* handle, bool hw_context_flow) load_once_and_update( []() { return xrt_core::config::get_pl_deadlock_detection() && - nullptr == std::getenv("XCL_EMULATION_MODE"); + !xrt_core::utils::is_env("XCL_EMULATION_MODE"); }, xrt_core::xdp::pl_deadlock::load, xrt_core::xdp::pl_deadlock::update_device, @@ -932,7 +926,7 @@ finish_flush_device(void* handle) #else if (xrt_core::config::get_pl_deadlock_detection() - && nullptr == std::getenv("XCL_EMULATION_MODE")) { + && !xrt_core::utils::is_env("XCL_EMULATION_MODE")) { xrt_core::xdp::pl_deadlock::finish_flush_device(handle); } if (xrt_core::config::get_aie_trace())