working random cmplog mutations
This commit is contained in:
parent
f61140bdcf
commit
16d4c36f12
@ -12,15 +12,15 @@ use libafl::{
|
|||||||
events::setup_restarting_mgr_std,
|
events::setup_restarting_mgr_std,
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
executors::{inprocess::InProcessExecutor, ExitKind},
|
||||||
feedback_or,
|
feedback_or,
|
||||||
feedbacks::{CmpFeedback, CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback},
|
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
inputs::{BytesInput, HasTargetBytes},
|
inputs::{BytesInput, HasTargetBytes},
|
||||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
mutators::token_mutations::{I2SRandReplace, Tokens},
|
mutators::token_mutations::I2SRandReplace,
|
||||||
observers::{StdMapObserver, TimeObserver},
|
observers::{StdMapObserver, TimeObserver},
|
||||||
stages::{StdMutationalStage, TracingStage},
|
stages::{StdMutationalStage, TracingStage},
|
||||||
state::{HasCorpus, StdState},
|
state::{HasCorpus, StdState},
|
||||||
stats::MultiStats,
|
stats::SimpleStats,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
|
|||||||
let time_observer = TimeObserver::new("time");
|
let time_observer = TimeObserver::new("time");
|
||||||
|
|
||||||
let cmplog = unsafe { &mut CMPLOG_MAP };
|
let cmplog = unsafe { &mut CMPLOG_MAP };
|
||||||
let cmplog_observer = CmpLogObserver::new("cmplog", cmplog);
|
let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true);
|
||||||
|
|
||||||
let cmplog = unsafe { &mut CMPLOG_MAP };
|
let cmplog = unsafe { &mut CMPLOG_MAP };
|
||||||
let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true);
|
let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true);
|
||||||
@ -155,9 +155,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Secondary harness due to mut ownership
|
// Secondary harness due to mut ownership
|
||||||
let mut harness = |input: &BytesInput| {
|
let mut harness = |buf: &[u8]| {
|
||||||
let target = input.target_bytes();
|
|
||||||
let buf = target.as_slice();
|
|
||||||
libfuzzer_test_one_input(buf);
|
libfuzzer_test_one_input(buf);
|
||||||
ExitKind::Ok
|
ExitKind::Ok
|
||||||
};
|
};
|
||||||
@ -172,14 +170,16 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
|
|||||||
)?);
|
)?);
|
||||||
|
|
||||||
// Setup a randomic Input2State stage
|
// Setup a randomic Input2State stage
|
||||||
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
|
|
||||||
|
//let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
|
||||||
|
|
||||||
// Setup a basic mutator
|
// Setup a basic mutator
|
||||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||||
let mutational = StdMutationalStage::new(mutator);
|
let mutational = StdMutationalStage::new(mutator);
|
||||||
|
|
||||||
// The order of the stages matter!
|
// The order of the stages matter!
|
||||||
let mut stages = tuple_list!(tracing, i2s, mutational);
|
|
||||||
|
let mut stages = tuple_list!(tracing, mutational);
|
||||||
|
|
||||||
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?;
|
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?;
|
||||||
|
|
||||||
|
@ -361,12 +361,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Merge two `TupeList`
|
/// Merge two `TupeList`
|
||||||
pub trait Merge<T>: TupleList
|
pub trait Merge<T> {
|
||||||
where
|
|
||||||
T: TupleList,
|
|
||||||
{
|
|
||||||
/// The Resulting [`TupleList`], of an [`Merge::merge()`] call
|
/// The Resulting [`TupleList`], of an [`Merge::merge()`] call
|
||||||
type MergeResult: TupleList;
|
type MergeResult;
|
||||||
|
|
||||||
/// Merge and return the merged tuple
|
/// Merge and return the merged tuple
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -374,10 +371,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Implement merge for an empty tuple list.
|
/// Implement merge for an empty tuple list.
|
||||||
impl<T> Merge<T> for ()
|
impl<T> Merge<T> for () {
|
||||||
where
|
|
||||||
T: TupleList,
|
|
||||||
{
|
|
||||||
type MergeResult = T;
|
type MergeResult = T;
|
||||||
|
|
||||||
fn merge(self, value: T) -> Self::MergeResult {
|
fn merge(self, value: T) -> Self::MergeResult {
|
||||||
@ -388,10 +382,7 @@ where
|
|||||||
/// Implement merge for non-empty tuple list.
|
/// Implement merge for non-empty tuple list.
|
||||||
impl<Head, Tail, T> Merge<T> for (Head, Tail)
|
impl<Head, Tail, T> Merge<T> for (Head, Tail)
|
||||||
where
|
where
|
||||||
T: TupleList,
|
|
||||||
Self: TupleList,
|
|
||||||
Tail: Merge<T>,
|
Tail: Merge<T>,
|
||||||
(Head, Tail::MergeResult): TupleList,
|
|
||||||
{
|
{
|
||||||
type MergeResult = (Head, Tail::MergeResult);
|
type MergeResult = (Head, Tail::MergeResult);
|
||||||
|
|
||||||
|
@ -1,122 +0,0 @@
|
|||||||
use alloc::string::{String, ToString};
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
bolts::{tuples::Named, AsSlice},
|
|
||||||
executors::ExitKind,
|
|
||||||
feedbacks::Feedback,
|
|
||||||
inputs::Input,
|
|
||||||
observers::{CmpMap, CmpObserver, CmpValues, ObserversTuple},
|
|
||||||
state::HasMetadata,
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A state metadata holding a list of values logged from comparisons
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct CmpValuesMetadata {
|
|
||||||
/// A `list` of values.
|
|
||||||
pub list: Vec<CmpValues>,
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::impl_serdeany!(CmpValuesMetadata);
|
|
||||||
|
|
||||||
impl AsSlice<CmpValues> for CmpValuesMetadata {
|
|
||||||
/// Convert to a slice
|
|
||||||
#[must_use]
|
|
||||||
fn as_slice(&self) -> &[CmpValues] {
|
|
||||||
self.list.as_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CmpValuesMetadata {
|
|
||||||
/// Creates a new [`struct@CmpValuesMetadata`]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self { list: vec![] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub struct CmpFeedback<CM, CO>
|
|
||||||
where
|
|
||||||
CO: CmpObserver<CM>,
|
|
||||||
CM: CmpMap,
|
|
||||||
{
|
|
||||||
name: String,
|
|
||||||
phantom: PhantomData<(CM, CO)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<CM, CO, I, S> Feedback<I, S> for CmpFeedback<CM, CO>
|
|
||||||
where
|
|
||||||
I: Input,
|
|
||||||
CO: CmpObserver<CM>,
|
|
||||||
CM: CmpMap,
|
|
||||||
S: HasMetadata,
|
|
||||||
{
|
|
||||||
fn is_interesting<OT>(
|
|
||||||
&mut self,
|
|
||||||
state: &mut S,
|
|
||||||
_input: &I,
|
|
||||||
observers: &OT,
|
|
||||||
_exit_kind: &ExitKind,
|
|
||||||
) -> Result<bool, Error>
|
|
||||||
where
|
|
||||||
OT: ObserversTuple,
|
|
||||||
{
|
|
||||||
if state.metadata().get::<CmpValuesMetadata>().is_none() {
|
|
||||||
state.add_metadata(CmpValuesMetadata::new());
|
|
||||||
}
|
|
||||||
let meta = state.metadata_mut().get_mut::<CmpValuesMetadata>().unwrap();
|
|
||||||
meta.list.clear();
|
|
||||||
// TODO Replace with match_name_type when stable
|
|
||||||
let observer = observers.match_name::<CO>(self.name()).unwrap();
|
|
||||||
let count = observer.usable_count();
|
|
||||||
for i in 0..count {
|
|
||||||
let execs = observer.map().usable_executions_for(i);
|
|
||||||
if execs > 0 {
|
|
||||||
// Recongize loops and discard
|
|
||||||
// TODO
|
|
||||||
for j in 0..execs {
|
|
||||||
meta.list.push(observer.map().values_of(i, j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<CM, CO> Named for CmpFeedback<CM, CO>
|
|
||||||
where
|
|
||||||
CO: CmpObserver<CM>,
|
|
||||||
CM: CmpMap,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
self.name.as_str()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<CM, CO> CmpFeedback<CM, CO>
|
|
||||||
where
|
|
||||||
CO: CmpObserver<CM>,
|
|
||||||
CM: CmpMap,
|
|
||||||
{
|
|
||||||
/// Creates a new [`CmpFeedback`]
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_name(name: &'static str) -> Self {
|
|
||||||
Self {
|
|
||||||
name: name.to_string(),
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new [`CmpFeedback`]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(observer: &CO) -> Self {
|
|
||||||
Self {
|
|
||||||
name: observer.name().to_string(),
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,9 +4,6 @@
|
|||||||
pub mod map;
|
pub mod map;
|
||||||
pub use map::*;
|
pub use map::*;
|
||||||
|
|
||||||
pub mod cmp;
|
|
||||||
pub use cmp::*;
|
|
||||||
|
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -15,10 +15,9 @@ use std::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::rands::Rand,
|
bolts::rands::Rand,
|
||||||
feedbacks::cmp::CmpValuesMetadata,
|
|
||||||
inputs::{HasBytesVec, Input},
|
inputs::{HasBytesVec, Input},
|
||||||
mutators::{buffer_self_copy, mutations::buffer_copy, MutationResult, Mutator, Named},
|
mutators::{buffer_self_copy, mutations::buffer_copy, MutationResult, Mutator, Named},
|
||||||
observers::cmp::CmpValues,
|
observers::cmp::{CmpValues, CmpValuesMetadata},
|
||||||
state::{HasMaxSize, HasMetadata, HasRand},
|
state::{HasMaxSize, HasMetadata, HasRand},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
@ -449,6 +448,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//println!("{:?}", result);
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,14 @@ use alloc::{
|
|||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::{ownedref::OwnedRefMut, tuples::Named},
|
bolts::{ownedref::OwnedRefMut, tuples::Named, AsSlice},
|
||||||
executors::HasExecHooks,
|
executors::HasExecHooks,
|
||||||
observers::Observer,
|
observers::Observer,
|
||||||
|
state::HasMetadata,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum CmpValues {
|
pub enum CmpValues {
|
||||||
U8((u8, u8)),
|
U8((u8, u8)),
|
||||||
U16((u16, u16)),
|
U16((u16, u16)),
|
||||||
@ -23,6 +24,53 @@ pub enum CmpValues {
|
|||||||
Bytes((Vec<u8>, Vec<u8>)),
|
Bytes((Vec<u8>, Vec<u8>)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CmpValues {
|
||||||
|
pub fn is_numeric(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
CmpValues::U8(_) => true,
|
||||||
|
CmpValues::U16(_) => true,
|
||||||
|
CmpValues::U32(_) => true,
|
||||||
|
CmpValues::U64(_) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_u64_tuple(&self) -> Option<(u64, u64)> {
|
||||||
|
match self {
|
||||||
|
CmpValues::U8(t) => Some((t.0 as u64, t.1 as u64)),
|
||||||
|
CmpValues::U16(t) => Some((t.0 as u64, t.1 as u64)),
|
||||||
|
CmpValues::U32(t) => Some((t.0 as u64, t.1 as u64)),
|
||||||
|
CmpValues::U64(t) => Some((t.0 as u64, t.1 as u64)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A state metadata holding a list of values logged from comparisons
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct CmpValuesMetadata {
|
||||||
|
/// A `list` of values.
|
||||||
|
pub list: Vec<CmpValues>,
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::impl_serdeany!(CmpValuesMetadata);
|
||||||
|
|
||||||
|
impl AsSlice<CmpValues> for CmpValuesMetadata {
|
||||||
|
/// Convert to a slice
|
||||||
|
#[must_use]
|
||||||
|
fn as_slice(&self) -> &[CmpValues] {
|
||||||
|
self.list.as_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmpValuesMetadata {
|
||||||
|
/// Creates a new [`struct@CmpValuesMetadata`]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { list: vec![] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A [`CmpMap`] traces comparisons during the current execution
|
/// A [`CmpMap`] traces comparisons during the current execution
|
||||||
pub trait CmpMap: Serialize + DeserializeOwned {
|
pub trait CmpMap: Serialize + DeserializeOwned {
|
||||||
/// Get the number of cmps
|
/// Get the number of cmps
|
||||||
@ -49,6 +97,64 @@ where
|
|||||||
fn map(&self) -> &CM;
|
fn map(&self) -> &CM;
|
||||||
|
|
||||||
fn map_mut(&mut self) -> &mut CM;
|
fn map_mut(&mut self) -> &mut CM;
|
||||||
|
|
||||||
|
fn add_cmpvalues_meta<S>(&mut self, state: &mut S)
|
||||||
|
where
|
||||||
|
S: HasMetadata,
|
||||||
|
{
|
||||||
|
if state.metadata().get::<CmpValuesMetadata>().is_none() {
|
||||||
|
state.add_metadata(CmpValuesMetadata::new());
|
||||||
|
}
|
||||||
|
let meta = state.metadata_mut().get_mut::<CmpValuesMetadata>().unwrap();
|
||||||
|
meta.list.clear();
|
||||||
|
let count = self.usable_count();
|
||||||
|
for i in 0..count {
|
||||||
|
let execs = self.map().usable_executions_for(i);
|
||||||
|
if execs > 0 {
|
||||||
|
// Recongize loops and discard if needed
|
||||||
|
if execs > 4 {
|
||||||
|
let mut increasing_v0 = 0;
|
||||||
|
let mut increasing_v1 = 0;
|
||||||
|
let mut decreasing_v0 = 0;
|
||||||
|
let mut decreasing_v1 = 0;
|
||||||
|
|
||||||
|
let mut last: Option<CmpValues> = None;
|
||||||
|
for j in 0..execs {
|
||||||
|
let val = self.map().values_of(i, j);
|
||||||
|
if let Some(l) = last.and_then(|x| x.to_u64_tuple()) {
|
||||||
|
if let Some(v) = val.to_u64_tuple() {
|
||||||
|
if l.0.wrapping_add(1) == v.0 {
|
||||||
|
increasing_v0 += 1;
|
||||||
|
}
|
||||||
|
if l.1.wrapping_add(1) == v.1 {
|
||||||
|
increasing_v1 += 1;
|
||||||
|
}
|
||||||
|
if l.0.wrapping_sub(1) == v.0 {
|
||||||
|
decreasing_v0 += 1;
|
||||||
|
}
|
||||||
|
if l.1.wrapping_sub(1) == v.1 {
|
||||||
|
decreasing_v1 += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last = Some(val);
|
||||||
|
}
|
||||||
|
// We check for execs-2 because the logged execs may wrap and have something like
|
||||||
|
// 8 9 10 3 4 5 6 7
|
||||||
|
if increasing_v0 >= execs - 2
|
||||||
|
|| increasing_v1 >= execs - 2
|
||||||
|
|| decreasing_v0 >= execs - 2
|
||||||
|
|| decreasing_v1 >= execs - 2
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for j in 0..execs {
|
||||||
|
meta.list.push(self.map().values_of(i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A standard [`CmpObserver`] observer
|
/// A standard [`CmpObserver`] observer
|
||||||
|
@ -5,6 +5,7 @@ use libafl::{
|
|||||||
bolts::{ownedref::OwnedRefMut, tuples::Named},
|
bolts::{ownedref::OwnedRefMut, tuples::Named},
|
||||||
executors::HasExecHooks,
|
executors::HasExecHooks,
|
||||||
observers::{CmpMap, CmpObserver, CmpValues, Observer},
|
observers::{CmpMap, CmpObserver, CmpValues, Observer},
|
||||||
|
state::HasMetadata,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -148,6 +149,7 @@ pub use libafl_cmplog_enabled as CMPLOG_ENABLED;
|
|||||||
pub struct CmpLogObserver<'a> {
|
pub struct CmpLogObserver<'a> {
|
||||||
map: OwnedRefMut<'a, CmpLogMap>,
|
map: OwnedRefMut<'a, CmpLogMap>,
|
||||||
size: Option<OwnedRefMut<'a, usize>>,
|
size: Option<OwnedRefMut<'a, usize>>,
|
||||||
|
add_meta: bool,
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +173,10 @@ impl<'a> CmpObserver<CmpLogMap> for CmpLogObserver<'a> {
|
|||||||
|
|
||||||
impl<'a> Observer for CmpLogObserver<'a> {}
|
impl<'a> Observer for CmpLogObserver<'a> {}
|
||||||
|
|
||||||
impl<'a, EM, I, S, Z> HasExecHooks<EM, I, S, Z> for CmpLogObserver<'a> {
|
impl<'a, EM, I, S, Z> HasExecHooks<EM, I, S, Z> for CmpLogObserver<'a>
|
||||||
|
where
|
||||||
|
S: HasMetadata,
|
||||||
|
{
|
||||||
fn pre_exec(
|
fn pre_exec(
|
||||||
&mut self,
|
&mut self,
|
||||||
_fuzzer: &mut Z,
|
_fuzzer: &mut Z,
|
||||||
@ -189,13 +194,17 @@ impl<'a, EM, I, S, Z> HasExecHooks<EM, I, S, Z> for CmpLogObserver<'a> {
|
|||||||
fn post_exec(
|
fn post_exec(
|
||||||
&mut self,
|
&mut self,
|
||||||
_fuzzer: &mut Z,
|
_fuzzer: &mut Z,
|
||||||
_state: &mut S,
|
state: &mut S,
|
||||||
_mgr: &mut EM,
|
_mgr: &mut EM,
|
||||||
_input: &I,
|
_input: &I,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
CMPLOG_ENABLED = 0;
|
CMPLOG_ENABLED = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.add_meta {
|
||||||
|
self.add_cmpvalues_meta(state);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,11 +218,12 @@ impl<'a> Named for CmpLogObserver<'a> {
|
|||||||
impl<'a> CmpLogObserver<'a> {
|
impl<'a> CmpLogObserver<'a> {
|
||||||
/// Creates a new [`CmpLogObserver`] with the given name.
|
/// Creates a new [`CmpLogObserver`] with the given name.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(name: &'static str, map: &'a mut CmpLogMap) -> Self {
|
pub fn new(name: &'static str, map: &'a mut CmpLogMap, add_meta: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
size: None,
|
size: None,
|
||||||
|
|
||||||
|
add_meta,
|
||||||
map: OwnedRefMut::Ref(map),
|
map: OwnedRefMut::Ref(map),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user