Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,7 @@ jobs:
sudo mkdir -p /usr/lib/local/lib/python3.10/dist-packages/lldb
sudo ln -s /usr/lib/llvm-15/lib/python3.10/dist-packages/lldb/* /usr/lib/python3/dist-packages/lldb/
# Only testing release since it is more likely to expose issues with our low-level symbol handling.
cargo test --release --test all -- --ignored --test-threads 1 debug::
cargo test --release --test all -- --ignored --test-threads 1 native_debug::
env:
LLDB: lldb-18
WASI_SDK_PATH: /tmp/wasi-sdk
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ default = [
"stack-switching",
"winch",
"pulley",
"debug",

# Enable some nice features of clap by default, but they come at a binary size
# cost, so allow disabling this through disabling of our own `default`
Expand Down Expand Up @@ -531,6 +532,7 @@ gc-drc = ["gc", "wasmtime/gc-drc", "wasmtime-cli-flags/gc-drc"]
gc-null = ["gc", "wasmtime/gc-null", "wasmtime-cli-flags/gc-null"]
pulley = ["wasmtime-cli-flags/pulley"]
stack-switching = ["wasmtime/stack-switching", "wasmtime-cli-flags/stack-switching"]
debug = ["wasmtime-cli-flags/debug", "wasmtime/debug"]

# CLI subcommands for the `wasmtime` executable. See `wasmtime $cmd --help`
# for more information on each subcommand.
Expand Down
2 changes: 1 addition & 1 deletion crates/c-api/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub extern "C" fn wasm_config_new() -> Box<wasm_config_t> {

#[unsafe(no_mangle)]
pub extern "C" fn wasmtime_config_debug_info_set(c: &mut wasm_config_t, enable: bool) {
c.config.debug_info(enable);
c.config.native_debug_info(enable);
}

#[unsafe(no_mangle)]
Expand Down
1 change: 1 addition & 0 deletions crates/cli-flags/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ threads = ["wasmtime/threads"]
memory-protection-keys = ["wasmtime/memory-protection-keys"]
pulley = ["wasmtime/pulley"]
stack-switching = ["wasmtime/stack-switching"]
debug = ["wasmtime/debug"]
13 changes: 10 additions & 3 deletions crates/cli-flags/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,9 @@ wasmtime_option_group! {
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct DebugOptions {
/// Enable generation of DWARF debug information in compiled code.
pub debug_info: Option<bool>,
pub native_debug_info: Option<bool>,
/// Enable debug instrumentation for perfect value reconstruction.
pub debug_instrumentation: Option<bool>,
/// Configure whether compiled code can map native addresses to wasm.
pub address_map: Option<bool>,
/// Configure whether logging is enabled.
Expand Down Expand Up @@ -701,8 +703,13 @@ impl CommonOptions {
enable => config.cranelift_debug_verifier(enable),
true => err,
}
if let Some(enable) = self.debug.debug_info {
config.debug_info(enable);
if let Some(enable) = self.debug.native_debug_info {
config.native_debug_info(enable);
}
match_feature! {
["debug" : self.debug.debug_instrumentation]
enable => config.debug_instrumentation(enable),
_ => err,
}
if self.debug.coredump.is_some() {
#[cfg(feature = "coredump")]
Expand Down
21 changes: 13 additions & 8 deletions crates/cranelift/src/compiled_function.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use crate::{Relocation, mach_reloc_to_reloc, mach_trap_to_trap};
use cranelift_codegen::{
Final, MachBufferFinalized, MachSrcLoc, ValueLabelsRanges, ir, isa::unwind::CfaUnwindInfo,
isa::unwind::UnwindInfo,
Final, MachBufferFinalized, MachBufferFrameLayout, MachSrcLoc, ValueLabelsRanges, ir,
isa::unwind::CfaUnwindInfo, isa::unwind::UnwindInfo,
};
use wasmtime_environ::{
FilePos, FrameStateSlotBuilder, InstructionAddressMap, PrimaryMap, TrapInformation,
};
use wasmtime_environ::{FilePos, InstructionAddressMap, PrimaryMap, TrapInformation};

#[derive(Debug, Clone, PartialEq, Eq, Default)]
/// Metadata to translate from binary offsets back to the original
Expand Down Expand Up @@ -44,8 +46,6 @@ pub struct CompiledFunctionMetadata {
pub cfa_unwind_info: Option<CfaUnwindInfo>,
/// Mapping of value labels and their locations.
pub value_labels_ranges: ValueLabelsRanges,
/// Allocated stack slots.
pub sized_stack_slots: ir::StackSlots,
/// Start source location.
pub start_srcloc: FilePos,
/// End source location.
Expand All @@ -63,6 +63,8 @@ pub struct CompiledFunction {
/// The metadata for the compiled function, including unwind information
/// the function address map.
metadata: CompiledFunctionMetadata,
/// Debug metadata for the top-level function's state slot.
pub debug_slot_descriptor: Option<FrameStateSlotBuilder>,
}

impl CompiledFunction {
Expand All @@ -79,6 +81,7 @@ impl CompiledFunction {
name_map,
alignment,
metadata: Default::default(),
debug_slot_descriptor: None,
}
}

Expand Down Expand Up @@ -155,9 +158,11 @@ impl CompiledFunction {
self.metadata.cfa_unwind_info = Some(unwind);
}

/// Set the sized stack slots.
pub fn set_sized_stack_slots(&mut self, slots: ir::StackSlots) {
self.metadata.sized_stack_slots = slots;
/// Returns the frame-layout metadata for this function.
pub fn frame_layout(&self) -> &MachBufferFrameLayout {
self.buffer
.frame_layout()
.expect("Single-function MachBuffer must have frame layout information")
}
}

Expand Down
136 changes: 123 additions & 13 deletions crates/cranelift/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ use cranelift_codegen::isa::{
unwind::{UnwindInfo, UnwindInfoKind},
};
use cranelift_codegen::print_errors::pretty_error;
use cranelift_codegen::{CompiledCode, Context, FinalizedMachCallSite};
use cranelift_codegen::{
CompiledCode, Context, FinalizedMachCallSite, MachBufferDebugTagList, MachBufferFrameLayout,
MachDebugTagPos,
};
use cranelift_entity::PrimaryMap;
use cranelift_frontend::FunctionBuilder;
use object::write::{Object, StandardSegment, SymbolId};
Expand All @@ -28,13 +31,13 @@ use std::ops::Range;
use std::path;
use std::sync::{Arc, Mutex};
use wasmparser::{FuncValidatorAllocations, FunctionBody};
use wasmtime_environ::obj::ELF_WASMTIME_EXCEPTIONS;
use wasmtime_environ::obj::{ELF_WASMTIME_EXCEPTIONS, ELF_WASMTIME_FRAMES};
use wasmtime_environ::{
Abi, AddressMapSection, BuiltinFunctionIndex, CacheStore, CompileError, CompiledFunctionBody,
DefinedFuncIndex, FlagValue, FuncKey, FunctionBodyData, FunctionLoc, HostCall,
InliningCompiler, ModuleTranslation, ModuleTypesBuilder, PtrSize, StackMapSection,
StaticModuleIndex, TrapEncodingBuilder, TrapSentinel, TripleExt, Tunables, VMOffsets,
WasmFuncType, WasmValType,
DefinedFuncIndex, FlagValue, FrameInstPos, FrameStackShape, FrameStateSlotBuilder,
FrameTableBuilder, FuncKey, FunctionBodyData, FunctionLoc, HostCall, InliningCompiler,
ModuleTranslation, ModuleTypesBuilder, PtrSize, StackMapSection, StaticModuleIndex,
TrapEncodingBuilder, TrapSentinel, TripleExt, Tunables, VMOffsets, WasmFuncType, WasmValType,
};
use wasmtime_unwinder::ExceptionTableBuilder;

Expand All @@ -53,6 +56,7 @@ struct CompilerContext {
codegen_context: Context,
incremental_cache_ctx: Option<IncrementalCacheContext>,
validator_allocations: FuncValidatorAllocations,
debug_slot_descriptor: Option<FrameStateSlotBuilder>,
abi: Option<Abi>,
}

Expand All @@ -63,6 +67,7 @@ impl Default for CompilerContext {
codegen_context: Context::new(),
incremental_cache_ctx: None,
validator_allocations: Default::default(),
debug_slot_descriptor: None,
abi: None,
}
}
Expand Down Expand Up @@ -252,7 +257,7 @@ impl wasmtime_environ::Compiler for Compiler {
context.func.collect_debug_info();
}

let mut func_env = FuncEnvironment::new(self, translation, types, wasm_func_ty);
let mut func_env = FuncEnvironment::new(self, translation, types, wasm_func_ty, key);

// The `stack_limit` global value below is the implementation of stack
// overflow checks in Wasmtime.
Expand Down Expand Up @@ -326,13 +331,19 @@ impl wasmtime_environ::Compiler for Compiler {
.map_err(|e| CompileError::Codegen(e.to_string()))?;
}

let needs_gc_heap = func_env.needs_gc_heap();

if let Some((_, slot_builder)) = func_env.state_slot {
compiler.cx.debug_slot_descriptor = Some(slot_builder);
}

let timing = cranelift_codegen::timing::take_current();
log::debug!("`{symbol}` translated to CLIF in {:?}", timing.total());
log::trace!("`{symbol}` timing info\n{timing}");

Ok(CompiledFunctionBody {
code: box_dyn_any_compiler_context(Some(compiler.cx)),
needs_gc_heap: func_env.needs_gc_heap(),
needs_gc_heap,
})
}

Expand Down Expand Up @@ -558,12 +569,12 @@ impl wasmtime_environ::Compiler for Compiler {
fn append_code(
&self,
obj: &mut Object<'static>,
funcs: &[(String, Box<dyn Any + Send + Sync>)],
funcs: &[(String, FuncKey, Box<dyn Any + Send + Sync>)],
resolve_reloc: &dyn Fn(usize, FuncKey) -> usize,
) -> Result<Vec<(SymbolId, FunctionLoc)>> {
log::trace!(
"appending functions to object file: {:#?}",
funcs.iter().map(|(sym, _)| sym).collect::<Vec<_>>()
funcs.iter().map(|(sym, _, _)| sym).collect::<Vec<_>>()
);

let mut builder =
Expand All @@ -575,9 +586,26 @@ impl wasmtime_environ::Compiler for Compiler {
let mut traps = TrapEncodingBuilder::default();
let mut stack_maps = StackMapSection::default();
let mut exception_tables = ExceptionTableBuilder::default();
let mut frame_tables = FrameTableBuilder::default();

let mut frame_descriptors = HashMap::new();
if self.tunables.debug_instrumentation {
for (_, key, func) in funcs {
debug_assert!(!func.is::<Option<CompilerContext>>());
debug_assert!(func.is::<CompiledFunction>());
let func = func.downcast_ref::<CompiledFunction>().unwrap();
frame_descriptors.insert(
*key,
func.debug_slot_descriptor
.as_ref()
.map(|builder| builder.serialize())
.unwrap_or_else(|| vec![]),
);
}
}

let mut ret = Vec::with_capacity(funcs.len());
for (i, (sym, func)) in funcs.iter().enumerate() {
for (i, (sym, _key, func)) in funcs.iter().enumerate() {
debug_assert!(!func.is::<Option<CompilerContext>>());
debug_assert!(func.is::<CompiledFunction>());
let func = func.downcast_ref::<CompiledFunction>().unwrap();
Expand All @@ -602,6 +630,17 @@ impl wasmtime_environ::Compiler for Compiler {
range.clone(),
func.buffer.call_sites(),
)?;
if self.tunables.debug_instrumentation
&& let Some(frame_layout) = func.buffer.frame_layout()
{
clif_to_env_frame_tables(
&mut frame_tables,
range.clone(),
func.buffer.debug_tags(),
frame_layout,
&frame_descriptors,
)?;
}
builder.append_padding(self.linkopts.padding_between_functions);

let info = FunctionLoc {
Expand All @@ -628,6 +667,17 @@ impl wasmtime_environ::Compiler for Compiler {
obj.append_section_data(exception_section, bytes, 1);
});

if self.tunables.debug_instrumentation {
let frame_table_section = obj.add_section(
obj.segment_name(StandardSegment::Data).to_vec(),
ELF_WASMTIME_FRAMES.as_bytes().to_vec(),
SectionKind::ReadOnlyData,
);
frame_tables.serialize(|bytes| {
obj.append_section_data(frame_table_section, bytes, 1);
});
}

Ok(ret)
}

Expand Down Expand Up @@ -1380,6 +1430,10 @@ impl FunctionCompiler<'_> {
}
}

if let Some(builder) = self.cx.debug_slot_descriptor.take() {
compiled_function.debug_slot_descriptor = Some(builder);
}

if body_and_tunables
.map(|(_, t)| t.generate_native_debuginfo)
.unwrap_or(false)
Expand All @@ -1401,8 +1455,6 @@ impl FunctionCompiler<'_> {
}
}

compiled_function
.set_sized_stack_slots(std::mem::take(&mut context.func.sized_stack_slots));
self.compiler.contexts.lock().unwrap().push(self.cx);

Ok(compiled_function)
Expand Down Expand Up @@ -1447,6 +1499,64 @@ fn clif_to_env_exception_tables<'a>(
builder.add_func(CodeOffset::try_from(range.start).unwrap(), call_sites)
}

/// Convert from Cranelift's representation of frame state slots and
/// debug tags to Wasmtime's serialized metadata.
fn clif_to_env_frame_tables<'a>(
builder: &mut FrameTableBuilder,
range: Range<u64>,
tag_sites: impl Iterator<Item = MachBufferDebugTagList<'a>>,
frame_layout: &MachBufferFrameLayout,
frame_descriptors: &HashMap<FuncKey, Vec<u8>>,
) -> anyhow::Result<()> {
let mut frame_descriptor_indices = HashMap::new();
for tag_site in tag_sites {
// Split into frames; each has three debug tags.
let mut frames = vec![];
for frame_tags in tag_site.tags.chunks_exact(3) {
let &[
ir::DebugTag::StackSlot(slot),
ir::DebugTag::User(wasm_pc),
ir::DebugTag::User(stack_shape),
] = frame_tags
else {
panic!("Invalid tags");
};

let func_key = frame_layout.stackslots[slot]
.key
.expect("Key must be present on stackslot used as state slot")
.bits();
let func_key = FuncKey::from_raw_u64(func_key);
let frame_descriptor = *frame_descriptor_indices.entry(slot).or_insert_with(|| {
let slot_to_fp_offset =
frame_layout.frame_to_fp_offset - frame_layout.stackslots[slot].offset;
let descriptor = frame_descriptors
.get(&func_key)
.expect("frame descriptor not present for FuncKey");
builder.add_frame_descriptor(slot_to_fp_offset, &descriptor)
});

frames.push((
wasm_pc,
frame_descriptor,
FrameStackShape::from_raw(stack_shape),
));
}

let native_pc_in_code_section = u32::try_from(range.start)
.unwrap()
.checked_add(tag_site.offset)
.unwrap();
let pos = match tag_site.pos {
MachDebugTagPos::Post => FrameInstPos::Post,
MachDebugTagPos::Pre => FrameInstPos::Pre,
};
builder.add_program_point(native_pc_in_code_section, pos, &frames);
}

Ok(())
}

fn save_last_wasm_entry_context(
builder: &mut FunctionBuilder,
pointer_type: ir::Type,
Expand Down
Loading