1
1
//! Stage to compute and report AFL++ stats
2
2
use alloc:: { borrow:: Cow , string:: String , vec:: Vec } ;
3
- use core:: { fmt:: Display , marker:: PhantomData , time:: Duration } ;
3
+ use core:: {
4
+ fmt:: { Debug , Display } ,
5
+ marker:: PhantomData ,
6
+ time:: Duration ,
7
+ } ;
4
8
use std:: {
5
9
fs:: { File , OpenOptions } ,
6
10
io:: { BufRead , BufReader , Write } ,
@@ -14,7 +18,7 @@ use libafl_bolts::{
14
18
Named ,
15
19
core_affinity:: CoreId ,
16
20
current_time,
17
- tuples:: { Handle , Handled , MatchNameRef } ,
21
+ tuples:: { Handle , Handled , MatchName } ,
18
22
} ;
19
23
use serde:: { Deserialize , Serialize } ;
20
24
@@ -25,6 +29,7 @@ use crate::{
25
29
corpus:: { Corpus , HasCurrentCorpusId , SchedulerTestcaseMetadata , Testcase } ,
26
30
events:: { Event , EventFirer , EventWithStats } ,
27
31
executors:: HasObservers ,
32
+ feedbacks:: MapFeedbackMetadata ,
28
33
monitors:: stats:: { AggregatorOps , UserStats , UserStatsValue } ,
29
34
mutators:: Tokens ,
30
35
observers:: MapObserver ,
@@ -73,8 +78,9 @@ libafl_bolts::impl_serdeany!(FuzzTime);
73
78
/// The [`AflStatsStage`] is a Stage that calculates and writes
74
79
/// AFL++'s `fuzzer_stats` and `plot_data` information.
75
80
#[ derive( Debug , Clone ) ]
76
- pub struct AflStatsStage < C , E , EM , I , O , S , Z > {
81
+ pub struct AflStatsStage < C , I , O > {
77
82
map_observer_handle : Handle < C > ,
83
+ map_name : Cow < ' static , str > ,
78
84
stats_file_path : Option < PathBuf > ,
79
85
plot_file_path : Option < PathBuf > ,
80
86
start_time : u64 ,
@@ -112,7 +118,7 @@ pub struct AflStatsStage<C, E, EM, I, O, S, Z> {
112
118
autotokens_enabled : bool ,
113
119
/// The core we are bound to
114
120
core_id : CoreId ,
115
- phantom_data : PhantomData < ( E , EM , I , O , S , Z ) > ,
121
+ phantom : PhantomData < ( I , O ) > ,
116
122
}
117
123
118
124
/// AFL++'s `fuzzer_stats`
@@ -190,9 +196,9 @@ pub struct AflFuzzerStats<'a> {
190
196
/// TODO
191
197
cpu_affinity : usize ,
192
198
/// how many edges have been found
193
- edges_found : u64 ,
199
+ edges_found : usize ,
194
200
/// Size of our edges map
195
- total_edges : u64 ,
201
+ total_edges : usize ,
196
202
/// how many edges are non-deterministic
197
203
var_byte_count : usize ,
198
204
/// TODO:
@@ -224,20 +230,21 @@ pub struct AFLPlotData<'a> {
224
230
pending_total : & ' a usize ,
225
231
pending_favs : & ' a usize ,
226
232
/// Note: renamed `map_size` -> `total_edges` for consistency with `fuzzer_stats`
227
- total_edges : & ' a u64 ,
233
+ total_edges : & ' a usize ,
228
234
saved_crashes : & ' a u64 ,
229
235
saved_hangs : & ' a u64 ,
230
236
max_depth : & ' a u64 ,
231
237
execs_per_sec : & ' a u64 ,
232
238
/// Note: renamed `total_execs` -> `execs_done` for consistency with `fuzzer_stats`
233
239
execs_done : & ' a u64 ,
234
- edges_found : & ' a u64 ,
240
+ edges_found : & ' a usize ,
235
241
}
236
242
237
- impl < C , E , EM , I , O , S , Z > Stage < E , EM , S , Z > for AflStatsStage < C , E , EM , I , O , S , Z >
243
+ impl < C , E , EM , I , O , S , Z > Stage < E , EM , S , Z > for AflStatsStage < C , I , O >
238
244
where
239
245
C : AsRef < O > + Named ,
240
246
E : HasObservers ,
247
+ <E as HasObservers >:: Observers : MatchName ,
241
248
EM : EventFirer < I , S > ,
242
249
Z : HasScheduler < I , S > ,
243
250
S : HasImported
@@ -248,8 +255,8 @@ where
248
255
+ HasNamedMetadata
249
256
+ Stoppable
250
257
+ HasCurrentCorpusId ,
251
- E :: Observers : MatchNameRef ,
252
258
O : MapObserver ,
259
+ for < ' de > <O as MapObserver >:: Entry : Serialize + Deserialize < ' de > + ' static + Debug ,
253
260
C : AsRef < O > + Named ,
254
261
Z :: Scheduler : HasQueueCycles ,
255
262
{
@@ -299,13 +306,16 @@ where
299
306
self . maybe_update_cycles ( queue_cycles) ;
300
307
self . maybe_update_cycles_wo_finds ( queue_cycles) ;
301
308
309
+ let map_feedback = state
310
+ . named_metadata_map ( )
311
+ . get :: < MapFeedbackMetadata < <O as MapObserver >:: Entry > > ( & self . map_name )
312
+ . unwrap ( ) ;
313
+
314
+ let filled_entries_in_map = map_feedback. num_covered_map_indexes ;
302
315
let observers = executor. observers ( ) ;
303
- let map_observer = observers
304
- . get ( & self . map_observer_handle )
305
- . ok_or_else ( || Error :: key_not_found ( "invariant: MapObserver not found" . to_string ( ) ) ) ?
306
- . as_ref ( ) ;
307
- let filled_entries_in_map = map_observer. count_bytes ( ) ;
308
- let map_size = map_observer. usable_count ( ) ;
316
+ let map = observers[ & self . map_observer_handle ] . as_ref ( ) ;
317
+ let map_size = map. usable_count ( ) ;
318
+
309
319
// Since we do not calibrate when using `QueueScheduler`; we cannot calculate unstable entries.
310
320
let unstable_entries_in_map = state
311
321
. metadata_map ( )
@@ -370,7 +380,7 @@ where
370
380
#[ cfg( not( unix) ) ]
371
381
peak_rss_mb : 0 , // TODO for Windows
372
382
cpu_affinity : self . core_id . 0 ,
373
- total_edges : map_size as u64 ,
383
+ total_edges : map_size,
374
384
edges_found : filled_entries_in_map,
375
385
var_byte_count : unstable_entries_in_map,
376
386
havoc_expansion : 0 , // TODO
@@ -435,7 +445,7 @@ where
435
445
}
436
446
}
437
447
438
- impl < C , E , EM , I , O , S , Z > Restartable < S > for AflStatsStage < C , E , EM , I , O , S , Z > {
448
+ impl < C , I , O , S > Restartable < S > for AflStatsStage < C , I , O > {
439
449
fn should_restart ( & mut self , _state : & mut S ) -> Result < bool , Error > {
440
450
Ok ( true )
441
451
}
@@ -445,17 +455,13 @@ impl<C, E, EM, I, O, S, Z> Restartable<S> for AflStatsStage<C, E, EM, I, O, S, Z
445
455
}
446
456
}
447
457
448
- impl < C , E , EM , I , O , S , Z > AflStatsStage < C , E , EM , I , O , S , Z >
458
+ impl < C , I , O > AflStatsStage < C , I , O >
449
459
where
450
- E : HasObservers ,
451
- EM : EventFirer < I , S > ,
452
- S : HasImported + HasMetadata + HasExecutions ,
453
- C : AsRef < O > + Named ,
454
- O : MapObserver ,
460
+ C : Named ,
455
461
{
456
462
/// Builder for `AflStatsStage`
457
463
#[ must_use]
458
- pub fn builder ( ) -> AflStatsStageBuilder < C , E , EM , I , O , S , Z > {
464
+ pub fn builder ( ) -> AflStatsStageBuilder < C , I , O > {
459
465
AflStatsStageBuilder :: new ( )
460
466
}
461
467
@@ -514,7 +520,10 @@ where
514
520
}
515
521
516
522
#[ cfg( feature = "track_hit_feedbacks" ) ]
517
- fn maybe_update_last_crash ( & mut self , testcase : & Testcase < I > , state : & S ) {
523
+ fn maybe_update_last_crash < S > ( & mut self , testcase : & Testcase < I > , state : & S )
524
+ where
525
+ S : HasExecutions ,
526
+ {
518
527
#[ cfg( feature = "track_hit_feedbacks" ) ]
519
528
if testcase
520
529
. hit_objectives ( )
@@ -526,7 +535,10 @@ where
526
535
}
527
536
528
537
#[ cfg( feature = "track_hit_feedbacks" ) ]
529
- fn maybe_update_last_hang ( & mut self , testcase : & Testcase < I > , state : & S ) {
538
+ fn maybe_update_last_hang < S > ( & mut self , testcase : & Testcase < I > , state : & S )
539
+ where
540
+ S : HasExecutions ,
541
+ {
530
542
if testcase
531
543
. hit_objectives ( )
532
544
. contains ( & Cow :: Borrowed ( TIMEOUT_FEEDBACK_NAME ) )
@@ -558,7 +570,7 @@ where
558
570
559
571
#[ expect( clippy:: cast_precision_loss) ]
560
572
#[ expect( clippy:: unused_self) ]
561
- fn calculate_stability ( & self , unstable_entries : usize , filled_entries : u64 ) -> f64 {
573
+ fn calculate_stability ( & self , unstable_entries : usize , filled_entries : usize ) -> f64 {
562
574
( ( filled_entries as f64 - unstable_entries as f64 ) / filled_entries as f64 ) * 100.0
563
575
}
564
576
}
@@ -648,28 +660,25 @@ pub fn get_run_cmdline() -> Cow<'static, str> {
648
660
649
661
/// The Builder for `AflStatsStage`
650
662
#[ derive( Debug ) ]
651
- pub struct AflStatsStageBuilder < C , E , EM , I , O , S , Z > {
663
+ pub struct AflStatsStageBuilder < C , I , O > {
652
664
stats_file_path : Option < PathBuf > ,
653
665
plot_file_path : Option < PathBuf > ,
654
666
core_id : Option < CoreId > ,
655
667
map_observer_handle : Option < Handle < C > > ,
668
+ map_name : Option < String > ,
656
669
uses_autotokens : bool ,
657
670
report_interval : Duration ,
658
671
dict_count : usize ,
659
672
exec_timeout : u64 ,
660
673
banner : String ,
661
674
version : String ,
662
675
target_mode : String ,
663
- phantom_data : PhantomData < ( E , EM , I , O , S , Z ) > ,
676
+ phantom_data : PhantomData < ( I , O ) > ,
664
677
}
665
678
666
- impl < C , E , EM , I , O , S , Z > AflStatsStageBuilder < C , E , EM , I , O , S , Z >
679
+ impl < C , I , O > AflStatsStageBuilder < C , I , O >
667
680
where
668
- C : AsRef < O > + Named ,
669
- E : HasObservers ,
670
- EM : EventFirer < I , S > ,
671
- O : MapObserver ,
672
- S : HasImported + HasMetadata + HasExecutions ,
681
+ C : Named ,
673
682
{
674
683
fn new ( ) -> Self {
675
684
Self {
@@ -678,6 +687,7 @@ where
678
687
plot_file_path : None ,
679
688
core_id : None ,
680
689
map_observer_handle : None ,
690
+ map_name : None ,
681
691
uses_autotokens : false ,
682
692
dict_count : 0 ,
683
693
exec_timeout : 0 ,
@@ -718,6 +728,17 @@ where
718
728
self . map_observer_handle = Some ( map_observer. handle ( ) ) ;
719
729
self
720
730
}
731
+
732
+ /// map name to check the filled count
733
+ #[ must_use]
734
+ pub fn map_name < F > ( mut self , map_feedback : & F ) -> Self
735
+ where
736
+ F : Named ,
737
+ {
738
+ self . map_name = Some ( map_feedback. name ( ) . to_string ( ) ) ;
739
+ self
740
+ }
741
+
721
742
/// If we use autotokens provided by the target
722
743
#[ must_use]
723
744
pub fn uses_autotokens ( mut self , uses : bool ) -> Self {
@@ -782,10 +803,13 @@ where
782
803
/// No `MapObserver` supplied to the builder
783
804
/// No `stats_file_path` provieded
784
805
#[ allow( clippy:: type_complexity) ]
785
- pub fn build ( self ) -> Result < AflStatsStage < C , E , EM , I , O , S , Z > , Error > {
806
+ pub fn build ( self ) -> Result < AflStatsStage < C , I , O > , Error > {
786
807
if self . map_observer_handle . is_none ( ) {
787
808
return Err ( Error :: illegal_argument ( "Must set `map_observer`" ) ) ;
788
809
}
810
+ let Some ( map_name) = self . map_name else {
811
+ return Err ( Error :: illegal_argument ( "Must set `map_name`" ) ) ;
812
+ } ;
789
813
if let Some ( ref plot_file) = self . plot_file_path {
790
814
Self :: create_plot_data_file ( plot_file) ?;
791
815
}
@@ -794,6 +818,7 @@ where
794
818
}
795
819
Ok ( AflStatsStage {
796
820
stats_file_path : self . stats_file_path ,
821
+ map_name : Cow :: Owned ( map_name) ,
797
822
plot_file_path : self . plot_file_path ,
798
823
map_observer_handle : self . map_observer_handle . unwrap ( ) ,
799
824
start_time : current_time ( ) . as_secs ( ) ,
@@ -820,7 +845,7 @@ where
820
845
dict_count : self . dict_count ,
821
846
core_id : self . core_id . unwrap_or ( CoreId ( 0 ) ) ,
822
847
autotokens_enabled : self . uses_autotokens ,
823
- phantom_data : PhantomData ,
848
+ phantom : PhantomData ,
824
849
} )
825
850
}
826
851
}
0 commit comments