dusk_wasmtime/runtime/
instantiate.rs1use crate::{code_memory::CodeMemory, profiling_agent::ProfilingAgent};
7use anyhow::Result;
8use std::str;
9use std::sync::Arc;
10use wasmtime_environ::{
11 CompiledFunctionInfo, CompiledModuleInfo, DefinedFuncIndex, FuncIndex, FunctionLoc,
12 FunctionName, Metadata, Module, ModuleInternedTypeIndex, PrimaryMap, StackMapInformation,
13 WasmFunctionInfo,
14};
15use wasmtime_runtime::{CompiledModuleId, CompiledModuleIdAllocator, MmapVec};
16
17pub struct CompiledModule {
19 module: Arc<Module>,
20 funcs: PrimaryMap<DefinedFuncIndex, CompiledFunctionInfo>,
21 wasm_to_native_trampolines: Vec<(ModuleInternedTypeIndex, FunctionLoc)>,
22 meta: Metadata,
23 code_memory: Arc<CodeMemory>,
24 #[cfg(feature = "debug-builtins")]
25 dbg_jit_registration: Option<wasmtime_runtime::GdbJitImageRegistration>,
26 unique_id: CompiledModuleId,
28 func_names: Vec<FunctionName>,
29}
30
31impl CompiledModule {
32 pub fn from_artifacts(
49 code_memory: Arc<CodeMemory>,
50 info: CompiledModuleInfo,
51 profiler: &dyn ProfilingAgent,
52 id_allocator: &CompiledModuleIdAllocator,
53 ) -> Result<Self> {
54 let mut ret = Self {
55 module: Arc::new(info.module),
56 funcs: info.funcs,
57 wasm_to_native_trampolines: info.wasm_to_native_trampolines,
58 #[cfg(feature = "debug-builtins")]
59 dbg_jit_registration: None,
60 code_memory,
61 meta: info.meta,
62 unique_id: id_allocator.alloc(),
63 func_names: info.func_names,
64 };
65 ret.register_debug_and_profiling(profiler)?;
66
67 Ok(ret)
68 }
69
70 fn register_debug_and_profiling(&mut self, profiler: &dyn ProfilingAgent) -> Result<()> {
71 #[cfg(feature = "debug-builtins")]
72 if self.meta.native_debug_info_present {
73 use anyhow::Context;
74
75 let text = self.text();
76 let bytes = crate::debug::create_gdbjit_image(
77 self.mmap().to_vec(),
78 (text.as_ptr(), text.len()),
79 )
80 .context("failed to create jit image for gdb")?;
81 let reg = wasmtime_runtime::GdbJitImageRegistration::register(bytes);
82 self.dbg_jit_registration = Some(reg);
83 }
84 profiler.register_module(&self.code_memory.mmap()[..], &|addr| {
85 let (idx, _) = self.func_by_text_offset(addr)?;
86 let idx = self.module.func_index(idx);
87 let name = self.func_name(idx)?;
88 let mut demangled = String::new();
89 wasmtime_environ::demangle_function_name(&mut demangled, name).unwrap();
90 Some(demangled)
91 });
92 Ok(())
93 }
94
95 pub fn unique_id(&self) -> CompiledModuleId {
98 self.unique_id
99 }
100
101 pub fn mmap(&self) -> &MmapVec {
104 self.code_memory.mmap()
105 }
106
107 pub fn code_memory(&self) -> &Arc<CodeMemory> {
109 &self.code_memory
110 }
111
112 #[inline]
116 pub fn text(&self) -> &[u8] {
117 self.code_memory.text()
118 }
119
120 pub fn module(&self) -> &Arc<Module> {
122 &self.module
123 }
124
125 pub fn func_name(&self, idx: FuncIndex) -> Option<&str> {
128 let i = self.func_names.binary_search_by_key(&idx, |n| n.idx).ok()?;
130 let name = &self.func_names[i];
131
132 let data = self.code_memory().func_name_data();
137 Some(str::from_utf8(&data[name.offset as usize..][..name.len as usize]).unwrap())
138 }
139
140 pub fn module_mut(&mut self) -> Option<&mut Module> {
142 Arc::get_mut(&mut self.module)
143 }
144
145 #[inline]
148 pub fn finished_functions(
149 &self,
150 ) -> impl ExactSizeIterator<Item = (DefinedFuncIndex, &[u8])> + '_ {
151 self.funcs
152 .iter()
153 .map(move |(i, _)| (i, self.finished_function(i)))
154 }
155
156 #[inline]
158 pub fn finished_function(&self, index: DefinedFuncIndex) -> &[u8] {
159 let loc = self.funcs[index].wasm_func_loc;
160 &self.text()[loc.start as usize..][..loc.length as usize]
161 }
162
163 pub fn array_to_wasm_trampoline(&self, index: DefinedFuncIndex) -> Option<&[u8]> {
171 let loc = self.funcs[index].array_to_wasm_trampoline?;
172 Some(&self.text()[loc.start as usize..][..loc.length as usize])
173 }
174
175 #[inline]
183 pub fn native_to_wasm_trampoline(&self, index: DefinedFuncIndex) -> Option<&[u8]> {
184 let loc = self.funcs[index].native_to_wasm_trampoline?;
185 Some(&self.text()[loc.start as usize..][..loc.length as usize])
186 }
187
188 pub fn wasm_to_native_trampoline(&self, signature: ModuleInternedTypeIndex) -> &[u8] {
194 let idx = self
195 .wasm_to_native_trampolines
196 .binary_search_by_key(&signature, |entry| entry.0)
197 .expect("should have a Wasm-to-native trampline for all signatures");
198 let (_, loc) = self.wasm_to_native_trampolines[idx];
199 &self.text()[loc.start as usize..][..loc.length as usize]
200 }
201
202 pub fn stack_maps(&self) -> impl Iterator<Item = (&[u8], &[StackMapInformation])> {
208 self.finished_functions().map(|(_, f)| f).zip(
209 self.funcs
210 .values()
211 .map(|f| &f.wasm_func_info.stack_maps[..]),
212 )
213 }
214
215 pub fn func_by_text_offset(&self, text_offset: usize) -> Option<(DefinedFuncIndex, u32)> {
220 let text_offset = u32::try_from(text_offset).unwrap();
221
222 let index = match self.funcs.binary_search_values_by_key(&text_offset, |e| {
223 debug_assert!(e.wasm_func_loc.length > 0);
224 e.wasm_func_loc.start + e.wasm_func_loc.length - 1
226 }) {
227 Ok(k) => {
228 k
230 }
231 Err(k) => {
232 k
236 }
237 };
238
239 let CompiledFunctionInfo { wasm_func_loc, .. } = self.funcs.get(index)?;
240 let start = wasm_func_loc.start;
241 let end = wasm_func_loc.start + wasm_func_loc.length;
242
243 if text_offset < start || end < text_offset {
244 return None;
245 }
246
247 Some((index, text_offset - wasm_func_loc.start))
248 }
249
250 pub fn func_loc(&self, index: DefinedFuncIndex) -> &FunctionLoc {
252 &self
253 .funcs
254 .get(index)
255 .expect("defined function should be present")
256 .wasm_func_loc
257 }
258
259 pub fn wasm_func_info(&self, index: DefinedFuncIndex) -> &WasmFunctionInfo {
261 &self
262 .funcs
263 .get(index)
264 .expect("defined function should be present")
265 .wasm_func_info
266 }
267
268 #[cfg(feature = "addr2line")]
274 pub fn symbolize_context(&self) -> Result<Option<SymbolizeContext<'_>>> {
275 use anyhow::Context;
276 use gimli::EndianSlice;
277 if !self.meta.has_wasm_debuginfo {
278 return Ok(None);
279 }
280 let dwarf = gimli::Dwarf::load(|id| -> Result<_> {
281 let data = self
287 .meta
288 .dwarf
289 .binary_search_by_key(&(id as u8), |(id, _)| *id)
290 .map(|i| {
291 let (_, range) = &self.meta.dwarf[i];
292 &self.code_memory().dwarf()[range.start as usize..range.end as usize]
293 })
294 .unwrap_or(&[]);
295 Ok(EndianSlice::new(data, gimli::LittleEndian))
296 })?;
297 let cx = addr2line::Context::from_dwarf(dwarf)
298 .context("failed to create addr2line dwarf mapping context")?;
299 Ok(Some(SymbolizeContext {
300 inner: cx,
301 code_section_offset: self.meta.code_section_offset,
302 }))
303 }
304
305 pub fn has_unparsed_debuginfo(&self) -> bool {
308 self.meta.has_unparsed_debuginfo
309 }
310
311 pub fn has_address_map(&self) -> bool {
317 !self.code_memory.address_map_data().is_empty()
318 }
319}
320
321#[cfg(feature = "addr2line")]
322type Addr2LineContext<'a> = addr2line::Context<gimli::EndianSlice<'a, gimli::LittleEndian>>;
323
324#[cfg(feature = "addr2line")]
327pub struct SymbolizeContext<'a> {
328 inner: Addr2LineContext<'a>,
329 code_section_offset: u64,
330}
331
332#[cfg(feature = "addr2line")]
333impl<'a> SymbolizeContext<'a> {
334 pub fn addr2line(&self) -> &Addr2LineContext<'a> {
337 &self.inner
338 }
339
340 pub fn code_section_offset(&self) -> u64 {
343 self.code_section_offset
344 }
345}