Skip to content

Commit

Permalink
Move JIT debug image registration to CodeMemory (bytecodealliance#9470)
Browse files Browse the repository at this point in the history
* Move JIT debug image registration to CodeMemory

JIT images correspond to ELF images, which may represent
multiple modules within a single component.

* Add the last ifdef

* Remove ManuallyDrop as per feedback
  • Loading branch information
SingleAccretion authored Oct 16, 2024
1 parent edad0bb commit ae3bf36
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 73 deletions.
1 change: 0 additions & 1 deletion crates/environ/src/compile/module_artifacts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,6 @@ impl<'a> ObjectBuilder<'a> {
wasm_to_array_trampolines,
func_names,
meta: Metadata {
native_debug_info_present: self.tunables.generate_native_debuginfo,
has_unparsed_debuginfo,
code_section_offset: debuginfo.wasm_file.code_section_offset,
has_wasm_debuginfo: self.tunables.parse_wasm_debuginfo,
Expand Down
3 changes: 0 additions & 3 deletions crates/environ/src/module_artifacts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,6 @@ pub struct FunctionName {
/// Metadata associated with a compiled ELF artifact.
#[derive(Serialize, Deserialize)]
pub struct Metadata {
/// Whether or not native debug information is available in `obj`
pub native_debug_info_present: bool,

/// Whether or not the original wasm module contained debug information that
/// we skipped and did not parse.
pub has_unparsed_debuginfo: bool,
Expand Down
67 changes: 48 additions & 19 deletions crates/wasmtime/src/runtime/code_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

use crate::prelude::*;
use crate::runtime::vm::{libcalls, MmapVec, UnwindRegistration};
use core::mem::ManuallyDrop;
use core::ops::Range;
use object::endian::NativeEndian;
use object::read::{elf::ElfFile64, Object, ObjectSection};
Expand All @@ -15,12 +14,14 @@ use wasmtime_jit_icache_coherence as icache_coherence;
/// This type consumes ownership of a region of memory and will manage the
/// executable permissions of the contained JIT code as necessary.
pub struct CodeMemory {
// NB: these are `ManuallyDrop` because `unwind_registration` must be
// dropped first since it refers to memory owned by `mmap`.
mmap: ManuallyDrop<MmapVec>,
unwind_registration: ManuallyDrop<Option<UnwindRegistration>>,
mmap: MmapVec,
unwind_registration: Option<UnwindRegistration>,
#[cfg(feature = "debug-builtins")]
debug_registration: Option<crate::runtime::vm::GdbJitImageRegistration>,
published: bool,
enable_branch_protection: bool,
#[cfg(feature = "debug-builtins")]
has_native_debug_info: bool,

relocations: Vec<(usize, obj::LibCall)>,

Expand All @@ -32,16 +33,15 @@ pub struct CodeMemory {
address_map_data: Range<usize>,
func_name_data: Range<usize>,
info_data: Range<usize>,
dwarf: Range<usize>,
wasm_dwarf: Range<usize>,
}

impl Drop for CodeMemory {
fn drop(&mut self) {
// Drop `unwind_registration` before `self.mmap`
unsafe {
ManuallyDrop::drop(&mut self.unwind_registration);
ManuallyDrop::drop(&mut self.mmap);
}
// Drop the registrations before `self.mmap` since they (implicitly) refer to it.
let _ = self.unwind_registration.take();
#[cfg(feature = "debug-builtins")]
let _ = self.debug_registration.take();
}
}

Expand All @@ -65,12 +65,14 @@ impl CodeMemory {
let mut text = 0..0;
let mut unwind = 0..0;
let mut enable_branch_protection = None;
#[cfg(feature = "debug-builtins")]
let mut has_native_debug_info = false;
let mut trap_data = 0..0;
let mut wasm_data = 0..0;
let mut address_map_data = 0..0;
let mut func_name_data = 0..0;
let mut info_data = 0..0;
let mut dwarf = 0..0;
let mut wasm_dwarf = 0..0;
for section in obj.sections() {
let data = section.data().err2anyhow()?;
let name = section.name().err2anyhow()?;
Expand Down Expand Up @@ -124,23 +126,29 @@ impl CodeMemory {
obj::ELF_WASMTIME_TRAPS => trap_data = range,
obj::ELF_NAME_DATA => func_name_data = range,
obj::ELF_WASMTIME_INFO => info_data = range,
obj::ELF_WASMTIME_DWARF => dwarf = range,
obj::ELF_WASMTIME_DWARF => wasm_dwarf = range,
#[cfg(feature = "debug-builtins")]
".debug_info" => has_native_debug_info = true,

_ => log::debug!("ignoring section {name}"),
}
}
Ok(Self {
mmap: ManuallyDrop::new(mmap),
unwind_registration: ManuallyDrop::new(None),
mmap,
unwind_registration: None,
#[cfg(feature = "debug-builtins")]
debug_registration: None,
published: false,
enable_branch_protection: enable_branch_protection
.ok_or_else(|| anyhow!("missing `{}` section", obj::ELF_WASM_BTI))?,
#[cfg(feature = "debug-builtins")]
has_native_debug_info,
text,
unwind,
trap_data,
address_map_data,
func_name_data,
dwarf,
wasm_dwarf,
info_data,
wasm_data,
relocations,
Expand All @@ -162,8 +170,8 @@ impl CodeMemory {

/// Returns the contents of the `ELF_WASMTIME_DWARF` section.
#[inline]
pub fn dwarf(&self) -> &[u8] {
&self.mmap[self.dwarf.clone()]
pub fn wasm_dwarf(&self) -> &[u8] {
&self.mmap[self.wasm_dwarf.clone()]
}

/// Returns the data in the `ELF_NAME_DATA` section.
Expand Down Expand Up @@ -211,6 +219,7 @@ impl CodeMemory {
///
/// * Change page protections from read/write to read/execute.
/// * Register unwinding information with the OS
/// * Register this image with the debugger if native DWARF is present
///
/// After this function executes all JIT code should be ready to execute.
pub fn publish(&mut self) -> Result<()> {
Expand Down Expand Up @@ -268,6 +277,9 @@ impl CodeMemory {
// runtime that there's unwinding information available for all
// our just-published JIT functions.
self.register_unwind_info()?;

#[cfg(feature = "debug-builtins")]
self.register_debug_image()?;
}

Ok(())
Expand Down Expand Up @@ -314,7 +326,24 @@ impl CodeMemory {
let registration =
UnwindRegistration::new(text.as_ptr(), unwind_info.as_ptr(), unwind_info.len())
.context("failed to create unwind info registration")?;
*self.unwind_registration = Some(registration);
self.unwind_registration = Some(registration);
Ok(())
}

#[cfg(feature = "debug-builtins")]
fn register_debug_image(&mut self) -> Result<()> {
if !self.has_native_debug_info {
return Ok(());
}

// TODO-DebugInfo: we're copying the whole image here, which is pretty wasteful.
// Use the existing memory by teaching code here about relocations in DWARF sections
// and anything else necessary that is done in "create_gdbjit_image" right now.
let image = self.mmap().to_vec();
let text: &[u8] = self.text();
let bytes = crate::debug::create_gdbjit_image(image, (text.as_ptr(), text.len()))?;
let reg = crate::runtime::vm::GdbJitImageRegistration::register(bytes);
self.debug_registration = Some(reg);
Ok(())
}

Expand Down
23 changes: 5 additions & 18 deletions crates/wasmtime/src/runtime/instantiate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ pub struct CompiledModule {
wasm_to_array_trampolines: Vec<(ModuleInternedTypeIndex, FunctionLoc)>,
meta: Metadata,
code_memory: Arc<CodeMemory>,
#[cfg(feature = "debug-builtins")]
dbg_jit_registration: Option<crate::runtime::vm::GdbJitImageRegistration>,
/// A unique ID used to register this module with the engine.
unique_id: CompiledModuleId,
func_names: Vec<FunctionName>,
Expand Down Expand Up @@ -54,30 +52,19 @@ impl CompiledModule {
module: Arc::new(info.module),
funcs: info.funcs,
wasm_to_array_trampolines: info.wasm_to_array_trampolines,
#[cfg(feature = "debug-builtins")]
dbg_jit_registration: None,
code_memory,
meta: info.meta,
unique_id: CompiledModuleId::new(),
func_names: info.func_names,
};
ret.register_debug_and_profiling(profiler)?;
ret.register_profiling(profiler)?;

Ok(ret)
}

fn register_debug_and_profiling(&mut self, profiler: &dyn ProfilingAgent) -> Result<()> {
#[cfg(feature = "debug-builtins")]
if self.meta.native_debug_info_present {
let text = self.text();
let bytes = crate::debug::create_gdbjit_image(
self.mmap().to_vec(),
(text.as_ptr(), text.len()),
)
.context("failed to create jit image for gdb")?;
let reg = crate::runtime::vm::GdbJitImageRegistration::register(bytes);
self.dbg_jit_registration = Some(reg);
}
fn register_profiling(&mut self, profiler: &dyn ProfilingAgent) -> Result<()> {
// TODO-Bug?: "code_memory" is not exclusive for this module in the case of components,
// so we may be registering the same code range multiple times here.
profiler.register_module(&self.code_memory.mmap()[..], &|addr| {
let (idx, _) = self.func_by_text_offset(addr)?;
let idx = self.module.func_index(idx);
Expand Down Expand Up @@ -279,7 +266,7 @@ impl CompiledModule {
let (_, range) = &self.meta.dwarf[i];
let start = range.start.try_into().ok()?;
let end = range.end.try_into().ok()?;
self.code_memory().dwarf().get(start..end)
self.code_memory().wasm_dwarf().get(start..end)
})
.unwrap_or(&[]);
Ok(EndianSlice::new(data, gimli::LittleEndian))
Expand Down
32 changes: 0 additions & 32 deletions tests/all/debug/lldb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@ fn check_lldb_output(output: &str, directives: &str) -> Result<()> {

#[test]
#[ignore]
#[cfg(all(
any(target_os = "linux", target_os = "macos"),
target_pointer_width = "64"
))]
pub fn test_debug_dwarf_lldb() -> Result<()> {
let output = lldb_with_script(
&[
Expand Down Expand Up @@ -104,10 +100,6 @@ check: exited with status

#[test]
#[ignore]
#[cfg(all(
any(target_os = "linux", target_os = "macos"),
target_pointer_width = "64"
))]
pub fn test_debug_dwarf5_lldb() -> Result<()> {
let output = lldb_with_script(
&[
Expand Down Expand Up @@ -145,10 +137,6 @@ check: exited with status

#[test]
#[ignore]
#[cfg(all(
any(target_os = "linux", target_os = "macos"),
target_pointer_width = "64"
))]
pub fn test_debug_split_dwarf4_lldb() -> Result<()> {
let output = lldb_with_script(
&[
Expand Down Expand Up @@ -186,10 +174,6 @@ check: exited with status

#[test]
#[ignore]
#[cfg(all(
any(target_os = "linux", target_os = "macos"),
target_pointer_width = "64"
))]
pub fn test_debug_dwarf_ref() -> Result<()> {
let output = lldb_with_script(
&[
Expand Down Expand Up @@ -220,10 +204,6 @@ check: resuming

#[test]
#[ignore]
#[cfg(all(
any(target_os = "linux", target_os = "macos"),
target_pointer_width = "64"
))]
pub fn test_debug_inst_offsets_are_correct_when_branches_are_removed() -> Result<()> {
let output = lldb_with_script(
&[
Expand All @@ -247,10 +227,6 @@ check: exited with status

#[test]
#[ignore]
#[cfg(all(
any(target_os = "linux", target_os = "macos"),
target_pointer_width = "64"
))]
pub fn test_spilled_frame_base_is_accessible() -> Result<()> {
let output = lldb_with_script(
&[
Expand Down Expand Up @@ -305,10 +281,6 @@ int main()
*/
#[test]
#[ignore]
#[cfg(all(
any(target_os = "linux", target_os = "macos"),
target_pointer_width = "64"
))]
pub fn test_debug_dwarf5_fission_lldb() -> Result<()> {
let output = lldb_with_script(
&[
Expand Down Expand Up @@ -341,10 +313,6 @@ check: exited with status = 0
Ok(())
}

#[cfg(all(
any(target_os = "linux", target_os = "macos"),
target_pointer_width = "64"
))]
mod test_programs {
use super::{check_lldb_output, lldb_with_script};
use anyhow::Result;
Expand Down

0 comments on commit ae3bf36

Please sign in to comment.