@@ -506,8 +506,11 @@ pub struct Mollusk {
506506 #[ cfg( feature = "invocation-inspect-callback" ) ]
507507 pub invocation_inspect_callback : Box < dyn InvocationInspectCallback > ,
508508
509- #[ cfg( feature = "register-tracing" ) ]
510- pub enable_register_tracing : bool ,
509+ /// Dictates whether or not register tracing was enabled.
510+ /// Provided as input to the invocation inspect callback for potential
511+ /// register trace consumption.
512+ #[ cfg( feature = "invocation-inspect-callback" ) ]
513+ enable_register_tracing : bool ,
511514
512515 /// This field stores the slot only to be able to convert to and from FD
513516 /// fixtures and a Mollusk instance, since FD fixtures have a
@@ -528,7 +531,7 @@ pub trait InvocationInspectCallback {
528531 invoke_context : & InvokeContext ,
529532 ) ;
530533
531- fn after_invocation ( & self , invoke_context : & InvokeContext ) ;
534+ fn after_invocation ( & self , invoke_context : & InvokeContext , register_tracing_enabled : bool ) ;
532535}
533536
534537#[ cfg( feature = "invocation-inspect-callback" ) ]
@@ -539,71 +542,12 @@ impl InvocationInspectCallback for EmptyInvocationInspectCallback {
539542 fn before_invocation ( & self , _: & Pubkey , _: & [ u8 ] , _: & [ InstructionAccount ] , _: & InvokeContext ) {
540543 }
541544
542- fn after_invocation ( & self , _: & InvokeContext ) { }
545+ fn after_invocation ( & self , _: & InvokeContext , _register_tracing_enabled : bool ) { }
543546}
544547
545548impl Default for Mollusk {
546549 fn default ( ) -> Self {
547- #[ rustfmt:: skip]
548- solana_logger:: setup_with_default (
549- "solana_rbpf::vm=debug,\
550- solana_runtime::message_processor=debug,\
551- solana_runtime::system_instruction_processor=trace",
552- ) ;
553- let compute_budget = ComputeBudget :: new_with_defaults ( true , true ) ;
554-
555- #[ cfg( feature = "fuzz" ) ]
556- let feature_set = {
557- // Omit "test features" (they have the same u64 ID).
558- let mut fs = FeatureSet :: all_enabled ( ) ;
559- fs. active_mut ( )
560- . remove ( & agave_feature_set:: disable_sbpf_v0_execution:: id ( ) ) ;
561- fs. active_mut ( )
562- . remove ( & agave_feature_set:: reenable_sbpf_v0_execution:: id ( ) ) ;
563- fs
564- } ;
565- #[ cfg( not( feature = "fuzz" ) ) ]
566- let feature_set = FeatureSet :: all_enabled ( ) ;
567-
568- let program_cache = ProgramCache :: new (
569- & feature_set,
570- & compute_budget,
571- #[ cfg( feature = "register-tracing" ) ]
572- {
573- true
574- } ,
575- #[ cfg( not( feature = "register-tracing" ) ) ]
576- {
577- false
578- } ,
579- ) ;
580-
581- Self {
582- config : Config :: default ( ) ,
583- compute_budget,
584- epoch_stake : EpochStake :: default ( ) ,
585- feature_set,
586- logger : None ,
587- program_cache,
588- sysvars : Sysvars :: default ( ) ,
589-
590- // Register tracing feature requires `invocation-inspect-callback`.
591- // Use tracing callback when both are active.
592- #[ cfg( all( feature = "invocation-inspect-callback" , feature = "register-tracing" ) ) ]
593- invocation_inspect_callback : Box :: new ( DefaultRegisterTracingCallback :: default ( ) ) ,
594- // Use empty callback when only `invocation-inspect-callback` is active.
595- #[ cfg( all(
596- feature = "invocation-inspect-callback" ,
597- not( feature = "register-tracing" ) ,
598- ) ) ]
599- invocation_inspect_callback : Box :: new ( EmptyInvocationInspectCallback { } ) ,
600-
601- #[ cfg( feature = "register-tracing" ) ]
602- enable_register_tracing : true ,
603-
604- #[ cfg( feature = "fuzz-fd" ) ]
605- slot : 0 ,
606- }
550+ Self :: new_inner ( /* enable_register_tracing */ false )
607551 }
608552}
609553
@@ -669,6 +613,61 @@ impl InvokeContextCallback for MolluskInvokeContextCallback<'_> {
669613}
670614
671615impl Mollusk {
616+ fn new_inner ( #[ allow( unused) ] enable_register_tracing : bool ) -> Self {
617+ #[ rustfmt:: skip]
618+ solana_logger:: setup_with_default (
619+ "solana_rbpf::vm=debug,\
620+ solana_runtime::message_processor=debug,\
621+ solana_runtime::system_instruction_processor=trace",
622+ ) ;
623+ let compute_budget = ComputeBudget :: new_with_defaults ( true , true ) ;
624+
625+ #[ cfg( feature = "fuzz" ) ]
626+ let feature_set = {
627+ // Omit "test features" (they have the same u64 ID).
628+ let mut fs = FeatureSet :: all_enabled ( ) ;
629+ fs. active_mut ( )
630+ . remove ( & agave_feature_set:: disable_sbpf_v0_execution:: id ( ) ) ;
631+ fs. active_mut ( )
632+ . remove ( & agave_feature_set:: reenable_sbpf_v0_execution:: id ( ) ) ;
633+ fs
634+ } ;
635+ #[ cfg( not( feature = "fuzz" ) ) ]
636+ let feature_set = FeatureSet :: all_enabled ( ) ;
637+
638+ let program_cache =
639+ ProgramCache :: new ( & feature_set, & compute_budget, enable_register_tracing) ;
640+
641+ #[ allow( unused_mut) ]
642+ let mut me = Self {
643+ config : Config :: default ( ) ,
644+ compute_budget,
645+ epoch_stake : EpochStake :: default ( ) ,
646+ feature_set,
647+ logger : None ,
648+ program_cache,
649+ sysvars : Sysvars :: default ( ) ,
650+
651+ #[ cfg( feature = "invocation-inspect-callback" ) ]
652+ invocation_inspect_callback : Box :: new ( EmptyInvocationInspectCallback { } ) ,
653+
654+ #[ cfg( feature = "invocation-inspect-callback" ) ]
655+ enable_register_tracing,
656+
657+ #[ cfg( feature = "fuzz-fd" ) ]
658+ slot : 0 ,
659+ } ;
660+
661+ #[ cfg( feature = "register-tracing" ) ]
662+ if enable_register_tracing {
663+ // Have a default register tracing callback if register tracing is
664+ // enabled.
665+ me. invocation_inspect_callback = Box :: new ( DefaultRegisterTracingCallback :: default ( ) ) ;
666+ }
667+
668+ me
669+ }
670+
672671 /// Create a new Mollusk instance containing the provided program.
673672 ///
674673 /// Attempts to load the program's ELF file from the default search paths.
@@ -689,6 +688,28 @@ impl Mollusk {
689688 mollusk
690689 }
691690
691+ /// Create a new Mollusk instance with configurable debugging features.
692+ ///
693+ /// This constructor allows enabling low-level VM debugging capabilities,
694+ /// such as register tracing, which are baked into program executables at
695+ /// load time and cannot be changed afterwards.
696+ ///
697+ /// When `enable_register_tracing` is `true`:
698+ /// - Programs are compiled with register tracing support
699+ /// - A default [`DefaultRegisterTracingCallback`] is installed
700+ /// - Trace data is written to `SBF_TRACE_DIR` (or `target/sbf/trace` by
701+ /// default)
702+ #[ cfg( feature = "register-tracing" ) ]
703+ pub fn new_debuggable (
704+ program_id : & Pubkey ,
705+ program_name : & str ,
706+ enable_register_tracing : bool ,
707+ ) -> Self {
708+ let mut mollusk = Self :: new_inner ( enable_register_tracing) ;
709+ mollusk. add_program ( program_id, program_name, & DEFAULT_LOADER_KEY ) ;
710+ mollusk
711+ }
712+
692713 /// Add a program to the test environment.
693714 ///
694715 /// If you intend to CPI to a program, this is likely what you want to use.
@@ -822,7 +843,7 @@ impl Mollusk {
822843
823844 #[ cfg( feature = "invocation-inspect-callback" ) ]
824845 self . invocation_inspect_callback
825- . after_invocation ( & invoke_context) ;
846+ . after_invocation ( & invoke_context, self . enable_register_tracing ) ;
826847
827848 result
828849 } ;
0 commit comments