Refactor Output Observers (#856)
* Refactor Output Observers * Delete .gitmodules * modules * Drop need for OutputObserving list
This commit is contained in:
parent
5b75b6b8ac
commit
9695ce0029
@ -24,7 +24,7 @@ use libafl::{
|
||||
inputs::{HasTargetBytes, Input},
|
||||
monitors::SimpleMonitor,
|
||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||
observers::{get_asan_runtime_flags, ASANBacktraceObserver, StdMapObserver},
|
||||
observers::{get_asan_runtime_flags, AsanBacktraceObserver, StdMapObserver},
|
||||
schedulers::QueueScheduler,
|
||||
stages::mutational::StdMutationalStage,
|
||||
state::StdState,
|
||||
@ -40,7 +40,7 @@ pub fn main() {
|
||||
// Create an observation channel using the signals map
|
||||
let observer = StdMapObserver::new("signals", signals.as_mut_slice());
|
||||
// Create a stacktrace observer
|
||||
let bt_observer = ASANBacktraceObserver::new("ASANBacktraceObserver");
|
||||
let bt_observer = AsanBacktraceObserver::new("AsanBacktraceObserver");
|
||||
|
||||
// Feedback to rate the interestingness of an input, obtained by ANDing the interestingness of both feedbacks
|
||||
let mut feedback = MaxMapFeedback::new(&observer);
|
||||
@ -48,7 +48,7 @@ pub fn main() {
|
||||
// A feedback to choose if an input is a solution or not
|
||||
let mut objective = feedback_and!(
|
||||
CrashFeedback::new(),
|
||||
NewHashFeedback::<ASANBacktraceObserver>::new(&bt_observer)
|
||||
NewHashFeedback::<AsanBacktraceObserver>::new(&bt_observer)
|
||||
);
|
||||
// let mut objective = CrashFeedback::new();
|
||||
|
||||
@ -76,7 +76,7 @@ pub fn main() {
|
||||
// such as the notification of the addition of a new item to the corpus
|
||||
let mut mgr = SimpleEventManager::new(mon);
|
||||
|
||||
// A queue policy to get testcasess from the corpus
|
||||
// A queue policy to get testcases from the corpus
|
||||
let scheduler = QueueScheduler::new();
|
||||
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
@ -108,12 +108,7 @@ pub fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
let mut executor = MyExecutor { shmem_id }.into_executor(
|
||||
tuple_list!(observer, bt_observer),
|
||||
None,
|
||||
None,
|
||||
Some("ASANBacktraceObserver".to_string()),
|
||||
);
|
||||
let mut executor = MyExecutor { shmem_id }.into_executor(tuple_list!(observer, bt_observer));
|
||||
|
||||
// Generator of printable bytearrays of max size 32
|
||||
let mut generator = RandPrintablesGenerator::new(32);
|
||||
|
@ -22,7 +22,7 @@ use libafl::{
|
||||
inputs::BytesInput,
|
||||
monitors::SimpleMonitor,
|
||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||
observers::{ASANBacktraceObserver, ConstMapObserver, HitcountsMapObserver},
|
||||
observers::{AsanBacktraceObserver, ConstMapObserver, HitcountsMapObserver},
|
||||
schedulers::QueueScheduler,
|
||||
stages::mutational::StdMutationalStage,
|
||||
state::StdState,
|
||||
@ -50,7 +50,7 @@ pub fn main() {
|
||||
shmem_map,
|
||||
));
|
||||
|
||||
let bt_observer = ASANBacktraceObserver::new("ASANBacktraceObserver");
|
||||
let bt_observer = AsanBacktraceObserver::new("AsanBacktraceObserver");
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
// This one is composed by two Feedbacks in OR
|
||||
|
@ -224,7 +224,7 @@ fn fuzz(
|
||||
// Create a concolic trace
|
||||
ConcolicTracingStage::new(
|
||||
TracingStage::new(
|
||||
MyCommandConfigurator::default().into_executor(tuple_list!(concolic_observer), None, None, None)
|
||||
MyCommandConfigurator::default().into_executor(tuple_list!(concolic_observer))
|
||||
),
|
||||
concolic_observer_name,
|
||||
),
|
||||
|
@ -36,7 +36,7 @@ pub fn read_time_counter() -> u64 {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[must_use]
|
||||
pub fn read_time_counter() -> u64 {
|
||||
let mut v: u64 = 0;
|
||||
let mut v: u64;
|
||||
unsafe {
|
||||
// TODO pushing a change in core::arch::aarch64 ?
|
||||
asm!("mrs {v}, cntvct_el0", v = out(reg) v);
|
||||
|
@ -27,7 +27,7 @@ use crate::{
|
||||
AsSlice,
|
||||
},
|
||||
inputs::HasTargetBytes,
|
||||
observers::{ASANBacktraceObserver, ObserversTuple, StdErrObserver, StdOutObserver},
|
||||
observers::ObserversTuple,
|
||||
std::borrow::ToOwned,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
@ -45,10 +45,10 @@ enum InputLocation {
|
||||
},
|
||||
/// Deliver input via `StdIn`
|
||||
StdIn,
|
||||
/// Deliver the iniput via the specified [`InputFile`]
|
||||
/// Deliver the input via the specified [`InputFile`]
|
||||
/// You can use specify [`InputFile::create(INPUTFILE_STD)`] to use a default filename.
|
||||
File {
|
||||
/// The fiel to write input to. The target should read input from this location.
|
||||
/// The file to write input to. The target should read input from this location.
|
||||
out_file: InputFile,
|
||||
},
|
||||
}
|
||||
@ -79,7 +79,6 @@ pub struct StdCommandConfigurator {
|
||||
debug_child: bool,
|
||||
has_stdout_observer: bool,
|
||||
has_stderr_observer: bool,
|
||||
has_asan_observer: bool,
|
||||
/// true: input gets delivered via stdink
|
||||
input_location: InputLocation,
|
||||
/// The Command to execute
|
||||
@ -104,7 +103,7 @@ impl CommandConfigurator for StdCommandConfigurator {
|
||||
if self.has_stdout_observer {
|
||||
cmd.stdout(Stdio::piped());
|
||||
}
|
||||
if self.has_stderr_observer || self.has_asan_observer {
|
||||
if self.has_stderr_observer {
|
||||
cmd.stderr(Stdio::piped());
|
||||
}
|
||||
|
||||
@ -150,23 +149,16 @@ impl CommandConfigurator for StdCommandConfigurator {
|
||||
|
||||
/// A `CommandExecutor` is a wrapper around [`std::process::Command`] to execute a target as a child process.
|
||||
/// Construct a `CommandExecutor` by implementing [`CommandConfigurator`] for a type of your choice and calling [`CommandConfigurator::into_executor`] on it.
|
||||
/// Instead, you can use [`CommandExecutor::builder()`] to construct a [`CommandExecutor`] backed by a [`StdCommandConfigurator`].
|
||||
/// Instead, you can also use [`CommandExecutor::builder()`] to construct a [`CommandExecutor`] backed by a [`StdCommandConfigurator`].
|
||||
pub struct CommandExecutor<EM, I, OT, S, T, Z>
|
||||
where
|
||||
T: Debug,
|
||||
OT: Debug,
|
||||
{
|
||||
/// The wrapped comand configurer
|
||||
/// The wrapped command configurer
|
||||
configurer: T,
|
||||
/// The obsevers used by this executor
|
||||
observers: OT,
|
||||
/// cache if the AsanBacktraceObserver is present
|
||||
has_asan_observer: Option<String>,
|
||||
/// If set, we found a [`StdErrObserver`] in the observer list.
|
||||
/// Pipe the child's `stderr` instead of closing it.
|
||||
has_stdout_observer: Option<String>,
|
||||
/// If set, we found a [`StdOutObserver`] in the observer list
|
||||
/// Pipe the child's `stdout` instead of closing it.
|
||||
has_stderr_observer: Option<String>,
|
||||
phantom: PhantomData<(EM, I, S, Z)>,
|
||||
}
|
||||
|
||||
@ -215,18 +207,15 @@ where
|
||||
|
||||
impl<EM, I, OT, S, Z> CommandExecutor<EM, I, OT, S, StdCommandConfigurator, Z>
|
||||
where
|
||||
OT: MatchName + Debug,
|
||||
OT: MatchName + Debug + ObserversTuple<I, S>,
|
||||
{
|
||||
/// Creates a new `CommandExecutor`.
|
||||
/// Instead of parsing the Command for `@@`, it will
|
||||
pub fn from_cmd_with_file<P>(
|
||||
cmd: &Command,
|
||||
debug_child: bool,
|
||||
observers: OT,
|
||||
mut observers: OT,
|
||||
path: P,
|
||||
has_stdout_observer: Option<String>,
|
||||
has_stderr_observer: Option<String>,
|
||||
has_asan_observer: Option<String>,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
@ -238,43 +227,37 @@ where
|
||||
}
|
||||
command.stdin(Stdio::null());
|
||||
|
||||
if has_stdout_observer.is_some() {
|
||||
let has_stdout_observer = observers.observes_stdout();
|
||||
if has_stdout_observer {
|
||||
command.stdout(Stdio::piped());
|
||||
}
|
||||
if has_stderr_observer.is_some() || has_asan_observer.is_some() {
|
||||
let has_stderr_observer = observers.observes_stderr();
|
||||
if has_stderr_observer {
|
||||
command.stderr(Stdio::piped());
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
observers,
|
||||
|
||||
configurer: StdCommandConfigurator {
|
||||
input_location: InputLocation::File {
|
||||
out_file: InputFile::create(path)?,
|
||||
},
|
||||
command,
|
||||
debug_child,
|
||||
has_stdout_observer: has_stdout_observer.is_some(),
|
||||
has_stderr_observer: has_stderr_observer.is_some(),
|
||||
has_asan_observer: has_asan_observer.is_some(),
|
||||
},
|
||||
has_stdout_observer,
|
||||
has_stderr_observer,
|
||||
has_asan_observer,
|
||||
},
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses an AFL-like comandline, replacing `@@` with the input file.
|
||||
/// Parses an AFL-like commandline, replacing `@@` with the input file.
|
||||
/// If no `@@` was found, will use stdin for input.
|
||||
/// The arg 0 is the program.
|
||||
pub fn parse_afl_cmdline<IT, O>(
|
||||
args: IT,
|
||||
observers: OT,
|
||||
debug_child: bool,
|
||||
has_stdout_observer: Option<String>,
|
||||
has_stderr_observer: Option<String>,
|
||||
has_asan_observer: Option<String>,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
IT: IntoIterator<Item = O>,
|
||||
@ -283,16 +266,6 @@ where
|
||||
let mut atat_at = None;
|
||||
let mut builder = CommandExecutorBuilder::new();
|
||||
builder.debug_child(debug_child);
|
||||
if let Some(name) = has_stdout_observer {
|
||||
builder.stdout_observer(name);
|
||||
}
|
||||
if let Some(name) = has_stderr_observer {
|
||||
builder.stderr_observer(name);
|
||||
}
|
||||
if let Some(name) = has_asan_observer {
|
||||
builder.asan_observer(name);
|
||||
}
|
||||
|
||||
let afl_delim = OsStr::new("@@");
|
||||
|
||||
for (pos, arg) in args.into_iter().enumerate() {
|
||||
@ -326,7 +299,7 @@ impl<EM, I, OT, S, T, Z> Executor<EM, I, S, Z> for CommandExecutor<EM, I, OT, S,
|
||||
where
|
||||
I: Input + HasTargetBytes,
|
||||
T: CommandConfigurator,
|
||||
OT: Debug + MatchName,
|
||||
OT: Debug + MatchName + ObserversTuple<I, S>,
|
||||
T: Debug,
|
||||
{
|
||||
fn run_target(
|
||||
@ -361,37 +334,23 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
if self.has_asan_observer.is_some() || self.has_stderr_observer.is_some() {
|
||||
if self.observers.observes_stderr() {
|
||||
let mut stderr = String::new();
|
||||
child.stderr.as_mut().ok_or_else(|| {
|
||||
Error::illegal_state(
|
||||
"Observer tries to read stderr, but stderr was not `Stdio::pipe` in CommandExecutor",
|
||||
)
|
||||
})?.read_to_string(&mut stderr)?;
|
||||
if let Some(name) = self.has_asan_observer.as_ref() {
|
||||
self.observers
|
||||
.match_name_mut::<ASANBacktraceObserver>(name)
|
||||
.unwrap()
|
||||
.parse_asan_output(&stderr);
|
||||
self.observers.observe_stderr(&stderr);
|
||||
}
|
||||
if let Some(name) = self.has_stderr_observer.as_ref() {
|
||||
self.observers
|
||||
.match_name_mut::<StdErrObserver>(name)
|
||||
.unwrap()
|
||||
.stderr = Some(stderr);
|
||||
}
|
||||
}
|
||||
if let Some(name) = self.has_stdout_observer.as_ref() {
|
||||
if self.observers.observes_stdout() {
|
||||
let mut stdout = String::new();
|
||||
child.stdout.as_mut().ok_or_else(|| {
|
||||
Error::illegal_state(
|
||||
"Observer tries to read stdout, but stdout was not `Stdio::pipe` in CommandExecutor",
|
||||
)
|
||||
})?.read_to_string(&mut stdout)?;
|
||||
self.observers
|
||||
.match_name_mut::<StdOutObserver>(name)
|
||||
.unwrap()
|
||||
.stdout = Some(stdout);
|
||||
self.observers.observe_stdout(&stdout);
|
||||
}
|
||||
|
||||
res
|
||||
@ -419,9 +378,6 @@ pub struct CommandExecutorBuilder {
|
||||
input_location: InputLocation,
|
||||
cwd: Option<PathBuf>,
|
||||
envs: Vec<(OsString, OsString)>,
|
||||
has_stdout_observer: Option<String>,
|
||||
has_stderr_observer: Option<String>,
|
||||
has_asan_observer: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for CommandExecutorBuilder {
|
||||
@ -441,30 +397,9 @@ impl CommandExecutorBuilder {
|
||||
cwd: None,
|
||||
envs: vec![],
|
||||
debug_child: false,
|
||||
has_stdout_observer: None,
|
||||
has_stderr_observer: None,
|
||||
has_asan_observer: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the stdout observer name
|
||||
pub fn stdout_observer(&mut self, name: String) -> &mut Self {
|
||||
self.has_stdout_observer = Some(name);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the stderr observer name
|
||||
pub fn stderr_observer(&mut self, name: String) -> &mut Self {
|
||||
self.has_stderr_observer = Some(name);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the asan observer name
|
||||
pub fn asan_observer(&mut self, name: String) -> &mut Self {
|
||||
self.has_asan_observer = Some(name);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the binary to execute
|
||||
/// This option is required.
|
||||
pub fn program<O>(&mut self, program: O) -> &mut Self
|
||||
@ -573,13 +508,13 @@ impl CommandExecutorBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the `ComandExecutor`
|
||||
/// Builds the `CommandExecutor`.
|
||||
pub fn build<EM, I, OT, S, Z>(
|
||||
&self,
|
||||
observers: OT,
|
||||
mut observers: OT,
|
||||
) -> Result<CommandExecutor<EM, I, OT, S, StdCommandConfigurator, Z>, Error>
|
||||
where
|
||||
OT: Debug + MatchName,
|
||||
OT: Debug + MatchName + ObserversTuple<I, S>,
|
||||
{
|
||||
let program = if let Some(program) = &self.program {
|
||||
program
|
||||
@ -610,28 +545,22 @@ impl CommandExecutorBuilder {
|
||||
command.stdout(Stdio::null());
|
||||
command.stderr(Stdio::null());
|
||||
}
|
||||
if self.has_stderr_observer.is_some() || self.has_asan_observer.is_some() {
|
||||
// we need stderr for ASANBackt
|
||||
command.stderr(Stdio::piped());
|
||||
}
|
||||
if self.has_stdout_observer.is_some() {
|
||||
if observers.observes_stdout() {
|
||||
command.stdout(Stdio::piped());
|
||||
}
|
||||
if observers.observes_stderr() {
|
||||
// we need stderr for `AsanBacktaceObserver`, and others
|
||||
command.stderr(Stdio::piped());
|
||||
}
|
||||
|
||||
let configurator = StdCommandConfigurator {
|
||||
debug_child: self.debug_child,
|
||||
has_stdout_observer: self.has_stdout_observer.is_some(),
|
||||
has_stderr_observer: self.has_stderr_observer.is_some(),
|
||||
has_asan_observer: self.has_asan_observer.is_some(),
|
||||
has_stdout_observer: observers.observes_stdout(),
|
||||
has_stderr_observer: observers.observes_stderr(),
|
||||
input_location: self.input_location.clone(),
|
||||
command,
|
||||
};
|
||||
Ok(configurator.into_executor::<EM, I, OT, S, Z>(
|
||||
observers,
|
||||
self.has_stdout_observer.clone(),
|
||||
self.has_stderr_observer.clone(),
|
||||
self.has_asan_observer.clone(),
|
||||
))
|
||||
Ok(configurator.into_executor::<EM, I, OT, S, Z>(observers))
|
||||
}
|
||||
}
|
||||
|
||||
@ -663,7 +592,7 @@ impl CommandExecutorBuilder {
|
||||
/// }
|
||||
///
|
||||
/// fn make_executor<EM, I: Input + HasTargetBytes, S, Z>() -> impl Executor<EM, I, S, Z> {
|
||||
/// MyExecutor.into_executor((), None, None, None)
|
||||
/// MyExecutor.into_executor(())
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@ -675,21 +604,13 @@ pub trait CommandConfigurator: Sized + Debug {
|
||||
I: Input + HasTargetBytes;
|
||||
|
||||
/// Create an `Executor` from this `CommandConfigurator`.
|
||||
fn into_executor<EM, I, OT, S, Z>(
|
||||
self,
|
||||
observers: OT,
|
||||
has_stdout_observer: Option<String>,
|
||||
has_stderr_observer: Option<String>,
|
||||
has_asan_observer: Option<String>,
|
||||
) -> CommandExecutor<EM, I, OT, S, Self, Z>
|
||||
/// It will observe the outputs with the respective given observer name.
|
||||
fn into_executor<EM, I, OT, S, Z>(self, observers: OT) -> CommandExecutor<EM, I, OT, S, Self, Z>
|
||||
where
|
||||
OT: Debug + MatchName,
|
||||
{
|
||||
CommandExecutor {
|
||||
observers,
|
||||
has_asan_observer,
|
||||
has_stdout_observer,
|
||||
has_stderr_observer,
|
||||
configurer: self,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
@ -740,14 +661,8 @@ mod tests {
|
||||
println!("{status}");
|
||||
}));
|
||||
|
||||
let mut executor = CommandExecutor::parse_afl_cmdline(
|
||||
&["file".to_string(), "@@".to_string()],
|
||||
(),
|
||||
true,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
let mut executor =
|
||||
CommandExecutor::parse_afl_cmdline(&["file".to_string(), "@@".to_string()], (), true)
|
||||
.unwrap();
|
||||
executor
|
||||
.run_target(
|
||||
|
@ -146,6 +146,29 @@ where
|
||||
.as_mut()
|
||||
.post_exec_child_all(state, input, exit_kind)
|
||||
}
|
||||
|
||||
/// Returns true if a `stdout` observer was added to the list
|
||||
#[inline]
|
||||
fn observes_stdout(&mut self) -> bool {
|
||||
self.primary.as_mut().observes_stdout() || self.secondary.as_mut().observes_stdout()
|
||||
}
|
||||
/// Returns true if a `stderr` observer was added to the list
|
||||
#[inline]
|
||||
fn observes_stderr(&mut self) -> bool {
|
||||
self.primary.as_mut().observes_stderr() || self.secondary.as_mut().observes_stderr()
|
||||
}
|
||||
|
||||
/// Runs `observe_stdout` for all stdout observers in the list
|
||||
fn observe_stdout(&mut self, stdout: &str) {
|
||||
self.primary.as_mut().observe_stderr(stdout);
|
||||
self.secondary.as_mut().observe_stderr(stdout);
|
||||
}
|
||||
|
||||
/// Runs `observe_stderr` for all stderr observers in the list
|
||||
fn observe_stderr(&mut self, stderr: &str) {
|
||||
self.primary.as_mut().observe_stderr(stderr);
|
||||
self.secondary.as_mut().observe_stderr(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> MatchName for ProxyObserversTuple<A, B>
|
||||
|
@ -33,7 +33,7 @@ use crate::{
|
||||
executors::{Executor, ExitKind, HasObservers},
|
||||
inputs::{HasTargetBytes, Input},
|
||||
mutators::Tokens,
|
||||
observers::{get_asan_runtime_flags_with_log_path, ASANBacktraceObserver, ObserversTuple},
|
||||
observers::{get_asan_runtime_flags_with_log_path, AsanBacktraceObserver, ObserversTuple},
|
||||
Error,
|
||||
};
|
||||
|
||||
@ -966,13 +966,13 @@ where
|
||||
if self.has_asan_observer.is_none() {
|
||||
self.has_asan_observer = Some(
|
||||
self.observers()
|
||||
.match_name::<ASANBacktraceObserver>("ASANBacktraceObserver")
|
||||
.match_name::<AsanBacktraceObserver>("AsanBacktraceObserver")
|
||||
.is_some(),
|
||||
);
|
||||
}
|
||||
if self.has_asan_observer.unwrap() {
|
||||
self.observers_mut()
|
||||
.match_name_mut::<ASANBacktraceObserver>("ASANBacktraceObserver")
|
||||
.match_name_mut::<AsanBacktraceObserver>("AsanBacktraceObserver")
|
||||
.unwrap()
|
||||
.parse_asan_output_from_asan_log_file(pid)?;
|
||||
}
|
||||
|
@ -85,6 +85,28 @@ pub trait Observer<I, S>: Named + Debug {
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If this observer observes `stdout`
|
||||
#[inline]
|
||||
fn observes_stdout(&mut self) -> bool {
|
||||
false
|
||||
}
|
||||
/// If this observer observes `stderr`
|
||||
#[inline]
|
||||
fn observes_stderr(&mut self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// React to new `stdout`
|
||||
/// To use this, always return `true` from `observes_stdout`
|
||||
#[inline]
|
||||
#[allow(unused_variables)]
|
||||
fn observe_stdout(&mut self, stdout: &str) {}
|
||||
/// React to new `stderr`
|
||||
/// To use this, always return `true` from `observes_stderr`
|
||||
#[inline]
|
||||
#[allow(unused_variables)]
|
||||
fn observe_stderr(&mut self, stderr: &str) {}
|
||||
}
|
||||
|
||||
/// A haskell-style tuple of observers
|
||||
@ -110,6 +132,16 @@ pub trait ObserversTuple<I, S>: MatchName + Debug {
|
||||
input: &I,
|
||||
exit_kind: &ExitKind,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Returns true if a `stdout` observer was added to the list
|
||||
fn observes_stdout(&mut self) -> bool;
|
||||
/// Returns true if a `stderr` observer was added to the list
|
||||
fn observes_stderr(&mut self) -> bool;
|
||||
|
||||
/// Runs `observe_stdout` for all stdout observers in the list
|
||||
fn observe_stdout(&mut self, stdout: &str);
|
||||
/// Runs `observe_stderr` for all stderr observers in the list
|
||||
fn observe_stderr(&mut self, stderr: &str);
|
||||
}
|
||||
|
||||
impl<I, S> ObserversTuple<I, S> for () {
|
||||
@ -138,6 +170,26 @@ impl<I, S> ObserversTuple<I, S> for () {
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns true if a `stdout` observer was added to the list
|
||||
#[inline]
|
||||
fn observes_stdout(&mut self) -> bool {
|
||||
false
|
||||
}
|
||||
/// Returns true if a `stderr` observer was added to the list
|
||||
#[inline]
|
||||
fn observes_stderr(&mut self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Runs `observe_stdout` for all stdout observers in the list
|
||||
#[inline]
|
||||
#[allow(unused_variables)]
|
||||
fn observe_stdout(&mut self, stdout: &str) {}
|
||||
/// Runs `observe_stderr` for all stderr observers in the list
|
||||
#[inline]
|
||||
#[allow(unused_variables)]
|
||||
fn observe_stderr(&mut self, stderr: &str) {}
|
||||
}
|
||||
|
||||
impl<Head, Tail, I, S> ObserversTuple<I, S> for (Head, Tail)
|
||||
@ -174,9 +226,31 @@ where
|
||||
self.0.post_exec_child(state, input, exit_kind)?;
|
||||
self.1.post_exec_child_all(state, input, exit_kind)
|
||||
}
|
||||
|
||||
/// Returns true if a `stdout` observer was added to the list
|
||||
#[inline]
|
||||
fn observes_stdout(&mut self) -> bool {
|
||||
self.0.observes_stdout() || self.1.observes_stdout()
|
||||
}
|
||||
/// Returns true if a `stderr` observer was added to the list
|
||||
#[inline]
|
||||
fn observes_stderr(&mut self) -> bool {
|
||||
self.0.observes_stderr() || self.1.observes_stderr()
|
||||
}
|
||||
|
||||
/// Runs `observe_stdout` for all stdout observers in the list
|
||||
#[inline]
|
||||
fn observe_stdout(&mut self, stdout: &str) {
|
||||
self.0.observe_stdout(stdout);
|
||||
}
|
||||
/// Runs `observe_stderr` for all stderr observers in the list
|
||||
#[inline]
|
||||
fn observe_stderr(&mut self, stderr: &str) {
|
||||
self.1.observe_stderr(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for obervers with a hash field
|
||||
/// A trait for [`Observer`]`s` with a hash field
|
||||
pub trait ObserverWithHashField {
|
||||
/// get the value of the hash field
|
||||
fn hash(&self) -> &Option<u64>;
|
||||
@ -876,6 +950,23 @@ pub mod pybind {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: expose stdout/stderr to python
|
||||
#[inline]
|
||||
fn observes_stdout(&mut self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn observes_stderr(&mut self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn observe_stderr(&mut self, _: &str) {}
|
||||
|
||||
#[inline]
|
||||
fn observe_stdout(&mut self, _: &str) {}
|
||||
}
|
||||
|
||||
impl MatchName for PythonObserversTuple {
|
||||
|
@ -165,12 +165,12 @@ pub fn get_asan_runtime_flags() -> String {
|
||||
|
||||
/// An observer looking at the backtrace of target command using ASAN output
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ASANBacktraceObserver {
|
||||
pub struct AsanBacktraceObserver {
|
||||
observer_name: String,
|
||||
hash: Option<u64>,
|
||||
}
|
||||
|
||||
impl ASANBacktraceObserver {
|
||||
impl AsanBacktraceObserver {
|
||||
/// Creates a new [`BacktraceObserver`] with the given name.
|
||||
#[must_use]
|
||||
pub fn new(observer_name: &str) -> Self {
|
||||
@ -216,7 +216,7 @@ impl ASANBacktraceObserver {
|
||||
}
|
||||
}
|
||||
|
||||
impl ObserverWithHashField for ASANBacktraceObserver {
|
||||
impl ObserverWithHashField for AsanBacktraceObserver {
|
||||
/// Gets the hash value of this observer.
|
||||
#[must_use]
|
||||
fn hash(&self) -> &Option<u64> {
|
||||
@ -234,13 +234,13 @@ impl ObserverWithHashField for ASANBacktraceObserver {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ASANBacktraceObserver {
|
||||
impl Default for AsanBacktraceObserver {
|
||||
fn default() -> Self {
|
||||
Self::new("ASANBacktraceObserver")
|
||||
Self::new("AsanBacktraceObserver")
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S> Observer<I, S> for ASANBacktraceObserver
|
||||
impl<I, S> Observer<I, S> for AsanBacktraceObserver
|
||||
where
|
||||
I: Debug,
|
||||
{
|
||||
@ -256,9 +256,20 @@ where
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Do nothing on new `stderr`
|
||||
#[inline]
|
||||
fn observes_stderr(&mut self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Do nothing on new `stderr`
|
||||
fn observe_stderr(&mut self, stderr: &str) {
|
||||
self.parse_asan_output(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for ASANBacktraceObserver {
|
||||
impl Named for AsanBacktraceObserver {
|
||||
fn name(&self) -> &str {
|
||||
&self.observer_name
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! The [`StdOutObserver`] and [`StdErrObserver`] observers look at the stdout of a program
|
||||
//! The executor must explicitely support these observers.
|
||||
//! The executor must explicitly support these observers.
|
||||
//! For example, they are supported on the [`crate::executors::CommandExecutor`].
|
||||
|
||||
use alloc::string::String;
|
||||
@ -27,7 +27,16 @@ impl StdOutObserver {
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S> Observer<I, S> for StdOutObserver {}
|
||||
impl<I, S> Observer<I, S> for StdOutObserver {
|
||||
#[inline]
|
||||
fn observes_stdout(&mut self) -> bool {
|
||||
true
|
||||
}
|
||||
/// React to new `stdout`
|
||||
fn observe_stdout(&mut self, stdout: &str) {
|
||||
self.stdout = Some(stdout.into());
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for StdOutObserver {
|
||||
fn name(&self) -> &str {
|
||||
@ -54,7 +63,17 @@ impl StdErrObserver {
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S> Observer<I, S> for StdErrObserver {}
|
||||
impl<I, S> Observer<I, S> for StdErrObserver {
|
||||
#[inline]
|
||||
fn observes_stderr(&mut self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Do nothing on new `stderr`
|
||||
fn observe_stderr(&mut self, stderr: &str) {
|
||||
self.stderr = Some(stderr.into());
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for StdErrObserver {
|
||||
fn name(&self) -> &str {
|
||||
|
Loading…
x
Reference in New Issue
Block a user