Extract jit_int.rs and most of jitdump_linux.rs for use outside of wasmtime (#2744)

* Extract gdb jit_int into wasmtime-jit-debug

* Move a big chunk of the jitdump code to wasmtime-jit-debug

* Fix doc markdown in perf_jitdump.rs
This commit is contained in:
bjorn3
2022-02-22 18:23:44 +01:00
committed by GitHub
parent 2616c28957
commit 4ed353a7e1
11 changed files with 392 additions and 289 deletions

12
Cargo.lock generated
View File

@@ -3628,10 +3628,20 @@ dependencies = [
"target-lexicon", "target-lexicon",
"thiserror", "thiserror",
"wasmtime-environ", "wasmtime-environ",
"wasmtime-jit-debug",
"wasmtime-runtime", "wasmtime-runtime",
"winapi", "winapi",
] ]
[[package]]
name = "wasmtime-jit-debug"
version = "0.34.0"
dependencies = [
"lazy_static",
"object",
"rustix",
]
[[package]] [[package]]
name = "wasmtime-runtime" name = "wasmtime-runtime"
version = "0.34.0" version = "0.34.0"
@@ -3641,7 +3651,6 @@ dependencies = [
"cc", "cc",
"cfg-if 1.0.0", "cfg-if 1.0.0",
"indexmap", "indexmap",
"lazy_static",
"libc", "libc",
"log", "log",
"mach", "mach",
@@ -3655,6 +3664,7 @@ dependencies = [
"userfaultfd", "userfaultfd",
"wasmtime-environ", "wasmtime-environ",
"wasmtime-fiber", "wasmtime-fiber",
"wasmtime-jit-debug",
"winapi", "winapi",
] ]

View File

@@ -0,0 +1,25 @@
[package]
name = "wasmtime-jit-debug"
version = "0.34.0"
authors = ["The Wasmtime Project Developers"]
description = "JIT debug interfaces support for Wasmtime"
license = "Apache-2.0 WITH LLVM-exception"
categories = ["development-tools::debugging"]
keywords = ["gdb", "jit"]
repository = "https://github.com/bytecodealliance/wasmtime"
readme = "README.md"
edition = "2018"
[dependencies]
lazy_static = {version = "1.3.0", optional = true }
object = { version = "0.27.0", default-features = false, features = ["std", "read_core"], optional = true }
[target.'cfg(target_os = "linux")'.dependencies]
rustix = { version = "0.33.0", optional = true }
[badges]
maintenance = { status = "actively-developed" }
[features]
gdb_jit_int = ["lazy_static"]
perf_jitdump = ["rustix", "object"]

View File

@@ -0,0 +1 @@
This is the `wasmtime-jit-debug` crate, which contains JIT debug interfaces support for Wasmtime.

View File

@@ -0,0 +1,5 @@
#[cfg(feature = "gdb_jit_int")]
pub mod gdb_jit_int;
#[cfg(all(feature = "perf_jitdump", target_os = "linux"))]
pub mod perf_jitdump;

View File

