Skip to content

Commit 4fe87ae

Browse files
authored
Add a --generate-dwarf[=true|false] flag to the wast subcommand (#10780)
This commit adds support for the `wast` subcommand to generate DWARF debugging information for the text-to-binary translation that happens. This leans on the support within the `wast` crate already and enables more easily seeing where a trap is happening during development of tests due to the fact that line numbers are available as opposed to just having binary offsets with a stack trace. Currently this is enabled-by-default with an option to disable it, but I'm not actually sure if we have a use case for disabling it...
1 parent f124315 commit 4fe87ae

File tree

8 files changed

+85
-21
lines changed

8 files changed

+85
-21
lines changed

Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/wast/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ workspace = true
1616
[dependencies]
1717
anyhow = { workspace = true }
1818
wasmtime = { workspace = true, features = ['cranelift', 'wat', 'runtime', 'gc', 'async'] }
19-
wast = { workspace = true }
19+
wast = { workspace = true, features = ['dwarf'] }
2020
log = { workspace = true }
2121
tokio = { workspace = true, features = ['rt'] }
2222

crates/wast/src/wast.rs

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::path::Path;
88
use std::str;
99
use std::thread;
1010
use wasmtime::*;
11+
use wast::core::{EncodeOptions, GenerateDwarf};
1112
use wast::lexer::Lexer;
1213
use wast::parser::{self, ParseBuffer};
1314
use wast::{QuoteWat, Wast, WastArg, WastDirective, WastExecute, WastInvoke, WastRet, Wat};
@@ -24,6 +25,7 @@ pub struct WastContext<T: 'static> {
2425
component_linker: component::Linker<T>,
2526
pub(crate) store: Store<T>,
2627
pub(crate) async_runtime: Option<tokio::runtime::Runtime>,
28+
generate_dwarf: bool,
2729
}
2830

2931
enum Outcome<T = Results> {
@@ -118,6 +120,7 @@ where
118120
} else {
119121
None
120122
},
123+
generate_dwarf: true,
121124
}
122125
}
123126

@@ -187,20 +190,25 @@ where
187190
}
188191

