diff --git a/cargo-klee/src/cargo_out.rs b/cargo-klee/src/cargo_out.rs index 6c0011c961231c794989c7b40b0e8af03a8f6159..920db7183b915a25f57f866a6fba036018e28f9f 100644 --- a/cargo-klee/src/cargo_out.rs +++ b/cargo-klee/src/cargo_out.rs @@ -9,7 +9,7 @@ pub fn get_hash(s: &str) -> Option<String> { } pub fn get_out_dir(s: &str) -> Option<String> { - let re = Regex::new(r"(--out-dir)( )([[:alpha:]\d/-]*)").unwrap(); + let re = Regex::new(r"(--out-dir)( )([[:alpha:]\d/-_]*)").unwrap(); let cap = re.captures(s)?; Some(cap[3].to_string()) } diff --git a/cargo-klee/src/main.rs b/cargo-klee/src/main.rs index 8964e552e3dbf62ee338d91232d7e5c4c4d705f8..e358382de3a168efa3bf459cf7be32256dba2e16 100644 --- a/cargo-klee/src/main.rs +++ b/cargo-klee/src/main.rs @@ -90,12 +90,12 @@ fn main() -> Result<(), Error> { .arg("--") // ignore linking .args(&["-C", "linker=true"]) - // force LTO, to get a single oject file + // force LTO, to get a single object file .args(&["-C", "lto"]) // output the LLVM-IR (.ll file) for KLEE analysis - .arg("--emit=llvm-ir") - // force panic=abort in all crates, override .cargo settings - .env("RUSTFLAGS", "-C panic=abort"); + .arg("--emit=llvm-ir"); + // force panic=abort in all crates, override .cargo settings + // .env("RUSTFLAGS", "-C panic=abort"); // TODO, force `incremental=false`, `codegen-units=1`? println!("cargo {:?}", cargo); @@ -136,7 +136,9 @@ fn main() -> Result<(), Error> { println!("out_dir {}", out_dir); println!("hash {}", hash); - let ll = format!("{}/{}-{}.ll", out_dir, file, hash); + let binary = format!("{}/{}-{}", out_dir, file, hash); + + let ll = format!("{}.ll", binary); println!("ll {}", ll); // KLEE analysis @@ -154,6 +156,7 @@ fn main() -> Result<(), Error> { // --disable-very to suppress know issue in klee https://github.com/klee/klee/issues/937 klee.arg("--disable-verify") // enable coloring of output + // Notice, seem not to work as expected .arg("--color") // ll file to analyse .arg(ll.clone()); @@ -166,12 +169,12 @@ fn main() -> Result<(), Error> { // eprintln!("{}", stderr); // Workaround using `.status` to propagate `klee` output to `stderr`. - let output = klee - .stdout(Stdio::piped()) + let status = klee + // .stdout(Stdio::piped()) .status() .expect("failed to invoke `klee`, exiting `cargo-klee`"); - if !output.success() { + if !status.success() { return Err(anyhow!("`klee` failed, exiting `cargo-klee`")); } } @@ -182,26 +185,40 @@ fn main() -> Result<(), Error> { let mut llc = Command::new("llc"); llc.arg("-filetype=obj") .arg("-relocation-model=pic") + .arg("-opaque-pointers") .arg(ll.clone()); if args.verbose { eprintln!("\n{:?}\n", llc); } - let output = llc - .stdout(Stdio::piped()) + println!("LLC: \n{:?}\n", llc); + + let status = llc .status() - .expect("failed to invoke `llc`, exiting `cargo-klee`"); + .expect("failed to launch `llc`, exiting `cargo-klee`"); - if !output.success() { + if !status.success() { return Err(anyhow!("`llc` failed, exiting `cargo-klee`")); } + // let output = llc + // //.stdout(Stdio::piped()) + // .output() + // .expect("failed to invoke `llc`, exiting `cargo-klee`"); + + // if !output.status.success() { + // return Err(anyhow!("`llc` failed, exiting `cargo-klee`")); + // } + + // println!("llc stdout {:?}", output.stdout); + // println!("llc stderr {:?}", output.stderr); + // compile to executable for replay using `clang` let mut clang = Command::new("clang"); - let obj_name = format!("{}.o", ll); - let replay_name = format!("{}.replay", ll); + let obj_name = format!("{}.o", binary); + let replay_name = format!("{}.replay", binary); clang .arg(obj_name) @@ -213,15 +230,28 @@ fn main() -> Result<(), Error> { eprintln!("\n{:?}\n", clang); } - let output = llc - .stdout(Stdio::piped()) + println!("CLANG: \n{:?}\n", clang); + + let status = clang .status() - .expect("failed to invoke `clang`, exiting `cargo-klee`"); + .expect("failed to launch `clang`, exiting `cargo-klee`"); - if !output.success() { + if !status.success() { return Err(anyhow!("`clang` failed, exiting `cargo-klee`")); } + // let output = llc + // // .stdout(Stdio::piped()) + // .output() + // .expect("failed to invoke `clang`, exiting `cargo-klee`"); + + // println!("llc stdout {:?}", output.stdout); + // println!("llc stderr {:?}", output.stderr); + + // if !output.status.success() { + // return Err(anyhow!("`clang` failed, exiting `cargo-klee`")); + // } + let mut gdb = Command::new("gdb"); if let Ok(cwd) = env::var("GDB_CWD") { // set gdb current dir to `GDB_CWD` @@ -238,12 +268,23 @@ fn main() -> Result<(), Error> { eprintln!("\n{:?}\n", gdb); } - let output = gdb - .stdout(Stdio::piped()) + println!("GDB: \n{:?}\n", gdb); + + // let output = gdb + // // .stdout(Stdio::piped()) + // .output() + // .expect("failed to invoke `gdb`, exiting `cargo-klee`"); + + // if !output.status.success() { + // return Err(anyhow!("`gdb` failed, exiting `cargo-klee`")); + // } + + let status = gdb + // .stdout(Stdio::piped()) .status() .expect("failed to invoke `gdb`, exiting `cargo-klee`"); - if !output.success() { + if !status.success() { return Err(anyhow!("`gdb` failed, exiting `cargo-klee`")); } } @@ -252,306 +293,3 @@ fn main() -> Result<(), Error> { Ok(()) } - -// fn run() -> Result<i32, failure::Error> { -// let matches = App::new("cargo-klee") -// .version("0.4.0") -// .author("Lulea University of Technology (LTU)") -// .about("KLEE analysis of Rust application") -// // as this is used as a Cargo sub-command the first argument will be the name of the binary -// // we ignore this argument -// .arg(Arg::with_name("binary-name").hidden(true)) -// // TODO: custom target support (for now only target host is supported) -// // .arg( -// // Arg::with_name("target") -// // .long("target") -// // .takes_value(true) -// // .value_name("TRIPLE") -// // .help("Target triple for which the code is compiled"), -// // ) -// .arg( -// Arg::with_name("verbose") -// .long("verbose") -// .short("v") -// .help("Use verbose output"), -// ) -// .arg( -// Arg::with_name("example") -// .long("example") -// .takes_value(true) -// .value_name("NAME") -// .required_unless("bin") -// .conflicts_with("bin") -// .help("Build only the specified example"), -// ) -// .arg( -// Arg::with_name("bin") -// .long("bin") -// .takes_value(true) -// .value_name("NAME") -// .required_unless("example") -// .conflicts_with("example") -// .help("Build only the specified binary"), -// ) -// .arg( -// Arg::with_name("release") -// .long("release") -// .help("Build artifacts in release mode, with optimizations"), -// ) -// .arg( -// Arg::with_name("features") -// .long("features") -// .takes_value(true) -// .value_name("FEATURES") -// .help("Space-separated list of features to activate"), -// ) -// .arg( -// Arg::with_name("all-features") -// .long("all-features") -// .takes_value(false) -// .help("Activate all available features"), -// ) -// // TODO, support additional parameters to KLEE -// .arg( -// Arg::with_name("klee") -// .long("klee") -// .short("k") -// .help("Run KLEE test generation [default enabled unless --replay]"), -// ) -// .arg( -// Arg::with_name("replay") -// .long("replay") -// .short("r") -// .help("Generate replay binary in target directory"), -// ) -// .arg( -// Arg::with_name("gdb") -// .long("gdb") -// .short("g") -// .help("Run the generated replay binary in `gdb`. The environment variable `GDB_CWD` determines the `gdb` working directory, if unset `gdb` will execute in the current working directory"), -// ) -// .get_matches(); - -// let is_example = matches.is_present("example"); -// let is_binary = matches.is_present("bin"); -// let verbose = matches.is_present("verbose"); -// let is_release = matches.is_present("release"); -// let is_replay = matches.is_present("replay"); -// let is_ktest = matches.is_present("klee"); -// let is_gdb = matches.is_present("gdb"); - -// // let target_flag = matches.value_of("target"); // not currently supported - -// // we rely on `clap` for either `example` or `bin` -// let file = if is_example { -// matches.value_of("example").unwrap() -// } else { -// matches.value_of("bin").unwrap() -// }; - -// // turn `cargo klee --example foo` into `cargo rustc --example foo -- (..)` -// let mut cargo = Command::new("cargo"); -// cargo -// // compile using rustc -// .arg("rustc") -// // verbose output for debugging purposes -// .arg("-v"); - -// // set features, always including `klee-analysis` -// if matches.is_present("all-features") { -// cargo.arg("--all-features"); -// } else { -// if let Some(features) = matches.value_of("features") { -// let mut vec: Vec<&str> = features.split(" ").collect::<Vec<&str>>(); -// vec.push("klee-analysis"); -// cargo.args(&["--features", &vec.join(" ")]); -// } else { -// cargo.args(&["--features", "klee-analysis"]); -// } -// } - -// // select (single) application to compile -// // derive basic settings from `cargo` -// if is_example { -// cargo.args(&["--example", file]); -// } else { -// cargo.args(&["--bin", file]); -// } - -// // default is debug mode -// if is_release { -// cargo.arg("--release"); -// } - -// cargo -// // enable shell coloring of result -// .arg("--color=always") -// .arg("--") -// // ignore linking -// .args(&["-C", "linker=true"]) -// // force LTO, to get a single oject file -// .args(&["-C", "lto"]) -// // output the LLVM-IR (.ll file) for KLEE analysis -// .arg("--emit=llvm-ir") -// // force panic=abort in all crates, override .cargo settings -// .env("RUSTFLAGS", "-C panic=abort"); -// // TODO, force `incremental=false`, `codegen-units=1`? - -// if verbose { -// eprintln!("\n{:?}\n", cargo); -// } - -// // execute the command and unwrap the result into status -// let status = cargo.status()?; - -// if !status.success() { -// return Ok(status.code().unwrap_or(1)); -// } - -// let cwd = env::current_dir()?; - -// let meta = rustc_version::version_meta()?; -// let host = meta.host; - -// let project = Project::query(cwd)?; - -// let profile = if is_release { -// Profile::Release -// } else { -// Profile::Dev -// }; - -// let mut path: PathBuf = if is_example { -// project.path(Artifact::Example(file), profile, None, &host)? -// } else { -// project.path(Artifact::Bin(file), profile, None, &host)? -// }; - -// // llvm-ir file -// let mut ll = None; -// // most recently modified -// let mut mrm = SystemTime::UNIX_EPOCH; -// let prefix = format!("{}-", file.replace('-', "_")); - -// path = path.parent().expect("unreachable").to_path_buf(); - -// if is_binary { -// path = path.join("deps"); // the .ll file is placed in ../deps -// } - -// // lookup the latest .ll file -// // TODO: stable support for `metadata` from Cargo. -// for e in fs::read_dir(path)? { -// let e = e?; -// let p = e.path(); - -// if p.extension().map(|e| e == "ll").unwrap_or(false) { -// if p.file_stem() -// .expect("unreachable") -// .to_str() -// .expect("unreachable") -// .starts_with(&prefix) -// { -// let modified = e.metadata()?.modified()?; -// if ll.is_none() { -// ll = Some(p); -// mrm = modified; -// } else { -// if modified > mrm { -// ll = Some(p); -// mrm = modified; -// } -// } -// } -// } -// } - -// let mut obj = ll.clone().unwrap(); -// let replay_name = obj.with_file_name(file).with_extension("replay"); - -// // replay compilation -// if is_replay { -// // compile to object code for replay using `llc` -// let mut llc = Command::new("llc"); -// llc.arg("-filetype=obj") -// .arg("-relocation-model=pic") -// .arg(ll.clone().unwrap()); - -// if verbose { -// eprintln!("\n{:?}\n", llc); -// } - -// // TODO: better error handling, e.g., if `llc` is not installed/in path -// let status = llc.status()?; -// if !status.success() { -// println!("llc failed: {:?}", status.code().unwrap_or(1)); -// } else { -// // compile to executable for replay using `clang` -// let mut clang = Command::new("clang"); - -// obj = obj.with_extension("o"); - -// clang -// .arg(obj) -// .arg("--rtlib=compiler-rt") -// .arg("-lkleeRuntest") -// .args(&["-o", replay_name.to_str().unwrap()]); - -// if verbose { -// eprintln!("\n{:?}\n", clang); -// } - -// // TODO: better error handling, e.g., if `clang` in not installed/in path -// let status = clang.status()?; -// if !status.success() { -// println!("clang failed: {:?}", status.code().unwrap_or(1)); -// } -// } -// } - -// // klee analysis -// if is_ktest || !is_replay { -// let mut klee = Command::new("klee"); -// klee -// // --disable-very to suppress know issue in klee https://github.com/klee/klee/issues/937 -// .arg("--disable-verify") -// // --silent-klee-assume, to silence provably false assumptions -// .arg("--silent-klee-assume") -// // ll file to analyse -// .arg(ll.unwrap()); - -// // execute the command and unwrap the result into status -// let status = klee.status()?; -// if !status.success() { -// return Ok(status.code().unwrap_or(1)); -// } -// } - -// // replay execution in `gdb` -// if is_gdb { -// let mut gdb = Command::new("gdb"); -// if let Ok(cwd) = env::var("GDB_CWD") { -// // set gdb current dir to `GDB_CWD` -// gdb.current_dir(cwd); -// // set replay name to be loaded by `gdb` -// gdb.arg(replay_name); -// } else { -// // set gdb current dir to the target directory -// gdb.current_dir(replay_name.parent().unwrap()); -// // set replay name to be loaded by gdb -// gdb.arg(replay_name.file_name().unwrap()); -// }; - -// if verbose { -// eprintln!("\n{:?}\n", gdb); -// } - -// // TODO: better error handling, e.g., if `gdb` is not installed/in path -// let status = gdb.status()?; -// if !status.success() { -// println!("gdb failed: {:?}", status.code().unwrap_or(1)); -// } -// } -// // return to shell without error -// Ok(0) -// }