From 52b31d787b42e21b8c9ac74c4d5e4384712c04c5 Mon Sep 17 00:00:00 2001
From: Erwan Jahier <erwan.jahier@univ-grenoble-alpes.fr>
Date: Thu, 19 Jan 2023 16:31:25 +0100
Subject: [PATCH] feat: add a fault injection button in the GUI

---
 lib/sasa/sasaRun.ml               | 108 ++++++++++++++++--------------
 lib/sasacore/round.ml             |  41 +++++++-----
 lib/sasacore/simuState.ml         |  28 +++++++-
 lib/sasacore/simuState.mli        |  16 ++++-
 salut/test/Makefile.inc           |   8 ++-
 src/sasaMain.ml                   |  20 ++----
 test/Makefile.inc                 |  12 ++--
 test/dune2copy                    |   2 +-
 tools/rdbg4sasa/gtkgui.ml         |  82 ++++++++++++++++-------
 tools/rdbg4sasa/sasa-rdbg-cmds.ml |  16 +++++
 10 files changed, 214 insertions(+), 119 deletions(-)

diff --git a/lib/sasa/sasaRun.ml b/lib/sasa/sasaRun.ml
index c57e6504..f7b5c128 100644
--- a/lib/sasa/sasaRun.ml
+++ b/lib/sasa/sasaRun.ml
@@ -1,12 +1,12 @@
 open Sasacore
 (* open SasArg *)
- 
+
 let (get_action_value : (string * Data.v) list -> string -> string -> bool) =
   fun sl pid a ->
     let vn = Printf.sprintf "%s_%s" pid (StringOf.action a) in
     match List.assoc_opt vn sl with
     | Some (Data.B b) -> b
-    | None 
+    | None
     | Some _ ->
       Printf.printf "Error: can not find %s in [%s]\n" vn
         (String.concat "," (fst (List.split sl)));
