///|
/// DWARF Debug Info Builder
///
/// This module provides a high-level interface for generating DWARF debug
/// information and registering JIT code with debuggers like LLDB.
///
/// Usage:
/// let dwarf = DWARFBuilder::DWARFBuilder()
/// dwarf.add_function("func_0", addr, size, 0)
/// dwarf.add_function("func_1", addr, size, 1)
/// dwarf.register() // Register with LLDB
/// // ... JIT code runs here ...
/// dwarf.destroy() // Cleanup when done
///|
/// DWARF debug info builder
/// Accumulates function metadata and generates DWARF sections
pub struct DWARFBuilder {
priv handle : Int64
}
///|
/// Create a new DWARF builder
pub fn DWARFBuilder::DWARFBuilder() -> DWARFBuilder {
{ handle: c_dwarf_create() }
}
///|
/// Add a function to the DWARF debug info
/// Parameters:
/// name: Function name (e.g., "func_42" or "my_function")
/// addr: Function start address in memory
/// size: Function size in bytes
/// func_idx: WebAssembly function index
pub fn DWARFBuilder::add_function(
self : DWARFBuilder,
name : String,
addr : Int64,
size : Int,
func_idx : Int,
) -> Unit {
// Convert string to null-terminated bytes
let name_bytes = Bytes::from_array(
name.to_array().map(fn(c) { c.to_int().to_byte() }),
)
c_dwarf_add_function(self.handle, name_bytes, addr, size, func_idx)
}
///|
/// Register the DWARF debug info with the debugger
/// This generates an in-memory Mach-O/ELF object file with DWARF sections
/// and registers it with LLDB/GDB via the standard JIT interface.
/// After calling this, function names will appear in stack traces.
/// Also sets this builder as the active one for backtrace lookups.
/// verbose: if true, print debug info to stderr
pub fn DWARFBuilder::register(
self : DWARFBuilder,
verbose? : Bool = false,
) -> Unit {
c_dwarf_register(self.handle, if verbose { 1 } else { 0 })
// Set as active for backtrace lookups
c_dwarf_set_active(self.handle)
}
///|
/// Unregister DWARF debug info from the debugger
/// Call this before destroying if you want to explicitly unregister
pub fn DWARFBuilder::unregister(self : DWARFBuilder) -> Unit {
c_dwarf_unregister(self.handle)
}
///|
/// Destroy the DWARF builder and free all resources
/// This also unregisters the debug info if it was registered
pub fn DWARFBuilder::destroy(self : DWARFBuilder) -> Unit {
c_dwarf_destroy(self.handle)
}
///|
/// Function lookup result
pub struct FunctionInfo {
name : String
func_idx : Int
offset : Int64
}
///|
/// Lookup an address to find which function it belongs to
/// Returns None if the address is not in any known function
pub fn DWARFBuilder::lookup_address(
self : DWARFBuilder,
addr : Int64,
) -> FunctionInfo? {
let name_buf = FixedArray::make(256, b'\x00')
let func_idx_buf = FixedArray::make(1, 0)
let offset_buf = FixedArray::make(1, 0L)
let found = c_dwarf_lookup_address(
self.handle,
addr,
name_buf,
256,
func_idx_buf,
offset_buf,
)
if found != 0 {
// Convert null-terminated bytes to string
let mut name_len = 0
for i in 0..<256 {
if name_buf[i] == b'\x00' {
name_len = i
break
}
}
let name = String::from_array(
FixedArray::makei(name_len, fn(i) {
name_buf[i].to_int().unsafe_to_char()
}),
)
Some({ name, func_idx: func_idx_buf[0], offset: offset_buf[0] })
} else {
None
}
}
///|
/// Backtrace frame
pub struct BacktraceFrame {
pc : Int64
name : String
offset : Int64
func_idx : Int
}
///|
/// Capture and format a backtrace from the current trap state
/// Returns an array of backtrace frames
pub fn DWARFBuilder::capture_backtrace(
self : DWARFBuilder,
) -> Array[BacktraceFrame] {
let pc = c_jit_get_trap_pc()
let fp = c_jit_get_trap_fp()
let lr = c_jit_get_trap_lr()
if pc == 0L {
return []
}
// Capture up to 32 frames
// Use the extended function that includes LR for when FP is 0
let max_frames = 32
let frames_buf = FixedArray::make(max_frames * 2, 0L)
let frame_count = c_dwarf_capture_backtrace_ex(
pc, fp, lr, frames_buf, max_frames,
)
// Convert to BacktraceFrame array
let result : Array[BacktraceFrame] = []
for i in 0..
result.push({
pc: frame_pc,
name: info.name,
offset: info.offset,
func_idx: info.func_idx,
})
None =>
result.push({
pc: frame_pc,
name: "",
offset: 0L,
func_idx: -1,
})
}
}
result
}
///|
/// Format a backtrace as a string
pub fn DWARFBuilder::format_backtrace(
_self : DWARFBuilder,
frames : Array[BacktraceFrame],
) -> String {
let sb = StringBuilder::StringBuilder()
for i, frame in frames {
if frame.func_idx >= 0 {
sb.write_string(" #\{i} 0x")
sb.write_string(format_hex(frame.pc))
sb.write_string(" \{frame.name}+0x")
sb.write_string(format_hex(frame.offset))
sb.write_string("\n")
} else {
sb.write_string(" #\{i} 0x")
sb.write_string(format_hex(frame.pc))
sb.write_string(" \n")
}
}
sb.to_string()
}
///|
/// Format an Int64 as hexadecimal string
fn format_hex(n : Int64) -> String {
if n == 0L {
return "0"
}
let hex_chars = "0123456789abcdef"
let mut result = ""
let mut value = n.reinterpret_as_uint64()
while value > 0UL {
let digit = (value & 0xFUL).to_int()
result = hex_chars.code_unit_at(digit).unsafe_to_char().to_string() + result
value = value >> 4
}
result
}