@@ -0,0 +1,293 @@
//! Support for jitdump files which can be used by perf for profiling jitted code.
//! Spec definitions for the output format is as described here:
//! <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/perf/Documentation/jitdump-specification.txt>
//!
//! Usage Example:
//! Record
//! sudo perf record -k 1 -e instructions:u target/debug/wasmtime -g --jitdump test.wasm
//! Combine
//! sudo perf inject -v -j -i perf.data -o perf.jit.data
//! Report
//! sudo perf report -i perf.jit.data -F+period,srcline
use std::fmt::Debug;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::Write;
use std::path::Path;
use std::ptr;
use std::{mem, process};
/// Defines jitdump record types
#[repr(u32)]
pub enum RecordId {
/// Value 0: JIT_CODE_LOAD: record describing a jitted function
JitCodeLoad = 0,
/// Value 1: JIT_CODE_MOVE: record describing an already jitted function which is moved
_JitCodeMove = 1,
/// Value 2: JIT_CODE_DEBUG_INFO: record describing the debug information for a jitted function
JitCodeDebugInfo = 2,
/// Value 3: JIT_CODE_CLOSE: record marking the end of the jit runtime (optional)
_JitCodeClose = 3,
/// Value 4: JIT_CODE_UNWINDING_INFO: record describing a function unwinding information
_JitCodeUnwindingInfo = 4,
}
/// Each record starts with this fixed size record header which describes the record that follows
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct RecordHeader {
/// uint32_t id: a value identifying the record type (see below)
pub id: u32,
/// uint32_t total_size: the size in bytes of the record including the header.
pub record_size: u32,
/// uint64_t timestamp: a timestamp of when the record was created.
pub timestamp: u64,
}
unsafe impl object::Pod for RecordHeader {}
/// The CodeLoadRecord is used for describing jitted functions
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct CodeLoadRecord {
/// Fixed sized header that describes this record
pub header: RecordHeader,
/// `uint32_t pid`: OS process id of the runtime generating the jitted code
pub pid: u32,
/// `uint32_t tid`: OS thread identification of the runtime thread generating the jitted code
pub tid: u32,
/// `uint64_t vma`: virtual address of jitted code start
pub virtual_address: u64,
/// `uint64_t code_addr`: code start address for the jitted code. By default vma = code_addr
pub address: u64,
/// `uint64_t code_size`: size in bytes of the generated jitted code
pub size: u64,
/// `uint64_t code_index`: unique identifier for the jitted code (see below)
pub index: u64,
}
unsafe impl object::Pod for CodeLoadRecord {}
/// Describes source line information for a jitted function
#[derive(Debug, Default)]
#[repr(C)]
pub struct DebugEntry {
/// `uint64_t code_addr`: address of function for which the debug information is generated
pub address: u64,
/// `uint32_t line`: source file line number (starting at 1)
pub line: u32,
/// `uint32_t discrim`: column discriminator, 0 is default
pub discriminator: u32,
/// `char name[n]`: source file name in ASCII, including null termination
pub filename: String,
}
/// Describes debug information for a jitted function. An array of debug entries are
/// appended to this record during writting. Note, this record must preceed the code
/// load record that describes the same jitted function.
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct DebugInfoRecord {
/// Fixed sized header that describes this record
pub header: RecordHeader,
/// `uint64_t code_addr`: address of function for which the debug information is generated
pub address: u64,
/// `uint64_t nr_entry`: number of debug entries for the function appended to this record
pub count: u64,
}
unsafe impl object::Pod for DebugInfoRecord {}
/// Fixed-sized header for each jitdump file
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct FileHeader {
/// `uint32_t magic`: a magic number tagging the file type. The value is 4-byte long and represents the
/// string "JiTD" in ASCII form. It is 0x4A695444 or 0x4454694a depending on the endianness. The field can
/// be used to detect the endianness of the file
pub magic: u32,
/// `uint32_t version`: a 4-byte value representing the format version. It is currently set to 2
pub version: u32,
/// `uint32_t total_size`: size in bytes of file header
pub size: u32,
/// `uint32_t elf_mach`: ELF architecture encoding (ELF e_machine value as specified in /usr/include/elf.h)
pub e_machine: u32,
/// `uint32_t pad1`: padding. Reserved for future use
pub pad1: u32,
/// `uint32_t pid`: JIT runtime process identification (OS specific)
pub pid: u32,
/// `uint64_t timestamp`: timestamp of when the file was created
pub timestamp: u64,
/// `uint64_t flags`: a bitmask of flags
pub flags: u64,
}
unsafe impl object::Pod for FileHeader {}
/// Interface for driving the creation of jitdump files
pub struct JitDumpFile {
/// File instance for the jit dump file
jitdump_file: File,
map_addr: usize,
/// Unique identifier for jitted code
code_index: u64,
e_machine: u32,
}
impl JitDumpFile {
/// Intialize a JitDumpAgent and write out the header
pub fn new(filename: impl AsRef<Path>, e_machine: u32) -> io::Result<Self> {
let jitdump_file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(filename.as_ref())?;
// After we make our `*.dump` file we execute an `mmap` syscall,
// specifically with executable permissions, to map it into our address
// space. This is required so `perf inject` will work later. The `perf
// inject` command will see that an mmap syscall happened, and it'll see
// the filename we mapped, and that'll trigger it to actually read and
// parse the file.
//
// To match what some perf examples are doing we keep this `mmap` alive
// until this agent goes away.
let map_addr = unsafe {
let ptr = rustix::io::mmap(
ptr::null_mut(),
rustix::process::page_size(),
rustix::io::ProtFlags::EXEC | rustix::io::ProtFlags::READ,
rustix::io::MapFlags::PRIVATE,
&jitdump_file,
0,
)?;
ptr as usize
};
let mut state = JitDumpFile {
jitdump_file,
map_addr,
code_index: 0,
e_machine,
};
state.write_file_header()?;
Ok(state)
}
}
impl JitDumpFile {
/// Returns timestamp from a single source
pub fn get_time_stamp(&self) -> u64 {
// We need to use `CLOCK_MONOTONIC` on Linux which is what `Instant`
// conveniently also uses, but `Instant` doesn't allow us to get access
// to nanoseconds as an internal detail, so we calculate the nanoseconds
// ourselves here.
let ts = rustix::time::clock_gettime(rustix::time::ClockId::Monotonic);
// TODO: What does it mean for either sec or nsec to be negative?
(ts.tv_sec * 1_000_000_000 + ts.tv_nsec) as u64
}
/// Returns the next code index
pub fn next_code_index(&mut self) -> u64 {
let code_index = self.code_index;
self.code_index += 1;
code_index
}
pub fn write_file_header(&mut self) -> io::Result<()> {
let header = FileHeader {
timestamp: self.get_time_stamp(),
e_machine: self.e_machine,
magic: 0x4A695444,
version: 1,
size: mem::size_of::<FileHeader>() as u32,
pad1: 0,
pid: process::id(),
flags: 0,
};
self.jitdump_file.write_all(object::bytes_of(&header))?;
Ok(())
}
pub fn write_code_load_record(
&mut self,
record_name: &str,
cl_record: CodeLoadRecord,
code_buffer: &[u8],
) -> io::Result<()> {
self.jitdump_file.write_all(object::bytes_of(&cl_record))?;
self.jitdump_file.write_all(record_name.as_bytes())?;
self.jitdump_file.write_all(b"\0")?;
self.jitdump_file.write_all(code_buffer)?;
Ok(())
}
/// Write DebugInfoRecord to open jit dump file.
/// Must be written before the corresponding CodeLoadRecord.
pub fn write_debug_info_record(&mut self, dir_record: DebugInfoRecord) -> io::Result<()> {
self.jitdump_file.write_all(object::bytes_of(&dir_record))?;
Ok(())
}
/// Write DebugInfoRecord to open jit dump file.
/// Must be written before the corresponding CodeLoadRecord.
pub fn write_debug_info_entries(&mut self, die_entries: Vec<DebugEntry>) -> io::Result<()> {
for entry in die_entries.iter() {
self.jitdump_file
.write_all(object::bytes_of(&entry.address))?;
self.jitdump_file.write_all(object::bytes_of(&entry.line))?;
self.jitdump_file
.write_all(object::bytes_of(&entry.discriminator))?;
self.jitdump_file.write_all(entry.filename.as_bytes())?;
self.jitdump_file.write_all(b"\0")?;
}
Ok(())
}
pub fn dump_code_load_record(
&mut self,
method_name: &str,
addr: *const u8,
len: usize,
timestamp: u64,
pid: u32,
tid: u32,
) -> io::Result<()> {
let name_len = method_name.len() + 1;
let size_limit = mem::size_of::<CodeLoadRecord>();
let rh = RecordHeader {
id: RecordId::JitCodeLoad as u32,
record_size: size_limit as u32 + name_len as u32 + len as u32,
timestamp,
};
let clr = CodeLoadRecord {
header: rh,
pid,
tid,
virtual_address: addr as u64,
address: addr as u64,
size: len as u64,
index: self.next_code_index(),
};
unsafe {
let code_buffer: &[u8] = std::slice::from_raw_parts(addr, len);
self.write_code_load_record(method_name, clr, code_buffer)
}
}
}
impl Drop for JitDumpFile {
fn drop(&mut self) {
unsafe {
rustix::io::munmap(self.map_addr as *mut _, rustix::process::page_size()).unwrap();
}
}
}

