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