Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/runtime_src/core/common/api/elf_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include <variant>
#include <vector>

namespace xrt_core { class device; }

namespace xrt {

////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -542,6 +544,20 @@ get_kernel_properties_and_args(std::shared_ptr<xrt::elf_impl> elf_impl,
std::string
get_filename(const xrt::elf_impl* elf_impl);

// Package a raw AIE coredump blob into an ET_CORE ELF with metadata.
// Metadata (timestamp, firmware version, device info, context status) is built
// internally by querying the device. The AIE architecture is derived from the
// ELF's OS/ABI byte.
//
// uuid: UUID to embed in the coredump metadata. Pass the UUID of the specific
// ELF that caused the fault (e.g. the timed-out run's ELF). Pass empty string
// when no single ELF is attributable — e.g. a partition-level dump triggered
// from the public API where multiple ELFs may be loaded.
std::vector<char>
make_aie_coredump_elf(const xrt::elf& elf, const std::vector<char>& blob,
const xrt_core::device* device, uint32_t slot,
const std::string& uuid = "");

} // namespace xrt_core::elf_int

#endif
9 changes: 9 additions & 0 deletions src/runtime_src/core/common/api/hw_context_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ XRT_CORE_COMMON_EXPORT
std::map<std::string, xrt::elf>
get_elf_map(const xrt::hw_context& hwctx);

// get_aie_coredump_elf() - Package AIE coredump for a specific faulting ELF
//
// Use this overload when the caller knows exactly which ELF caused the fault
// (e.g. abort_coredump_or_noop passes the ELF of the timed-out run).
// The UUID embedded in the coredump metadata is taken from the provided ELF.
// The raw AIE dump blob is fetched internally from the hw_context.
std::vector<char>
get_aie_coredump_elf(const xrt::hw_context& hwctx, const xrt::elf& elf);

// Get the configuration parameter / QoS map from the hw context.
// The returned map contains key-value pairs
// key: string, value: uint32_t for both QoS-related settings and
Expand Down
103 changes: 103 additions & 0 deletions src/runtime_src/core/common/api/xrt_elf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,27 @@
#define XRT_CORE_COMMON_SOURCE // in same dll as core_common
#include "xrt/experimental/xrt_aie.h"
#include "xrt/experimental/xrt_elf.h"
#include "xrt/xrt_hw_context.h"
#include "xrt/xrt_uuid.h"

#include "elf_int.h"
#include "elf_patcher.h"
#include "core/common/aiebu/src/cpp/elf/aie_elf_constants.h"
#include "core/common/aiebu/src/cpp/include/aiebu/aiebu_assembler.h"
#include "core/common/config_reader.h"
#include "core/common/device.h"
#include "core/common/error.h"
#include "core/common/message.h"
#include "core/common/query_requests.h"
#include "core/common/system.h"
#include "core/common/trace.h"
#include "core/common/xclbin_parser.h"
#include "core/common/runner/capture.h"

#include <boost/interprocess/streams/bufferstream.hpp>
#include <elfio/elfio.hpp>

#include <chrono>
#include <cstdint>
#include <map>
#include <memory>
Expand Down Expand Up @@ -1661,6 +1668,102 @@ get_filename(const xrt::elf_impl* elf_impl)
: "";
}

aiebu::aiebu_assembler::buffer_type

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was previously in a nested unnamed namespace, I suggested either moving the namespace or making the function static,

Suggested change
aiebu::aiebu_assembler::buffer_type
static aiebu::aiebu_assembler::buffer_type

osabi_to_coredump_type(uint8_t os_abi)
{
switch (os_abi) {
case aiebu::osabi_aie2p:
return aiebu::aiebu_assembler::buffer_type::coredump_aie2p;
case aiebu::osabi_aie2ps:
return aiebu::aiebu_assembler::buffer_type::coredump_aie2ps;
case aiebu::osabi_aie4:
return aiebu::aiebu_assembler::buffer_type::coredump_aie4;
case aiebu::osabi_aie4a:
return aiebu::aiebu_assembler::buffer_type::coredump_aie4a;
case aiebu::osabi_aie4z:
return aiebu::aiebu_assembler::buffer_type::coredump_aie4z;
default:
throw std::runtime_error("AIE coredump not supported for this ELF architecture");
}
}

