@@ -42,6 +42,7 @@ use libafl_targets::AFLppCmpLogMap;
4242use magic:: MagicNumber ;
4343use num_traits:: FromPrimitive as _;
4444use serde:: { Deserialize , Serialize } ;
45+ use serde_json:: to_writer;
4546use simics:: {
4647 break_simulation, class, debug, error, free_attribute, get_class, get_interface,
4748 get_processor_number, info, lookup_file, object_clock, run_command, run_python, simics_init,
@@ -66,15 +67,19 @@ use std::{
6667 alloc:: { alloc_zeroed, Layout } ,
6768 cell:: OnceCell ,
6869 collections:: { hash_map:: Entry , BTreeSet , HashMap , HashSet } ,
69- fs:: File ,
70+ fs:: { create_dir_all, File } ,
71+ hash:: { DefaultHasher , Hash , Hasher } ,
7072 path:: PathBuf ,
7173 ptr:: null_mut,
7274 str:: FromStr ,
7375 sync:: mpsc:: { Receiver , Sender } ,
7476 thread:: JoinHandle ,
7577 time:: SystemTime ,
7678} ;
77- use tracer:: tsffs:: { on_instruction_after, on_instruction_before} ;
79+ use tracer:: {
80+ tsffs:: { on_instruction_after, on_instruction_before} ,
81+ ExecutionTrace ,
82+ } ;
7883use typed_builder:: TypedBuilder ;
7984use versions:: { Requirement , Versioning } ;
8085
@@ -375,6 +380,25 @@ pub(crate) struct Tsffs {
375380 #[ class( attribute( optional, default = true ) ) ]
376381 /// Whether to quit on iteration limit
377382 pub quit_on_iteration_limit : bool ,
383+ #[ class( attribute( optional, default = false ) ) ]
384+ /// Whether to save execution traces of test cases which result in a solution
385+ pub save_solution_execution_traces : bool ,
386+ #[ class( attribute( optional, default = false ) ) ]
387+ /// Whether to save execution traces of test cases which result in an interesting input
388+ pub save_interesting_execution_traces : bool ,
389+ #[ class( attribute( optional, default = false ) ) ]
390+ /// Whether to save all execution traces. This will consume a very large amount of resources
391+ /// and should only be used for debugging and testing purposes.
392+ pub save_all_execution_traces : bool ,
393+ #[ class( attribute( optional, default = lookup_file( "%simics%" ) ?. join( "execution-traces" ) ) ) ]
394+ #[ attr_value( fallible) ]
395+ /// The directory to save execution traces to, if any are set to be saved. This
396+ /// directory may be a SIMICS relative path prefixed with "%simics%". If not
397+ /// provided, "%simics%/execution-traces" will be used by default.
398+ pub execution_trace_directory : PathBuf ,
399+ #[ class( attribute( optional, default = false ) ) ]
400+ /// Whether execution traces should include just PC (vs instruction text and bytes)
401+ pub execution_trace_pc_only : bool ,
378402
379403 #[ attr_value( skip) ]
380404 /// Handle for the core simulation stopped hap
@@ -440,8 +464,11 @@ pub(crate) struct Tsffs {
440464 edges_seen : HashSet < u64 > ,
441465 #[ attr_value( skip) ]
442466 /// A map of the new edges to their AFL indices seen since the last time the fuzzer
443- /// provided an update
467+ /// provided an update. This is not cleared every execution.
444468 edges_seen_since_last : HashMap < u64 , u64 > ,
469+ #[ attr_value( skip) ]
470+ /// The set of PCs comprising the current execution trace. This is cleared every execution.
471+ execution_trace : ExecutionTrace ,
445472
446473 #[ attr_value( skip) ]
447474 /// The name of the fuzz snapshot, if saved
@@ -865,6 +892,29 @@ impl Tsffs {
865892 }
866893 Ok ( ( ) )
867894 }
895+
896+ /// Save the current execution trace to a file
897+ pub fn save_execution_trace ( & mut self ) -> Result < ( ) > {
898+ let mut hasher = DefaultHasher :: new ( ) ;
899+ self . execution_trace . hash ( & mut hasher) ;
900+ let hash = hasher. finish ( ) ;
901+
902+ if !self . execution_trace_directory . is_dir ( ) {
903+ create_dir_all ( & self . execution_trace_directory ) ?;
904+ }
905+
906+ let trace_path = self
907+ . execution_trace_directory
908+ . join ( format ! ( "{:x}.json" , hash) ) ;
909+
910+ if !trace_path. exists ( ) {
911+ let trace_file = File :: create ( trace_path) ?;
912+
913+ to_writer ( trace_file, & self . execution_trace ) ?;
914+ }
915+
916+ Ok ( ( ) )
917+ }
868918}
869919
870920#[ simics_init( name = "tsffs" , class = "tsffs" ) ]
0 commit comments