Allow two different observers for DiffExecutor (#843)
* DifferentialExecutor for CommandExecutor along with StdIO observer * format * fix CI issues * fix format and unit test * fix documentation * allow three structs and doc only for linux * resolve documentation test failure * minor * running fmt_all.sh * into_executor() takes 4 params, not just 1 Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
parent
64bc5d5bdb
commit
0307dadcd1
@ -108,7 +108,12 @@ pub fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
let mut executor = MyExecutor { shmem_id }.into_executor(tuple_list!(observer, bt_observer));
|
||||
let mut executor = MyExecutor { shmem_id }.into_executor(
|
||||
tuple_list!(observer, bt_observer),
|
||||
None,
|
||||
None,
|
||||
Some("ASANBacktraceObserver".to_string()),
|
||||
);
|
||||
|
||||
// Generator of printable bytearrays of max size 32
|
||||
let mut generator = RandPrintablesGenerator::new(32);
|
||||
|
@ -224,7 +224,7 @@ fn fuzz(
|
||||
// Create a concolic trace
|
||||
ConcolicTracingStage::new(
|
||||
TracingStage::new(
|
||||
MyCommandConfigurator::default().into_executor(tuple_list!(concolic_observer))
|
||||
MyCommandConfigurator::default().into_executor(tuple_list!(concolic_observer), None, None, None)
|
||||
),
|
||||
concolic_observer_name,
|
||||
),
|
||||
|
@ -1,6 +1,5 @@
|
||||
//! The command executor executes a sub program for each run
|
||||
#[cfg(feature = "std")]
|
||||
use alloc::{borrow::ToOwned, string::String, vec::Vec};
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use core::{
|
||||
fmt::{self, Debug, Formatter},
|
||||
marker::PhantomData,
|
||||
@ -29,6 +28,7 @@ use crate::{
|
||||
},
|
||||
inputs::HasTargetBytes,
|
||||
observers::{ASANBacktraceObserver, ObserversTuple, StdErrObserver, StdOutObserver},
|
||||
std::borrow::ToOwned,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use crate::{inputs::Input, Error};
|
||||
@ -37,7 +37,7 @@ use crate::{inputs::Input, Error};
|
||||
/// `StdIn`: The traget reads from stdin
|
||||
/// `File`: The target reads from the specified [`InputFile`]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum InputLocation {
|
||||
enum InputLocation {
|
||||
/// Mutate a commandline argument to deliver an input
|
||||
Arg {
|
||||
/// The offset of the argument to mutate
|
||||
@ -45,11 +45,11 @@ pub enum InputLocation {
|
||||
},
|
||||
/// Deliver input via `StdIn`
|
||||
StdIn,
|
||||
/// Deliver the input via the specified [`InputFile`]
|
||||
/// Deliver the iniput via the specified [`InputFile`]
|
||||
/// You can use specify [`InputFile::create(INPUTFILE_STD)`] to use a default filename.
|
||||
File {
|
||||
/// The file to write input to. The target should read input from this location.
|
||||
input_file: InputFile,
|
||||
/// The fiel to write input to. The target should read input from this location.
|
||||
out_file: InputFile,
|
||||
},
|
||||
}
|
||||
|
||||
@ -71,11 +71,15 @@ fn clone_command(cmd: &Command) -> Command {
|
||||
/// A simple Configurator that takes the most common parameters
|
||||
/// Writes the input either to stdio or to a file
|
||||
/// Use [`CommandExecutor::builder()`] to use this configurator.
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug)]
|
||||
pub struct StdCommandConfigurator {
|
||||
/// If set to true, the child output will remain visible
|
||||
/// By default, the child output is hidden to increase execution speed
|
||||
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
|
||||
@ -97,6 +101,13 @@ impl CommandConfigurator for StdCommandConfigurator {
|
||||
cmd.stderr(Stdio::null());
|
||||
}
|
||||
|
||||
if self.has_stdout_observer {
|
||||
cmd.stdout(Stdio::piped());
|
||||
}
|
||||
if self.has_stderr_observer || self.has_asan_observer {
|
||||
cmd.stderr(Stdio::piped());
|
||||
}
|
||||
|
||||
for (i, arg) in args.enumerate() {
|
||||
if i == *argnum {
|
||||
debug_assert_eq!(arg, "DUMMY");
|
||||
@ -129,8 +140,8 @@ impl CommandConfigurator for StdCommandConfigurator {
|
||||
drop(stdin);
|
||||
Ok(handle)
|
||||
}
|
||||
InputLocation::File { input_file } => {
|
||||
input_file.write_buf(input.target_bytes().as_slice())?;
|
||||
InputLocation::File { out_file } => {
|
||||
out_file.write_buf(input.target_bytes().as_slice())?;
|
||||
Ok(self.command.spawn()?)
|
||||
}
|
||||
}
|
||||
@ -149,13 +160,13 @@ where
|
||||
configurer: T,
|
||||
observers: OT,
|
||||
/// cache if the AsanBacktraceObserver is present
|
||||
has_asan_observer: bool,
|
||||
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: bool,
|
||||
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: bool,
|
||||
has_stderr_observer: Option<String>,
|
||||
phantom: PhantomData<(EM, I, S, Z)>,
|
||||
}
|
||||
|
||||
@ -213,6 +224,9 @@ where
|
||||
debug_child: bool,
|
||||
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>,
|
||||
@ -224,35 +238,29 @@ where
|
||||
}
|
||||
command.stdin(Stdio::null());
|
||||
|
||||
let has_stdout_observer = observers
|
||||
.match_name::<StdOutObserver>("StdOutObserver")
|
||||
.is_some();
|
||||
if has_stdout_observer {
|
||||
command.stderr(Stdio::piped());
|
||||
if has_stdout_observer.is_some() {
|
||||
command.stdout(Stdio::piped());
|
||||
}
|
||||
|
||||
let has_stderr_observer = observers
|
||||
.match_name::<StdErrObserver>("StdErrObserver")
|
||||
.is_some();
|
||||
let has_asan_observer = observers
|
||||
.match_name::<ASANBacktraceObserver>("ASANBacktraceObserver")
|
||||
.is_some();
|
||||
if has_stderr_observer || has_asan_observer {
|
||||
if has_stderr_observer.is_some() || has_asan_observer.is_some() {
|
||||
command.stderr(Stdio::piped());
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
observers,
|
||||
has_asan_observer,
|
||||
|
||||
configurer: StdCommandConfigurator {
|
||||
input_location: InputLocation::File {
|
||||
input_file: InputFile::create(path)?,
|
||||
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,
|
||||
})
|
||||
}
|
||||
@ -264,6 +272,9 @@ where
|
||||
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>,
|
||||
@ -272,6 +283,16 @@ 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() {
|
||||
@ -340,27 +361,27 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
if self.has_asan_observer || self.has_stderr_observer {
|
||||
if self.has_asan_observer.is_some() || self.has_stderr_observer.is_some() {
|
||||
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 self.has_asan_observer {
|
||||
if let Some(name) = self.has_asan_observer.as_ref() {
|
||||
self.observers
|
||||
.match_name_mut::<ASANBacktraceObserver>("ASANBacktraceObserver")
|
||||
.match_name_mut::<ASANBacktraceObserver>(name)
|
||||
.unwrap()
|
||||
.parse_asan_output(&stderr);
|
||||
}
|
||||
if self.has_stderr_observer {
|
||||
if let Some(name) = self.has_stderr_observer.as_ref() {
|
||||
self.observers
|
||||
.match_name_mut::<StdErrObserver>("StdErrObserver")
|
||||
.match_name_mut::<StdErrObserver>(name)
|
||||
.unwrap()
|
||||
.stderr = Some(stderr);
|
||||
}
|
||||
}
|
||||
if self.has_stdout_observer {
|
||||
if let Some(name) = self.has_stdout_observer.as_ref() {
|
||||
let mut stdout = String::new();
|
||||
child.stdout.as_mut().ok_or_else(|| {
|
||||
Error::illegal_state(
|
||||
@ -368,7 +389,7 @@ where
|
||||
)
|
||||
})?.read_to_string(&mut stdout)?;
|
||||
self.observers
|
||||
.match_name_mut::<StdOutObserver>("StdOutObserver")
|
||||
.match_name_mut::<StdOutObserver>(name)
|
||||
.unwrap()
|
||||
.stdout = Some(stdout);
|
||||
}
|
||||
@ -398,6 +419,9 @@ 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 {
|
||||
@ -417,9 +441,30 @@ 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
|
||||
@ -466,9 +511,9 @@ impl CommandExecutorBuilder {
|
||||
/// and adds the filename as arg to at the current position.
|
||||
pub fn arg_input_file<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
|
||||
self.arg(path.as_ref());
|
||||
let input_file_std = InputFile::create(path.as_ref()).unwrap();
|
||||
let out_file_std = InputFile::create(path.as_ref()).unwrap();
|
||||
self.input(InputLocation::File {
|
||||
input_file: input_file_std,
|
||||
out_file: out_file_std,
|
||||
});
|
||||
self
|
||||
}
|
||||
@ -565,29 +610,28 @@ impl CommandExecutorBuilder {
|
||||
command.stdout(Stdio::null());
|
||||
command.stderr(Stdio::null());
|
||||
}
|
||||
if observers
|
||||
.match_name::<ASANBacktraceObserver>("ASANBacktraceObserver")
|
||||
.is_some()
|
||||
|| observers
|
||||
.match_name::<StdErrObserver>("StdErrObserver")
|
||||
.is_some()
|
||||
{
|
||||
if self.has_stderr_observer.is_some() || self.has_asan_observer.is_some() {
|
||||
// we need stderr for ASANBackt
|
||||
command.stderr(Stdio::piped());
|
||||
}
|
||||
if observers
|
||||
.match_name::<StdOutObserver>("StdOutObserver")
|
||||
.is_some()
|
||||
{
|
||||
if self.has_stdout_observer.is_some() {
|
||||
command.stdout(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(),
|
||||
input_location: self.input_location.clone(),
|
||||
command,
|
||||
};
|
||||
Ok(configurator.into_executor(observers))
|
||||
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(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -619,9 +663,10 @@ impl CommandExecutorBuilder {
|
||||
/// }
|
||||
///
|
||||
/// fn make_executor<EM, I: Input + HasTargetBytes, S, Z>() -> impl Executor<EM, I, S, Z> {
|
||||
/// MyExecutor.into_executor(())
|
||||
/// MyExecutor.into_executor((), None, None, None)
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
#[cfg(all(feature = "std", any(unix, doc)))]
|
||||
pub trait CommandConfigurator: Sized + Debug {
|
||||
/// Spawns a new process with the given configuration.
|
||||
@ -630,22 +675,16 @@ 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) -> CommandExecutor<EM, I, OT, S, Self, Z>
|
||||
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>
|
||||
where
|
||||
OT: Debug + MatchName,
|
||||
{
|
||||
let has_asan_observer = observers
|
||||
.match_name::<ASANBacktraceObserver>("ASANBacktraceObserver")
|
||||
.is_some();
|
||||
|
||||
let has_stdout_observer = observers
|
||||
.match_name::<StdOutObserver>("StdOutObserver")
|
||||
.is_some();
|
||||
|
||||
let has_stderr_observer = observers
|
||||
.match_name::<StdErrObserver>("StdErrObserver")
|
||||
.is_some();
|
||||
|
||||
CommandExecutor {
|
||||
observers,
|
||||
has_asan_observer,
|
||||
@ -697,13 +736,18 @@ mod tests {
|
||||
#[cfg(unix)]
|
||||
fn test_parse_afl_cmdline() {
|
||||
use alloc::string::ToString;
|
||||
|
||||
let mut mgr = SimpleEventManager::<BytesInput, _, ()>::new(SimpleMonitor::new(|status| {
|
||||
println!("{status}");
|
||||
}));
|
||||
|
||||
let mut executor =
|
||||
CommandExecutor::parse_afl_cmdline(&["file".to_string(), "@@".to_string()], (), true)
|
||||
let mut executor = CommandExecutor::parse_afl_cmdline(
|
||||
&["file".to_string(), "@@".to_string()],
|
||||
(),
|
||||
true,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
executor
|
||||
.run_target(
|
||||
|
@ -2,9 +2,12 @@
|
||||
//! It wraps two exeutors that will be run after each other with the same input.
|
||||
//! In comparison to the [`crate::executors::CombinedExecutor`] it also runs the secondary executor in `run_target`.
|
||||
//!
|
||||
use core::fmt::Debug;
|
||||
use core::{cell::UnsafeCell, fmt::Debug};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
bolts::{ownedref::OwnedPtrMut, tuples::MatchName},
|
||||
executors::{Executor, ExitKind, HasObservers},
|
||||
inputs::Input,
|
||||
observers::ObserversTuple,
|
||||
@ -13,19 +16,24 @@ use crate::{
|
||||
|
||||
/// A [`DiffExecutor`] wraps a primary executor, forwarding its methods, and a secondary one
|
||||
#[derive(Debug)]
|
||||
pub struct DiffExecutor<A, B>
|
||||
pub struct DiffExecutor<A, B, OTA, OTB>
|
||||
where
|
||||
A: Debug,
|
||||
B: Debug,
|
||||
OTA: Debug,
|
||||
OTB: Debug,
|
||||
{
|
||||
primary: A,
|
||||
secondary: B,
|
||||
observers: UnsafeCell<ProxyObserversTuple<OTA, OTB>>,
|
||||
}
|
||||
|
||||
impl<A, B> DiffExecutor<A, B>
|
||||
impl<A, B, OTA, OTB> DiffExecutor<A, B, OTA, OTB>
|
||||
where
|
||||
A: Debug,
|
||||
B: Debug,
|
||||
OTA: Debug,
|
||||
OTB: Debug,
|
||||
{
|
||||
/// Create a new `DiffExecutor`, wrapping the given `executor`s.
|
||||
pub fn new<EM, I, S, Z>(primary: A, secondary: B) -> Self
|
||||
@ -34,7 +42,14 @@ where
|
||||
B: Executor<EM, I, S, Z>,
|
||||
I: Input,
|
||||
{
|
||||
Self { primary, secondary }
|
||||
Self {
|
||||
primary,
|
||||
secondary,
|
||||
observers: UnsafeCell::new(ProxyObserversTuple {
|
||||
primary: OwnedPtrMut::Ptr(core::ptr::null_mut()),
|
||||
secondary: OwnedPtrMut::Ptr(core::ptr::null_mut()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the primary `Executor` that is wrapped by this `DiffExecutor`.
|
||||
@ -48,11 +63,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, EM, I, S, Z> Executor<EM, I, S, Z> for DiffExecutor<A, B>
|
||||
impl<A, B, EM, I, OTA, OTB, S, Z> Executor<EM, I, S, Z> for DiffExecutor<A, B, OTA, OTB>
|
||||
where
|
||||
A: Executor<EM, I, S, Z>,
|
||||
B: Executor<EM, I, S, Z>,
|
||||
I: Input,
|
||||
OTA: Debug,
|
||||
OTB: Debug,
|
||||
{
|
||||
fn run_target(
|
||||
&mut self,
|
||||
@ -77,19 +94,115 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, I, OT, S> HasObservers<I, OT, S> for DiffExecutor<A, B>
|
||||
/// Proxy the observers of the inner executors
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(
|
||||
bound = "A: serde::Serialize + serde::de::DeserializeOwned, B: serde::Serialize + serde::de::DeserializeOwned"
|
||||
)]
|
||||
pub struct ProxyObserversTuple<A, B> {
|
||||
primary: OwnedPtrMut<A>,
|
||||
secondary: OwnedPtrMut<B>,
|
||||
}
|
||||
|
||||
impl<A, B, I, S> ObserversTuple<I, S> for ProxyObserversTuple<A, B>
|
||||
where
|
||||
A: HasObservers<I, OT, S>,
|
||||
B: Debug,
|
||||
OT: ObserversTuple<I, S>,
|
||||
A: ObserversTuple<I, S>,
|
||||
B: ObserversTuple<I, S>,
|
||||
{
|
||||
fn pre_exec_all(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
|
||||
self.primary.as_mut().pre_exec_all(state, input)?;
|
||||
self.secondary.as_mut().pre_exec_all(state, input)
|
||||
}
|
||||
|
||||
fn post_exec_all(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &I,
|
||||
exit_kind: &ExitKind,
|
||||
) -> Result<(), Error> {
|
||||
self.primary
|
||||
.as_mut()
|
||||
.post_exec_all(state, input, exit_kind)?;
|
||||
self.secondary
|
||||
.as_mut()
|
||||
.post_exec_all(state, input, exit_kind)
|
||||
}
|
||||
|
||||
fn pre_exec_child_all(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
|
||||
self.primary.as_mut().pre_exec_child_all(state, input)?;
|
||||
self.secondary.as_mut().pre_exec_child_all(state, input)
|
||||
}
|
||||
|
||||
fn post_exec_child_all(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &I,
|
||||
exit_kind: &ExitKind,
|
||||
) -> Result<(), Error> {
|
||||
self.primary
|
||||
.as_mut()
|
||||
.post_exec_child_all(state, input, exit_kind)?;
|
||||
self.secondary
|
||||
.as_mut()
|
||||
.post_exec_child_all(state, input, exit_kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> MatchName for ProxyObserversTuple<A, B>
|
||||
where
|
||||
A: MatchName,
|
||||
B: MatchName,
|
||||
{
|
||||
fn match_name<T>(&self, name: &str) -> Option<&T> {
|
||||
if let Some(t) = self.primary.as_ref().match_name::<T>(name) {
|
||||
return Some(t);
|
||||
}
|
||||
self.secondary.as_ref().match_name::<T>(name)
|
||||
}
|
||||
fn match_name_mut<T>(&mut self, name: &str) -> Option<&mut T> {
|
||||
if let Some(t) = self.primary.as_mut().match_name_mut::<T>(name) {
|
||||
return Some(t);
|
||||
}
|
||||
self.secondary.as_mut().match_name_mut::<T>(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> ProxyObserversTuple<A, B> {
|
||||
fn set(&mut self, primary: &A, secondary: &B) {
|
||||
self.primary = OwnedPtrMut::Ptr(primary as *const A as *mut A);
|
||||
self.secondary = OwnedPtrMut::Ptr(secondary as *const B as *mut B);
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, I, OTA, OTB, S> HasObservers<I, ProxyObserversTuple<OTA, OTB>, S>
|
||||
for DiffExecutor<A, B, OTA, OTB>
|
||||
where
|
||||
A: HasObservers<I, OTA, S>,
|
||||
B: HasObservers<I, OTB, S>,
|
||||
OTA: ObserversTuple<I, S>,
|
||||
OTB: ObserversTuple<I, S>,
|
||||
{
|
||||
#[inline]
|
||||
fn observers(&self) -> &OT {
|
||||
self.primary.observers()
|
||||
fn observers(&self) -> &ProxyObserversTuple<OTA, OTB> {
|
||||
unsafe {
|
||||
self.observers
|
||||
.get()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.set(self.primary.observers(), self.secondary.observers());
|
||||
self.observers.get().as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn observers_mut(&mut self) -> &mut OT {
|
||||
self.primary.observers_mut()
|
||||
fn observers_mut(&mut self) -> &mut ProxyObserversTuple<OTA, OTB> {
|
||||
unsafe {
|
||||
self.observers
|
||||
.get()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.set(self.primary.observers(), self.secondary.observers());
|
||||
self.observers.get().as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,6 @@ where
|
||||
fn err(name: &str) -> Error {
|
||||
Error::illegal_argument(format!("DiffFeedback: observer {name} not found"))
|
||||
}
|
||||
|
||||
let o1: &O1 = observers
|
||||
.match_name(&self.o1_name)
|
||||
.ok_or_else(|| err(&self.o1_name))?;
|
||||
@ -148,7 +147,7 @@ where
|
||||
.match_name(&self.o2_name)
|
||||
.ok_or_else(|| err(&self.o2_name))?;
|
||||
|
||||
Ok(o1 != o2)
|
||||
Ok((self.compare_fn)(o1, o2) == DiffResult::Diff)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,11 +4,13 @@
|
||||
|
||||
use alloc::string::String;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{bolts::tuples::Named, observers::Observer};
|
||||
|
||||
/// An observer that captures stdout of a target.
|
||||
/// Only works for supported executors.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct StdOutObserver {
|
||||
/// The name of the observer.
|
||||
pub name: String,
|
||||
|
Loading…
x
Reference in New Issue
Block a user