Improve introspection (#200)
* remove NUM_FEEDBACKS * working introspection * adust introspection stats * bugfixes, clippy * removed outdated define * more clippy; * no_std Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
parent
204b15a432
commit
44f6e4c389
@ -20,7 +20,7 @@ which = { version = "4.0.2" }
|
||||
num_cpus = "1.0"
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/" }
|
||||
libafl = { path = "../../libafl/", features = ["std", "anymap_debug", "derive", "llmp_compression", "introspection"] }
|
||||
libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] }
|
||||
# TODO Include it only when building cc
|
||||
libafl_cc = { path = "../../libafl_cc/" }
|
||||
|
@ -1012,7 +1012,7 @@ where
|
||||
let shm = self.out_maps.last().unwrap();
|
||||
println!(
|
||||
"LLMP_DEBUG: End of page reached for map {} with len {}, sending EOP, bt: {:?}",
|
||||
shm.shmem.id().to_string(),
|
||||
shm.shmem.id(),
|
||||
shm.shmem.len(),
|
||||
bt
|
||||
);
|
||||
@ -1369,7 +1369,7 @@ where
|
||||
#[cfg(all(feature = "llmp_debug", feature = "std"))]
|
||||
println!(
|
||||
"LLMP_DEBUG: Got a new recv map {} with len {:?}",
|
||||
self.current_recv_map.shmem.id().to_string(),
|
||||
self.current_recv_map.shmem.id(),
|
||||
self.current_recv_map.shmem.len()
|
||||
);
|
||||
// After we mapped the new page, return the next message, if available
|
||||
@ -1500,7 +1500,7 @@ where
|
||||
#[cfg(all(feature = "llmp_debug", feature = "std"))]
|
||||
println!(
|
||||
"LLMP_DEBUG: Initializing map on {} with size {}",
|
||||
new_map.id().to_string(),
|
||||
new_map.id(),
|
||||
new_map.len()
|
||||
);
|
||||
|
||||
@ -1520,7 +1520,7 @@ where
|
||||
//let bt = "<n/a (release)>";
|
||||
dbg!(
|
||||
"LLMP_DEBUG: Using existing map {} with size {}",
|
||||
existing_map.id().to_string(),
|
||||
existing_map.id(),
|
||||
existing_map.len(),
|
||||
//bt
|
||||
);
|
||||
|
@ -94,8 +94,7 @@ impl ServedShMemProvider {
|
||||
.expect("Did not receive a response");
|
||||
|
||||
let server_id = ShMemId::from_slice(&shm_slice);
|
||||
let server_id_str = server_id.to_string();
|
||||
let server_fd: i32 = server_id_str.parse()?;
|
||||
let server_fd: i32 = server_id.into();
|
||||
Ok((server_fd, fd_buf[0]))
|
||||
}
|
||||
}
|
||||
@ -139,7 +138,7 @@ impl ShMemProvider for ServedShMemProvider {
|
||||
}
|
||||
|
||||
fn from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result<Self::Mem, Error> {
|
||||
let parts = id.to_string().split(':').collect::<Vec<&str>>();
|
||||
let parts = id.as_str().split(':').collect::<Vec<&str>>();
|
||||
let server_id_str = parts.get(0).unwrap();
|
||||
let (server_fd, client_fd) = self.send_receive(AshmemRequest::ExistingMap(
|
||||
ShMemDescription::from_string_and_size(server_id_str, size),
|
||||
@ -257,16 +256,17 @@ impl AshmemService {
|
||||
let description = new_map.description();
|
||||
let new_rc = Rc::new(RefCell::new(new_map));
|
||||
self.all_maps
|
||||
.insert(description.id.to_int(), Rc::downgrade(&new_rc));
|
||||
.insert(description.id.into(), Rc::downgrade(&new_rc));
|
||||
Ok(AshmemResponse::Mapping(new_rc))
|
||||
}
|
||||
AshmemRequest::ExistingMap(description) => {
|
||||
let client = self.clients.get_mut(&client_id).unwrap();
|
||||
if client.maps.contains_key(&description.id.to_int()) {
|
||||
let description_id: i32 = description.id.into();
|
||||
if client.maps.contains_key(&description_id) {
|
||||
Ok(AshmemResponse::Mapping(
|
||||
client
|
||||
.maps
|
||||
.get_mut(&description.id.to_int())
|
||||
.get_mut(&description_id)
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.first()
|
||||
@ -277,7 +277,7 @@ impl AshmemService {
|
||||
} else {
|
||||
Ok(AshmemResponse::Mapping(
|
||||
self.all_maps
|
||||
.get_mut(&description.id.to_int())
|
||||
.get_mut(&description_id)
|
||||
.unwrap()
|
||||
.clone()
|
||||
.upgrade()
|
||||
|
@ -38,7 +38,11 @@ use serde::{Deserialize, Serialize};
|
||||
use std::env;
|
||||
|
||||
use alloc::{rc::Rc, string::ToString};
|
||||
use core::{cell::RefCell, fmt::Debug, mem::ManuallyDrop};
|
||||
use core::{
|
||||
cell::RefCell,
|
||||
fmt::{self, Debug, Display},
|
||||
mem::ManuallyDrop,
|
||||
};
|
||||
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
use crate::bolts::os::pipes::Pipe;
|
||||
@ -102,18 +106,28 @@ impl ShMemId {
|
||||
&self.id
|
||||
}
|
||||
|
||||
/// Get a string representation of this id
|
||||
/// Returns the first null-byte in or the end of the buffer
|
||||
#[must_use]
|
||||
pub fn to_string(&self) -> &str {
|
||||
let eof_pos = self.id.iter().position(|&c| c == 0).unwrap();
|
||||
alloc::str::from_utf8(&self.id[..eof_pos]).unwrap()
|
||||
pub fn null_pos(&self) -> usize {
|
||||
self.id.iter().position(|&c| c == 0).unwrap()
|
||||
}
|
||||
|
||||
/// Get an integer representation of this id
|
||||
/// Returns a `str` representation of this [`ShMemId`]
|
||||
#[must_use]
|
||||
pub fn to_int(&self) -> i32 {
|
||||
let id: i32 = self.to_string().parse().unwrap();
|
||||
id
|
||||
pub fn as_str(&self) -> &str {
|
||||
alloc::str::from_utf8(&self.id[..self.null_pos()]).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ShMemId> for i32 {
|
||||
fn from(id: ShMemId) -> i32 {
|
||||
id.as_str().parse().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ShMemId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
@ -523,7 +537,8 @@ pub mod unix_shmem {
|
||||
/// Get a [`UnixShMem`] of the existing shared memory mapping identified by id
|
||||
pub fn from_id_and_size(id: ShMemId, map_size: usize) -> Result<Self, Error> {
|
||||
unsafe {
|
||||
let map = shmat(id.to_int(), ptr::null(), 0) as *mut c_uchar;
|
||||
let id_int: i32 = id.into();
|
||||
let map = shmat(id_int, ptr::null(), 0) as *mut c_uchar;
|
||||
|
||||
if map == usize::MAX as *mut c_void as *mut c_uchar || map.is_null() {
|
||||
return Err(Error::Unknown(
|
||||
@ -560,7 +575,8 @@ pub mod unix_shmem {
|
||||
impl Drop for CommonUnixShMem {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
shmctl(self.id.to_int(), 0, ptr::null_mut());
|
||||
let id_int: i32 = self.id.into();
|
||||
shmctl(id_int, 0, ptr::null_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ where
|
||||
client.update_executions(*executions as u64, *time);
|
||||
|
||||
// Update the performance stats for this client
|
||||
client.update_introspection_stats(**introspection_stats);
|
||||
client.update_introspection_stats((**introspection_stats).clone());
|
||||
|
||||
// Display the stats via `.display` only on core #1
|
||||
stats.display(event.name().to_string(), sender_id);
|
||||
|
@ -176,7 +176,8 @@ where
|
||||
} => {
|
||||
// TODO: The stats buffer should be added on client add.
|
||||
stats.client_stats_mut()[0].update_executions(*executions as u64, *time);
|
||||
stats.client_stats_mut()[0].update_introspection_stats(**introspection_stats);
|
||||
stats.client_stats_mut()[0]
|
||||
.update_introspection_stats((**introspection_stats).clone());
|
||||
stats.display(event.name().to_string(), 0);
|
||||
Ok(BrokerEventResult::Handled)
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ use crate::{
|
||||
fuzzer::HasObjective,
|
||||
inputs::Input,
|
||||
observers::ObserversTuple,
|
||||
state::HasSolutions,
|
||||
state::{HasClientPerfStats, HasSolutions},
|
||||
Error,
|
||||
};
|
||||
|
||||
@ -170,7 +170,7 @@ where
|
||||
EM: EventFirer<I, S> + EventRestarter<S>,
|
||||
OC: Corpus<I>,
|
||||
OF: Feedback<I, S>,
|
||||
S: HasSolutions<OC, I>,
|
||||
S: HasSolutions<OC, I> + HasClientPerfStats,
|
||||
Z: HasObjective<I, OF, S>,
|
||||
{
|
||||
#[cfg(unix)]
|
||||
@ -255,7 +255,7 @@ mod unix_signal_handler {
|
||||
fuzzer::HasObjective,
|
||||
inputs::Input,
|
||||
observers::ObserversTuple,
|
||||
state::HasSolutions,
|
||||
state::{HasClientPerfStats, HasSolutions},
|
||||
};
|
||||
|
||||
pub type HandlerFuncPtr =
|
||||
@ -343,7 +343,7 @@ mod unix_signal_handler {
|
||||
OT: ObserversTuple<I, S>,
|
||||
OC: Corpus<I>,
|
||||
OF: Feedback<I, S>,
|
||||
S: HasSolutions<OC, I>,
|
||||
S: HasSolutions<OC, I> + HasClientPerfStats,
|
||||
I: Input,
|
||||
Z: HasObjective<I, OF, S>,
|
||||
{
|
||||
@ -417,7 +417,7 @@ mod unix_signal_handler {
|
||||
OT: ObserversTuple<I, S>,
|
||||
OC: Corpus<I>,
|
||||
OF: Feedback<I, S>,
|
||||
S: HasSolutions<OC, I>,
|
||||
S: HasSolutions<OC, I> + HasClientPerfStats,
|
||||
I: Input,
|
||||
Z: HasObjective<I, OF, S>,
|
||||
{
|
||||
@ -562,7 +562,7 @@ mod windows_exception_handler {
|
||||
fuzzer::HasObjective,
|
||||
inputs::Input,
|
||||
observers::ObserversTuple,
|
||||
state::HasSolutions,
|
||||
state::{HasClientPerfStats, HasSolutions},
|
||||
};
|
||||
|
||||
pub type HandlerFuncPtr =
|
||||
@ -628,7 +628,7 @@ mod windows_exception_handler {
|
||||
OT: ObserversTuple<I, S>,
|
||||
OC: Corpus<I>,
|
||||
OF: Feedback<I, S>,
|
||||
S: HasSolutions<OC, I>,
|
||||
S: HasSolutions<OC, I> + HasClientPerfStats,
|
||||
I: Input,
|
||||
Z: HasObjective<I, OF, S>,
|
||||
{
|
||||
|
@ -16,7 +16,7 @@ use crate::{
|
||||
feedbacks::{Feedback, FeedbackState, FeedbackStatesTuple},
|
||||
inputs::Input,
|
||||
observers::{MapObserver, ObserversTuple},
|
||||
state::{HasFeedbackStates, HasMetadata},
|
||||
state::{HasClientPerfStats, HasFeedbackStates, HasMetadata},
|
||||
stats::UserStats,
|
||||
Error,
|
||||
};
|
||||
@ -211,7 +211,7 @@ where
|
||||
R: Reducer<T>,
|
||||
O: MapObserver<T>,
|
||||
I: Input,
|
||||
S: HasFeedbackStates<FT>,
|
||||
S: HasFeedbackStates<FT> + HasClientPerfStats,
|
||||
FT: FeedbackStatesTuple,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
@ -430,6 +430,7 @@ impl<I, O, S> Feedback<I, S> for ReachabilityFeedback<O>
|
||||
where
|
||||
I: Input,
|
||||
O: MapObserver<usize>,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
|
@ -14,12 +14,10 @@ use crate::{
|
||||
executors::ExitKind,
|
||||
inputs::Input,
|
||||
observers::{ObserversTuple, TimeObserver},
|
||||
state::HasClientPerfStats,
|
||||
Error,
|
||||
};
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
use crate::stats::NUM_FEEDBACKS;
|
||||
|
||||
use core::{marker::PhantomData, time::Duration};
|
||||
|
||||
/// Feedbacks evaluate the observers.
|
||||
@ -28,6 +26,7 @@ use core::{marker::PhantomData, time::Duration};
|
||||
pub trait Feedback<I, S>: Named
|
||||
where
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
/// `is_interesting ` return if an input is worth the addition to the corpus
|
||||
fn is_interesting<EM, OT>(
|
||||
@ -44,15 +43,13 @@ where
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn is_interesting_with_perf<EM, OT>(
|
||||
fn is_interesting_introspection<EM, OT>(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
manager: &mut EM,
|
||||
input: &I,
|
||||
observers: &OT,
|
||||
exit_kind: &ExitKind,
|
||||
feedback_stats: &mut [u64; NUM_FEEDBACKS],
|
||||
feedback_index: usize,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I, S>,
|
||||
@ -67,10 +64,10 @@ where
|
||||
// Get the elapsed time for checking this feedback
|
||||
let elapsed = crate::bolts::cpu::read_time_counter() - start_time;
|
||||
|
||||
// TODO: A more meaningful way to get perf for each feedback
|
||||
|
||||
// Add this stat to the feedback metrics
|
||||
feedback_stats[feedback_index] = elapsed;
|
||||
state
|
||||
.introspection_stats_mut()
|
||||
.update_feedback(self.name(), elapsed);
|
||||
|
||||
ret
|
||||
}
|
||||
@ -114,6 +111,7 @@ where
|
||||
B: Feedback<I, S>,
|
||||
FL: FeedbackLogic<A, B, I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
pub first: A,
|
||||
pub second: B,
|
||||
@ -127,6 +125,7 @@ where
|
||||
B: Feedback<I, S>,
|
||||
FL: FeedbackLogic<A, B, I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
self.name.as_ref()
|
||||
@ -139,6 +138,7 @@ where
|
||||
B: Feedback<I, S>,
|
||||
FL: FeedbackLogic<A, B, I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
pub fn new(first: A, second: B) -> Self {
|
||||
let name = format!("{} ({},{})", FL::name(), first.name(), second.name());
|
||||
@ -157,6 +157,7 @@ where
|
||||
B: Feedback<I, S>,
|
||||
FL: FeedbackLogic<A, B, I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
@ -182,21 +183,19 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
fn is_interesting_with_perf<EM, OT>(
|
||||
fn is_interesting_introspection<EM, OT>(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
manager: &mut EM,
|
||||
input: &I,
|
||||
observers: &OT,
|
||||
exit_kind: &ExitKind,
|
||||
feedback_stats: &mut [u64; NUM_FEEDBACKS],
|
||||
feedback_index: usize,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I, S>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
FL::is_pair_interesting_with_perf(
|
||||
FL::is_pair_interesting_introspection(
|
||||
&mut self.first,
|
||||
&mut self.second,
|
||||
state,
|
||||
@ -204,8 +203,6 @@ where
|
||||
input,
|
||||
observers,
|
||||
exit_kind,
|
||||
feedback_stats,
|
||||
feedback_index,
|
||||
)
|
||||
}
|
||||
|
||||
@ -227,6 +224,7 @@ where
|
||||
A: Feedback<I, S>,
|
||||
B: Feedback<I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn name() -> &'static str;
|
||||
|
||||
@ -245,7 +243,7 @@ where
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn is_pair_interesting_with_perf<EM, OT>(
|
||||
fn is_pair_interesting_introspection<EM, OT>(
|
||||
first: &mut A,
|
||||
second: &mut B,
|
||||
state: &mut S,
|
||||
@ -253,8 +251,6 @@ where
|
||||
input: &I,
|
||||
observers: &OT,
|
||||
exit_kind: &ExitKind,
|
||||
feedback_stats: &mut [u64; NUM_FEEDBACKS],
|
||||
feedback_index: usize,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I, S>,
|
||||
@ -271,6 +267,7 @@ where
|
||||
A: Feedback<I, S>,
|
||||
B: Feedback<I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn name() -> &'static str {
|
||||
"Eager OR"
|
||||
@ -295,7 +292,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
fn is_pair_interesting_with_perf<EM, OT>(
|
||||
fn is_pair_interesting_introspection<EM, OT>(
|
||||
first: &mut A,
|
||||
second: &mut B,
|
||||
state: &mut S,
|
||||
@ -303,33 +300,15 @@ where
|
||||
input: &I,
|
||||
observers: &OT,
|
||||
exit_kind: &ExitKind,
|
||||
feedback_stats: &mut [u64; NUM_FEEDBACKS],
|
||||
feedback_index: usize,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I, S>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
// Execute this feedback
|
||||
let a = first.is_interesting_with_perf(
|
||||
state,
|
||||
manager,
|
||||
input,
|
||||
observers,
|
||||
exit_kind,
|
||||
feedback_stats,
|
||||
feedback_index,
|
||||
)?;
|
||||
let a = first.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
|
||||
|
||||
let b = second.is_interesting_with_perf(
|
||||
state,
|
||||
manager,
|
||||
input,
|
||||
observers,
|
||||
exit_kind,
|
||||
feedback_stats,
|
||||
feedback_index + 1,
|
||||
)?;
|
||||
let b = second.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
|
||||
Ok(a || b)
|
||||
}
|
||||
}
|
||||
@ -339,6 +318,7 @@ where
|
||||
A: Feedback<I, S>,
|
||||
B: Feedback<I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn name() -> &'static str {
|
||||
"Fast OR"
|
||||
@ -366,7 +346,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
fn is_pair_interesting_with_perf<EM, OT>(
|
||||
fn is_pair_interesting_introspection<EM, OT>(
|
||||
first: &mut A,
|
||||
second: &mut B,
|
||||
state: &mut S,
|
||||
@ -374,37 +354,19 @@ where
|
||||
input: &I,
|
||||
observers: &OT,
|
||||
exit_kind: &ExitKind,
|
||||
feedback_stats: &mut [u64; NUM_FEEDBACKS],
|
||||
feedback_index: usize,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I, S>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
// Execute this feedback
|
||||
let a = first.is_interesting_with_perf(
|
||||
state,
|
||||
manager,
|
||||
input,
|
||||
observers,
|
||||
exit_kind,
|
||||
feedback_stats,
|
||||
feedback_index,
|
||||
)?;
|
||||
let a = first.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
|
||||
|
||||
if a {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
second.is_interesting_with_perf(
|
||||
state,
|
||||
manager,
|
||||
input,
|
||||
observers,
|
||||
exit_kind,
|
||||
feedback_stats,
|
||||
feedback_index + 1,
|
||||
)
|
||||
second.is_interesting_introspection(state, manager, input, observers, exit_kind)
|
||||
}
|
||||
}
|
||||
|
||||
@ -413,6 +375,7 @@ where
|
||||
A: Feedback<I, S>,
|
||||
B: Feedback<I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn name() -> &'static str {
|
||||
"Eager AND"
|
||||
@ -437,7 +400,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
fn is_pair_interesting_with_perf<EM, OT>(
|
||||
fn is_pair_interesting_introspection<EM, OT>(
|
||||
first: &mut A,
|
||||
second: &mut B,
|
||||
state: &mut S,
|
||||
@ -445,33 +408,15 @@ where
|
||||
input: &I,
|
||||
observers: &OT,
|
||||
exit_kind: &ExitKind,
|
||||
feedback_stats: &mut [u64; NUM_FEEDBACKS],
|
||||
feedback_index: usize,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I, S>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
// Execute this feedback
|
||||
let a = first.is_interesting_with_perf(
|
||||
state,
|
||||
manager,
|
||||
input,
|
||||
observers,
|
||||
exit_kind,
|
||||
feedback_stats,
|
||||
feedback_index,
|
||||
)?;
|
||||
let a = first.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
|
||||
|
||||
let b = second.is_interesting_with_perf(
|
||||
state,
|
||||
manager,
|
||||
input,
|
||||
observers,
|
||||
exit_kind,
|
||||
feedback_stats,
|
||||
feedback_index + 1,
|
||||
)?;
|
||||
let b = second.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
|
||||
Ok(a && b)
|
||||
}
|
||||
}
|
||||
@ -481,6 +426,7 @@ where
|
||||
A: Feedback<I, S>,
|
||||
B: Feedback<I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn name() -> &'static str {
|
||||
"Fast AND"
|
||||
@ -508,7 +454,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
fn is_pair_interesting_with_perf<EM, OT>(
|
||||
fn is_pair_interesting_introspection<EM, OT>(
|
||||
first: &mut A,
|
||||
second: &mut B,
|
||||
state: &mut S,
|
||||
@ -516,37 +462,19 @@ where
|
||||
input: &I,
|
||||
observers: &OT,
|
||||
exit_kind: &ExitKind,
|
||||
feedback_stats: &mut [u64; NUM_FEEDBACKS],
|
||||
feedback_index: usize,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I, S>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
// Execute this feedback
|
||||
let a = first.is_interesting_with_perf(
|
||||
state,
|
||||
manager,
|
||||
input,
|
||||
observers,
|
||||
exit_kind,
|
||||
feedback_stats,
|
||||
feedback_index,
|
||||
)?;
|
||||
let a = first.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
|
||||
|
||||
if !a {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
second.is_interesting_with_perf(
|
||||
state,
|
||||
manager,
|
||||
input,
|
||||
observers,
|
||||
exit_kind,
|
||||
feedback_stats,
|
||||
feedback_index + 1,
|
||||
)
|
||||
second.is_interesting_introspection(state, manager, input, observers, exit_kind)
|
||||
}
|
||||
}
|
||||
|
||||
@ -573,6 +501,7 @@ pub struct NotFeedback<A, I, S>
|
||||
where
|
||||
A: Feedback<I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
/// The feedback to invert
|
||||
pub first: A,
|
||||
@ -585,6 +514,7 @@ impl<A, I, S> Feedback<I, S> for NotFeedback<A, I, S>
|
||||
where
|
||||
A: Feedback<I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
@ -618,6 +548,7 @@ impl<A, I, S> Named for NotFeedback<A, I, S>
|
||||
where
|
||||
A: Feedback<I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
@ -629,6 +560,7 @@ impl<A, I, S> NotFeedback<A, I, S>
|
||||
where
|
||||
A: Feedback<I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
/// Creates a new [`NotFeedback`].
|
||||
pub fn new(first: A) -> Self {
|
||||
@ -696,6 +628,7 @@ macro_rules! feedback_not {
|
||||
impl<I, S> Feedback<I, S> for ()
|
||||
where
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
@ -727,6 +660,7 @@ pub struct CrashFeedback {}
|
||||
impl<I, S> Feedback<I, S> for CrashFeedback
|
||||
where
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
@ -776,6 +710,7 @@ pub struct TimeoutFeedback {}
|
||||
impl<I, S> Feedback<I, S> for TimeoutFeedback
|
||||
where
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
@ -830,6 +765,7 @@ pub struct TimeFeedback {
|
||||
impl<I, S> Feedback<I, S> for TimeFeedback
|
||||
where
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
|
@ -44,6 +44,7 @@ pub trait HasFeedback<F, I, S>
|
||||
where
|
||||
F: Feedback<I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
/// The feedback
|
||||
fn feedback(&self) -> &F;
|
||||
@ -57,6 +58,7 @@ pub trait HasObjective<I, OF, S>
|
||||
where
|
||||
OF: Feedback<I, S>,
|
||||
I: Input,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
/// The objective feedback
|
||||
fn objective(&self) -> &OF;
|
||||
@ -240,6 +242,7 @@ where
|
||||
F: Feedback<I, S>,
|
||||
I: Input,
|
||||
OF: Feedback<I, S>,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
scheduler: CS,
|
||||
feedback: F,
|
||||
@ -254,6 +257,7 @@ where
|
||||
F: Feedback<I, S>,
|
||||
I: Input,
|
||||
OF: Feedback<I, S>,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn scheduler(&self) -> &CS {
|
||||
&self.scheduler
|
||||
@ -270,6 +274,7 @@ where
|
||||
F: Feedback<I, S>,
|
||||
I: Input,
|
||||
OF: Feedback<I, S>,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn feedback(&self) -> &F {
|
||||
&self.feedback
|
||||
@ -286,6 +291,7 @@ where
|
||||
F: Feedback<I, S>,
|
||||
I: Input,
|
||||
OF: Feedback<I, S>,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn objective(&self) -> &OF {
|
||||
&self.objective
|
||||
@ -323,11 +329,15 @@ where
|
||||
{
|
||||
let mut res = ExecuteInputResult::None;
|
||||
|
||||
start_timer!(state);
|
||||
#[cfg(not(feature = "introspection"))]
|
||||
let is_solution = self
|
||||
.objective_mut()
|
||||
.is_interesting(state, manager, &input, observers, &exit_kind)?;
|
||||
mark_feature_time!(state, PerfFeature::GetObjectivesInterestingAll);
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
let is_solution = self
|
||||
.objective_mut()
|
||||
.is_interesting_introspection(state, manager, &input, observers, &exit_kind)?;
|
||||
|
||||
if is_solution {
|
||||
res = ExecuteInputResult::Solution;
|
||||
@ -338,31 +348,9 @@ where
|
||||
.is_interesting(state, manager, &input, observers, &exit_kind)?;
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
let is_corpus = {
|
||||
// Init temporary feedback stats here. We can't use the typical pattern above
|
||||
// since we need a `mut self` for `feedbacks_mut`, so we can't also hand a
|
||||
// new `mut self` to `is_interesting_with_perf`. We use this stack
|
||||
// variable to get the stats and then update the feedbacks directly
|
||||
let mut feedback_stats = [0_u64; crate::stats::NUM_FEEDBACKS];
|
||||
let feedback_index = 0;
|
||||
let is_corpus = self.feedback_mut().is_interesting_with_perf(
|
||||
state,
|
||||
manager,
|
||||
&input,
|
||||
observers,
|
||||
&exit_kind,
|
||||
&mut feedback_stats,
|
||||
feedback_index,
|
||||
)?;
|
||||
|
||||
// Update the feedback stats
|
||||
state
|
||||
.introspection_stats_mut()
|
||||
.update_feedbacks(feedback_stats);
|
||||
|
||||
// Return the total fitness
|
||||
is_corpus
|
||||
};
|
||||
let is_corpus = self
|
||||
.feedback_mut()
|
||||
.is_interesting_introspection(state, manager, &input, observers, &exit_kind)?;
|
||||
|
||||
if is_corpus {
|
||||
res = ExecuteInputResult::Corpus;
|
||||
@ -569,7 +557,7 @@ where
|
||||
Event::UpdatePerfStats {
|
||||
executions: *state.executions(),
|
||||
time: cur,
|
||||
introspection_stats: Box::new(*state.introspection_stats()),
|
||||
introspection_stats: Box::new(state.introspection_stats().clone()),
|
||||
phantom: PhantomData,
|
||||
},
|
||||
)?;
|
||||
|
@ -113,7 +113,7 @@ where
|
||||
where
|
||||
S: HasMetadata,
|
||||
{
|
||||
#[allow(clippy::clippy::option_if_let_else)] // we can't mutate state in a closure
|
||||
#[allow(clippy::option_if_let_else)] // we can't mutate state in a closure
|
||||
let meta = if let Some(meta) = state.metadata_mut().get_mut::<CmpValuesMetadata>() {
|
||||
meta
|
||||
} else {
|
||||
|
@ -19,12 +19,6 @@ use crate::bolts::current_time;
|
||||
|
||||
const CLIENT_STATS_TIME_WINDOW_SECS: u64 = 5; // 5 seconds
|
||||
|
||||
/// Number of stages in the fuzzer
|
||||
pub(crate) const NUM_STAGES: usize = 8;
|
||||
|
||||
/// Number of feedback mechanisms to measure for performance
|
||||
pub(crate) const NUM_FEEDBACKS: usize = 16;
|
||||
|
||||
/// User-defined stats types
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub enum UserStats {
|
||||
@ -294,7 +288,7 @@ where
|
||||
{
|
||||
// Print the client performance stats.
|
||||
let fmt = format!(
|
||||
"Client {:03}: {}",
|
||||
"Client {:03}:\n{}",
|
||||
sender_id, self.client_stats[sender_id as usize].introspection_stats
|
||||
);
|
||||
(self.print_fn)(fmt);
|
||||
@ -358,7 +352,7 @@ macro_rules! mark_feedback_time {
|
||||
}
|
||||
|
||||
/// Client performance statistics
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ClientPerfStats {
|
||||
/// Starting counter (in clock cycles from [`cpu::read_time_counter`])
|
||||
start_time: u64,
|
||||
@ -375,26 +369,22 @@ pub struct ClientPerfStats {
|
||||
/// Current stage index to write the next stage benchmark time
|
||||
curr_stage: u8,
|
||||
|
||||
/// Current feedback index to write the next feedback benchmark time
|
||||
curr_feedback: u8,
|
||||
|
||||
/// Flag to dictate this stage is in use. Used during printing to not print the empty
|
||||
/// stages if they are not in use.
|
||||
stages_used: [bool; NUM_STAGES],
|
||||
stages_used: Vec<bool>,
|
||||
|
||||
// TODO(andrea) use an hashmap and indetify feaures using a &'static str
|
||||
/// Clock cycles spent in the the various features of each stage
|
||||
stages: [[u64; PerfFeature::Count as usize]; NUM_STAGES],
|
||||
stages: Vec<[u64; PerfFeature::Count as usize]>,
|
||||
|
||||
/// Clock cycles spent in each feedback mechanism of the fuzzer.
|
||||
feedbacks: [u64; NUM_FEEDBACKS],
|
||||
feedbacks: HashMap<String, u64>,
|
||||
|
||||
/// Current time set by `start_timer`
|
||||
timer_start: Option<u64>,
|
||||
}
|
||||
|
||||
/// Various features that are measured for performance
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum PerfFeature {
|
||||
/// Getting an input from the corpus
|
||||
@ -477,7 +467,7 @@ impl From<usize> for PerfFeature {
|
||||
|
||||
/// Number of features we can measure for performance
|
||||
#[cfg(feature = "introspection")]
|
||||
const NUM_PERF_FEATURES: usize = PerfFeature::Count as usize;
|
||||
pub const NUM_PERF_FEATURES: usize = PerfFeature::Count as usize;
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
impl ClientPerfStats {
|
||||
@ -493,10 +483,9 @@ impl ClientPerfStats {
|
||||
scheduler: 0,
|
||||
manager: 0,
|
||||
curr_stage: 0,
|
||||
curr_feedback: 0,
|
||||
stages: [[0; NUM_PERF_FEATURES]; NUM_STAGES],
|
||||
stages_used: [false; NUM_STAGES],
|
||||
feedbacks: [0; NUM_FEEDBACKS],
|
||||
stages: vec![],
|
||||
stages_used: vec![],
|
||||
feedbacks: HashMap::new(),
|
||||
timer_start: None,
|
||||
}
|
||||
}
|
||||
@ -514,12 +503,12 @@ impl ClientPerfStats {
|
||||
}
|
||||
|
||||
/// Update the current [`ClientPerfStats`] with the given [`ClientPerfStats`]
|
||||
pub fn update(&mut self, stats: ClientPerfStats) {
|
||||
pub fn update(&mut self, stats: &ClientPerfStats) {
|
||||
self.set_current_time(stats.current_time);
|
||||
self.update_scheduler(stats.scheduler);
|
||||
self.update_manager(stats.manager);
|
||||
self.update_stages(stats.stages);
|
||||
self.update_feedbacks(stats.feedbacks);
|
||||
self.update_stages(&stats.stages);
|
||||
self.update_feedbacks(&stats.feedbacks);
|
||||
}
|
||||
|
||||
/// Gets the elapsed time since the internal timer started. Resets the timer when
|
||||
@ -578,33 +567,6 @@ impl ClientPerfStats {
|
||||
self.update_feature(feature, elapsed);
|
||||
}
|
||||
|
||||
/// Update the time spent in the current [`Feedback`] with the elapsed time that we
|
||||
/// have seen
|
||||
pub fn mark_feedback_time(&mut self) {
|
||||
// Sanity check that these stats have enough room for these benchmarks
|
||||
assert!(
|
||||
(self.curr_feedback as usize) < NUM_FEEDBACKS,
|
||||
"Current fuzzer has more
|
||||
stages than the `ClientPerfStats` supports ({}). Please update the
|
||||
NUM_FEEDBACKS const value in src/stats/mod.rs and rebuild",
|
||||
NUM_FEEDBACKS
|
||||
);
|
||||
|
||||
// Get the current elapsed time
|
||||
let elapsed = self.mark_time();
|
||||
|
||||
// Get a `usize` for the index
|
||||
let index: usize = self.curr_feedback.try_into().unwrap();
|
||||
|
||||
// Update the current feedback's time with the given time
|
||||
self.feedbacks[index] = self.feedbacks[index]
|
||||
.checked_add(elapsed)
|
||||
.expect("mark_feedback_time overflow");
|
||||
|
||||
// Increment the feedback index to the next feedback
|
||||
self.curr_feedback += 1;
|
||||
}
|
||||
|
||||
/// Add the given `time` to the `scheduler` stats
|
||||
#[inline]
|
||||
pub fn update_scheduler(&mut self, time: u64) {
|
||||
@ -637,23 +599,32 @@ impl ClientPerfStats {
|
||||
self.curr_stage = 0;
|
||||
}
|
||||
|
||||
/// Reset the feedback index counter to zero
|
||||
#[inline]
|
||||
pub fn reset_feedback_index(&mut self) {
|
||||
self.curr_feedback = 0;
|
||||
/// Update the time spent in the feedback
|
||||
pub fn update_feedback(&mut self, name: &str, time: u64) {
|
||||
self.feedbacks.insert(
|
||||
name.into(),
|
||||
self.feedbacks
|
||||
.get(name)
|
||||
.unwrap_or(&0)
|
||||
.checked_add(time)
|
||||
.expect("update_feedback overflow"),
|
||||
);
|
||||
}
|
||||
|
||||
/// Update the time spent in the feedbacks
|
||||
pub fn update_feedbacks(&mut self, feedbacks: [u64; NUM_FEEDBACKS]) {
|
||||
for (feedback_index, feedback) in feedbacks.iter().enumerate() {
|
||||
self.feedbacks[feedback_index] = self.feedbacks[feedback_index]
|
||||
.checked_add(*feedback)
|
||||
.expect("update_feedback overflow");
|
||||
/// Update the time spent in all the feedbacks
|
||||
pub fn update_feedbacks(&mut self, feedbacks: &HashMap<String, u64>) {
|
||||
for (key, value) in feedbacks {
|
||||
self.update_feedback(key, *value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the time spent in the stages
|
||||
pub fn update_stages(&mut self, stages: [[u64; NUM_PERF_FEATURES]; NUM_STAGES]) {
|
||||
pub fn update_stages(&mut self, stages: &[[u64; PerfFeature::Count as usize]]) {
|
||||
if self.stages.len() < stages.len() {
|
||||
self.stages
|
||||
.resize(stages.len(), [0; PerfFeature::Count as usize]);
|
||||
self.stages_used.resize(stages.len(), false);
|
||||
}
|
||||
for (stage_index, features) in stages.iter().enumerate() {
|
||||
for (feature_index, feature) in features.iter().enumerate() {
|
||||
self.stages[stage_index][feature_index] = self.stages[stage_index][feature_index]
|
||||
@ -665,21 +636,18 @@ impl ClientPerfStats {
|
||||
|
||||
/// Update the given [`PerfFeature`] with the given `time`
|
||||
pub fn update_feature(&mut self, feature: PerfFeature, time: u64) {
|
||||
// Sanity check that these stats have enough room for these benchmarks
|
||||
assert!(
|
||||
(self.curr_stage as usize) < NUM_STAGES,
|
||||
"Current fuzzer has more stages
|
||||
than the `ClientPerfStats` supports ({}). Please update the NUM_STAGES
|
||||
const value in src/stats/mod.rs and rebuild",
|
||||
NUM_STAGES
|
||||
);
|
||||
|
||||
// Get the current stage index as `usize`
|
||||
let stage_index: usize = self.curr_stage.try_into().unwrap();
|
||||
|
||||
// Get the index of the given feature
|
||||
let feature_index: usize = feature.try_into().unwrap();
|
||||
|
||||
if stage_index >= self.stages.len() {
|
||||
self.stages
|
||||
.resize(stage_index + 1, [0; PerfFeature::Count as usize]);
|
||||
self.stages_used.resize(stage_index + 1, false);
|
||||
}
|
||||
|
||||
// Update the given feature
|
||||
self.stages[stage_index][feature_index] = self.stages[stage_index][feature_index]
|
||||
.checked_add(time)
|
||||
@ -689,18 +657,25 @@ impl ClientPerfStats {
|
||||
self.stages_used[stage_index] = true;
|
||||
}
|
||||
|
||||
/// The elapsed cycles (or time)
|
||||
#[must_use]
|
||||
pub fn elapsed_cycles(&self) -> u64 {
|
||||
self.current_time - self.start_time
|
||||
}
|
||||
|
||||
/// The amount of cycles the `manager` did
|
||||
#[must_use]
|
||||
pub fn manager_cycles(&self) -> u64 {
|
||||
self.manager
|
||||
}
|
||||
|
||||
/// The amount of cycles the `scheduler` did
|
||||
#[must_use]
|
||||
pub fn scheduler_cycles(&self) -> u64 {
|
||||
self.scheduler
|
||||
}
|
||||
|
||||
/// Iterator over all used stages
|
||||
pub fn used_stages(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (usize, &[u64; PerfFeature::Count as usize])> {
|
||||
@ -711,8 +686,10 @@ impl ClientPerfStats {
|
||||
.filter(move |(stage_index, _)| used[*stage_index as usize])
|
||||
}
|
||||
|
||||
pub fn feedbacks(&self) -> impl Iterator<Item = (usize, &u64)> {
|
||||
self.feedbacks.iter().enumerate()
|
||||
/// A map of all `feedbacks`
|
||||
#[must_use]
|
||||
pub fn feedbacks(&self) -> &HashMap<String, u64> {
|
||||
&self.feedbacks
|
||||
}
|
||||
}
|
||||
|
||||
@ -735,7 +712,7 @@ impl core::fmt::Display for ClientPerfStats {
|
||||
// Create the formatted string
|
||||
writeln!(
|
||||
f,
|
||||
"Scheduler: {:4.2} | Manager: {:4.2} | Stages:",
|
||||
" {:6.4}: Scheduler\n {:6.4}: Manager",
|
||||
scheduler_percent, manager_percent
|
||||
)?;
|
||||
|
||||
@ -763,10 +740,13 @@ impl core::fmt::Display for ClientPerfStats {
|
||||
// Write the percentage for this feature
|
||||
writeln!(f, " {:6.4}: {:?}", feature_percent, feature)?;
|
||||
}
|
||||
}
|
||||
|
||||
for (feedback_index, feedback) in self.feedbacks() {
|
||||
writeln!(f, " Feedbacks:")?;
|
||||
|
||||
for (feedback_name, feedback_time) in self.feedbacks() {
|
||||
// Calculate this current stage's percentage
|
||||
let feedback_percent = *feedback as f64 / elapsed;
|
||||
let feedback_percent = *feedback_time as f64 / elapsed;
|
||||
|
||||
// Ignore this feedback if it isn't used
|
||||
if feedback_percent == 0.0 {
|
||||
@ -777,15 +757,10 @@ impl core::fmt::Display for ClientPerfStats {
|
||||
other_percent -= feedback_percent;
|
||||
|
||||
// Write the percentage for this feedback
|
||||
writeln!(
|
||||
f,
|
||||
" {:6.4}: Feedback index {}",
|
||||
feedback_percent, feedback_index
|
||||
)?;
|
||||
}
|
||||
writeln!(f, " {:6.4}: {}", feedback_percent, feedback_name)?;
|
||||
}
|
||||
|
||||
write!(f, " Not Measured: {:4.2}", other_percent)?;
|
||||
write!(f, " {:6.4}: Not Measured", other_percent)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ where
|
||||
{
|
||||
// Print the client performance stats. Skip the Client 0 which is the broker
|
||||
for (i, client) in self.client_stats.iter().skip(1).enumerate() {
|
||||
let fmt = format!("Client {:03}: {}", i + 1, client.introspection_stats);
|
||||
let fmt = format!("Client {:03}:\n{}", i + 1, client.introspection_stats);
|
||||
(self.print_fn)(fmt);
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ use libafl::{
|
||||
feedbacks::Feedback,
|
||||
inputs::{HasTargetBytes, Input},
|
||||
observers::{Observer, ObserversTuple},
|
||||
state::HasMetadata,
|
||||
state::{HasClientPerfStats, HasMetadata},
|
||||
Error, SerdeAny,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -510,6 +510,7 @@ pub struct AsanErrorsFeedback {
|
||||
impl<I, S> Feedback<I, S> for AsanErrorsFeedback
|
||||
where
|
||||
I: Input + HasTargetBytes,
|
||||
S: HasClientPerfStats,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
|
@ -73,8 +73,6 @@
|
||||
#define CHECK_WEAK_FN(Name) (Name != NULL)
|
||||
#endif // _MSC_VER
|
||||
|
||||
#define EXT_FUNC_DEF(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN)
|
||||
#define EXT_FUNC_IMPL(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN)
|
||||
|
||||
@ -84,10 +82,7 @@
|
||||
#else
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// TODO: Find a proper way to deal with weak fns on Apple!
|
||||
// On Apple, weak_import and weak attrs behave differently to linux.
|
||||
#define EXT_FUNC_DEF(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) { return (RETURN_TYPE) 0; }
|
||||
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
__attribute__((weak, visibility("default"))) RETURN_TYPE NAME FUNC_SIG { \
|
||||
|
Loading…
x
Reference in New Issue
Block a user