@@ -35,9 +35,9 @@ let (get_sl_out: bool -> 'v Process.t list -> bool list list -> RdbgPlugin.sl) =
   fun enab pl ll ->
     let str = if enab then "Enab_" else "" in
     List.flatten (
-      (* assert (List.length pl = List.length  ll); *)
+      assert (List.length pl = List.length  ll);
       List.map2 (fun p enab_l ->
-          (* assert (List.length p.actions = List.length enab_l); *)
+          assert (List.length p.actions = List.length enab_l);
           List.map2 (fun a enab ->
               Printf.sprintf "%s%s_%s" str p.pid (StringOf.action a), Data.B enab)
             p.actions enab_l)
@@ -52,12 +52,12 @@ let (compute_potentiel: 'v SimuState.t -> RdbgPlugin.sl) =
   if Register.get_potential () = None then []
   else
     [("potential", Data.F (SimuState.compute_potentiel st))]
-    
+
 let (compute_legitimate: bool -> 'v SimuState.t -> bool) =
   fun silent st ->
   silent ||
   match Register.get_legitimate () with
-  | None -> silent 
+  | None -> silent
   | Some f ->
     let pidl = List.map (fun p -> p.Process.pid) st.network in
     f pidl (SimuState.neigbors_of_pid st)
@@ -96,7 +96,7 @@ let (make_do: string array -> 'v SimuState.t -> RdbgPlugin.t) =
       | None  -> ( (* the first step *)
           (* 1: Get enable processes *)
           let pnall, enab_ll = Sasacore.SimuState.get_enable_processes st in
-          let sasa_nenv = from_sasa_env st in 
+          let sasa_nenv = from_sasa_env st in
           let pot_sl = compute_potentiel st in
           let silent = List.for_all (fun b -> not b) (List.flatten enab_ll) in
           let legit = compute_legitimate silent st in
@@ -105,7 +105,7 @@ let (make_do: string array -> 'v SimuState.t -> RdbgPlugin.t) =
           ("silent", Data.B silent)::("legitimate", Data.B legit)::
           ("round", Data.B Round.s.is_round)::("round_nb", Data.I Round.s.cpt)::pot_sl
           @ sasa_nenv @ (get_sl_out true pl enab_ll)
-      )
+        )
       | Some (pre_pnall, pre_enab_ll) ->
         (* let was_silent = List.for_all (fun b -> not b) (List.flatten pre_enab_ll) in *)
         (* if was_silent then failwith "Silent"; *)
@@ -126,10 +126,10 @@ let (make_do: string array -> 'v SimuState.t -> RdbgPlugin.t) =
         let legit = compute_legitimate silent nst in
         Round.update legit false activate_val enab_ll;
         pre_enable_processes_opt := Some(pnall, enab_ll);
-        sasa_config := st.config;    
+        sasa_config := st.config;
         ("silent", Data.B silent)::("legitimate", Data.B legit)::
         ("round", Data.B Round.s.is_round)::("round_nb", Data.I Round.s.cpt)::pot_sl @
-        sasa_nenv @ (get_sl_out true pl enab_ll) 
+        sasa_nenv @ (get_sl_out true pl enab_ll)
   in
   let (step_internal_daemon: RdbgPlugin.sl -> RdbgPlugin.sl) =
     fun sl_in ->
@@ -143,8 +143,8 @@ let (make_do: string array -> 'v SimuState.t -> RdbgPlugin.t) =
       if silent then (
         let enab = get_sl_out true pl enab_ll in
         let trigger = (* set all trig var to false *)
-          List.map (fun (n,v) -> String.sub n 5 ((String.length n) -5),v) enab 
-        in        
+          List.map (fun (n,v) -> String.sub n 5 ((String.length n) -5),v) enab
+        in
         ("silent", Data.B silent)::("legitimate", Data.B legit)::
         ("round", Data.B Round.s.is_round)::("round_nb", Data.I Round.s.cpt)::pot_sl@
         (from_sasa_env st) @ enab @ trigger
@@ -200,17 +200,15 @@ let (make_do: string array -> 'v SimuState.t -> RdbgPlugin.t) =
       flush stderr
   in
   let exhaustive_search_reset ()  =
-      match !exaustive_search_res with
-        | None -> ()
-        | Some _ ->
-          let log = open_out (st.sasarg.topo ^ ".log") in
-          ExhaustSearch.reset ();
-          let path = ExhaustSearch.f log (st.sasarg.daemon=ExhaustCentralSearch) st in
-          exaustive_search_res := Some path
-  in
-  let _exhaustive_search_reset ()  =
-    ()
+    match !exaustive_search_res with
+    | None -> ()
+    | Some _ ->
+      let log = open_out (st.sasarg.topo ^ ".log") in
+      ExhaustSearch.reset ();
+      let path = ExhaustSearch.f log (st.sasarg.daemon=ExhaustCentralSearch) st in
+      exaustive_search_res := Some path
   in
+  let _exhaustive_search_reset () = () in
   let step =
     match st.sasarg.daemon with
     | DaemonType.Custom -> step_custom
@@ -220,7 +218,7 @@ let (make_do: string array -> 'v SimuState.t -> RdbgPlugin.t) =
   in
   let ss_table = Hashtbl.create 10 in
   let step_dbg sl_in ctx cont =
-    let sl_out = step sl_in in 
+    let sl_out = step sl_in in
     { ctx with
       (* RdbgEvent.nb = 0; *)
       (* RdbgEvent.step = 0; (* we are actually in the middle of the first step! *) *)
@@ -242,44 +240,54 @@ let (make_do: string array -> 'v SimuState.t -> RdbgPlugin.t) =
   let (mems_in  : Data.subst list) = [] in
   let (mems_out : Data.subst list) = [] in
   let sasa_save_state i =
-        let prgs = Random.get_state () in
-        (* Printf.eprintf "Save state %i from sasa\n%!" i; *)
-        Hashtbl.replace ss_table i
-          (prgs, !sasa_config, !pre_enable_processes_opt, Round.get())
+    let prgs = Random.get_state () in
+    (* Printf.eprintf "Save state %i from sasa\n%!" i; *)
+    Hashtbl.replace ss_table i
+      (prgs, !sasa_config, !pre_enable_processes_opt, Round.get())
   in
-  let sasa_restore_state i = 
-    match Hashtbl.find_opt ss_table i with
-    | Some (prgs, e, pepo, r) ->
-      Random.set_state prgs;
-      (* Printf.eprintf "Restore state %i from sasa\n%!" i; *)
-      sasa_config := e; pre_enable_processes_opt := pepo;
-      Round.set r;
-    | None ->
-      Printf.eprintf "Cannot restore state %i from sasa\n" i;
-      flush stderr
+  let sasa_restore_state i =
+    if i = min_int then (* Is it a good idea to use this for fault injection? Not sure. *)
+      (match Register.get_fault () with
+       | None -> Printf.eprintf "Cannot inject faults: no fault function has been provided\n%!";
+       | Some f ->
+         Printf.eprintf "Perform a fault injection in sasa\n%!";
+         pre_enable_processes_opt := None;
+         sasa_config := SimuState.inject_fault_in_conf f st !sasa_config;
+         Round.reinit()
+      )
+    else
+      match Hashtbl.find_opt ss_table i with
+      | Some (prgs, e, pepo, r) ->
+        Random.set_state prgs;
+        (* Printf.eprintf "Restore state %i from sasa\n%!" i; *)
+        sasa_config := e; pre_enable_processes_opt := pepo;
+        Round.set r;
+      | None ->
+        Printf.eprintf "Cannot restore state %i from sasa\n" i;
+        flush stderr
   in
-  Round.reinit();
+  Round.reinit_mask();
   {
     id = prog_id;
     inputs = vntl_i;
     outputs= vntl_o;
     reset= (match st.sasarg.daemon with
-      | ExhaustCentralSearch | ExhaustSearch -> exhaustive_search_reset
-      | _ -> reset
-    );
+        | ExhaustCentralSearch | ExhaustSearch -> exhaustive_search_reset
+        | _ -> reset
+      );
     kill=(fun _ -> flush stdout; flush stderr);
     init_inputs=mems_in;
     init_outputs=mems_out;
-    step=step;     
+    step=step;
     step_dbg = step_dbg;
     save_state = (match st.sasarg.daemon with
-      | ExhaustCentralSearch | ExhaustSearch -> exhaustive_search_save_state
-      | _ -> sasa_save_state
-    );
+        | ExhaustCentralSearch | ExhaustSearch -> exhaustive_search_save_state
+        | _ -> sasa_save_state
+      );
     restore_state = (match st.sasarg.daemon with
-      | ExhaustCentralSearch | ExhaustSearch -> exhaustive_search_restore_state
-      | _ -> sasa_restore_state
-    );
+        | ExhaustCentralSearch | ExhaustSearch -> exhaustive_search_restore_state
+        | _ -> sasa_restore_state
+      );
   }
 
 
@@ -297,10 +305,10 @@ let (make: string array -> RdbgPlugin.t) =
       flush stdout;
       exit 2
 
-    | Assert_failure (file, line, col)  -> 
+    | Assert_failure (file, line, col)  ->
       prerr_string (
         "\nError: oops, sasa internal error\n\tFile \""^ file ^
         "\", line " ^ (string_of_int line) ^ ", column " ^
-        (string_of_int col) ^ "\n") ; 
+        (string_of_int col) ^ "\n") ;
       flush stderr;
       exit  2
diff --git a/lib/sasacore/round.ml b/lib/sasacore/round.ml
index 8d1a82d3..1c5c7193 100644
--- a/lib/sasacore/round.ml
+++ b/lib/sasacore/round.ml
@@ -1,4 +1,4 @@
-(* Time-stamp: <modified the 22/06/2022 (at 11:55) by Erwan Jahier> *)
+(* Time-stamp: <modified the 19/01/2023 (at 15:55) by Erwan Jahier> *)
 
 type t = {
   mutable first : bool;
@@ -19,6 +19,7 @@ let s:t = {
   pre_acti_ll= [];
 }
 
+
 let all_false ll = List.for_all (List.for_all (not)) ll
 
 let bool2str b = if b then "t" else "f"
@@ -42,7 +43,7 @@ let (update : bool -> bool -> bool list list -> bool list list -> unit) =
     s.cpt <- 0;
     s.round_mask <- enab_ll;
     s.pre_acti_ll <- curr_acti_ll
-  ) else (  
+  ) else (
     if s.cpt = 0 then (
       (*        Printf.printf "Round.update: the first round begins\n%!" ;  *)
       s.cpt <- 1
@@ -53,24 +54,27 @@ let (update : bool -> bool -> bool list list -> bool list list -> unit) =
       s.is_round <- false;
     )
     else
-      let new_mask = List.map2 (List.map2 (&&)) s.round_mask enab_ll in (* neutralize *)
-      let new_mask = List.map2 (List.map2 (fun m a -> m&&not a)) new_mask acti_ll in
-      s.pre_acti_ll <- curr_acti_ll;
-      let empty mask = all_false mask in
-      if empty new_mask then
-        (
-          if not legit then s.cpt <- s.cpt+1;
-          s.is_round <- true;
-          s.round_mask <- enab_ll
+      try (
+        let new_mask = List.map2 (List.map2 (&&)) s.round_mask enab_ll in (* neutralize *)
+        let new_mask = List.map2 (List.map2 (fun m a -> m&&not a)) new_mask acti_ll in
+        s.pre_acti_ll <- curr_acti_ll;
+        let empty mask = all_false mask in
+        if empty new_mask then
+          (
+            if not legit then s.cpt <- s.cpt+1;
+            s.is_round <- true;
+            s.round_mask <- enab_ll
+          )
+        else (
+          s.is_round <- false;
+          s.round_mask <- new_mask
         )
-      else (
-        s.is_round <- false;
-        s.round_mask <- new_mask
       )
+      with _ -> assert false
   )
 (*    ; debug_update "fin" legit *)
 
-  
+
 let (get : unit -> t_save) = fun () ->
   s.first, s.moves, s.cpt, s.round_mask, s.pre_acti_ll
 
@@ -81,10 +85,15 @@ let (set : t_save -> unit) = fun (first, moves, cpt, round_mask, pre_acti_ll) ->
  s.round_mask  <- round_mask;
  s.pre_acti_ll <- pre_acti_ll
 
-let reinit () = 
+let reinit () =
  s.first <- true;
  s.is_round <- false;
  s.moves <- 0;
  s.cpt <- 0;
  s.round_mask <- [];
  s.pre_acti_ll <- []
+
+let reinit_mask () =
+ s.is_round <- false;
+ s.round_mask <- [];
+ s.pre_acti_ll <- []
diff --git a/lib/sasacore/simuState.ml b/lib/sasacore/simuState.ml
index 4f843ad7..eaa88394 100644
--- a/lib/sasacore/simuState.ml
+++ b/lib/sasacore/simuState.ml
@@ -1,4 +1,4 @@
-(* Time-stamp: <modified the 17/01/2023 (at 16:37) by Erwan Jahier> *)
+(* Time-stamp: <modified the 19/01/2023 (at 11:08) by Erwan Jahier> *)
 
 open Register
 open Topology
@@ -409,3 +409,29 @@ let (legitimate: 'v t -> bool) = fun st ->
   | Some ulf ->
     let pidl = List.map (fun p -> p.Process.pid) st.network in
     ulf pidl  (neigbors_of_pid st)
+
+
+let injected_fault_nb = ref 0
+let get_injected_fault_nb () = !injected_fault_nb
+
+
+
+let (inject_fault_in_conf : (int -> string -> 'v -> 'v) -> 'v t -> 'v Conf.t -> 'v Conf.t) =
+  fun ff st c ->
+
+  let update_nodes e p =
+    let nl = StringMap.find p.Process.pid st.neighbors in
+    let pid = p.Process.pid in
+    let v = Conf.get e pid in
+    let v = ff (List.length nl) pid v in
+    incr injected_fault_nb;
+    Conf.set e pid v
+  in
+  let nc = List.fold_left update_nodes c st.network in
+  nc
+
+let (inject_fault : (int -> string -> 'v -> 'v) -> 'v t -> 'v t) =
+  fun ff st ->
+  let nc = inject_fault_in_conf ff st st.config in
+  let st = { st with config = nc } in
+  update_config nc st
diff --git a/lib/sasacore/simuState.mli b/lib/sasacore/simuState.mli
index 156738d9..1ed8aac3 100644
--- a/lib/sasacore/simuState.mli
+++ b/lib/sasacore/simuState.mli
@@ -1,4 +1,4 @@
-(* Time-stamp: <modified the 06/04/2022 (at 14:51) by Erwan Jahier> *)
+(* Time-stamp: <modified the 19/01/2023 (at 11:07) by Erwan Jahier> *)
 
 (** The module is used by
     - the main sasa simulation loop (in ../../src/sasaMain.ml)
@@ -7,7 +7,7 @@
 *)
 
 
-   
+
 (* type 'v t = SasArg.t * 'v layout * 'v Conf.t *)
 type 'v t = {
   config: 'v Conf.t;
@@ -21,7 +21,7 @@ val make : bool -> string array -> 'v t
 
 type 'v enable_processes =
   ('v Process.t * 'v Register.neighbor list * Register.action) list list * bool list list
-    
+
 val get_enable_processes: 'v t -> 'v enable_processes
 
 (** [update_config e c] updates c using e *)
@@ -29,6 +29,16 @@ val update_config: 'v Conf.t -> 'v t -> 'v t
 
 val update_env_with_init : 'v Conf.t -> 'v Process.t list -> 'v Conf.t
 
+(** inject a fault in a configuration using the registered (user) fault function
+
+nb : inject_fault_in_conf uses only the static part of SimuState.t
+*)
+val inject_fault : (int -> string -> 'v -> 'v) -> 'v t -> 'v t
+
+
+val inject_fault_in_conf : (int -> string -> 'v -> 'v) -> 'v t -> 'v Conf.t -> 'v Conf.t
+val get_injected_fault_nb : unit -> int
+
 (** Get pid's state and neighbors *)
 val neigbors_of_pid : 'v t -> string -> 'v * ('v Register.neighbor * string) list
 
diff --git a/salut/test/Makefile.inc b/salut/test/Makefile.inc
index e65fb7f1..b1839174 100644
--- a/salut/test/Makefile.inc
+++ b/salut/test/Makefile.inc
@@ -47,8 +47,12 @@ RUN_KIND2=$(ROOTDIR)/salut/test/run-kind2.sh
 	make $*.dot $*_const.lus
 	luciole-rif lv6 $(LIB_LUS) $*.lus $*_const.lus -n $* -exec
 
-%.rdbg:
-	make $*.dot $*_const.lus
+%.rdbg: %.cma
+	make $*_const.lus
+	rdbg -l 10000 -sut-nd "lv6 $(LIB_LUS) $*_const.lus $*.lus  -n $* -exec"
+
+%.rdbgui: %.cma
+	make $*_const.lus
 	rdbgui4sasa -l 10000 -sut-nd "lv6 $(LIB_LUS) $*_const.lus $*.lus  -n $* -exec"
 
 ##########################################################################################"
diff --git a/src/sasaMain.ml b/src/sasaMain.ml
index e70fbdf1..ea2ee657 100644
--- a/src/sasaMain.ml
+++ b/src/sasaMain.ml
@@ -50,18 +50,6 @@ let (print_step : out_channel -> 'v SimuState.t -> int -> int -> string -> strin
 open Sasacore.SimuState
 module StringMap = Map.Make(String)
 
-let injected_fault_nb = ref 0
-let inject_fault ff st =
-  let update_nodes e p =
-    let nl = StringMap.find p.Process.pid st.neighbors in
-    let pid = p.Process.pid in
-    let v = Conf.get e pid in
-    let v = ff (List.length nl) pid v in
-    incr injected_fault_nb;
-    Conf.set e pid v
-  in
-  let e = List.fold_left update_nodes st.config st.network in
-  update_config e st
 
 let plur i = if i>1 then "s" else ""
 
@@ -93,7 +81,7 @@ let (simustep: out_channel -> int -> int -> string -> 'v SimuState.t -> 'v SimuS
       (* not (args.rif) && *)
       silent
     then (
-      match if !injected_fault_nb > 0 then None else Register.get_fault () with
+      match if SimuState.get_injected_fault_nb () > 0 then None else Register.get_fault () with
       | None ->
         Round.update leg true enab_ll enab_ll;
         print_step log st n i "t" pot st.sasarg st.config pl (bll2str enab_ll) enab_ll;
@@ -107,12 +95,12 @@ let (simustep: out_channel -> int -> int -> string -> 'v SimuState.t -> 'v SimuS
           str Round.s.moves (plur Round.s.moves) (n-i) (plur (n-i))
           Round.s.cpt (plur Round.s.cpt);
         Printf.fprintf log "%s==> Inject a fault\n%!" str;
-        let st = inject_fault ff st in
+        let st = SimuState.inject_fault ff st in
         let all, enab_ll = Sasacore.SimuState.get_enable_processes st in
         st, all, enab_ll
     )
     else if leg then (
-      match if !injected_fault_nb > 0 then None else Register.get_fault () with
+      match if SimuState.get_injected_fault_nb() > 0 then None else Register.get_fault () with
       | None ->
         Round.update leg true enab_ll enab_ll;
         print_step log st n i "t" pot st.sasarg st.config pl activate_val enab_ll;
@@ -125,7 +113,7 @@ let (simustep: out_channel -> int -> int -> string -> 'v SimuState.t -> 'v SimuS
           "\n%sThis algo reached a legitimate configuration after %i move%s, %i step%s, %i round%s.\n"
           str Round.s.moves (plur Round.s.moves) (n-i) (plur (n-i)) (Round.s.cpt-1) (plur (Round.s.cpt-1));
         Printf.fprintf log "%s==> Inject a fault\n%!" str;
-        let st = inject_fault ff st in
+        let st = SimuState.inject_fault ff st in
         let all, enab_ll = Sasacore.SimuState.get_enable_processes st in
         st, all, enab_ll
     )
diff --git a/test/Makefile.inc b/test/Makefile.inc
index 030a0d95..5a47df66 100644
--- a/test/Makefile.inc
+++ b/test/Makefile.inc
@@ -1,4 +1,4 @@
-# Time-stamp: <modified the 18/01/2023 (at 21:20) by Erwan Jahier>
+# Time-stamp: <modified the 19/01/2023 (at 14:18) by Erwan Jahier>
 #
 # Define some default rules that ought to work most of the time
 #
@@ -33,6 +33,10 @@ endif
 	topology=$* dune build
 %.cma: %.dot
 	topology=$* dune build
+%.cmxa: %.dot
+	topology=$* dune build
+%.a: %.dot
+	topology=$* dune build
 
 
 genclean:
@@ -95,10 +99,10 @@ endif
 ############################################################################################
 
 %.rdbg: %.dot
-	ledit -h rdbg-history -x rdbg --sasa -sut "sasa $< $(DAEMON)"  -l 500
+	ledit -h rdbg-history -x rdbg --sasa -sut "sasa $< $(DAEMON)"  -l 10000
 
 %.rdbg-test: %.cma
-	rdbg --sasa -sut "sasa $*.dot $(DAEMON)" -go --input some_session -l 500
+	rdbg --sasa -sut "sasa $*.dot $(DAEMON)" -go --input some_session -l 10000
 
 %.rdbgui: %.cma
-	rdbgui4sasa -sut "sasa $*.dot $(DAEMON)"
+	rdbgui4sasa -sut "sasa $*.dot $(DAEMON)"  -l 10000
diff --git a/test/dune2copy b/test/dune2copy
index 62240466..70215208 100644
--- a/test/dune2copy
+++ b/test/dune2copy
@@ -3,7 +3,7 @@
 (library
  (name user_algo_files) ;; a fake name because "%{env:topology=ring4}" is rejected
  (libraries algo)
- (wrapped false) ; so that user_algo_files is not used in the .cm*
+; (wrapped false) ; so that user_algo_files is not used in the .cm*
  (library_flags -linkall)
 )
 
diff --git a/tools/rdbg4sasa/gtkgui.ml b/tools/rdbg4sasa/gtkgui.ml
index 12acb22e..41750b2d 100644
--- a/tools/rdbg4sasa/gtkgui.ml
+++ b/tools/rdbg4sasa/gtkgui.ml
@@ -1,4 +1,4 @@
-(* Time-stamp: <modified the 16/01/2023 (at 21:36) by Erwan Jahier> *)
+(* Time-stamp: <modified the 19/01/2023 (at 16:30) by Erwan Jahier> *)
 
 #thread
 #require "lablgtk3"
@@ -242,9 +242,7 @@ let start () =
   ckpt_list := [!e];
   !e.save_state !e.nb
 
-let restart p _ =
-  Seed.replay_seed := true;
-  let seed = Seed.get dotfile in
+let start_with_seed p seed =
   Seed.set seed;
   p (Printf.sprintf "Restarting using the seed %d" seed);
   !e.RdbgEvent.reset(); (* should be done after the see reset! *)
@@ -253,6 +251,52 @@ let restart p _ =
   start ();
   d()
 
+let restart p _ =
+  Seed.replay_seed := true;
+  let seed = Seed.get dotfile in
+  (* change_sasa_seed seed; *)
+  start_with_seed p seed
+
+
+let restart_new_seed p _ =
+  Seed.replay_seed := false;
+  Seed.reset();
+  let seed = Seed.get "restart_new_seed" in
+  change_sasa_seed seed;
+  p (Printf.sprintf "Restarting using the seed %d" seed);
+  start_with_seed p seed
+
+let _restart_new_seed p _ =
+  Seed.reset();
+  let seed = Seed.get "restart_new_seed" in
+  change_sasa_seed seed;
+  Seed.set (seed);
+  !e.RdbgEvent.reset();
+  p (Printf.sprintf "Restarting using the seed %d" seed);
+
+  e:=RdbgStdLib.run ~call_hooks:true ();
+  Round.reinit();
+  redos := [1];
+  ckpt_list := [!e];
+
+  if args.salut_mode then
+    (* in this mode, the hook plays first to provide fake values to sasa
+       but the hook does not need input at this first step
+    *)
+    e:=goto_hook_exit !e;
+  e:=goto_hook_call !e;
+  d()
+
+let inject p _ =
+  let seed = Seed.get dotfile in
+  Seed.set seed;
+  p (Printf.sprintf "A fault is injected.\nThe seed is set to %d\n" seed);
+  Round.reinit_mask();
+  !e.restore_state min_int;
+  start ();
+  d()
+
+
 let counter_map = Hashtbl.create 0
 
 let update_daemongui_tbl e =
@@ -869,31 +913,17 @@ let main () =
     oracle_button#misc#hide(); (* indeed, in the defaut mode (manual central), it should be hided *)
     oracle_button_ref := Some oracle_button
   );
-  let _ = make_button `REFRESH "Restar_t" "Restart from the beginning"
+  if not args.salut_mode then (
+    let _ =
+      make_button `ADD "Inject _Fault" "Inject a fault (the Algo.fault_function should be set)"
+        (button_cb true true (inject p))
+    in
+    ());
+  let _ = make_button `GOTO_FIRST "Restar_t" "Restart from the beginning"
       (button_cb true true (restart p))
   in
   let _ = make_button `REFRESH "_New Seed" "Restart from the beginning using a New Seed"
-      (button_cb true true
-         (fun ()->
-            Seed.reset(); Seed.replay_seed := false;
-            let seed = Seed.get dotfile in
-            change_sasa_seed seed;
-            Seed.set (seed);
-            !e.RdbgEvent.reset();
-            p (Printf.sprintf "Restarting using the seed %d" seed);
-
-            e:=RdbgStdLib.run ~call_hooks:true ();
-            Round.reinit();
-            redos := [1];
-            ckpt_list := [!e];
-
-            if args.salut_mode then
-              (* in this mode, the hook plays first to provide fake values to sasa
-                 but the hook does not need input at this first step
-              *)
-              e:=goto_hook_exit !e;
-            e:=goto_hook_call !e;
-            d()))
+      (button_cb true true (restart_new_seed p))
   in
   let graph () =
     let graph_button = button  ~use_mnemonic:true  ~packing:bbox#add () in
diff --git a/tools/rdbg4sasa/sasa-rdbg-cmds.ml b/tools/rdbg4sasa/sasa-rdbg-cmds.ml
index a69192e2..8bc28af8 100644
--- a/tools/rdbg4sasa/sasa-rdbg-cmds.ml
+++ b/tools/rdbg4sasa/sasa-rdbg-cmds.ml
@@ -189,6 +189,21 @@ let r () =
   (* if !ckpt_list = [] then *)
   ckpt_list := [!e];;
 
+(**********************************************************************)
+(* Fault injection  *)
+
+let inject_a_fault () =
+  let seed = Seed.get dotfile in
+  Seed.set seed;
+  Printf.printf "Restarting using the seed %d\n" seed;
+  Round.reinit_mask();
+  !e.restore_state min_int;
+  redos := [1];
+  ckpt_list := [!e];;
+
+
+let iaf = inject_a_fault
+
 (**********************************************************************)
 (* Move forward until silence *)
 
@@ -314,6 +329,7 @@ let _ = add_doc_entry
           "viol" "unit -> unit" "Move forward until the oracle is violated"
           "sasa" "sasa-rdbg-cmds.ml"
 
+
 (**********************************************************************)
 (* Potential  *)
 
-- 
GitLab