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 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 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")] 80 fn dwarf_dead_code() {} // this is tested over in `translate.rs` 81 82 #[test] 83 #[ignore] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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 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] 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] 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] 512 fn dwarf_shared_memory() -> Result<()> { 513 test_dwarf_simple(DWARF_SHARED_MEMORY, &[]) 514 } 515 516 #[test] 517 #[ignore] 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] 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