Skip to content

Commit cac3ac1

Browse files
committed
Added support for easy fuzzing with rustlantis
1 parent d80802b commit cac3ac1

File tree

3 files changed

+192
-2
lines changed

3 files changed

+192
-2
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ tools/llvmint-2
1919
llvm
2020
build_system/target
2121
config.toml
22-
build
22+
build
23+
rustlantis

build_system/src/fuzz.rs

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use std::ffi::OsStr;
2+
use std::path::Path;
3+
4+
use crate::utils::run_command_with_output;
5+
6+
fn show_usage() {
7+
println!(
8+
r#"
9+
`fuzz` command help:
10+
--help : Show this help"#
11+
);
12+
}
13+
14+
pub fn run() -> Result<(), String> {
15+
// We skip binary name and the `fuzz` command.
16+
let mut args = std::env::args().skip(2);
17+
let mut start = 0;
18+
let mut count = 100;
19+
let mut threads =
20+
std::thread::available_parallelism().map(|threads| threads.get()).unwrap_or(1);
21+
while let Some(arg) = args.next() {
22+
match arg.as_str() {
23+
"--help" => {
24+
show_usage();
25+
return Ok(());
26+
}
27+
"--start" => {
28+
start =
29+
str::parse(&args.next().ok_or_else(|| "Fuzz start not provided!".to_string())?)
30+
.map_err(|err| (format!("Fuzz start not a number {err:?}!")))?;
31+
}
32+
"--count" => {
33+
count =
34+
str::parse(&args.next().ok_or_else(|| "Fuzz count not provided!".to_string())?)
35+
.map_err(|err| (format!("Fuzz count not a number {err:?}!")))?;
36+
}
37+
"-j" | "--jobs" => {
38+
threads = str::parse(
39+
&args.next().ok_or_else(|| "Fuzz thread count not provided!".to_string())?,
40+
)
41+
.map_err(|err| (format!("Fuzz thread count not a number {err:?}!")))?;
42+
}
43+
_ => return Err(format!("Unknown option {}", arg)),
44+
}
45+
}
46+
// Ensure that we have a cloned version of rustlantis on hand.
47+
if !std::fs::exists("rustlantis").unwrap_or_default() {
48+
let cmd: &[&dyn AsRef<OsStr>] =
49+
&[&"git", &"clone", &"https://github.com/cbeuw/rustlantis.git"];
50+
run_command_with_output(cmd, Some(&Path::new(".")))?;
51+
}
52+
// Ensure that we are on the newest rustlantis commit.
53+
let cmd: &[&dyn AsRef<OsStr>] = &[&"git", &"pull", &"origin"];
54+
run_command_with_output(cmd, Some(&Path::new("rustlantis")))?;
55+
// Build the release version of rustlantis
56+
let cmd: &[&dyn AsRef<OsStr>] = &[&"cargo", &"build", &"--release"];
57+
run_command_with_output(cmd, Some(&Path::new("rustlantis")))?;
58+
fuzz_range(start, start + count, threads);
59+
Ok(())
60+
}
61+
fn fuzz_range(start: u64, end: u64, threads: usize) {
62+
use std::sync::Arc;
63+
use std::sync::atomic::{AtomicU64, Ordering};
64+
use std::time::{Duration, Instant};
65+
66+
let total = end - start;
67+
let start = Arc::new(AtomicU64::new(start));
68+
let start_time = Instant::now();
69+
for _ in 0..threads {
70+
let start = start.clone();
71+
std::thread::spawn(move || {
72+
while start.load(Ordering::Relaxed) < end {
73+
let next = start.fetch_add(1, Ordering::Relaxed);
74+
75+
match test(next) {
76+
Err(err) => {
77+
println!("test({}) failed because {err:?}", next);
78+
let mut out_path: std::path::PathBuf =
79+
"target/fuzz/compiletime_error".into();
80+
std::fs::create_dir_all(&out_path).unwrap();
81+
out_path.push(&format!("fuzz{next}.rs"));
82+
std::fs::copy(err, out_path).unwrap();
83+
}
84+
Ok(Err(err)) => {
85+
println!("The LLVM and GCC results don't match for {err:?}");
86+
let mut out_path: std::path::PathBuf = "target/fuzz/runtime_error".into();
87+
std::fs::create_dir_all(&out_path).unwrap();
88+
out_path.push(&format!("fuzz{next}.rs"));
89+
std::fs::copy(err, out_path).unwrap();
90+
}
91+
Ok(Ok(())) => (),
92+
}
93+
}
94+
});
95+
}
96+
while start.load(Ordering::Relaxed) < end {
97+
let hundred_millis = Duration::from_millis(500);
98+
std::thread::sleep(hundred_millis);
99+
let remaining = end - start.load(Ordering::Relaxed);
100+
let fuzzed = total - remaining;
101+
let iter_per_sec = fuzzed as f64 / start_time.elapsed().as_secs_f64();
102+
println!(
103+
"fuzzed {fuzzed} cases({}%), at rate {iter_per_sec} iter/s, remaining ~{}s",
104+
(100 * fuzzed) as f64 / total as f64,
105+
(remaining as f64) / iter_per_sec
106+
)
107+
}
108+
}
109+
/// Builds & runs a file with LLVM.
110+
fn debug_llvm(path: &std::path::Path) -> Result<Vec<u8>, String> {
111+
let exe_path = path.with_extension("llvm_elf");
112+
let output = std::process::Command::new("rustc")
113+
.arg(path)
114+
.arg("-o")
115+
.arg(&exe_path)
116+
.output()
117+
.map_err(|err| format!("{err:?}"))?;
118+
if !output.status.success() {
119+
return Err(format!("LLVM compilation failed:{output:?}"));
120+
}
121+
let output =
122+
std::process::Command::new(&exe_path).output().map_err(|err| format!("{err:?}"))?;
123+
if !output.status.success() {
124+
return Err(format!(
125+
"The program at {path:?}, compiled with LLVM, exited unsuccessfully:{output:?}"
126+
));
127+
}
128+
std::fs::remove_file(exe_path).map_err(|err| format!("{err:?}"))?;
129+
Ok(output.stdout)
130+
}
131+
132+
/// Builds & runs a file with GCC.
133+
fn release_gcc(path: &std::path::Path) -> Result<Vec<u8>, String> {
134+
let exe_path = path.with_extension("gcc_elf");
135+
let output = std::process::Command::new("./y.sh")
136+
.arg("rustc")
137+
.arg(path)
138+
.arg("-O")
139+
.arg("-o")
140+
.arg(&exe_path)
141+
.output()
142+
.map_err(|err| format!("{err:?}"))?;
143+
if !output.status.success() {
144+
return Err(format!("GCC compilation failed:{output:?}"));
145+
}
146+
let output =
147+
std::process::Command::new(&exe_path).output().map_err(|err| format!("{err:?}"))?;
148+
if !output.status.success() {
149+
return Err(format!(
150+
"The program at {path:?}, compiled with GCC, exited unsuccessfully:{output:?}"
151+
));
152+
}
153+
std::fs::remove_file(exe_path).map_err(|err| format!("{err:?}"))?;
154+
Ok(output.stdout)
155+
}
156+
/// Generates a new rustlantis file, & compares the result of running it with GCC and LLVM.
157+
fn test(seed: u64) -> Result<Result<(), std::path::PathBuf>, String> {
158+
let source_file = generate(seed)?;
159+
let llvm_res = debug_llvm(&source_file)?;
160+
let gcc_res = release_gcc(&source_file)?;
161+
if llvm_res != gcc_res {
162+
Ok(Err(source_file))
163+
} else {
164+
std::fs::remove_file(source_file).map_err(|err| format!("{err:?}"))?;
165+
Ok(Ok(()))
166+
}
167+
}
168+
/// Generates a new rustlantis file for us to run tests on.
169+
fn generate(seed: u64) -> Result<std::path::PathBuf, String> {
170+
use std::io::Write;
171+
let mut out_path = std::env::temp_dir();
172+
out_path.push(&format!("fuzz{seed}.rs"));
173+
// We need to get the command output here.
174+
let out = std::process::Command::new("cargo")
175+
.args(["run", "--release", "--bin", "generate"])
176+
.arg(&format!("{seed}"))
177+
.current_dir("rustlantis")
178+
.output()
179+
.map_err(|err| format!("{err:?}"))?;
180+
std::fs::File::create(&out_path)
181+
.map_err(|err| format!("{err:?}"))?
182+
.write_all(&out.stdout)
183+
.map_err(|err| format!("{err:?}"))?;
184+
Ok(out_path)
185+
}

