1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
//! Rust API for building [Wasm Coredump].
//!
//! # Examples
//!
//! ```
//! let mut coredump_builder = wasm_coredump_builder::CoredumpBuilder::new()
//! .executable_name("/usr/bin/true.exe");
//!
//! {
//! let mut thread_builder = wasm_coredump_builder::ThreadBuilder::new()
//! .thread_name("main");
//!
//! let coredump_frame = wasm_coredump_builder::FrameBuilder::new()
//! .codeoffset(123)
//! .funcidx(6)
//! .build();
//! thread_builder.add_frame(coredump_frame);
//!
//! coredump_builder.add_thread(thread_builder.build());
//! }
//!
//! let coredump = coredump_builder.serialize().unwrap();
//! ```
//!
//! [Wasm Coredump]: https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md
type BoxError = Box<dyn std::error::Error + Sync + Send>;
#[cfg(test)]
mod test;
#[derive(Default)]
/// Coredump stack frame builder
pub struct FrameBuilder {
funcidx: u32,
codeoffset: u32,
}
impl FrameBuilder {
/// Create a new stack frame builder.
pub fn new() -> Self {
FrameBuilder::default()
}
/// WebAssembly function index in the module.
pub fn funcidx(mut self, funcidx: u32) -> Self {
self.funcidx = funcidx;
self
}
/// Binary offset of the instruction, relative to the function's start.
pub fn codeoffset(mut self, codeoffset: u32) -> Self {
self.codeoffset = codeoffset;
self
}
/// Build the coredump stack frame
pub fn build(self) -> wasm_coredump_types::StackFrame {
wasm_coredump_types::StackFrame {
funcidx: self.funcidx,
codeoffset: self.codeoffset,
locals: vec![],
stack: vec![],
}
}
}
#[derive(Default)]
/// Coredump builder
pub struct CoredumpBuilder {
executable_name: String,
threads: Vec<wasm_coredump_types::CoreStack>,
memory: (u32, Option<u32>),
data: Vec<u8>,
}
impl CoredumpBuilder {
/// Create a new coredump builder
pub fn new() -> Self {
CoredumpBuilder::default()
}
/// Set the executable name
pub fn executable_name(mut self, name: &str) -> Self {
self.executable_name = name.to_owned();
self
}
/// Set the complete process image
/// Note that partial process image dump are not supported yet.
pub fn data(mut self, bytes: &[u8]) -> Self {
self.data = bytes.to_owned();
self
}
/// Indicate the process memory usage
pub fn memory(mut self, min: u32, max: Option<u32>) -> Self {
self.memory = (min, max);
self
}
/// Add a thread to the coredump
pub fn add_thread(&mut self, thread: wasm_coredump_types::CoreStack) {
self.threads.push(thread);
}
/// Build the coredump
pub fn build(self) -> wasm_coredump_types::Coredump {
wasm_coredump_types::Coredump {
process_info: wasm_coredump_types::ProcessInfo {
executable_name: self.executable_name,
},
stacks: self.threads,
memory: vec![self.memory],
data: self.data,
}
}
/// Serialize the coredump to bytes, using the Wasm binary format.
pub fn serialize(self) -> Result<Vec<u8>, BoxError> {
let mut module = wasm_encoder::Module::new();
// core
{
let mut data = vec![];
let process_info = wasm_coredump_types::ProcessInfo {
executable_name: self.executable_name,
};
wasm_coredump_encoder::encode_coredump_process(&mut data, &process_info)?;
module.section(&wasm_encoder::CustomSection {
name: "core",
data: &data,
});
}
// corestack
for thread in self.threads {
let mut data = vec![];
wasm_coredump_encoder::encode_coredump_stack(&mut data, &thread)?;
module.section(&wasm_encoder::CustomSection {
name: "corestack",
data: &data,
});
}
// memory
{
let mut memories = wasm_encoder::MemorySection::new();
memories.memory(wasm_encoder::MemoryType {
minimum: self.memory.0 as u64,
maximum: self.memory.1.map(|v| v as u64),
memory64: false,
shared: false,
});
module.section(&memories);
}
// data
{
let mut data = wasm_encoder::DataSection::new();
let offset = wasm_encoder::ConstExpr::i32_const(0);
data.active(0, &offset, self.data);
module.section(&data);
}
Ok(module.finish())
}
}
#[derive(Default)]
/// Coredump thread builder
pub struct ThreadBuilder {
thread_name: String,
frames: Vec<wasm_coredump_types::StackFrame>,
}
impl ThreadBuilder {
/// Create a new thread builder
pub fn new() -> Self {
ThreadBuilder::default()
}
/// Set the thread name. By conventions "main" should be set for single
/// threaded applications.
pub fn thread_name(mut self, name: &str) -> Self {
self.thread_name = name.to_owned();
self
}
/// Add a stack frame to the thread
pub fn add_frame(&mut self, frame: wasm_coredump_types::StackFrame) {
self.frames.push(frame);
}
/// Build the thread
pub fn build(self) -> wasm_coredump_types::CoreStack {
wasm_coredump_types::CoreStack {
frames: self.frames,
thread_info: wasm_coredump_types::ThreadInfo {
thread_name: self.thread_name,
},
}
}
}