std::vector<char>
make_aie_coredump_elf(const xrt::elf& elf, const std::vector<char>& blob,
const xrt_core::device* device, uint32_t slot,
const std::string& uuid)
{
// Fail fast: verify arch is supported before doing any device queries.
auto buf_type = osabi_to_coredump_type(elf.get_handle()->get_os_abi());

aiebu::aie_coredump_meta meta{};
meta.timestamp_ns = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now().time_since_epoch()).count());
meta.uuid = uuid;

try {
boost::property_tree::ptree pt_xrt;
xrt_core::get_driver_info(pt_xrt);
std::string drv_str;
if (const auto drivers = pt_xrt.get_child_optional("drivers")) {
for (const auto& kv : *drivers) {
const auto& drv = kv.second;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is generally preferred to use structured bindings for anything return tuples.

Suggested change
for (const auto& kv : *drivers) {
const auto& drv = kv.second;
for (const auto& [dummy, drv] : *drivers) {

auto name = drv.get<std::string>("name", "");
auto ver = drv.get<std::string>("version", "");
if (!name.empty() && !ver.empty() && ver != "unknown") {
if (!drv_str.empty())
drv_str += "; ";

drv_str += name + " " + ver;
Comment thread
stsoe marked this conversation as resolved.
}
}
}
meta.driver_version = drv_str;
}
catch (const std::exception&) {
/* leave empty if not available */
}

try {
auto data = xrt_core::device_query<xrt_core::query::aie_partition_info>(device);
auto islot = static_cast<int>(slot);

for (const auto& entry : data) {
if (std::stoi(entry.metadata.id) != islot)
continue;

meta.context_status = entry.is_suspended
? aiebu::aie_context_status::idle
: aiebu::aie_context_status::running;
break;
}
}
catch (const std::exception&) {
/* leave as default idle if query not supported */
}

try {
auto fw = xrt_core::device_query<xrt_core::query::firmware_version>(
device,
xrt_core::query::firmware_version::firmware_type::npu_firmware);
meta.fw_version = std::to_string(fw.major) + "." + std::to_string(fw.minor)
+ "." + std::to_string(fw.patch) + "." + std::to_string(fw.build);
}
catch (const std::exception&) {
/* leave empty if not supported */
}

try {
meta.device_info = xrt_core::device_query<xrt_core::query::rom_vbnv>(device);
}
catch (const std::exception&) {
/* leave empty if not supported */
}

aiebu::aiebu_assembler a(buf_type, blob, meta);
return a.get_elf();
}

} // xrt_core::elf_int

////////////////////////////////////////////////////////////////
Expand Down
33 changes: 33 additions & 0 deletions src/runtime_src/core/common/api/xrt_hw_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,22 @@ class hw_context_impl : public std::enable_shared_from_this<hw_context_impl>
}
}

// Public-API path — picks any ELF for OS/ABI; UUID is left empty because
// no single ELF is attributable for a partition-level dump.
std::vector<char>
get_aie_coredump_elf() const
Comment thread
stsoe marked this conversation as resolved.
{
xrt::elf elf = [this] {
std::lock_guard lk(m_mutex);
if (m_elf_map.empty())
throw std::runtime_error("AIE coredump ELF not available: no ELF loaded in this context");
return m_elf_map.begin()->second;
}();

return xrt_core::elf_int::make_aie_coredump_elf(
elf, get_aie_coredump(), m_core_device.get(), m_hdl->get_slotidx(), "");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You defaulted the uuid string in make_aie_coredump_elf, which I think is fine, but then this function should not mention anything about UUID and not call with the empty string.

}

// Returns map of kernel names to their corresponding elf files
// registered with this hardware context
std::map<std::string, xrt::elf>
Expand Down Expand Up @@ -858,6 +874,16 @@ append_dtrace_result(const xrt::hw_context& hwctx,
hwctx.get_handle()->append_dtrace_result(key, result_json);
}