build_system/src/main.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ Commands:
4242
test : Runs tests for the project.
4343
info : Displays information about the build environment and project configuration.
4444
clone-gcc : Clones the GCC compiler from a specified source.
45-
fmt : Runs rustfmt"
45+
fmt : Runs rustfmt
46+
fuzz : Fuzzes `cg_gcc` using rustlantis"
4647
);
4748
}
4849

@@ -56,6 +57,7 @@ pub enum Command {
5657
Test,
5758
Info,
5859
Fmt,
60+
Fuzz,
5961
}
6062

6163
fn main() {
@@ -75,6 +77,7 @@ fn main() {
7577
Some("info") => Command::Info,
7678
Some("clone-gcc") => Command::CloneGcc,
7779
Some("fmt") => Command::Fmt,
80+
Some("fuzz") => Command::Fuzz,
7881
Some("--help") => {
7982
usage();
8083
process::exit(0);
@@ -97,6 +100,7 @@ fn main() {
97100
Command::Info => info::run(),
98101
Command::CloneGcc => clone_gcc::run(),
99102
Command::Fmt => fmt::run(),
103+
Command::Fuzz => fuzz::run(),
100104
} {
101105
eprintln!("Command failed to run: {e}");
102106
process::exit(1);

0 commit comments

Comments
 (0)