View File

@@ -12,6 +12,7 @@ edition = "2018"
[dependencies] [dependencies]
wasmtime-environ = { path = "../environ", version = "=0.34.0" } wasmtime-environ = { path = "../environ", version = "=0.34.0" }
wasmtime-jit-debug = { path = "../jit-debug", version = "=0.34.0", features = ["perf_jitdump"], optional = true }
wasmtime-runtime = { path = "../runtime", version = "=0.34.0" } wasmtime-runtime = { path = "../runtime", version = "=0.34.0" }
region = "2.2.0" region = "2.2.0"
thiserror = "1.0.4" thiserror = "1.0.4"
@@ -35,7 +36,7 @@ winapi = { version = "0.3.8", features = ["winnt", "impl-default"] }
rustix = "0.33.0" rustix = "0.33.0"
[features] [features]
jitdump = [] jitdump = ['wasmtime-jit-debug']
vtune = ['ittapi-rs'] vtune = ['ittapi-rs']
[badges] [badges]

View File

@@ -14,124 +14,14 @@
use crate::{CompiledModule, ProfilingAgent}; use crate::{CompiledModule, ProfilingAgent};
use anyhow::Result; use anyhow::Result;
use object::{Object, ObjectSection}; use object::{Object, ObjectSection};
use std::fmt::Debug;
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::ptr;
use std::sync::Mutex; use std::sync::Mutex;
use std::{borrow, mem, process}; use std::{borrow, mem, process};
use target_lexicon::Architecture; use target_lexicon::Architecture;
use wasmtime_environ::EntityRef; use wasmtime_environ::EntityRef;
use wasmtime_jit_debug::perf_jitdump::*;
use object::elf; use object::elf;
/// Defines jitdump record types
#[repr(u32)]
pub enum RecordId {
/// Value 0: JIT_CODE_LOAD: record describing a jitted function
JitCodeLoad = 0,
/// Value 1: JIT_CODE_MOVE: record describing an already jitted function which is moved
_JitCodeMove = 1,
/// Value 2: JIT_CODE_DEBUG_INFO: record describing the debug information for a jitted function
JitCodeDebugInfo = 2,
/// Value 3: JIT_CODE_CLOSE: record marking the end of the jit runtime (optional)
_JitCodeClose = 3,
/// Value 4: JIT_CODE_UNWINDING_INFO: record describing a function unwinding information
_JitCodeUnwindingInfo = 4,
}
/// Each record starts with this fixed size record header which describes the record that follows
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct RecordHeader {
/// uint32_t id: a value identifying the record type (see below)
id: u32,
/// uint32_t total_size: the size in bytes of the record including the header.
record_size: u32,
/// uint64_t timestamp: a timestamp of when the record was created.
timestamp: u64,
}
unsafe impl object::Pod for RecordHeader {}
/// The CodeLoadRecord is used for describing jitted functions
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct CodeLoadRecord {
/// Fixed sized header that describes this record
header: RecordHeader,
/// uint32_t pid: OS process id of the runtime generating the jitted code
pid: u32,
/// uint32_t tid: OS thread identification of the runtime thread generating the jitted code
tid: u32,
/// uint64_t vma: virtual address of jitted code start
virtual_address: u64,
/// uint64_t code_addr: code start address for the jitted code. By default vma = code_addr
address: u64,
/// uint64_t code_size: size in bytes of the generated jitted code
size: u64,
/// uint64_t code_index: unique identifier for the jitted code (see below)
index: u64,
}
unsafe impl object::Pod for CodeLoadRecord {}
/// Describes source line information for a jitted function
#[derive(Debug, Default)]
#[repr(C)]
pub struct DebugEntry {
/// uint64_t code_addr: address of function for which the debug information is generated
address: u64,
/// uint32_t line: source file line number (starting at 1)
line: u32,
/// uint32_t discrim: column discriminator, 0 is default
discriminator: u32,
/// char name[n]: source file name in ASCII, including null termination
filename: String,
}
/// Describes debug information for a jitted function. An array of debug entries are
/// appended to this record during writting. Note, this record must preceed the code
/// load record that describes the same jitted function.
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct DebugInfoRecord {
/// Fixed sized header that describes this record
header: RecordHeader,
/// uint64_t code_addr: address of function for which the debug information is generated
address: u64,
/// uint64_t nr_entry: number of debug entries for the function appended to this record
count: u64,
}
unsafe impl object::Pod for DebugInfoRecord {}
/// Fixed-sized header for each jitdump file
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct FileHeader {
/// uint32_t magic: a magic number tagging the file type. The value is 4-byte long and represents the
/// string "JiTD" in ASCII form. It is 0x4A695444 or 0x4454694a depending on the endianness. The field can
/// be used to detect the endianness of the file
magic: u32,
/// uint32_t version: a 4-byte value representing the format version. It is currently set to 2
version: u32,
/// uint32_t total_size: size in bytes of file header
size: u32,
/// uint32_t elf_mach: ELF architecture encoding (ELF e_machine value as specified in /usr/include/elf.h)
e_machine: u32,
/// uint32_t pad1: padding. Reserved for future use
pad1: u32,
/// uint32_t pid: JIT runtime process identification (OS specific)
pid: u32,
/// uint64_t timestamp: timestamp of when the file was created
timestamp: u64,
/// uint64_t flags: a bitmask of flags
flags: u64,
}
unsafe impl object::Pod for FileHeader {}
/// Interface for driving the creation of jitdump files /// Interface for driving the creation of jitdump files
pub struct JitDumpAgent { pub struct JitDumpAgent {
// Note that we use a mutex internally to serialize writing out to our // Note that we use a mutex internally to serialize writing out to our
@@ -141,13 +31,7 @@ pub struct JitDumpAgent {
} }
struct State { struct State {
/// File instance for the jit dump file jitdump_file: JitDumpFile,
jitdump_file: File,
map_addr: usize,
/// Unique identifier for jitted code
code_index: u64,
/// Flag for experimenting with dumping code load record /// Flag for experimenting with dumping code load record
/// after each function (true) or after each module. This /// after each function (true) or after each module. This
@@ -159,42 +43,23 @@ impl JitDumpAgent {
/// Intialize a JitDumpAgent and write out the header /// Intialize a JitDumpAgent and write out the header
pub fn new() -> Result<Self> { pub fn new() -> Result<Self> {
let filename = format!("./jit-{}.dump", process::id()); let filename = format!("./jit-{}.dump", process::id());
let jitdump_file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(&filename)?;
// After we make our `*.dump` file we execute an `mmap` syscall, let e_machine = match target_lexicon::HOST.architecture {
// specifically with executable permissions, to map it into our address Architecture::X86_64 => elf::EM_X86_64 as u32,
// space. This is required so `perf inject` will work later. The `perf Architecture::X86_32(_) => elf::EM_386 as u32,
// inject` command will see that an mmap syscall happened, and it'll see Architecture::Arm(_) => elf::EM_ARM as u32,
// the filename we mapped, and that'll trigger it to actually read and Architecture::Aarch64(_) => elf::EM_AARCH64 as u32,
// parse the file. Architecture::S390x => elf::EM_S390 as u32,
// _ => unimplemented!("unrecognized architecture"),
// To match what some perf examples are doing we keep this `mmap` alive
// until this agent goes away.
let map_addr = unsafe {
let ptr = rustix::io::mmap(
ptr::null_mut(),
rustix::process::page_size(),
rustix::io::ProtFlags::EXEC | rustix::io::ProtFlags::READ,
rustix::io::MapFlags::PRIVATE,
&jitdump_file,
0,
)?;
ptr as usize
}; };
let mut state = State {
jitdump_file, let jitdump_file = JitDumpFile::new(filename, e_machine)?;
map_addr,
code_index: 0,
dump_funcs: true,
};
state.write_file_header()?;
Ok(JitDumpAgent { Ok(JitDumpAgent {
state: Mutex::new(state), state: Mutex::new(State {
jitdump_file,
dump_funcs: true,
}),
}) })
} }
} }
@@ -212,80 +77,6 @@ impl ProfilingAgent for JitDumpAgent {
} }
impl State { impl State {
/// Returns timestamp from a single source
fn get_time_stamp(&self) -> u64 {
// We need to use `CLOCK_MONOTONIC` on Linux which is what `Instant`
// conveniently also uses, but `Instant` doesn't allow us to get access
// to nanoseconds as an internal detail, so we calculate the nanoseconds
// ourselves here.
let ts = rustix::time::clock_gettime(rustix::time::ClockId::Monotonic);
// TODO: What does it mean for either sec or nsec to be negative?
(ts.tv_sec * 1_000_000_000 + ts.tv_nsec) as u64
}
/// Returns the ELF machine architecture.
fn get_e_machine(&self) -> u32 {
match target_lexicon::HOST.architecture {
Architecture::X86_64 => elf::EM_X86_64 as u32,
Architecture::X86_32(_) => elf::EM_386 as u32,
Architecture::Arm(_) => elf::EM_ARM as u32,
Architecture::Aarch64(_) => elf::EM_AARCH64 as u32,
Architecture::S390x => elf::EM_S390 as u32,
_ => unimplemented!("unrecognized architecture"),
}
}
fn write_file_header(&mut self) -> Result<()> {
let header = FileHeader {
timestamp: self.get_time_stamp(),
e_machine: self.get_e_machine(),
magic: 0x4A695444,
version: 1,
size: mem::size_of::<FileHeader>() as u32,
pad1: 0,
pid: process::id(),
flags: 0,
};
self.jitdump_file.write_all(object::bytes_of(&header))?;
Ok(())
}
fn write_code_load_record(
&mut self,
record_name: &str,
cl_record: CodeLoadRecord,
code_buffer: &[u8],
) -> Result<()> {
self.jitdump_file.write_all(object::bytes_of(&cl_record))?;
self.jitdump_file.write_all(record_name.as_bytes())?;
self.jitdump_file.write_all(b"\0")?;
self.jitdump_file.write_all(code_buffer)?;
Ok(())
}
/// Write DebugInfoRecord to open jit dump file.
/// Must be written before the corresponding CodeLoadRecord.
fn write_debug_info_record(&mut self, dir_record: DebugInfoRecord) -> Result<()> {
self.jitdump_file.write_all(object::bytes_of(&dir_record))?;
Ok(())
}
/// Write DebugInfoRecord to open jit dump file.
/// Must be written before the corresponding CodeLoadRecord.
fn write_debug_info_entries(&mut self, die_entries: Vec<DebugEntry>) -> Result<()> {
for entry in die_entries.iter() {
self.jitdump_file
.write_all(object::bytes_of(&entry.address))?;
self.jitdump_file.write_all(object::bytes_of(&entry.line))?;
self.jitdump_file
.write_all(object::bytes_of(&entry.discriminator))?;
self.jitdump_file.write_all(entry.filename.as_bytes())?;
self.jitdump_file.write_all(b"\0")?;
}
Ok(())
}
/// Sent when a method is compiled and loaded into memory by the VM. /// Sent when a method is compiled and loaded into memory by the VM.
pub fn module_load(&mut self, module: &CompiledModule, dbg_image: Option<&[u8]>) { pub fn module_load(&mut self, module: &CompiledModule, dbg_image: Option<&[u8]>) {
let pid = process::id(); let pid = process::id();
@@ -301,18 +92,28 @@ impl State {
); );
} }
} else { } else {
let timestamp = self.get_time_stamp(); let timestamp = self.jitdump_file.get_time_stamp();
let name = super::debug_name(module, idx); let name = super::debug_name(module, idx);
self.dump_code_load_record(&name, addr, len, timestamp, pid, tid); if let Err(err) = self
.jitdump_file
.dump_code_load_record(&name, addr, len, timestamp, pid, tid)
{
println!("Jitdump: write_code_load_failed_record failed: {:?}\n", err);
}
} }
} }
// Note: these are the trampolines into exported functions. // Note: these are the trampolines into exported functions.
for (idx, func, len) in module.trampolines() { for (idx, func, len) in module.trampolines() {
let (addr, len) = (func as usize as *const u8, len); let (addr, len) = (func as usize as *const u8, len);
let timestamp = self.get_time_stamp(); let timestamp = self.jitdump_file.get_time_stamp();
let name = format!("wasm::trampoline[{}]", idx.index()); let name = format!("wasm::trampoline[{}]", idx.index());
self.dump_code_load_record(&name, addr, len, timestamp, pid, tid); if let Err(err) = self
.jitdump_file
.dump_code_load_record(&name, addr, len, timestamp, pid, tid)
{
println!("Jitdump: write_code_load_failed_record failed: {:?}\n", err);
}
} }
} }
@@ -324,44 +125,12 @@ impl State {
pid: u32, pid: u32,
tid: u32, tid: u32,
) { ) {
let timestamp = self.get_time_stamp(); let timestamp = self.jitdump_file.get_time_stamp();
self.dump_code_load_record(name, addr, size, timestamp, pid, tid); if let Err(err) = self
} .jitdump_file
.dump_code_load_record(&name, addr, size, timestamp, pid, tid)
fn dump_code_load_record( {
&mut self, println!("Jitdump: write_code_load_failed_record failed: {:?}\n", err);
method_name: &str,
addr: *const u8,
len: usize,
timestamp: u64,
pid: u32,
tid: u32,
) {
let name_len = method_name.len() + 1;
let size_limit = mem::size_of::<CodeLoadRecord>();
let rh = RecordHeader {
id: RecordId::JitCodeLoad as u32,
record_size: size_limit as u32 + name_len as u32 + len as u32,
timestamp,
};
let clr = CodeLoadRecord {
header: rh,
pid,
tid,
virtual_address: addr as u64,
address: addr as u64,
size: len as u64,
index: self.code_index,
};
self.code_index += 1;
unsafe {
let code_buffer: &[u8] = std::slice::from_raw_parts(addr, len);
if let Err(err) = self.write_code_load_record(method_name, clr, code_buffer) {
println!("Jitdump: write_code_load_failed_record failed: {:?}\n", err);
}
} }
} }
@@ -414,8 +183,13 @@ impl State {
break; break;
} }
if !self.dump_funcs { if !self.dump_funcs {
let timestamp = self.get_time_stamp(); let timestamp = self.jitdump_file.get_time_stamp();
self.dump_code_load_record(module_name, addr, len, timestamp, pid, tid); if let Err(err) =
self.jitdump_file
.dump_code_load_record(module_name, addr, len, timestamp, pid, tid)
{
println!("Jitdump: write_code_load_failed_record failed: {:?}\n", err);
}
} }
Ok(()) Ok(())
} }
@@ -510,16 +284,17 @@ impl State {
clr.header.record_size = mem::size_of::<CodeLoadRecord>() as u32 clr.header.record_size = mem::size_of::<CodeLoadRecord>() as u32
+ (clr_name.len() + 1) as u32 + (clr_name.len() + 1) as u32
+ clr.size as u32; + clr.size as u32;
clr.index = self.code_index; clr.index = self.jitdump_file.next_code_index();
self.code_index += 1;
self.dump_debug_info(&unit, &dwarf, clr.address, clr.size, None)?; self.dump_debug_info(&unit, &dwarf, clr.address, clr.size, None)?;
clr.header.timestamp = self.get_time_stamp(); clr.header.timestamp = self.jitdump_file.get_time_stamp();
unsafe { unsafe {
let code_buffer: &[u8] = let code_buffer: &[u8] =
std::slice::from_raw_parts(clr.address as *const u8, clr.size as usize); std::slice::from_raw_parts(clr.address as *const u8, clr.size as usize);
let _ = self.write_code_load_record(&clr_name, clr, code_buffer); let _ =
self.jitdump_file
.write_code_load_record(&clr_name, clr, code_buffer);
} }
} }
} else { } else {
@@ -597,7 +372,7 @@ impl State {
size: u64, size: u64,
file_suffix: Option<&str>, file_suffix: Option<&str>,
) -> Result<()> { ) -> Result<()> {
let timestamp = self.get_time_stamp(); let timestamp = self.jitdump_file.get_time_stamp();
if let Some(program) = unit.line_program.clone() { if let Some(program) = unit.line_program.clone() {
let mut debug_info_record = DebugInfoRecord { let mut debug_info_record = DebugInfoRecord {
header: RecordHeader { header: RecordHeader {
@@ -654,21 +429,13 @@ impl State {
debug_info_record.header.record_size = debug_info_record.header.record_size =
mem::size_of::<DebugInfoRecord>() as u32 + debug_entries_size as u32; mem::size_of::<DebugInfoRecord>() as u32 + debug_entries_size as u32;
let _ = self.write_debug_info_record(debug_info_record); let _ = self.jitdump_file.write_debug_info_record(debug_info_record);
let _ = self.write_debug_info_entries(debug_entries); let _ = self.jitdump_file.write_debug_info_entries(debug_entries);
} }
Ok(()) Ok(())
} }
} }
impl Drop for State {
fn drop(&mut self) {
unsafe {
rustix::io::munmap(self.map_addr as *mut _, rustix::process::page_size()).unwrap();
}
}
}
trait Reader: gimli::Reader<Offset = usize> + Send + Sync {} trait Reader: gimli::Reader<Offset = usize> + Send + Sync {}
impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian> where impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian> where

View File

@@ -13,6 +13,7 @@ edition = "2018"
[dependencies] [dependencies]
wasmtime-environ = { path = "../environ", version = "=0.34.0" } wasmtime-environ = { path = "../environ", version = "=0.34.0" }
wasmtime-fiber = { path = "../fiber", version = "=0.34.0", optional = true } wasmtime-fiber = { path = "../fiber", version = "=0.34.0", optional = true }
wasmtime-jit-debug = { path = "../jit-debug", version = "=0.34.0", features = ["gdb_jit_int"] }
region = "2.1.0" region = "2.1.0"
libc = { version = "0.2.112", default-features = false } libc = { version = "0.2.112", default-features = false }
log = "0.4.8" log = "0.4.8"
@@ -22,7 +23,6 @@ thiserror = "1.0.4"
more-asserts = "0.2.1" more-asserts = "0.2.1"
cfg-if = "1.0" cfg-if = "1.0"
backtrace = "0.3.61" backtrace = "0.3.61"
lazy_static = "1.3.0"
rand = "0.8.3" rand = "0.8.3"
anyhow = "1.0.38" anyhow = "1.0.38"
memfd = { version = "0.4.1", optional = true } memfd = { version = "0.4.1", optional = true }

View File

@@ -34,7 +34,6 @@ mod export;
mod externref; mod externref;
mod imports; mod imports;
mod instance; mod instance;
mod jit_int;
mod memory; mod memory;
mod mmap; mod mmap;
mod mmap_vec; mod mmap_vec;
@@ -45,6 +44,8 @@ mod vmcontext;
pub mod debug_builtins; pub mod debug_builtins;
pub mod libcalls; pub mod libcalls;
pub use wasmtime_jit_debug::gdb_jit_int::GdbJitImageRegistration;
pub use crate::export::*; pub use crate::export::*;
pub use crate::externref::*; pub use crate::externref::*;
pub use crate::imports::Imports; pub use crate::imports::Imports;
@@ -56,7 +57,6 @@ pub use crate::instance::{
pub use crate::instance::{ pub use crate::instance::{
InstanceLimits, ModuleLimits, PoolingAllocationStrategy, PoolingInstanceAllocator, InstanceLimits, ModuleLimits, PoolingAllocationStrategy, PoolingInstanceAllocator,
}; };
pub use crate::jit_int::GdbJitImageRegistration;
pub use crate::memory::{DefaultMemoryCreator, Memory, RuntimeLinearMemory, RuntimeMemoryCreator}; pub use crate::memory::{DefaultMemoryCreator, Memory, RuntimeLinearMemory, RuntimeMemoryCreator};
pub use crate::mmap::Mmap; pub use crate::mmap::Mmap;
pub use crate::mmap_vec::MmapVec; pub use crate::mmap_vec::MmapVec;

View File

@@ -40,6 +40,7 @@ const CRATES_TO_PUBLISH: &[&str] = &[
"wiggle-generate", "wiggle-generate",
"wiggle-macro", "wiggle-macro",
// wasmtime // wasmtime
"wasmtime-jit-debug",
"wasmtime-fiber", "wasmtime-fiber",
"wasmtime-environ", "wasmtime-environ",
"wasmtime-runtime", "wasmtime-runtime",