std::vector<char>
get_aie_coredump_elf(const xrt::hw_context& hwctx, const xrt::elf& elf)
{
auto* impl = hwctx.get_handle().get();
return xrt_core::elf_int::make_aie_coredump_elf(
elf, impl->get_aie_coredump(), impl->get_core_device().get(),
static_cast<uint32_t>(static_cast<xrt_core::hwctx_handle*>(hwctx)->get_slotidx()),
elf.get_cfg_uuid().to_string());
}

} // xrt_core::hw_context_int

////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1015,6 +1041,13 @@ get_aie_coredump() const
return get_handle()->get_aie_coredump();
}

std::vector<char>
hw_context::
get_aie_coredump_elf() const
{
return get_handle()->get_aie_coredump_elf();
}

Comment thread
stsoe marked this conversation as resolved.
} // xrt

////////////////////////////////////////////////////////////////
Expand Down
17 changes: 15 additions & 2 deletions src/runtime_src/core/common/api/xrt_kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3154,7 +3154,10 @@ class run_impl : public std::enable_shared_from_this<run_impl>
}

// abort_coredump_or_noop() - AIE coredump if enabled
// Dump core and abort, or do nothing.
// Write AIE coredump ELF to the configured file then abort.
// std::abort() ensures the OS reclaims driver resources (hw_context, hardware
// state) after a timeout. Users who set xrt.ini to capture AIE coredump
// have already observed the timeout exception on a prior run.
void
abort_coredump_or_noop(ert_cmd_state state) const
{
Expand All @@ -3167,12 +3170,22 @@ class run_impl : public std::enable_shared_from_this<run_impl>

try {
auto hwctx = kernel->get_hw_context();
auto core = hwctx.get_aie_coredump(); // may throw
// Use the ELF from this run's own module
// Each ELF has its own UUID; using the run's module ensures the
// correct UUID is embedded in the coredump metadata.
if (!m_module)
throw std::runtime_error("AIE coredump ELF not available: no ELF associated with this run");

auto elf = xrt::elf{xrt_core::module_int::get_elf_handle(m_module)};

auto core = xrt_core::hw_context_int::get_aie_coredump_elf(hwctx, elf);

std::ofstream ostr{file, std::ios::binary};
if (!ostr)
throw std::runtime_error("Could not open '" + file + "' for writing");

ostr.write(core.data(), static_cast<std::streamsize>(core.size()));
ostr.flush(); // flush before abort — destructors do not run after std::abort()
std::abort();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there was a mention that we should not abort on coredump, but the original spec said we should. I want to make sure remove std::abort() is indeed correct.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep the std::abort() to make sure driver clean up hw_context after timeout. We don't have to get the exception message and coredump() at one run. When usesrs set xrt.ini to save aie coredump, they already see the timeout and exception message.

}
catch (const std::exception& ex) {
Expand Down
17 changes: 17 additions & 0 deletions src/runtime_src/core/include/xrt/xrt_hw_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#ifdef __cplusplus

#include <map>
#include <string>
#include <vector>

// Opaque handle for internal use
namespace xrt_core {
Expand Down Expand Up @@ -357,6 +359,21 @@ class hw_context : public detail::pimpl<hw_context_impl>
std::vector<char>
get_aie_coredump() const;

/**
* get_aie_coredump_elf() - Returns the coredump of AIE Array as an ET_CORE ELF.
Comment thread
stsoe marked this conversation as resolved.
* Fetches the raw AIE dump blob and packages it into a coredump ELF.
* The AIE architecture is derived from the ELF loaded in this context.
* Metadata (timestamp, firmware version, device info, ELF UUID) is always
* embedded and populated internally from the hw_context and device.
* This function can throw.
*
* @return
* ELF bytes of the ET_CORE coredump ELF
*/
XRT_API_EXPORT
std::vector<char>
get_aie_coredump_elf() const;

public:
/// @cond
// Undocumented internal access to low level context handle
Expand Down
Loading