summaryrefslogtreecommitdiff
path: root/debugger/time_travel.ml
diff options
context:
space:
mode:
Diffstat (limited to 'debugger/time_travel.ml')
-rw-r--r--debugger/time_travel.ml642
1 files changed, 0 insertions, 642 deletions
diff --git a/debugger/time_travel.ml b/debugger/time_travel.ml
deleted file mode 100644
index eb98f1059c..0000000000
--- a/debugger/time_travel.ml
+++ /dev/null
@@ -1,642 +0,0 @@
-(***********************************************************************)
-(* *)
-(* Objective Caml *)
-(* *)
-(* Jerome Vouillon, projet Cristal, INRIA Rocquencourt *)
-(* Objective Caml port by John Malecki and Xavier Leroy *)
-(* *)
-(* Copyright 1996 Institut National de Recherche en Informatique et *)
-(* en Automatique. All rights reserved. This file is distributed *)
-(* under the terms of the Q Public License version 1.0. *)
-(* *)
-(***********************************************************************)
-
-(* $Id$ *)
-
-(**************************** Time travel ******************************)
-
-open Int64ops
-open Instruct
-open Events
-open Debugcom
-open Primitives
-open Checkpoints
-open Breakpoints
-open Trap_barrier
-open Input_handling
-open Debugger_config
-open Program_loading
-
-exception Current_checkpoint_lost
-
-let remove_1st key list =
- let rec remove =
- function
- [] -> []
- | a::l -> if a == key then l else a::(remove l)
- in
- remove list
-
-(*** Debugging. ***)
-
-let debug_time_travel = ref false
-
-(*** Internal utilities. ***)
-
-(* Insert a checkpoint in the checkpoint list.
- * Raise `Exit' if there is already a checkpoint at the same time.
- *)
-let insert_checkpoint ({c_time = time} as checkpoint) =
- let rec traverse =
- function
- [] -> [checkpoint]
- | (({c_time = t} as a)::l) as l' ->
- if t > time then
- a::(traverse l)
- else if t = time then
- raise Exit
- else
- checkpoint::l'
- in
- checkpoints := traverse !checkpoints
-
-(* Remove a checkpoint from the checkpoint list.
- * --- No error if not found.
- *)
-let remove_checkpoint checkpoint =
- checkpoints := remove_1st checkpoint !checkpoints
-
-(* Wait for the process used by `checkpoint' to connect.
- * --- Usually not called (the process is already connected).
- *)
-let wait_for_connection checkpoint =
- try
- Exec.unprotect
- (function () ->
- let old_controller = Input_handling.current_controller !connection in
- execute_with_other_controller
- (function
- fd ->
- old_controller fd;
- if checkpoint.c_valid = true then
- exit_main_loop ())
- !connection
- main_loop)
- with
- Sys.Break ->
- checkpoint.c_parent <- root;
- remove_checkpoint checkpoint;
- checkpoint.c_pid <- -1;
- raise Sys.Break
-
-(* Select a checkpoint as current. *)
-let set_current_checkpoint checkpoint =
- if !debug_time_travel then
- prerr_endline ("Select : " ^ (string_of_int checkpoint.c_pid));
- if not checkpoint.c_valid then
- wait_for_connection checkpoint;
- current_checkpoint := checkpoint;
- set_current_connection checkpoint.c_fd
-
-(* Kill `checkpoint'. *)
-let kill_checkpoint checkpoint =
- if !debug_time_travel then
- prerr_endline ("Kill : " ^ (string_of_int checkpoint.c_pid));
- if checkpoint.c_pid > 0 then (* Ghosts don't have to be killed ! *)
- (if not checkpoint.c_valid then
- wait_for_connection checkpoint;
- stop checkpoint.c_fd;
- if checkpoint.c_parent.c_pid > 0 then
- wait_child checkpoint.c_parent.c_fd;
- checkpoint.c_parent <- root;
- close_io checkpoint.c_fd;
- remove_file checkpoint.c_fd;
- remove_checkpoint checkpoint);
- checkpoint.c_pid <- -1 (* Don't exist anymore *)
-
-(*** Cleaning the checkpoint list. ***)
-
-(* Separe checkpoints before (<=) and after (>) `t'. *)
-(* ### t checkpoints -> (after, before) *)
-let cut t =
- let rec cut_t =
- function
- [] -> ([], [])
- | ({c_time = t'} as a::l) as l' ->
- if t' <= t then
- ([], l')
- else
- let (b, e) = cut_t l in
- (a::b, e)
- in
- cut_t
-
-(* Partition the checkpoints list. *)
-let cut2 t0 t l =
- let rec cut2_t0 t =
- function
- [] -> []
- | l ->
- let (after, before) = cut (t0 -- t -- _1) l in
- let l = cut2_t0 (t ++ t) before in
- after::l
- in
- let (after, before) = cut (t0 -- _1) l in
- after::(cut2_t0 t before)
-
-(* Separe first elements and last element of a list of checkpoint. *)
-let chk_merge2 cont =
- let rec chk_merge2_cont =
- function
- [] -> cont
- | [a] ->
- let (accepted, rejected) = cont in
- (a::accepted, rejected)
- | a::l ->
- let (accepted, rejected) = chk_merge2_cont l in
- (accepted, a::rejected)
- in chk_merge2_cont
-
-(* Separe the checkpoint list. *)
-(* ### list -> accepted * rejected *)
-let rec chk_merge =
- function
- [] -> ([], [])
- | l::tail ->
- chk_merge2 (chk_merge tail) l
-
-let new_checkpoint_list checkpoint_count accepted rejected =
- if List.length accepted >= checkpoint_count then
- let (k, l) = list_truncate2 checkpoint_count accepted in
- (k, l @ rejected)
- else
- let (k, l) =
- list_truncate2 (checkpoint_count - List.length accepted) rejected
- in
- (List.merge (fun {c_time = t1} {c_time = t2} -> compare t2 t1) accepted k,
- l)
-
-(* Clean the checkpoint list. *)
-(* Reference time is `time'. *)
-let clean_checkpoints time checkpoint_count =
- let (after, before) = cut time !checkpoints in
- let (accepted, rejected) =
- chk_merge (cut2 time !checkpoint_small_step before)
- in
- let (kept, lost) =
- new_checkpoint_list checkpoint_count accepted after
- in
- List.iter kill_checkpoint (lost @ rejected);
- checkpoints := kept
-
-(*** Internal functions for moving. ***)
-
-(* Find the first checkpoint before (or at) `time'.
- * Ask for reloading the program if necessary.
- *)
-let find_checkpoint_before time =
- let rec find =
- function
- [] ->
- print_string "Can't go that far in the past !"; print_newline ();
- if yes_or_no "Reload program" then begin
- load_program ();
- find !checkpoints
- end
- else
- raise Toplevel
- | { c_time = t } as a::l ->
- if t > time then
- find l
- else
- a
- in find !checkpoints
-
-(* Make a copy of the current checkpoint and clean the checkpoint list. *)
-(* --- The new checkpoint in not put in the list. *)
-let duplicate_current_checkpoint () =
- let checkpoint = !current_checkpoint in
- if not checkpoint.c_valid then
- wait_for_connection checkpoint;
- let new_checkpoint = (* Ghost *)
- {c_time = checkpoint.c_time;
- c_pid = 0;
- c_fd = checkpoint.c_fd;
- c_valid = false;
- c_report = checkpoint.c_report;
- c_state = C_stopped;
- c_parent = checkpoint;
- c_breakpoint_version = checkpoint.c_breakpoint_version;
- c_breakpoints = checkpoint.c_breakpoints;
- c_trap_barrier = checkpoint.c_trap_barrier}
- in
- checkpoints := list_replace checkpoint new_checkpoint !checkpoints;
- set_current_checkpoint checkpoint;
- clean_checkpoints (checkpoint.c_time ++ _1) (!checkpoint_max_count - 1);
- if new_checkpoint.c_pid = 0 then (* The ghost has not been killed *)
- (match do_checkpoint () with (* Duplicate checkpoint *)
- Checkpoint_done pid ->
- (new_checkpoint.c_pid <- pid;
- if !debug_time_travel then
- prerr_endline ("Waiting for connection : " ^ (string_of_int pid)))
- | Checkpoint_failed ->
- prerr_endline
- "A fork failed. Reducing maximum number of checkpoints.";
- checkpoint_max_count := List.length !checkpoints - 1;
- remove_checkpoint new_checkpoint)
-
-(* Was the movement interrupted ? *)
-(* --- An exception could have been used instead, *)
-(* --- but it is not clear where it should be caught. *)
-(* --- For instance, it should not be caught in `step' *)
-(* --- (as `step' is used in `next_1'). *)
-(* --- On the other side, other modules does not need to know *)
-(* --- about this exception. *)
-let interrupted = ref false
-
-(* Informations about last breakpoint encountered *)
-let last_breakpoint = ref None
-
-(* Ensure we stop on an event. *)
-let rec stop_on_event report =
- match report with
- {rep_type = Breakpoint; rep_program_pointer = pc;
- rep_stack_pointer = sp} ->
- last_breakpoint := Some (pc, sp);
- update_current_event ();
- begin match !current_event with
- None -> find_event ()
- | Some _ -> ()
- end
- | {rep_type = Trap_barrier; rep_stack_pointer = trap_frame} ->
- (* No event at current position. *)
- find_event ()
- | _ ->
- ()
-
-and find_event () =
- if !debug_time_travel then begin
- print_string "Searching next event...";
- print_newline ()
- end;
- let report = do_go _1 in
- !current_checkpoint.c_report <- Some report;
- stop_on_event report
-
-(* Internal function for running debugged program.
- * Requires `duration > 0'.
- *)
-let internal_step duration =
- match current_report () with
- Some {rep_type = Exited | Uncaught_exc} -> ()
- | _ ->
- Exec.protect
- (function () ->
- if !make_checkpoints then
- duplicate_current_checkpoint ()
- else
- remove_checkpoint !current_checkpoint;
- update_breakpoints ();
- update_trap_barrier ();
- !current_checkpoint.c_state <- C_running duration;
- let report = do_go duration in
- !current_checkpoint.c_report <- Some report;
- !current_checkpoint.c_state <- C_stopped;
- if report.rep_type = Event then begin
- !current_checkpoint.c_time <-
- !current_checkpoint.c_time ++ duration;
- interrupted := false;
- last_breakpoint := None
- end
- else begin
- !current_checkpoint.c_time <-
- !current_checkpoint.c_time ++ duration
- -- (Int64.of_int report.rep_event_count) ++ _1;
- interrupted := true;
- last_breakpoint := None;
- stop_on_event report
- end;
- (try
- insert_checkpoint !current_checkpoint
- with
- Exit ->
- kill_checkpoint !current_checkpoint;
- set_current_checkpoint
- (find_checkpoint_before (current_time ()))));
- if !debug_time_travel then begin
- print_string "Checkpoints : pid(time)"; print_newline ();
- List.iter
- (function {c_time = time; c_pid = pid; c_valid = valid} ->
- Printf.printf "%d(%Ld)%s " pid time
- (if valid then "" else "(invalid)"))
- !checkpoints;
- print_newline ()
- end
-
-(*** Miscellaneous functions (exported). ***)
-
-(* Create a checkpoint at time 0 (new program). *)
-let new_checkpoint pid fd =
- let new_checkpoint =
- {c_time = _0;
- c_pid = pid;
- c_fd = fd;
- c_valid = true;
- c_report = None;
- c_state = C_stopped;
- c_parent = root;
- c_breakpoint_version = 0;
- c_breakpoints = [];
- c_trap_barrier = 0}
- in
- insert_checkpoint new_checkpoint
-
-(* Set the file descriptor of a checkpoint *)
-(* (a new process has connected with the debugger). *)
-(* --- Return `true' on success (close the connection otherwise). *)
-let set_file_descriptor pid fd =
- let rec find =
- function
- [] ->
- prerr_endline "Unexpected connection";
- close_io fd;
- false
- | ({c_pid = pid'} as checkpoint)::l ->
- if pid <> pid' then
- find l
- else
- (checkpoint.c_fd <- fd;
- checkpoint.c_valid <- true;
- true)
- in
- if !debug_time_travel then
- prerr_endline ("New connection : " ^(string_of_int pid));
- find (!current_checkpoint::!checkpoints)
-
-(* Kill all the checkpoints. *)
-let kill_all_checkpoints () =
- List.iter kill_checkpoint (!current_checkpoint::!checkpoints)
-
-(* Kill a checkpoint without killing the process. *)
-(* (used when connection with the process is lost). *)
-(* --- Assume that the checkpoint is valid. *)
-let forget_process fd pid =
- let checkpoint =
- find (function c -> c.c_pid = pid) (!current_checkpoint::!checkpoints)
- in
- Printf.eprintf "Lost connection with process %d" pid;
- if checkpoint = !current_checkpoint then begin
- Printf.eprintf " (active process)\n";
- match !current_checkpoint.c_state with
- C_stopped ->
- Printf.eprintf "at time %Ld" !current_checkpoint.c_time
- | C_running duration ->
- Printf.eprintf "between time %Ld and time %Ld"
- !current_checkpoint.c_time
- (!current_checkpoint.c_time ++ duration)
- end;
- Printf.eprintf "\n"; flush stderr;
- Input_handling.remove_file fd;
- close_io checkpoint.c_fd;
- remove_file checkpoint.c_fd;
- remove_checkpoint checkpoint;
- checkpoint.c_pid <- -1; (* Don't exist anymore *)
- if checkpoint.c_parent.c_pid > 0 then
- wait_child checkpoint.c_parent.c_fd;
- if checkpoint = !current_checkpoint then
- raise Current_checkpoint_lost
-
-(* Try to recover when the current checkpoint is lost. *)
-let recover () =
- set_current_checkpoint
- (find_checkpoint_before (current_time ()))
-
-(*** Simple movements. ***)
-
-(* Forward stepping. Requires `duration >= 0'. *)
-let rec step_forward duration =
- if duration > !checkpoint_small_step then begin
- let first_step =
- if duration > !checkpoint_big_step then
- !checkpoint_big_step
- else
- !checkpoint_small_step
- in
- internal_step first_step;
- if not !interrupted then
- step_forward (duration -- first_step)
- end
- else if duration != _0 then
- internal_step duration
-
-(* Go to time `time' from current checkpoint (internal). *)
-let internal_go_to time =
- let duration = time -- (current_time ()) in
- if duration > _0 then
- execute_without_breakpoints (function () -> step_forward duration)
-
-(* Move to a given time. *)
-let go_to time =
- let checkpoint = find_checkpoint_before time in
- set_current_checkpoint checkpoint;
- internal_go_to time
-
-(* Return the time of the last breakpoint *)
-(* between current time and `max_time'. *)
-let rec find_last_breakpoint max_time =
- let rec find break =
- let time = current_time () in
- step_forward (max_time -- time);
- match !last_breakpoint, !temporary_breakpoint_position with
- (Some _, _) when current_time () < max_time ->
- find !last_breakpoint
- | (Some (pc, _), Some pc') when pc = pc' ->
- (max_time, !last_breakpoint)
- | _ ->
- (time, break)
- in
- find
- (match current_pc_sp () with
- (Some (pc, _)) as state when breakpoint_at_pc pc -> state
- | _ -> None)
-
-
-(* Run from `time_max' back to `time'. *)
-(* --- Assume 0 <= time < time_max *)
-let rec back_to time time_max =
- let
- {c_time = t} as checkpoint = find_checkpoint_before (pre64 time_max)
- in
- go_to (max time t);
- let (new_time, break) = find_last_breakpoint time_max in
- if break <> None || (new_time <= time) then begin
- go_to new_time;
- interrupted := break <> None;
- last_breakpoint := break
- end else
- back_to time new_time
-
-(* Backward stepping. *)
-(* --- Assume duration > 1 *)
-let step_backward duration =
- let time = current_time () in
- if time > _0 then
- back_to (max _0 (time -- duration)) time
-
-(* Run the program from current time. *)
-(* Stop at the first breakpoint, or at the end of the program. *)
-let rec run () =
- internal_step !checkpoint_big_step;
- if not !interrupted then
- run ()
-
-(* Run backward the program form current time. *)
-(* Stop at the first breakpoint, or at the beginning of the program. *)
-let back_run () =
- if current_time () > _0 then
- back_to _0 (current_time ())
-
-(* Step in any direction. *)
-(* Stop at the first brakpoint, or after `duration' steps. *)
-let step duration =
- if duration >= _0 then
- step_forward duration
- else
- step_backward (_0 -- duration)
-
-(*** Next, finish. ***)
-
-(* Finish current function. *)
-let finish () =
- update_current_event ();
- match !current_event with
- None ->
- prerr_endline "`finish' not meaningful in outermost frame.";
- raise Toplevel
- | Some curr_event ->
- set_initial_frame();
- let (frame, pc) = up_frame curr_event.ev_stacksize in
- if frame < 0 then begin
- prerr_endline "`finish' not meaningful in outermost frame.";
- raise Toplevel
- end;
- begin
- try ignore(Symbols.any_event_at_pc pc)
- with Not_found ->
- prerr_endline "Calling function has no debugging information.";
- raise Toplevel
- end;
- exec_with_trap_barrier
- frame
- (fun () ->
- exec_with_temporary_breakpoint
- pc
- (fun () ->
- while
- run ();
- match !last_breakpoint with
- Some (pc', frame') when pc = pc' ->
- interrupted := false;
- frame <> frame'
- | _ ->
- false
- do
- ()
- done))
-
-let next_1 () =
- update_current_event ();
- match !current_event with
- None -> (* Beginning of the program. *)
- step _1
- | Some event1 ->
- let (frame1, pc1) = initial_frame() in
- step _1;
- if not !interrupted then begin
- update_current_event ();
- match !current_event with
- None -> ()
- | Some event2 ->
- let (frame2, pc2) = initial_frame() in
- (* Call `finish' if we've entered a function. *)
- if frame1 >= 0 && frame2 >= 0 &&
- frame2 - event2.ev_stacksize > frame1 - event1.ev_stacksize
- then finish()
- end
-
-(* Same as `step' (forward) but skip over function calls. *)
-let rec next =
- function
- 0 -> ()
- | n ->
- next_1 ();
- if not !interrupted then
- next (n - 1)
-
-(* Run backward until just before current function. *)
-let start () =
- update_current_event ();
- match !current_event with
- None ->
- prerr_endline "`start not meaningful in outermost frame.";
- raise Toplevel
- | Some curr_event ->
- let (frame, _) = initial_frame() in
- let (frame', pc) = up_frame curr_event.ev_stacksize in
- if frame' < 0 then begin
- prerr_endline "`start not meaningful in outermost frame.";
- raise Toplevel
- end;
- let nargs =
- match
- try Symbols.any_event_at_pc pc with Not_found ->
- prerr_endline "Calling function has no debugging information.";
- raise Toplevel
- with
- {ev_info = Event_return nargs} -> nargs
- | _ -> Misc.fatal_error "Time_travel.start"
- in
- let offset = if nargs < 4 then 1 else 2 in
- let pc = pc - 4 * offset in
- while
- exec_with_temporary_breakpoint pc back_run;
- match !last_breakpoint with
- Some (pc', frame') when pc = pc' ->
- step _minus1;
- (not !interrupted)
- &&
- (frame' - nargs > frame - curr_event.ev_stacksize)
- | _ ->
- false
- do
- ()
- done
-
-let previous_1 () =
- update_current_event ();
- match !current_event with
- None -> (* End of the program. *)
- step _minus1
- | Some event1 ->
- let (frame1, pc1) = initial_frame() in
- step _minus1;
- if not !interrupted then begin
- update_current_event ();
- match !current_event with
- None -> ()
- | Some event2 ->
- let (frame2, pc2) = initial_frame() in
- (* Call `start' if we've entered a function. *)
- if frame1 >= 0 && frame2 >= 0 &&
- frame2 - event2.ev_stacksize > frame1 - event1.ev_stacksize
- then start()
- end
-
-(* Same as `step' (backward) but skip over function calls. *)
-let rec previous =
- function
- 0 -> ()
- | n ->
- previous_1 ();
- if not !interrupted then
- previous (n - 1)