1 use filecheck::{CheckerBuilder, NO_VARIABLES};
2 use std::env;
3 use std::io::Write;
4 use std::process::Command;
5 use tempfile::NamedTempFile;
6 use test_programs_artifacts::*;
7 use wasmtime::{Result, bail, format_err};
8
9 macro_rules! assert_test_exists {
10 ($name:ident) => {
11 #[expect(unused_imports, reason = "here to assert tests exist")]
12 use self::$name as _;
13 };
14 }
15 foreach_dwarf!(assert_test_exists);
16
lldb_with_script(args: &[&str], script: &str) -> Result<String>17 fn lldb_with_script(args: &[&str], script: &str) -> Result<String> {
18 let _ = env_logger::try_init();
19
20 let lldb_path = env::var("LLDB").unwrap_or("lldb".to_string());
21 let mut cmd = Command::new(&lldb_path);
22
23 cmd.arg("--batch");
24 if cfg!(target_os = "macos") {
25 cmd.args(&["-o", "settings set plugin.jit-loader.gdb.enable on"]);
26 }
27 let mut script_file = NamedTempFile::new()?;
28 script_file.write(script.as_bytes())?;
29 let script_path = script_file.path().to_str().unwrap();
30 cmd.args(&["-s", &script_path]);
31
32 let mut me = std::env::current_exe().expect("current_exe specified");
33 me.pop(); // chop off the file name
34 me.pop(); // chop off `deps`
35 if cfg!(target_os = "windows") {
36 me.push("wasmtime.exe");
37 } else {
38 me.push("wasmtime");
39 }
40 cmd.arg(me);
41
42 cmd.arg("--");
43 cmd.args(args);
44
45 log::trace!("Running command: {cmd:?}");
46 let output = cmd.output().expect("success");
47
48 let stdout = String::from_utf8(output.stdout)?;
49 log::trace!("--- sdout ---\n{stdout}");
50
51 let stderr = String::from_utf8(output.stderr)?;
52 log::trace!("--- sderr ---\n{stderr}");
53
54 if !output.status.success() {
55 bail!(
56 "failed to execute {cmd:?}:\n\
57 --- stderr ---\n\
58 {stderr}\n\
59 --- stdout ---\n\
60 {stdout}",
61 );
62 }
63 Ok(stdout)
64 }
65
check_lldb_output(output: &str, directives: &str) -> Result<()>66 fn check_lldb_output(output: &str, directives: &str) -> Result<()> {
67 let mut builder = CheckerBuilder::new();
68 builder
69 .text(directives)
70 .map_err(|e| format_err!("unable to build checker: {e:?}"))?;
71 let checker = builder.finish();
72 let check = checker
73 .explain(output, NO_VARIABLES)
74 .map_err(|e| format_err!("{e:?}"))?;
75 assert!(check.0, "didn't pass check {}", check.1);
76 Ok(())
77 }
78
79 #[allow(dead_code, reason = "tested elsewhere")]
dwarf_dead_code()80 fn dwarf_dead_code() {} // this is tested over in `translate.rs`
81
82 #[test]
83 #[ignore]
dwarf_fib_wasm() -> Result<()>84 pub fn dwarf_fib_wasm() -> Result<()> {
85 let output = lldb_with_script(
86 &[
87 "-Ccache=n",
88 "-Ddebug-info",
89 "-Oopt-level=0",
90 "--invoke",
91 "fib",
92 DWARF_FIB_WASM,
93 "3",
94 ],
95 r#"b fib
96 r
97 fr v
98 c"#,
99 )?;
100
101 check_lldb_output(
102 &output,
103 r#"
104 check: Breakpoint 1: no locations (pending)
105 check: Unable to resolve breakpoint to any actual locations.
106 check: 1 location added to breakpoint 1
107 check: stop reason = breakpoint 1.1
108 check: frame #0
109 sameln: JIT
110 sameln: fib(n=3)
111 check: n = 3
112 check: a = 0
113 check: resuming
114 check: exited with status
115 "#,
116 )?;
117 Ok(())
118 }
119
120 #[test]
121 #[ignore]
dwarf_fib_wasm_dwarf5() -> Result<()>122 pub fn dwarf_fib_wasm_dwarf5() -> Result<()> {
123 let output = lldb_with_script(
124 &[
125 "-Ccache=n",
126 "-Ddebug-info",
127 "-Oopt-level=0",
128 "--invoke",
129 "fib",
130 DWARF_FIB_WASM_DWARF5,
131 "3",
132 ],
133 r#"b fib
134 r
135 fr v
136 c"#,
137 )?;
138
139 check_lldb_output(
140 &output,
141 r#"
142 check: Breakpoint 1: no locations (pending)
143 check: Unable to resolve breakpoint to any actual locations.
144 check: 1 location added to breakpoint 1
145 check: stop reason = breakpoint 1.1
146 check: frame #0
147 sameln: JIT
148 sameln: fib(n=3)
149 check: n = 3
150 check: a = 0
151 check: resuming
152 check: exited with status
153 "#,
154 )?;
155 Ok(())
156 }
157
158 #[test]
159 #[ignore]
dwarf_fib_wasm_split4() -> Result<()>160 pub fn dwarf_fib_wasm_split4() -> Result<()> {
161 let output = lldb_with_script(
162 &[
163 "-Ccache=n",
164 "-Ddebug-info",
165 "-Oopt-level=0",
166 "--invoke",
167 "fib",
168 DWARF_FIB_WASM_SPLIT4,
169 "3",
170 ],
171 r#"b fib
172 r
173 fr v
174 c"#,
175 )?;
176
177 check_lldb_output(
178 &output,
179 r#"
180 check: Breakpoint 1: no locations (pending)
181 check: Unable to resolve breakpoint to any actual locations.
182 check: 1 location added to breakpoint 1
183 check: stop reason = breakpoint 1.1
184 check: frame #0
185 sameln: JIT
186 sameln: fib(n=3)
187 check: n = 3
188 check: a = 0
189 check: resuming
190 check: exited with status
191 "#,
192 )?;
193 Ok(())
194 }
195
196 #[test]
197 #[ignore]
dwarf_generic() -> Result<()>198 pub fn dwarf_generic() -> Result<()> {
199 let output = lldb_with_script(
200 &["-Ccache=n", "-Ddebug-info", "-Oopt-level=0", DWARF_GENERIC],
201 r#"br set -n debug_break -C up
202 r
203 p __vmctx->set()
204 p (x + x)
205 c
206 p (x + x)
207 c
208 p inst.BaseValue + inst.DerivedValue
209 c
210 type lookup DerivedType
211 c
212 p __this->BaseValue + __this->DerivedValue
213 c
214 p __this->BaseValue + __this->DerivedValue
215 c
216 p __this->BaseValue + __this->DerivedValue
217 c
218 f
219 n
220 s
221 v var0
222 v var1
223 c"#,
224 )?;
225
226 check_lldb_output(
227 &output,
228 r#"
229 check: stop reason = breakpoint 1.1
230 check: 2
231 check: stop reason = breakpoint 1.1
232 check: 4
233 check: stop reason = breakpoint 1.1
234 check: 3
235 check: stop reason = breakpoint 1.1
236 check: static int InstanceMethod
237 check: static int ConstInstanceMethod
238 check: stop reason = breakpoint 1.1
239 check: 6
240 check: stop reason = breakpoint 1.1
241 check: 7
242 check: stop reason = breakpoint 1.1
243 check: 8
244 check: stop reason = breakpoint 1.1
245 check: 9
246 check: 10
247 check: exited with status = 0
248 "#,
249 )?;
250 Ok(())
251 }
252
253 #[test]
254 #[ignore]
dwarf_codegen_optimized() -> Result<()>255 pub fn dwarf_codegen_optimized() -> Result<()> {
256 let output = lldb_with_script(
257 &[
258 "-Ccache=n",
259 "-Ddebug-info",
260 "-Oopt-level=2",
261 DWARF_CODEGEN_OPTIMIZED,
262 ],
263 r#"b InitializeTest
264 r
265 b dwarf_codegen_optimized.cc:25
266 b dwarf_codegen_optimized.cc:26
267 c
268 v x
269 c
270 v x
271 c"#,
272 )?;
273
274 check_lldb_output(
275 &output,
276 r#"
277 check: stop reason = breakpoint 1.1
278 check: stop reason = breakpoint 2.1
279 check: x = 42
280 check: stop reason = breakpoint 3.1
281 check: x = <variable not available>
282 check: exited with status = 0
283 "#,
284 )?;
285 Ok(())
286 }
287
288 #[test]
289 #[ignore]
dwarf_codegen_optimized_wasm_optimized() -> Result<()>290 pub fn dwarf_codegen_optimized_wasm_optimized() -> Result<()> {
291 let output = lldb_with_script(
292 &[
293 "-Ccache=n",
294 "-Ddebug-info",
295 "-Oopt-level=2",
296 DWARF_CODEGEN_OPTIMIZED_WASM_OPTIMIZED,
297 ],
298 r#"b InitializeTest
299 r
300 b dwarf_codegen_optimized_wasm_optimized.cc:23
301 b dwarf_codegen_optimized_wasm_optimized.cc:29
302 c
303 v b
304 c
305 v b
306 c"#,
307 )?;
308
309 check_lldb_output(
310 &output,
311 r#"
312 check: stop reason = breakpoint 1.1
313 check: stop reason = breakpoint 2.1
314 check: b = 42
315 check: stop reason = breakpoint 3.1
316 check: b = 43
317 check: exited with status = 0
318 "#,
319 )?;
320 Ok(())
321 }
322
323 #[test]
324 #[ignore]
dwarf_fraction_norm() -> Result<()>325 pub fn dwarf_fraction_norm() -> Result<()> {
326 let output = lldb_with_script(
327 &[
328 "-Ccache=n",
329 "-Oopt-level=0",
330 "-Ddebug-info",
331 DWARF_FRACTION_NORM,
332 ],
333 r#"b dwarf_fraction_norm.cc:26
334 r
335 p __vmctx->set(),n->denominator
336 c"#,
337 )?;
338
339 check_lldb_output(
340 &output,
341 r#"
342 check: Breakpoint 1: no locations (pending)
343 check: stop reason = breakpoint 1.1
344 check: frame #0
345 sameln: norm(n=(__ptr =
346 check: 27
347 check: resuming
348 "#,
349 )?;
350 Ok(())
351 }
352
353 #[test]
354 #[ignore]
dwarf_two_removed_branches() -> Result<()>355 pub fn dwarf_two_removed_branches() -> Result<()> {
356 let output = lldb_with_script(
357 &[
358 "-Ccache=n",
359 "-Oopt-level=0",
360 "-Ddebug-info",
361 DWARF_TWO_REMOVED_BRANCHES,
362 ],
363 r#"r"#,
364 )?;
365
366 // We are simply checking that the output compiles.
367 check_lldb_output(
368 &output,
369 r#"
370 check: exited with status
371 "#,
372 )?;
373 Ok(())
374 }
375
376 #[test]
377 #[ignore]
dwarf_spilled_frame_base() -> Result<()>378 pub fn dwarf_spilled_frame_base() -> Result<()> {
379 let output = lldb_with_script(
380 &[
381 "-Ccache=n",
382 "-Oopt-level=0",
383 "-Ddebug-info",
384 DWARF_SPILLED_FRAME_BASE,
385 ],
386 r#"b dwarf_spilled_frame_base.c:13
387 r
388 fr v i
389 n
390 fr v i
391 n
392 fr v i
393 n
394 fr v i
395 n
396 fr v i
397 n
398 fr v i
399 c
400 "#,
401 )?;
402
403 // Check that if the frame base (shadow frame pointer) local
404 // is spilled, we can still read locals that reference it.
405 check_lldb_output(
406 &output,
407 r#"
408 check: i = 0
409 check: i = 1
410 check: i = 1
411 check: i = 1
412 check: i = 1
413 check: i = 1
414 check: exited with status
415 "#,
416 )?;
417 Ok(())
418 }
419
420 #[test]
421 #[ignore]
dwarf_fission() -> Result<()>422 pub fn dwarf_fission() -> Result<()> {
423 let output = lldb_with_script(
424 &["-Ccache=n", "-Ddebug-info", "-Oopt-level=0", DWARF_FISSION],
425 r#"breakpoint set --file dwarf_fission.c --line 8
426 r
427 fr v
428 s
429 fr v
430 c"#,
431 )?;
432
433 check_lldb_output(
434 &output,
435 r#"
436 check: Breakpoint 1: no locations (pending)
437 check: Unable to resolve breakpoint to any actual locations.
438 check: 1 location added to breakpoint 1
439 check: stop reason = breakpoint 1.1
440 check: i = 1
441 check: stop reason = step in
442 check: i = 2
443 check: resuming
444 check: exited with status = 0
445 "#,
446 )?;
447 Ok(())
448 }
449
test_dwarf_simple(wasm: &str, extra_args: &[&str]) -> Result<()>450 fn test_dwarf_simple(wasm: &str, extra_args: &[&str]) -> Result<()> {
451 println!("testing {wasm:?}");
452 let mut args = vec![
453 "-Ccache=n",
454 "-Oopt-level=0",
455 "-Ddebug-info",
456 "-Wshared-memory",
457 ];
458 args.extend(extra_args);
459 args.push(wasm);
460 let output = lldb_with_script(
461 &args,
462 r#"
463 breakpoint set --file dwarf_simple.rs --line 3
464 breakpoint set --file dwarf_simple.rs --line 5
465 r
466 fr v
467 c
468 fr v
469 breakpoint disable 2
470 c"#,
471 )?;
472
473 check_lldb_output(
474 &output,
475 r#"
476 check: Breakpoint 1: no locations (pending)
477 check: Unable to resolve breakpoint to any actual locations.
478 check: 1 location added to breakpoint 1
479 check: stop reason = breakpoint 1.1
480 check: dwarf_simple.rs:3
481 check: a = 100
482 check: dwarf_simple.rs:5
483 check: a = 110
484 check: b = 117
485 check: resuming
486 check: exited with status = 0
487 "#,
488 )?;
489 Ok(())
490 }
491
492 #[test]
493 #[ignore]
dwarf_simple() -> Result<()>494 fn dwarf_simple() -> Result<()> {
495 for wasm in [DWARF_SIMPLE, DWARF_SIMPLE_COMPONENT] {
496 test_dwarf_simple(wasm, &[])?;
497 }
498 Ok(())
499 }
500
501 #[test]
502 #[ignore]
dwarf_imported_memory() -> Result<()>503 fn dwarf_imported_memory() -> Result<()> {
504 test_dwarf_simple(
505 DWARF_IMPORTED_MEMORY,
506 &["--preload=env=./tests/all/native_debug/satisfy_memory_import.wat"],
507 )
508 }
509
510 #[test]
511 #[ignore]
dwarf_shared_memory() -> Result<()>512 fn dwarf_shared_memory() -> Result<()> {
513 test_dwarf_simple(DWARF_SHARED_MEMORY, &[])
514 }
515
516 #[test]
517 #[ignore]
dwarf_multiple_codegen_units() -> Result<()>518 fn dwarf_multiple_codegen_units() -> Result<()> {
519 for wasm in [
520 DWARF_MULTIPLE_CODEGEN_UNITS,
521 DWARF_MULTIPLE_CODEGEN_UNITS_COMPONENT,
522 ] {
523 println!("testing {wasm:?}");
524 let output = lldb_with_script(
525 &["-Ccache=n", "-Oopt-level=0", "-Ddebug-info", wasm],
526 r#"
527 breakpoint set --file dwarf_multiple_codegen_units.rs --line 3
528 breakpoint set --file dwarf_multiple_codegen_units.rs --line 10
529 r
530 fr v
531 c
532 fr v
533 breakpoint delete 2
534 finish
535 c"#,
536 )?;
537
538 check_lldb_output(
539 &output,
540 r#"
541 check: Breakpoint 1: no locations (pending)
542 check: Breakpoint 2: no locations (pending)
543 check: stop reason = breakpoint 1.1
544 check: foo::bar(a)
545 check: a = 3
546 check: sum += i
547 check: x = 3
548 check: sum = 0
549 check: 1 breakpoints deleted
550 check: Return value: $(=.*) 3
551 check: exited with status = 0
552 "#,
553 )?;
554 }
555 Ok(())
556 }
557
558 #[test]
559 #[ignore]
dwarf_cold_block() -> Result<()>560 pub fn dwarf_cold_block() -> Result<()> {
561 let output = lldb_with_script(
562 &[
563 "-Ccache=n",
564 "-Oopt-level=0",
565 "-Ddebug-info",
566 DWARF_COLD_BLOCK,
567 ],
568 r#"b foo
569 r
570 p __vmctx->set(),*f1
571 n
572 p __vmctx->set(),*f1
573 n
574 p __vmctx->set(),*f1
575 n
576 p __vmctx->set(),*f1
577 c"#,
578 )?;
579
580 check_lldb_output(
581 &output,
582 r#"
583 check: Breakpoint 1: no locations (pending)
584 check: stop reason = breakpoint 1.1
585 check: frame #0
586 sameln: foo(f1=(__ptr =
587 check: data = 42
588 check: data = 42
589 check: data = 42
590 check: data = 42
591 check: resuming
592 "#,
593 )?;
594 Ok(())
595 }
596