File scoping and reporting works
This commit is contained in:
parent
6926711e83
commit
62f7e40c64
|
@ -6,7 +6,7 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nix = { version = "0.28.0", features = ["ptrace", "process"] }
|
nix = { version = "0.28.0", features = ["ptrace", "process", "fs"] }
|
||||||
linux-personality = "1.0.0"
|
linux-personality = "1.0.0"
|
||||||
anyhow = { version = "1", features = ["backtrace"] }
|
anyhow = { version = "1", features = ["backtrace"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::{
|
||||||
path::PathBuf, borrow::Cow,
|
path::PathBuf, borrow::Cow,
|
||||||
};
|
};
|
||||||
|
|
||||||
use gimli::{constants, AttributeValue, DW_TAG_compile_unit};
|
use gimli::{constants, DW_TAG_compile_unit};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::{digest::generic_array::{GenericArray, typenum::U32}, Digest, Sha256};
|
use sha2::{digest::generic_array::{GenericArray, typenum::U32}, Digest, Sha256};
|
||||||
use object::{Object, ReadCache, ObjectSection};
|
use object::{Object, ReadCache, ObjectSection};
|
||||||
|
@ -110,7 +110,7 @@ impl FileStore {
|
||||||
self.files
|
self.files
|
||||||
.get_mut(*e.get())
|
.get_mut(*e.get())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.output_names
|
.input_names
|
||||||
.insert(filename.clone());
|
.insert(filename.clone());
|
||||||
*e.get()
|
*e.get()
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ impl FileStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
let fp = fs::File::open(&filename)?;
|
let fp = fs::File::open(&filename)?;
|
||||||
self.ingest_output(filename, fp);
|
self.ingest_output(filename, fp)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
48
src/main.rs
48
src/main.rs
|
@ -1,5 +1,6 @@
|
||||||
mod tracer;
|
mod tracer;
|
||||||
mod filestore;
|
mod filestore;
|
||||||
|
mod reports;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
@ -13,18 +14,30 @@ struct Cli {
|
||||||
|
|
||||||
#[derive(Subcommand, Debug, Clone)]
|
#[derive(Subcommand, Debug, Clone)]
|
||||||
enum Subcommands {
|
enum Subcommands {
|
||||||
|
/// Run a command and record its execution
|
||||||
Run {
|
Run {
|
||||||
/// Any number of filepaths to treat as inputs. They will be hashed and accesses to
|
/// Any number of filepaths to treat as inputs. They will be hashed and accesses to
|
||||||
/// equivalent files will be treated specially.
|
/// equivalent files will be treated specially.
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
input: Vec<PathBuf>,
|
file_scope: Vec<PathBuf>,
|
||||||
|
|
||||||
/// The filepath to dump the json report to. will dump to stdout if unspecified.
|
/// The filepath to dump the json report to. will dump to stdout if unspecified.
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
output: Option<PathBuf>,
|
output: Option<PathBuf>,
|
||||||
|
|
||||||
|
#[arg(short, long)]
|
||||||
|
mute: bool,
|
||||||
|
|
||||||
/// The command to run. Have fun!
|
/// The command to run. Have fun!
|
||||||
cmd: Vec<String>,
|
cmd: Vec<String>,
|
||||||
|
},
|
||||||
|
/// Query from the report how in-scope items were used
|
||||||
|
QueryParameters {
|
||||||
|
/// The filepath of the report ot open. will read from stdin if unspecified.
|
||||||
|
input: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// The filepath to dump the json report to. will dump to stdout if unspecified.
|
||||||
|
output: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,20 +45,41 @@ fn main() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
match cli.cmd {
|
match cli.cmd {
|
||||||
Subcommands::Run { input, output, cmd } => {
|
Subcommands::Run { file_scope, output, cmd, mute } => {
|
||||||
let fp: Box<dyn std::io::Write> = if let Some(output) = &output {
|
let fp: Box<dyn std::io::Write> = if let Some(output) = &output {
|
||||||
Box::new(std::fs::File::options().write(true).create(true).open(output).unwrap())
|
Box::new(std::fs::File::options().write(true).create(true).open(output).unwrap())
|
||||||
} else {
|
} else {
|
||||||
Box::new(std::io::stdout())
|
Box::new(std::io::stdout())
|
||||||
};
|
};
|
||||||
let mut t = tracer::Tracer::new(input).unwrap();
|
let mut t = tracer::Tracer::new(file_scope).unwrap();
|
||||||
t.start_root_process(cmd).unwrap();
|
t.start_root_process(cmd, mute).unwrap();
|
||||||
|
|
||||||
if output.is_none() {
|
if output.is_none() {
|
||||||
serde_json::to_writer_pretty(fp, &t.report).unwrap();
|
serde_json::to_writer_pretty(fp, &t.report)
|
||||||
} else {
|
} else {
|
||||||
serde_json::to_writer(fp, &t.report).unwrap();
|
serde_json::to_writer(fp, &t.report)
|
||||||
}
|
}.expect("Could not serialize json trace report");
|
||||||
|
}
|
||||||
|
Subcommands::QueryParameters { input, output } => {
|
||||||
|
let fp: Box<dyn std::io::Write> = if let Some(output) = &output {
|
||||||
|
Box::new(std::fs::File::options().write(true).create(true).open(output).unwrap())
|
||||||
|
} else {
|
||||||
|
Box::new(std::io::stdout())
|
||||||
|
};
|
||||||
|
|
||||||
|
let in_report: tracer::TracerReport = if let Some(input) = &input {
|
||||||
|
serde_json::from_reader(std::fs::File::open(input).unwrap())
|
||||||
|
} else {
|
||||||
|
serde_json::from_reader(std::io::stdin())
|
||||||
|
}.expect("Could not deserialize json trace report");
|
||||||
|
|
||||||
|
let out_report = reports::parameters::run(&in_report).unwrap();
|
||||||
|
|
||||||
|
if output.is_none() {
|
||||||
|
serde_json::to_writer_pretty(fp, &out_report)
|
||||||
|
} else {
|
||||||
|
serde_json::to_writer(fp, &out_report)
|
||||||
|
}.expect("Could not serialize json parameter report");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
ffi::CString,
|
ffi::CString,
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
os::unix::prelude::OsStringExt,
|
os::{fd::{AsRawFd}, unix::prelude::OsStringExt},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::exit,
|
process::exit,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
|
@ -465,7 +465,7 @@ impl Tracer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_root_process(&mut self, args: Vec<String>) -> anyhow::Result<()> {
|
pub fn start_root_process(&mut self, args: Vec<String>, mute: bool) -> anyhow::Result<()> {
|
||||||
log::trace!("start_root_process: {:?}", args);
|
log::trace!("start_root_process: {:?}", args);
|
||||||
|
|
||||||
if let ForkResult::Parent { child: root_child } = unsafe { nix::unistd::fork()? } {
|
if let ForkResult::Parent { child: root_child } = unsafe { nix::unistd::fork()? } {
|
||||||
|
@ -633,6 +633,13 @@ impl Tracer {
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mute {
|
||||||
|
let null = std::fs::File::options().read(true).write(true).open("/dev/null").expect("Could not open /dev/null");
|
||||||
|
nix::unistd::dup2(null.as_raw_fd(), 0).expect("Could not dup /dev/null to /dev/stdin");
|
||||||
|
nix::unistd::dup2(null.as_raw_fd(), 1).expect("Could not dup /dev/null to /dev/stdout");
|
||||||
|
nix::unistd::dup2(null.as_raw_fd(), 2).expect("Could not dup /dev/null to /dev/stderr");
|
||||||
|
}
|
||||||
|
|
||||||
let args = args
|
let args = args
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(CString::new)
|
.map(CString::new)
|
||||||
|
|
Loading…
Reference in New Issue