Skip to content

Commit 1ad72bf

Browse files
committed
Print QEMU output directly instead of waiting until it finishes
The `output` method captures the output in a `Vec`, so we can only print it when the QEMU command is finished. This is problematic when a test panics inside the bootloader because we never exit QEMU in that case. Thus, we never print the error message, which makes debugging more difficult. This commit fixes this issue by piping the stdout and stderr directly to a background thread, which prints them directly. We also print the full QEMU command and a separator line between test runs now, which should make the output easier to read.
1 parent c73786d commit 1ad72bf

File tree

1 file changed

+70
-59
lines changed

1 file changed

+70
-59
lines changed

tests/runner/src/lib.rs

Lines changed: 70 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{io::Write, path::Path, process::Command};
1+
use std::{io::Read, path::Path, process::Command};
22

33
const QEMU_ARGS: &[&str] = &[
44
"-device",
@@ -9,6 +9,7 @@ const QEMU_ARGS: &[&str] = &[
99
"none",
1010
"--no-reboot",
1111
];
12+
const SEPARATOR: &str = "\n____________________________________\n";
1213

1314
pub fn run_test_kernel(kernel_binary_path: &str) {
1415
run_test_kernel_with_ramdisk(kernel_binary_path, None)
@@ -54,73 +55,83 @@ pub fn run_test_kernel_with_ramdisk(kernel_binary_path: &str, ramdisk_path: Opti
5455

5556
#[cfg(feature = "uefi")]
5657
pub fn run_test_kernel_on_uefi(out_gpt_path: &Path) {
57-
let mut run_cmd = Command::new("qemu-system-x86_64");
58-
run_cmd
59-
.arg("-drive")
60-
.arg(format!("format=raw,file={}", out_gpt_path.display()));
61-
run_cmd.args(QEMU_ARGS);
62-
run_cmd.arg("-bios").arg(ovmf_prebuilt::ovmf_pure_efi());
63-
64-
let child_output = run_cmd.output().unwrap();
65-
strip_ansi_escapes::Writer::new(std::io::stderr())
66-
.write_all(&child_output.stderr)
67-
.unwrap();
68-
strip_ansi_escapes::Writer::new(std::io::stderr())
69-
.write_all(&child_output.stdout)
70-
.unwrap();
71-
72-
match child_output.status.code() {
73-
Some(33) => {} // success
74-
Some(35) => panic!("Test failed"), // success
75-
other => panic!("Test failed with unexpected exit code `{:?}`", other),
76-
}
58+
let ovmf_pure_efi = ovmf_prebuilt::ovmf_pure_efi();
59+
let args = [
60+
"-bios",
61+
ovmf_pure_efi.to_str().unwrap(),
62+
"-drive",
63+
&format!("format=raw,file={}", out_gpt_path.display()),
64+
];
65+
run_qemu(args);
7766
}
7867

7968
#[cfg(feature = "bios")]
8069
pub fn run_test_kernel_on_bios(out_mbr_path: &Path) {
81-
let mut run_cmd = Command::new("qemu-system-x86_64");
82-
run_cmd
83-
.arg("-drive")
84-
.arg(format!("format=raw,file={}", out_mbr_path.display()));
85-
run_cmd.args(QEMU_ARGS);
86-
87-
let child_output = run_cmd.output().unwrap();
88-
strip_ansi_escapes::Writer::new(std::io::stderr())
89-
.write_all(&child_output.stderr)
90-
.unwrap();
91-
strip_ansi_escapes::Writer::new(std::io::stderr())
92-
.write_all(&child_output.stdout)
93-
.unwrap();
94-
95-
match child_output.status.code() {
96-
Some(33) => {} // success
97-
Some(35) => panic!("Test failed"), // success
98-
other => panic!("Test failed with unexpected exit code `{:?}`", other),
99-
}
70+
let args = [
71+
"-drive",
72+
&(format!("format=raw,file={}", out_mbr_path.display())),
73+
];
74+
run_qemu(args);
10075
}
10176

10277
#[cfg(feature = "uefi")]
10378
pub fn run_test_kernel_on_uefi_pxe(out_tftp_path: &Path) {
79+
let ovmf_pure_efi = ovmf_prebuilt::ovmf_pure_efi();
80+
let args = [
81+
"-netdev",
82+
&format!(
83+
"user,id=net0,net=192.168.17.0/24,tftp={},bootfile=bootloader,id=net0",
84+
out_tftp_path.display()
85+
),
86+
"-device",
87+
"virtio-net-pci,netdev=net0",
88+
"-bios",
89+
ovmf_pure_efi.to_str().unwrap(),
90+
];
91+
run_qemu(args);
92+
}
93+
94+
fn run_qemu<'a, A>(args: A)
95+
where
96+
A: IntoIterator<Item = &'a str>,
97+
{
98+
use std::process::Stdio;
99+
104100
let mut run_cmd = Command::new("qemu-system-x86_64");
105-
run_cmd.arg("-netdev").arg(format!(
106-
"user,id=net0,net=192.168.17.0/24,tftp={},bootfile=bootloader,id=net0",
107-
out_tftp_path.display()
108-
));
109-
run_cmd.arg("-device").arg("virtio-net-pci,netdev=net0");
101+
run_cmd.args(args);
110102
run_cmd.args(QEMU_ARGS);
111-
run_cmd.arg("-bios").arg(ovmf_prebuilt::ovmf_pure_efi());
112-
113-
let child_output = run_cmd.output().unwrap();
114-
strip_ansi_escapes::Writer::new(std::io::stderr())
115-
.write_all(&child_output.stderr)
116-
.unwrap();
117-
strip_ansi_escapes::Writer::new(std::io::stderr())
118-
.write_all(&child_output.stdout)
119-
.unwrap();
120-
121-
match child_output.status.code() {
122-
Some(33) => {} // success
123-
Some(35) => panic!("Test failed"),
124-
other => panic!("Test failed with unexpected exit code `{:?}`", other),
103+
let run_cmd_str = format!("{run_cmd:?}");
104+
105+
run_cmd.stdout(Stdio::piped());
106+
run_cmd.stderr(Stdio::piped());
107+
108+
let mut child = run_cmd.spawn().unwrap();
109+
110+
let child_stdout = child.stdout.take().unwrap();
111+
let mut child_stderr = child.stderr.take().unwrap();
112+
113+
let copy_stdout = std::thread::spawn(move || {
114+
let print_cmd = format!("\nRunning {run_cmd_str}\n\n").into_bytes();
115+
let mut output = print_cmd.chain(child_stdout).chain(SEPARATOR.as_bytes());
116+
std::io::copy(
117+
&mut output,
118+
&mut strip_ansi_escapes::Writer::new(std::io::stdout()),
119+
)
120+
});
121+
let copy_stderr = std::thread::spawn(move || {
122+
std::io::copy(
123+
&mut child_stderr,
124+
&mut strip_ansi_escapes::Writer::new(std::io::stderr()),
125+
)
126+
});
127+
128+
let exit_status = child.wait().unwrap();
129+
match exit_status.code() {
130+
Some(33) => {} // success
131+
Some(35) => panic!("Test failed"), // success
132+
other => panic!("Test failed with unexpected exit code `{other:?}`"),
125133
}
134+
135+
copy_stdout.join().unwrap().unwrap();
136+
copy_stderr.join().unwrap().unwrap();
126137
}

0 commit comments

Comments
 (0)