(**************************************************************************) (* *) (* OCaml *) (* *) (* Jerome Vouillon, projet Cristal, INRIA Rocquencourt *) (* OCaml 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 GNU Lesser General Public License version 2.1, with the *) (* special exception on linking described in the file LICENSE. *) (* *) (**************************************************************************) (************************ Reading and executing commands ***************) open Int64ops open Format open Instruct open Unix open Debugger_config open Types open Primitives open Unix_tools open Debugger_parser open Parser_aux open Debugger_lexer open Input_handling open Question open Debugcom open Program_loading open Program_management open Lexing open Parameters open Show_source open Show_information open Time_travel open Events open Symbols open Source open Breakpoints open Checkpoints open Frames open Printval module Lexer = Debugger_lexer (** Instructions, variables and infos lists. **) type dbg_instruction = { instr_name: string; (* Name of command *) instr_prio: bool; (* Has priority *) instr_action: formatter -> lexbuf -> unit; (* What to do *) instr_repeat: bool; (* Can be repeated *) instr_help: string } (* Help message *) let instruction_list = ref ([] : dbg_instruction list) type dbg_variable = { var_name: string; (* Name of variable *) var_action: (lexbuf -> unit) * (formatter -> unit); (* Reading, writing fns *) var_help: string } (* Help message *) let variable_list = ref ([] : dbg_variable list) type dbg_info = { info_name: string; (* Name of info *) info_action: lexbuf -> unit; (* What to do *) info_help: string } (* Help message *) let info_list = ref ([] : dbg_info list) (** Utilities. **) let error text = eprintf "%s@." text; raise Toplevel let check_not_windows feature = match Sys.os_type with | "Win32" -> error ("\'"^feature^"\' feature not supported on Windows") | _ -> () let eol = end_of_line Lexer.lexeme let matching_elements list name instr = List.filter (function a -> isprefix instr (name a)) !list let all_matching_instructions = matching_elements instruction_list (fun i -> i.instr_name) (* itz 04-21-96 don't do priority completion in emacs mode *) (* XL 25-02-97 why? I find it very confusing. *) let matching_instructions instr = let all = all_matching_instructions instr in let prio = List.filter (fun i -> i.instr_prio) all in if prio = [] then all else prio let matching_variables = matching_elements variable_list (fun v -> v.var_name) let matching_infos = matching_elements info_list (fun i -> i.info_name) let find_ident name matcher action alternative ppf lexbuf = match identifier_or_eol Lexer.lexeme lexbuf with | None -> alternative ppf | Some ident -> match matcher ident with | [] -> error ("Unknown " ^ name ^ ".") | [a] -> action a ppf lexbuf | _ -> error ("Ambiguous " ^ name ^ ".") let find_variable action alternative ppf lexbuf = find_ident "variable name" matching_variables action alternative ppf lexbuf let find_info action alternative ppf lexbuf = find_ident "info command" matching_infos action alternative ppf lexbuf let add_breakpoint_at_pc pc = try new_breakpoint (any_event_at_pc pc) with | Not_found -> eprintf "Can\'t add breakpoint at pc %i:%i: no event there.@." pc.frag pc.pos; raise Toplevel let add_breakpoint_after_pc pc = let rec try_add n = if n < 3 then begin try new_breakpoint (any_event_at_pc {pc with pos = pc.pos + n * 4}) with | Not_found -> try_add (n+1) end else begin error "Can\'t add breakpoint at beginning of function: no event there" end in try_add 0 let module_of_longident id = match id with | Some x -> Some (String.concat "." (Longident.flatten x)) | None -> None let convert_module mdle = match mdle with | Some m -> (* Strip .ml extension if any, and capitalize *) String.capitalize_ascii(if Filename.check_suffix m ".ml" then Filename.chop_suffix m ".ml" else m) | None -> try (get_current_event ()).ev_ev.ev_module with Not_found -> error "Not in a module." (** Toplevel. **) let current_line = ref "" let interprete_line ppf line = current_line := line; let lexbuf = Lexing.from_string line in try match identifier_or_eol Lexer.lexeme lexbuf with | Some x -> begin match matching_instructions x with | [] -> error "Unknown command." | [i] -> i.instr_action ppf lexbuf; resume_user_input (); i.instr_repeat | _ -> error "Ambiguous command." end | None -> resume_user_input (); false with | Parsing.Parse_error -> error "Syntax error." | Lexer.Int_overflow -> error "Integer overflow" let line_loop ppf line_buffer = resume_user_input (); let previous_line = ref "" in try while true do if !loaded then History.add_current_time (); let new_line = string_trim (line line_buffer) in let line = if new_line <> "" then new_line else !previous_line in previous_line := ""; if interprete_line ppf line then previous_line := line done with | Exit -> () (* | Sys_error s -> error ("System error: " ^ s) *) (** Instructions. **) let instr_cd _ppf lexbuf = let dir = argument_eol argument lexbuf in if ask_kill_program () then try Sys.chdir (expand_path dir) with | Sys_error s -> error s let instr_shell _ppf lexbuf = let cmdarg = argument_list_eol argument lexbuf in let cmd = String.concat " " cmdarg in (* perhaps we should use $SHELL -c ? *) let err = Sys.command cmd in if (err != 0) then eprintf "Shell command %S failed with exit code %d\n%!" cmd err let instr_env _ppf lexbuf = let cmdarg = argument_list_eol argument lexbuf in let cmdarg = string_trim (String.concat " " cmdarg) in if cmdarg <> "" then if ask_kill_program () then begin try let eqpos = String.index cmdarg '=' in if eqpos = 0 then raise Not_found; let name = String.sub cmdarg 0 eqpos in let value = String.sub cmdarg (eqpos + 1) (String.length cmdarg - eqpos - 1) in Debugger_config.environment := (name, value) :: List.remove_assoc name !Debugger_config.environment with Not_found -> eprintf "Environment variable must be in name=value format\n%!" end else List.iter (fun (vvar, vval) -> printf "%s=%s\n%!" vvar vval) (List.rev !Debugger_config.environment) let instr_pwd ppf lexbuf = eol lexbuf; fprintf ppf "%s@." (Sys.getcwd ()) let instr_dir ppf lexbuf = let new_directory = argument_list_eol argument lexbuf in if new_directory = [] then begin if yes_or_no "Reinitialize directory list" then begin Load_path.init ~auto_include:Compmisc.auto_include !default_load_path; Envaux.reset_cache (); Hashtbl.clear Debugger_config.load_path_for; flush_buffer_list () end end else begin let new_directory' = List.rev new_directory in match new_directory' with | mdl :: for_keyw :: tl when String.lowercase_ascii for_keyw = "for" && List.length tl > 0 -> List.iter (function x -> add_path_for mdl (expand_path x)) tl | _ -> List.iter (function x -> add_path (expand_path x)) new_directory' end; let print_dirs ppf l = List.iter (function x -> fprintf ppf "@ %s" x) l in fprintf ppf "@[<2>Directories: %a@]@." print_dirs (Load_path.get_paths ()); Hashtbl.iter (fun mdl dirs -> fprintf ppf "@[<2>Source directories for %s: %a@]@." mdl print_dirs dirs) Debugger_config.load_path_for let instr_kill _ppf lexbuf = eol lexbuf; if not !loaded then error "The program is not being run."; if (yes_or_no "Kill the program being debugged") then begin kill_program (); show_no_point() end let instr_pid ppf lexbuf = eol lexbuf; if not !loaded then error "The program is not being run."; fprintf ppf "@[%d@]@." !current_checkpoint.c_pid let instr_run ppf lexbuf = eol lexbuf; ensure_loaded (); reset_named_values (); run (); show_current_event ppf let instr_reverse ppf lexbuf = eol lexbuf; check_not_windows "reverse"; ensure_loaded (); reset_named_values(); back_run (); show_current_event ppf let instr_step ppf lexbuf = let step_count = match opt_signed_int64_eol Lexer.lexeme lexbuf with | None -> _1 | Some x -> x in ensure_loaded (); reset_named_values(); step step_count; show_current_event ppf let instr_back ppf lexbuf = let step_count = match opt_signed_int64_eol Lexer.lexeme lexbuf with | None -> _1 | Some x -> x in check_not_windows "backstep"; ensure_loaded (); reset_named_values(); step (_0 -- step_count); show_current_event ppf let instr_finish ppf lexbuf = eol lexbuf; ensure_loaded (); reset_named_values(); finish (); show_current_event ppf let instr_next ppf lexbuf = let step_count = match opt_integer_eol Lexer.lexeme lexbuf with | None -> 1 | Some x -> x in ensure_loaded (); reset_named_values(); next step_count; show_current_event ppf let instr_start ppf lexbuf = eol lexbuf; check_not_windows "start"; ensure_loaded (); reset_named_values(); start (); show_current_event ppf let instr_previous ppf lexbuf = let step_count = match opt_integer_eol Lexer.lexeme lexbuf with | None -> 1 | Some x -> x in check_not_windows "previous"; ensure_loaded (); reset_named_values(); previous step_count; show_current_event ppf let instr_goto ppf lexbuf = let time = int64_eol Lexer.lexeme lexbuf in ensure_loaded (); reset_named_values(); go_to time; show_current_event ppf let instr_quit _ = raise Exit let print_variable_list ppf = let pr_vars ppf = List.iter (fun v -> fprintf ppf "%s@ " v.var_name) in fprintf ppf "List of variables: %a@." pr_vars !variable_list let print_info_list ppf = let pr_infos ppf = List.iter (fun i -> fprintf ppf "%s@ " i.info_name) in fprintf ppf "List of info commands: %a@." pr_infos !info_list let instr_complete _ppf lexbuf = let ppf = Format.err_formatter in let rec print_list l = try eol lexbuf; List.iter (function i -> fprintf ppf "%s@." i) l with _ -> remove_file !user_channel and match_list lexbuf = match identifier_or_eol Lexer.lexeme lexbuf with | None -> List.map (fun i -> i.instr_name) !instruction_list | Some x -> match matching_instructions x with | [ {instr_name = ("set" | "show" as i_full)} ] -> if x = i_full then begin match identifier_or_eol Lexer.lexeme lexbuf with | Some ident -> begin match matching_variables ident with | [v] -> if v.var_name = ident then [] else [v.var_name] | l -> List.map (fun v -> v.var_name) l end | None -> List.map (fun v -> v.var_name) !variable_list end else [i_full] | [ {instr_name = "info"} ] -> if x = "info" then begin match identifier_or_eol Lexer.lexeme lexbuf with | Some ident -> begin match matching_infos ident with | [i] -> if i.info_name = ident then [] else [i.info_name] | l -> List.map (fun i -> i.info_name) l end | None -> List.map (fun i -> i.info_name) !info_list end else ["info"] | [ {instr_name = "help"} ] -> if x = "help" then match_list lexbuf else ["help"] | [ i ] -> if x = i.instr_name then [] else [i.instr_name] | l -> List.map (fun i -> i.instr_name) l in print_list(match_list lexbuf) let instr_help ppf lexbuf = let pr_instrs ppf = List.iter (fun i -> fprintf ppf "%s@ " i.instr_name) in match identifier_or_eol Lexer.lexeme lexbuf with | Some x -> let print_help nm hlp = eol lexbuf; fprintf ppf "%s: %s@." nm hlp in begin match matching_instructions x with | [] -> eol lexbuf; fprintf ppf "No matching command.@." | [ {instr_name = "set"} ] -> find_variable (fun v _ _ -> print_help ("set " ^ v.var_name) ("set " ^ v.var_help)) (fun ppf -> print_help "set" "set debugger variable."; print_variable_list ppf) ppf lexbuf | [ {instr_name = "show"} ] -> find_variable (fun v _ _ -> print_help ("show " ^ v.var_name) ("show " ^ v.var_help)) (fun _v -> print_help "show" "display debugger variable."; print_variable_list ppf) ppf lexbuf | [ {instr_name = "info"} ] -> find_info (fun i _ _ -> print_help ("info " ^ i.info_name) i.info_help) (fun ppf -> print_help "info" "display infos about the program being debugged."; print_info_list ppf) ppf lexbuf | [i] -> print_help i.instr_name i.instr_help | l -> eol lexbuf; fprintf ppf "Ambiguous command \"%s\": %a@." x pr_instrs l end | None -> fprintf ppf "List of commands: %a@." pr_instrs !instruction_list (* Printing values *) let print_expr depth ev env ppf expr = try let (v, ty) = Eval.expression ev env expr in print_named_value depth expr env v ppf ty with | Eval.Error msg -> Eval.report_error ppf msg; raise Toplevel let env_of_event = function None -> Env.empty | Some ev -> Envaux.env_from_summary ev.ev_ev.ev_typenv ev.ev_ev.ev_typsubst let print_command depth ppf lexbuf = let exprs = expression_list_eol Lexer.lexeme lexbuf in ensure_loaded (); let env = try env_of_event !selected_event with | Envaux.Error msg -> Envaux.report_error ppf msg; raise Toplevel in List.iter (print_expr depth !selected_event env ppf) exprs let instr_print ppf lexbuf = print_command !max_printer_depth ppf lexbuf let instr_display ppf lexbuf = print_command 1 ppf lexbuf let instr_address ppf lexbuf = let exprs = expression_list_eol Lexer.lexeme lexbuf in ensure_loaded (); let env = try env_of_event !selected_event with | Envaux.Error msg -> Envaux.report_error ppf msg; raise Toplevel in let print_addr expr = let (v, _ty) = try Eval.expression !selected_event env expr with Eval.Error msg -> Eval.report_error ppf msg; raise Toplevel in match Remote_value.pointer v with | "" -> fprintf ppf "[not a remote value]@." | s -> fprintf ppf "0x%s@." s in List.iter print_addr exprs (* Loading of command files *) let extract_filename arg = (* Allow enclosing filename in quotes *) let l = String.length arg in let pos1 = if l > 0 && arg.[0] = '\"' then 1 else 0 in let pos2 = if l > 0 && arg.[l-1] = '\"' then l-1 else l in String.sub arg pos1 (pos2 - pos1) let instr_source ppf lexbuf = let file = extract_filename(argument_eol argument lexbuf) and old_state = !interactif and old_channel = !user_channel in let io_chan = try io_channel_of_descr (openfile (Load_path.find (expand_path file)) [O_RDONLY] 0) with | Not_found -> error "Source file not found." | (Unix_error _) as x -> Unix_tools.report_error x; raise Toplevel in interactif := false; user_channel := io_chan; let loop () = line_loop ppf (Lexing.from_function read_user_input) and finally () = stop_user_input (); close_io io_chan; interactif := old_state; user_channel := old_channel in Fun.protect ~finally loop let instr_set = find_variable (fun {var_action = (funct, _)} _ppf lexbuf -> funct lexbuf) (function _ppf -> error "Argument required.") let instr_show = find_variable (fun {var_action = (_, funct)} ppf lexbuf -> eol lexbuf; funct ppf) (function ppf -> List.iter (function {var_name = nm; var_action = (_, funct)} -> fprintf ppf "%s: " nm; funct ppf) !variable_list) let instr_info = find_info (fun i _ppf lexbuf -> i.info_action lexbuf) (function _ppf -> error "\"info\" must be followed by the name of an info command.") let instr_break ppf lexbuf = let argument = break_argument_eol Lexer.lexeme lexbuf in ensure_loaded (); match argument with | BA_none -> (* break *) (match !selected_event with | Some ev -> new_breakpoint ev | None -> error "Can\'t add breakpoint at this point.") | BA_pc {frag; pos} -> (* break PC *) add_breakpoint_at_pc {frag; pos} | BA_function expr -> (* break FUNCTION *) let env = try env_of_event !selected_event with | Envaux.Error msg -> Envaux.report_error ppf msg; raise Toplevel in begin try let (v, ty) = Eval.expression !selected_event env expr in match get_desc ty with | Tarrow _ -> add_breakpoint_after_pc (Remote_value.closure_code v) | _ -> eprintf "Not a function.@."; raise Toplevel with | Eval.Error msg -> Eval.report_error ppf msg; raise Toplevel end | BA_pos1 (mdle, line, column) -> (* break @ [MODULE] LINE [COL] *) let module_name = convert_module (module_of_longident mdle) in new_breakpoint (try let ev = event_at_pos module_name 0 in let ev_pos = {Lexing.dummy_pos with pos_fname = (Events.get_pos ev.ev_ev).pos_fname} in let buffer = try get_buffer ev_pos module_name with | Not_found -> eprintf "No source file for %s.@." module_name; raise Toplevel in match column with | None -> event_at_pos module_name (fst (pos_of_line buffer line)) | Some col -> event_near_pos module_name (point_of_coord buffer line col) with | Not_found -> (* event_at_pos / event_near pos *) eprintf "Can\'t find any event there.@."; raise Toplevel | Out_of_range -> (* pos_of_line / point_of_coord *) eprintf "Position out of range.@."; raise Toplevel) | BA_pos2 (mdle, position) -> (* break @ [MODULE] # POSITION *) try new_breakpoint (event_near_pos (convert_module (module_of_longident mdle)) position) with | Not_found -> eprintf "Can\'t find any event there.@." let instr_delete _ppf lexbuf = match integer_list_eol Lexer.lexeme lexbuf with | [] -> if breakpoints_count () <> 0 && yes_or_no "Delete all breakpoints" then remove_all_breakpoints () | breakpoints -> List.iter (function x -> try remove_breakpoint x with | Not_found -> ()) breakpoints let instr_frame ppf lexbuf = let frame_number = match opt_integer_eol Lexer.lexeme lexbuf with | None -> !current_frame | Some x -> x in ensure_loaded (); try select_frame frame_number; show_current_frame ppf true with | Not_found -> error ("No frame number " ^ Int.to_string frame_number ^ ".") let instr_backtrace ppf lexbuf = let number = match opt_signed_integer_eol Lexer.lexeme lexbuf with | None -> 0 | Some x -> x in ensure_loaded (); match current_report() with | None | Some {rep_type = Exited | Uncaught_exc | Code_loaded _} -> () | Some _ -> let frame_counter = ref 0 in let print_frame first_frame last_frame = function | None -> fprintf ppf "(Encountered a function with no debugging information)@."; false | Some event -> if !frame_counter >= first_frame then show_one_frame !frame_counter ppf event; incr frame_counter; if !frame_counter >= last_frame then begin fprintf ppf "(More frames follow)@." end; !frame_counter < last_frame in fprintf ppf "Backtrace:@."; if number = 0 then do_backtrace (print_frame 0 max_int) else if number > 0 then do_backtrace (print_frame 0 number) else begin let num_frames = stack_depth() in if num_frames < 0 then fprintf ppf "(Encountered a function with no debugging information)@." else do_backtrace (print_frame (num_frames + number) max_int) end let instr_up ppf lexbuf = let offset = match opt_signed_integer_eol Lexer.lexeme lexbuf with | None -> 1 | Some x -> x in ensure_loaded (); try select_frame (!current_frame + offset); show_current_frame ppf true with | Not_found -> error "No such frame." let instr_down ppf lexbuf = let offset = match opt_signed_integer_eol Lexer.lexeme lexbuf with | None -> 1 | Some x -> x in ensure_loaded (); try select_frame (!current_frame - offset); show_current_frame ppf true with | Not_found -> error "No such frame." let instr_last ppf lexbuf = let count = match opt_signed_int64_eol Lexer.lexeme lexbuf with | None -> _1 | Some x -> x in check_not_windows "last"; reset_named_values(); go_to (History.previous_time count); show_current_event ppf let instr_list _ppf lexbuf = let (mo, beg, e) = list_arguments_eol Lexer.lexeme lexbuf in let (curr_mod, line, column) = try selected_point () with | Not_found -> ("", -1, -1) in let mdle = match mo with | None -> curr_mod | _ -> convert_module (module_of_longident mo) in let pos = Lexing.dummy_pos in let buffer = try get_buffer pos mdle with | Not_found -> error ("No source file for " ^ mdle ^ ".") in let point = if column <> -1 then try (point_of_coord buffer line 1) + column with Out_of_range -> -1 else -1 in let beginning = match beg with | None when (mo <> None) || (line = -1) -> 1 | None -> begin try Int.max 1 (line - 10) with Out_of_range -> 1 end | Some x -> x in let en = match e with | None -> beginning + 20 | Some x -> x in if mdle = curr_mod then show_listing pos mdle beginning en point (current_event_is_before ()) else show_listing pos mdle beginning en (-1) true (** Variables. **) let raw_variable kill name = (function lexbuf -> let argument = argument_eol argument lexbuf in if (not kill) || ask_kill_program () then name := argument), function ppf -> fprintf ppf "%s@." !name let raw_line_variable kill name = (function lexbuf -> let argument = argument_eol line_argument lexbuf in if (not kill) || ask_kill_program () then name := argument), function ppf -> fprintf ppf "%s@." !name let integer_variable kill min msg name = (function lexbuf -> let argument = integer_eol Lexer.lexeme lexbuf in if argument < min then print_endline msg else if (not kill) || ask_kill_program () then name := argument), function ppf -> fprintf ppf "%i@." !name let int64_variable kill min msg name = (function lexbuf -> let argument = int64_eol Lexer.lexeme lexbuf in if argument < min then print_endline msg else if (not kill) || ask_kill_program () then name := argument), function ppf -> fprintf ppf "%Li@." !name let boolean_variable kill name = (function lexbuf -> let argument = match identifier_eol Lexer.lexeme lexbuf with | "on" -> true | "of" | "off" -> false | _ -> error "Syntax error." in if (not kill) || ask_kill_program () then name := argument), function ppf -> fprintf ppf "%s@." (if !name then "on" else "off") let path_variable kill name = (function lexbuf -> let argument = argument_eol argument lexbuf in if (not kill) || ask_kill_program () then name := make_absolute (expand_path argument)), function ppf -> fprintf ppf "%s@." !name let loading_mode_variable ppf = (find_ident "loading mode" (matching_elements (ref loading_modes) fst) (fun (_, mode) _ppf lexbuf -> eol lexbuf; set_launching_function mode) (function _ppf -> error "Syntax error.") ppf), function ppf -> let rec find = function | [] -> () | (name, funct) :: l -> if funct == !launching_func then fprintf ppf "%s" name else find l in find loading_modes; fprintf ppf "@." let follow_fork_variable = (function lexbuf -> let mode = match identifier_eol Lexer.lexeme lexbuf with | "child" -> Fork_child | "parent" -> Fork_parent | _ -> error "Syntax error." in fork_mode := mode; if !loaded then update_follow_fork_mode ()), function ppf -> fprintf ppf "%s@." (match !fork_mode with Fork_child -> "child" | Fork_parent -> "parent") (** Infos. **) let pr_modules ppf mods = let pr_mods ppf = List.iter (function x -> fprintf ppf "%s@ " x) in fprintf ppf "Used modules: @.%a@?" pr_mods mods let info_modules ppf lexbuf = eol lexbuf; ensure_loaded (); pr_modules ppf !modules (******** print_endline "Opened modules: "; if !opened_modules_names = [] then print_endline "(no module opened)." else (List.iter (function x -> print_string x;print_space) !opened_modules_names; print_newline ()) *********) let info_checkpoints ppf lexbuf = eol lexbuf; if !checkpoints = [] then fprintf ppf "No checkpoint.@." else (if !debug_breakpoints then (prerr_endline " Time Pid Version"; List.iter (function {c_time = time; c_pid = pid; c_breakpoint_version = version} -> Printf.printf "%19Ld %5d %d\n" time pid version) !checkpoints) else (print_endline " Time Pid"; List.iter (function {c_time = time; c_pid = pid} -> Printf.printf "%19Ld %5d\n" time pid) !checkpoints)) let info_one_breakpoint ppf (num, ev) = fprintf ppf "%3d %d:%10d %s@." num ev.ev_frag ev.ev_ev.ev_pos (Pos.get_desc ev) let info_breakpoints ppf lexbuf = eol lexbuf; if !breakpoints = [] then fprintf ppf "No breakpoints.@." else begin fprintf ppf "Num Address Where@."; List.iter (info_one_breakpoint ppf) (List.rev !breakpoints); end let info_events _ppf lexbuf = ensure_loaded (); let mdle = convert_module (module_of_longident (opt_longident_eol Lexer.lexeme lexbuf)) in print_endline ("Module: " ^ mdle); print_endline " Address Characters Kind Repr."; let frag, events = events_in_module mdle in List.iter (function ev -> let start_char, end_char = try let buffer = get_buffer (Events.get_pos ev) ev.ev_module in (snd (start_and_cnum buffer ev.ev_loc.Location.loc_start)), (snd (start_and_cnum buffer ev.ev_loc.Location.loc_end)) with _ -> ev.ev_loc.Location.loc_start.Lexing.pos_cnum, ev.ev_loc.Location.loc_end.Lexing.pos_cnum in Printf.printf "%d:%10d %6d-%-6d %10s %10s\n" frag ev.ev_pos start_char end_char ((match ev.ev_kind with Event_before -> "before" | Event_after _ -> "after" | Event_pseudo -> "pseudo") ^ (match ev.ev_info with Event_function -> "/fun" | Event_return _ -> "/ret" | Event_other -> "")) (match ev.ev_repr with Event_none -> "" | Event_parent _ -> "(repr)" | Event_child repr -> Int.to_string !repr)) events (** User-defined printers **) let instr_load_printer ppf lexbuf = let filename = extract_filename(argument_eol argument lexbuf) in try Loadprinter.loadfile ppf filename with Loadprinter.Error e -> Loadprinter.report_error ppf e; raise Toplevel let instr_install_printer ppf lexbuf = let lid = longident_eol Lexer.lexeme lexbuf in try Loadprinter.install_printer ppf lid with Loadprinter.Error e -> Loadprinter.report_error ppf e; raise Toplevel let instr_remove_printer ppf lexbuf = let lid = longident_eol Lexer.lexeme lexbuf in try Loadprinter.remove_printer lid with Loadprinter.Error e -> Loadprinter.report_error ppf e; raise Toplevel (** Initialization. **) let init ppf = instruction_list := [ { instr_name = "cd"; instr_prio = false; instr_action = instr_cd; instr_repeat = true; instr_help = "set working directory to DIR for debugger and program being debugged." }; { instr_name = "complete"; instr_prio = false; instr_action = instr_complete; instr_repeat = false; instr_help = "complete word at cursor according to context. Useful for Emacs." }; { instr_name = "pwd"; instr_prio = false; instr_action = instr_pwd; instr_repeat = true; instr_help = "print working directory." }; { instr_name = "directory"; instr_prio = false; instr_action = instr_dir; instr_repeat = false; instr_help = "add directory DIR to beginning of search path for source and\n\ interface files.\n\ Forget cached info on source file locations and line positions.\n\ With no argument, reset the search path." }; { instr_name = "kill"; instr_prio = false; instr_action = instr_kill; instr_repeat = true; instr_help = "kill the program being debugged." }; { instr_name = "pid"; instr_prio = false; instr_action = instr_pid; instr_repeat = true; instr_help = "print the process ID of the current active process." }; { instr_name = "address"; instr_prio = false; instr_action = instr_address; instr_repeat = true; instr_help = "print the raw address of a value." }; { instr_name = "help"; instr_prio = false; instr_action = instr_help; instr_repeat = true; instr_help = "print list of commands." }; { instr_name = "quit"; instr_prio = false; instr_action = instr_quit; instr_repeat = false; instr_help = "exit the debugger." }; { instr_name = "shell"; instr_prio = false; instr_action = instr_shell; instr_repeat = true; instr_help = "Execute a given COMMAND through the system shell." }; { instr_name = "environment"; instr_prio = false; instr_action = instr_env; instr_repeat = false; instr_help = "environment variable to give to program being debugged when it is started." }; (* Displacements *) { instr_name = "run"; instr_prio = true; instr_action = instr_run; instr_repeat = true; instr_help = "run the program from current position." }; { instr_name = "reverse"; instr_prio = false; instr_action = instr_reverse; instr_repeat = true; instr_help = "run the program backward from current position." }; { instr_name = "step"; instr_prio = true; instr_action = instr_step; instr_repeat = true; instr_help = "step program until it reaches the next event.\n\ Argument N means do this N times (or till program stops for another reason)." }; { instr_name = "backstep"; instr_prio = true; instr_action = instr_back; instr_repeat = true; instr_help = "step program backward until it reaches the previous event.\n\ Argument N means do this N times (or till program stops for another reason)." }; { instr_name = "goto"; instr_prio = false; instr_action = instr_goto; instr_repeat = true; instr_help = "go to the given time." }; { instr_name = "finish"; instr_prio = true; instr_action = instr_finish; instr_repeat = true; instr_help = "execute until topmost stack frame returns." }; { instr_name = "next"; instr_prio = true; instr_action = instr_next; instr_repeat = true; instr_help = "step program until it reaches the next event.\n\ Skip over function calls.\n\ Argument N means do this N times (or till program stops for another reason)." }; { instr_name = "start"; instr_prio = false; instr_action = instr_start; instr_repeat = true; instr_help = "execute backward until the current function is exited." }; { instr_name = "previous"; instr_prio = false; instr_action = instr_previous; instr_repeat = true; instr_help = "step program until it reaches the previous event.\n\ Skip over function calls.\n\ Argument N means do this N times (or till program stops for another reason)." }; { instr_name = "print"; instr_prio = true; instr_action = instr_print; instr_repeat = true; instr_help = "print value of expressions (deep printing)." }; { instr_name = "display"; instr_prio = true; instr_action = instr_display; instr_repeat = true; instr_help = "print value of expressions (shallow printing)." }; { instr_name = "source"; instr_prio = false; instr_action = instr_source; instr_repeat = true; instr_help = "read command from file FILE." }; (* Breakpoints *) { instr_name = "break"; instr_prio = false; instr_action = instr_break; instr_repeat = false; instr_help = "Set breakpoint.\ \nSyntax: break\ \n break function-name\ \n break @ [module] linenum\ \n break @ [module] linenum columnnum\ \n break @ [module] # characternum\ \n break frag:pc\ \n break pc" }; { instr_name = "delete"; instr_prio = false; instr_action = instr_delete; instr_repeat = false; instr_help = "delete some breakpoints.\n\ Arguments are breakpoint numbers with spaces in between.\n\ To delete all breakpoints, give no argument." }; { instr_name = "set"; instr_prio = false; instr_action = instr_set; instr_repeat = false; instr_help = "--unused--" }; { instr_name = "show"; instr_prio = false; instr_action = instr_show; instr_repeat = true; instr_help = "--unused--" }; { instr_name = "info"; instr_prio = false; instr_action = instr_info; instr_repeat = true; instr_help = "--unused--" }; (* Frames *) { instr_name = "frame"; instr_prio = false; instr_action = instr_frame; instr_repeat = true; instr_help = "select and print a stack frame.\n\ With no argument, print the selected stack frame.\n\ An argument specifies the frame to select." }; { instr_name = "backtrace"; instr_prio = false; instr_action = instr_backtrace; instr_repeat = true; instr_help = "print backtrace of all stack frames, or innermost COUNT frames.\n\ With a negative argument, print outermost -COUNT frames." }; { instr_name = "bt"; instr_prio = false; instr_action = instr_backtrace; instr_repeat = true; instr_help = "print backtrace of all stack frames, or innermost COUNT frames.\n\ With a negative argument, print outermost -COUNT frames." }; { instr_name = "up"; instr_prio = false; instr_action = instr_up; instr_repeat = true; instr_help = "select and print stack frame that called this one.\n\ An argument says how many frames up to go." }; { instr_name = "down"; instr_prio = false; instr_action = instr_down; instr_repeat = true; instr_help = "select and print stack frame called by this one.\n\ An argument says how many frames down to go." }; { instr_name = "last"; instr_prio = true; instr_action = instr_last; instr_repeat = true; instr_help = "go back to previous time." }; { instr_name = "list"; instr_prio = false; instr_action = instr_list; instr_repeat = true; instr_help = "list the source code." }; (* User-defined printers *) { instr_name = "load_printer"; instr_prio = false; instr_action = instr_load_printer; instr_repeat = false; instr_help = "load in the debugger a .cmo or .cma file containing printing functions." }; { instr_name = "install_printer"; instr_prio = false; instr_action = instr_install_printer; instr_repeat = false; instr_help = "use the given function for printing values of its input type.\n\ The code for the function must have previously been loaded in the debugger\n\ using \"load_printer\"." }; { instr_name = "remove_printer"; instr_prio = false; instr_action = instr_remove_printer; instr_repeat = false; instr_help = "stop using the given function for printing values of its input type." } ]; variable_list := [ (* variable name, (writing, reading), help reading, help writing *) { var_name = "arguments"; var_action = raw_line_variable true arguments; var_help = "arguments to give program being debugged when it is started." }; { var_name = "program"; var_action = path_variable true program_name; var_help = "name of program to be debugged." }; { var_name = "loadingmode"; var_action = loading_mode_variable ppf; var_help = "mode of loading.\n\ It can be either:\n\ direct: the program is directly called by the debugger.\n\ runtime: the debugger execute `ocamlrun programname arguments\'.\n\ manual: the program is not launched by the debugger,\n\ but manually by the user." }; { var_name = "processcount"; var_action = integer_variable false 1 "Must be >= 1." checkpoint_max_count; var_help = "maximum number of process to keep." }; { var_name = "checkpoints"; var_action = boolean_variable false make_checkpoints; var_help = "whether to make checkpoints or not." }; { var_name = "bigstep"; var_action = int64_variable false _1 "Must be >= 1." checkpoint_big_step; var_help = "step between checkpoints during long displacements." }; { var_name = "smallstep"; var_action = int64_variable false _1 "Must be >= 1." checkpoint_small_step; var_help = "step between checkpoints during small displacements." }; { var_name = "socket"; var_action = raw_variable true socket_name; var_help = "name of the socket used by communications debugger-runtime." }; { var_name = "history"; var_action = integer_variable false 0 "" history_size; var_help = "history size." }; { var_name = "print_depth"; var_action = integer_variable false 1 "Must be at least 1" max_printer_depth; var_help = "maximal depth for printing of values." }; { var_name = "print_length"; var_action = integer_variable false 1 "Must be at least 1" max_printer_steps; var_help = "maximal number of value nodes printed." }; { var_name = "follow_fork_mode"; var_action = follow_fork_variable; var_help = "process to follow after forking.\n\ It can be either :\n\ child: the newly created process.\n\ parent: the process that called fork.\n" }; { var_name = "break_on_load"; var_action = boolean_variable false break_on_load; var_help = "whether to stop after loading new code (e.g. with Dynlink)." }]; info_list := (* info name, function, help *) [{ info_name = "modules"; info_action = info_modules ppf; info_help = "list opened modules." }; { info_name = "checkpoints"; info_action = info_checkpoints ppf; info_help = "list checkpoints." }; { info_name = "breakpoints"; info_action = info_breakpoints ppf; info_help = "list breakpoints." }; { info_name = "events"; info_action = info_events ppf; info_help = "list events in MODULE (default is current module)." }] let _ = init std_formatter