189192
/// Perform the action portion of a command.
190-
fn perform_execute(&mut self, exec: WastExecute<'_>) -> Result<Outcome> {
193+
fn perform_execute(
194+
&mut self,
195+
exec: WastExecute<'_>,
196+
filename: &str,
197+
wast: &str,
198+
) -> Result<Outcome> {
191199
match exec {
192200
WastExecute::Invoke(invoke) => self.perform_invoke(invoke),
193-
WastExecute::Wat(module) => {
194-
Ok(match self.module_definition(QuoteWat::Wat(module))? {
201+
WastExecute::Wat(module) => Ok(
202+
match self.module_definition(QuoteWat::Wat(module), filename, wast)? {
195203
(_, ModuleKind::Core(module)) => self
196204
.instantiate_module(&module)?
197205
.map(|_| Results::Core(Vec::new())),
198206
#[cfg(feature = "component-model")]
199207
(_, ModuleKind::Component(component)) => self
200208
.instantiate_component(&component)?
201209
.map(|_| Results::Component(Vec::new())),
202-
})
203-
}
210+
},
211+
),
204212
WastExecute::Get { module, global, .. } => self.get(module.map(|s| s.name()), global),
205213
}
206214
}
@@ -325,14 +333,25 @@ where
325333
fn module_definition<'a>(
326334
&mut self,
327335
mut wat: QuoteWat<'a>,
336+
filename: &str,
337+
wast: &str,
328338
) -> Result<(Option<&'a str>, ModuleKind)> {
329339
let (is_module, name) = match &wat {
330340
QuoteWat::Wat(Wat::Module(m)) => (true, m.id),
331341
QuoteWat::QuoteModule(..) => (true, None),
332342
QuoteWat::Wat(Wat::Component(m)) => (false, m.id),
333343
QuoteWat::QuoteComponent(..) => (false, None),
334344
};
335-
let bytes = wat.encode()?;
345+
let bytes = match &mut wat {
346+
QuoteWat::Wat(wat) => {
347+
let mut opts = EncodeOptions::new();
348+
if self.generate_dwarf {
349+
opts.dwarf(filename.as_ref(), wast, GenerateDwarf::Lines);
350+
}
351+
opts.encode_wat(wat)?
352+
}
353+
_ => wat.encode()?,
354+
};
336355
if is_module {
337356
let module = Module::new(self.store.engine(), &bytes)?;
338357
Ok((name.map(|n| n.name()), ModuleKind::Core(module)))
@@ -451,7 +470,8 @@ where
451470

452471
let mut lexer = Lexer::new(wast);
453472
lexer.allow_confusing_unicode(filename.ends_with("names.wast"));
454-
let buf = ParseBuffer::new_with_lexer(lexer).map_err(adjust_wast)?;
473+
let mut buf = ParseBuffer::new_with_lexer(lexer).map_err(adjust_wast)?;
474+
buf.track_instr_spans(self.generate_dwarf);
455475
let ast = parser::parse::<Wast>(&buf).map_err(adjust_wast)?;
456476

457477
self.run_directives(ast.directives, filename, wast)
@@ -506,11 +526,11 @@ where
506526

507527
match directive {
508528
Module(module) => {
509-
let (name, module) = self.module_definition(module)?;
529+
let (name, module) = self.module_definition(module, filename, wast)?;
510530
self.module(name, &module)?;
511531
}
512532
ModuleDefinition(module) => {
513-
let (name, module) = self.module_definition(module)?;
533+
let (name, module) = self.module_definition(module, filename, wast)?;
514534
if let Some(name) = name {
515535
self.modules.insert(name.to_string(), module.clone());
516536
}
@@ -541,15 +561,15 @@ where
541561
exec,
542562
results,
543563
} => {
544-
let result = self.perform_execute(exec)?;
564+
let result = self.perform_execute(exec, filename, wast)?;
545565
self.assert_return(result, &results)?;
546566
}
547567
AssertTrap {
548568
span: _,
549569
exec,
550570
message,
551571
} => {
552-
let result = self.perform_execute(exec)?;
572+
let result = self.perform_execute(exec, filename, wast)?;
553573
self.assert_trap(result, message)?;
554574
}
555575
AssertExhaustion {
@@ -565,7 +585,7 @@ where
565585
module,
566586
message,
567587
} => {
568-
let err = match self.module_definition(module) {
588+
let err = match self.module_definition(module, filename, wast) {
569589
Ok(_) => bail!("expected module to fail to build"),
570590
Err(e) => e,
571591
};
@@ -583,7 +603,7 @@ where
583603
span: _,
584604
message: _,
585605
} => {
586-
if let Ok(_) = self.module_definition(module) {
606+
if let Ok(_) = self.module_definition(module, filename, wast) {
587607
bail!("expected malformed module to fail to instantiate");
588608
}
589609
}
@@ -592,7 +612,8 @@ where
592612
module,
593613
message,
594614
} => {
595-
let (name, module) = self.module_definition(QuoteWat::Wat(module))?;
615+
let (name, module) =
616+
self.module_definition(QuoteWat::Wat(module), filename, wast)?;
596617
let err = match self.module(name, &module) {
597618
Ok(_) => bail!("expected module to fail to link"),
598619
Err(e) => e,
@@ -632,6 +653,7 @@ where
632653
.build()
633654
.unwrap()
634655
}),
656+
generate_dwarf: self.generate_dwarf,
635657
};
636658
let name = thread.name.name();
637659
let child =
@@ -662,6 +684,13 @@ where
662684
std::fs::read(path).with_context(|| format!("failed to read `{}`", path.display()))?;
663685
self.run_buffer(path.to_str().unwrap(), &bytes)
664686
}
687+
688+
/// Whether or not to generate DWARF debugging information in custom
689+
/// sections in modules being tested.
690+
pub fn generate_dwarf(&mut self, enable: bool) -> &mut Self {
691+
self.generate_dwarf = enable;
692+
self
693+
}
665694
}
666695

667696
fn is_matching_assert_invalid_error_message(test: &str, expected: &str, actual: &str) -> bool {

src/commands/wast.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ pub struct WastCommand {
1616
/// The path of the WebAssembly test script to run
1717
#[arg(required = true, value_name = "SCRIPT_FILE")]
1818
scripts: Vec<PathBuf>,
19+
20+
/// Whether or not to generate DWARF debugging information in text-to-binary
21+
/// transformations to show line numbers in backtraces.
22+
#[arg(long, require_equals = true, value_name = "true|false")]
23+
generate_dwarf: Option<Option<bool>>,
1924
}
2025

2126
impl WastCommand {
@@ -35,6 +40,7 @@ impl WastCommand {
3540
}
3641
let mut wast_context = WastContext::new(store, wasmtime_wast::Async::Yes);
3742

43+
wast_context.generate_dwarf(optional_flag_with_default(self.generate_dwarf, true));
3844
wast_context
3945
.register_spectest(&SpectestConfig {
4046
use_shared_memory: true,
@@ -51,3 +57,11 @@ impl WastCommand {
5157
Ok(())
5258
}
5359
}
60+
61+
fn optional_flag_with_default(flag: Option<Option<bool>>, default: bool) -> bool {
62+
match flag {
63+
None => default,
64+
Some(None) => true,
65+
Some(Some(val)) => val,
66+
}
67+
}

supply-chain/audits.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2018,6 +2018,12 @@ criteria = "safe-to-deploy"
20182018
delta = "0.29.0 -> 0.31.0"
20192019
notes = "Various updates here and there, nothing too major, what you'd expect from a DWARF parsing crate."
20202020

2021+
[[audits.gimli]]
2022+
who = "Alex Crichton <[email protected]>"
2023+
criteria = "safe-to-deploy"
2024+
delta = "0.31.0 -> 0.31.1"
2025+
notes = "No fundmanetally new `unsafe` code, some small refactoring of existing code. Lots of changes in tests, not as many changes in the rest of the crate. More dwarf!"
2026+
20212027
[[audits.glob]]
20222028
who = "Jamey Sharp <[email protected]>"
20232029
criteria = "safe-to-deploy"

supply-chain/config.toml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,10 +316,6 @@ criteria = "safe-to-deploy"
316316
version = "0.2.6"
317317
criteria = "safe-to-deploy"
318318

319-
[[exemptions.gimli]]
320-
version = "0.26.1"
321-
criteria = "safe-to-deploy"
322-
323319
[[exemptions.h2]]
324320
version = "0.4.4"
325321
criteria = "safe-to-deploy"

supply-chain/imports.lock

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2507,6 +2507,23 @@ documentation.
25072507
"""
25082508
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
25092509

2510+
[[audits.mozilla.audits.gimli]]
2511+
who = "Alex Franchuk <[email protected]>"
2512+
criteria = "safe-to-deploy"
2513+
version = "0.30.0"
2514+
notes = """
2515+
Unsafe code blocks are sound. Minimal dependencies used. No use of
2516+
side-effectful std functions.
2517+
"""
2518+
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
2519+
2520+
[[audits.mozilla.audits.gimli]]
2521+
who = "Chris Martin <[email protected]>"
2522+
criteria = "safe-to-deploy"
2523+
delta = "0.30.0 -> 0.29.0"
2524+
notes = "No unsafe code, mostly algorithms and parsing. Very unlikely to cause security issues."
2525+
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
2526+
25102527
[[audits.mozilla.audits.half]]
25112528
who = "John M. Schanck <[email protected]>"
25122529
criteria = "safe-to-deploy"

tests/wast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ fn run_wast(test: &WastTest, config: WastConfig) -> anyhow::Result<()> {
242242
let result = engine.and_then(|engine| {
243243
let store = Store::new(&engine, ());
244244
let mut wast_context = WastContext::new(store, Async::Yes);
245+
wast_context.generate_dwarf(true);
245246
wast_context.register_spectest(&SpectestConfig {
246247
use_shared_memory: true,
247248
suppress_prints: true,

0 commit comments

Comments
 (0)