| author | wenzelm | 
| Mon, 07 Mar 2016 20:44:47 +0100 | |
| changeset 62548 | f8ebb715e06d | 
| parent 62516 | 5732f1c31566 | 
| child 62663 | bea354f6ff21 | 
| permissions | -rw-r--r-- | 
| 60749 | 1 | (* Title: Pure/Tools/debugger.ML | 
| 2 | Author: Makarius | |
| 3 | ||
| 4 | Interactive debugger for Isabelle/ML. | |
| 5 | *) | |
| 6 | ||
| 60830 | 7 | signature DEBUGGER = | 
| 8 | sig | |
| 60834 | 9 | val writeln_message: string -> unit | 
| 10 | val warning_message: string -> unit | |
| 11 | val error_message: string -> unit | |
| 60830 | 12 | end; | 
| 13 | ||
| 14 | structure Debugger: DEBUGGER = | |
| 60765 | 15 | struct | 
| 16 | ||
| 60869 | 17 | (** global state **) | 
| 18 | ||
| 19 | (* output messages *) | |
| 60830 | 20 | |
| 60856 | 21 | val _ = Session.protocol_handler "isabelle.Debugger$Handler"; | 
| 22 | ||
| 60834 | 23 | fun output_message kind msg = | 
| 60864 | 24 | if msg = "" then () | 
| 25 | else | |
| 26 | Output.protocol_message | |
| 61556 | 27 | (Markup.debugger_output (Standard_Thread.the_name ())) | 
| 60864 | 28 | [Markup.markup (kind, Markup.serial_properties (serial ())) msg]; | 
| 60834 | 29 | |
| 30 | val writeln_message = output_message Markup.writelnN; | |
| 31 | val warning_message = output_message Markup.warningN; | |
| 32 | val error_message = output_message Markup.errorN; | |
| 60830 | 33 | |
| 60856 | 34 | fun error_wrapper e = e () | 
| 35 | handle exn => | |
| 62505 | 36 | if Exn.is_interrupt exn then Exn.reraise exn | 
| 60856 | 37 | else error_message (Runtime.exn_message exn); | 
| 38 | ||
| 60830 | 39 | |
| 60888 
35d85fd89fc1
eliminated cancel operation: disrupts normal evaluation of thread;
 wenzelm parents: 
60887diff
changeset | 40 | (* thread input *) | 
| 60765 | 41 | |
| 60888 
35d85fd89fc1
eliminated cancel operation: disrupts normal evaluation of thread;
 wenzelm parents: 
60887diff
changeset | 42 | val thread_input = | 
| 60891 | 43 | Synchronized.var "Debugger.state" (NONE: string list Queue.T Symtab.table option); | 
| 44 | ||
| 45 | fun init_input () = Synchronized.change thread_input (K (SOME Symtab.empty)); | |
| 46 | fun exit_input () = Synchronized.change thread_input (K NONE); | |
| 60765 | 47 | |
| 60842 | 48 | fun input thread_name msg = | 
| 60891 | 49 | if null msg then error "Empty input" | 
| 50 | else | |
| 51 | Synchronized.change thread_input | |
| 52 | (Option.map (Symtab.map_default (thread_name, Queue.empty) (Queue.enqueue msg))); | |
| 60887 | 53 | |
| 60842 | 54 | fun get_input thread_name = | 
| 60891 | 55 | Synchronized.guarded_access thread_input | 
| 56 | (fn NONE => SOME ([], NONE) | |
| 57 | | SOME input => | |
| 58 | (case Symtab.lookup input thread_name of | |
| 59 | NONE => NONE | |
| 60 | | SOME queue => | |
| 61 | let | |
| 62 | val (msg, queue') = Queue.dequeue queue; | |
| 63 | val input' = | |
| 64 | if Queue.is_empty queue' then Symtab.delete_safe thread_name input | |
| 65 | else Symtab.update (thread_name, queue') input; | |
| 66 | in SOME (msg, SOME input') end)); | |
| 60765 | 67 | |
| 68 | ||
| 60932 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 69 | (* global break *) | 
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 70 | |
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 71 | local | 
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 72 | val break = Synchronized.var "Debugger.break" false; | 
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 73 | in | 
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 74 | |
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 75 | fun is_break () = Synchronized.value break; | 
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 76 | fun set_break b = Synchronized.change break (K b); | 
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 77 | |
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 78 | end; | 
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 79 | |
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 80 | |
| 60869 | 81 | |
| 82 | (** thread state **) | |
| 83 | ||
| 84 | (* stack frame during debugging *) | |
| 60856 | 85 | |
| 86 | local | |
| 87 | val tag = Universal.tag () : ML_Debugger.state list Universal.tag; | |
| 88 | in | |
| 60765 | 89 | |
| 60856 | 90 | fun get_debugging () = the_default [] (Thread.getLocal tag); | 
| 91 | val is_debugging = not o null o get_debugging; | |
| 60765 | 92 | |
| 60856 | 93 | fun with_debugging e = | 
| 94 | setmp_thread_data tag (get_debugging ()) (ML_Debugger.state (Thread.self ())) e (); | |
| 60765 | 95 | |
| 60749 | 96 | end; | 
| 97 | ||
| 60858 | 98 | fun the_debug_state thread_name index = | 
| 99 | (case get_debugging () of | |
| 100 |     [] => error ("Missing debugger information for thread " ^ quote thread_name)
 | |
| 101 | | states => | |
| 102 | if index < 0 orelse index >= length states then | |
| 103 |         error ("Bad debugger stack index " ^ signed_string_of_int index ^ " for thread " ^
 | |
| 104 | quote thread_name) | |
| 105 | else nth states index); | |
| 106 | ||
| 107 | ||
| 60869 | 108 | (* flags for single-stepping *) | 
| 60765 | 109 | |
| 60869 | 110 | datatype stepping = Stepping of bool * int; (*stepping enabled, stack depth limit*) | 
| 111 | ||
| 112 | local | |
| 113 | val tag = Universal.tag () : stepping Universal.tag; | |
| 114 | in | |
| 115 | ||
| 116 | fun get_stepping () = the_default (Stepping (false, ~1)) (Thread.getLocal tag); | |
| 117 | fun put_stepping stepping = Thread.setLocal (tag, Stepping stepping); | |
| 118 | ||
| 119 | end; | |
| 120 | ||
| 121 | fun is_stepping () = | |
| 122 | let | |
| 123 | val stack = ML_Debugger.state (Thread.self ()); | |
| 124 | val Stepping (stepping, depth) = get_stepping (); | |
| 125 | in stepping andalso (depth < 0 orelse length stack <= depth) end; | |
| 126 | ||
| 60931 | 127 | fun continue () = put_stepping (false, ~1); | 
| 128 | fun step () = put_stepping (true, ~1); | |
| 129 | fun step_over () = put_stepping (true, length (get_debugging ())); | |
| 130 | fun step_out () = put_stepping (true, length (get_debugging ()) - 1); | |
| 60869 | 131 | |
| 132 | ||
| 60935 | 133 | |
| 60869 | 134 | (** eval ML **) | 
| 60765 | 135 | |
| 60862 | 136 | local | 
| 137 | ||
| 138 | val context_attempts = | |
| 139 | map ML_Lex.read | |
| 140 | ["Context.set_thread_data (SOME (Context.Theory ML_context))", | |
| 141 | "Context.set_thread_data (SOME (Context.Proof ML_context))", | |
| 142 | "Context.set_thread_data (SOME ML_context)"]; | |
| 143 | ||
| 144 | fun evaluate {SML, verbose} =
 | |
| 145 | ML_Context.eval | |
| 146 |     {SML = SML, exchange = false, redirect = false, verbose = verbose,
 | |
| 60956 | 147 | debug = SOME false, writeln = writeln_message, warning = warning_message} | 
| 60862 | 148 | Position.none; | 
| 149 | ||
| 60935 | 150 | fun eval_setup thread_name index SML context = | 
| 151 | context | |
| 152 | |> Context_Position.set_visible_generic false | |
| 153 |   |> ML_Env.add_name_space {SML = SML}
 | |
| 154 | (ML_Debugger.debug_name_space (the_debug_state thread_name index)); | |
| 155 | ||
| 60897 | 156 | fun eval_context thread_name index SML toks = | 
| 60858 | 157 | let | 
| 60935 | 158 | val context = ML_Context.the_generic_context (); | 
| 60897 | 159 | val context1 = | 
| 160 | if SML orelse forall (fn Antiquote.Text tok => ML_Lex.is_improper tok | _ => false) toks | |
| 60935 | 161 | then context | 
| 60862 | 162 | else | 
| 163 | let | |
| 60935 | 164 | val context' = context | 
| 165 | |> eval_setup thread_name index SML | |
| 60897 | 166 | |> ML_Context.exec (fn () => | 
| 167 |                 evaluate {SML = SML, verbose = true} (ML_Lex.read "val ML_context = " @ toks));
 | |
| 60862 | 168 | fun try_exec toks = | 
| 169 |             try (ML_Context.exec (fn () => evaluate {SML = false, verbose = false} toks)) context';
 | |
| 170 | in | |
| 171 | (case get_first try_exec context_attempts of | |
| 172 | SOME context2 => context2 | |
| 173 | | NONE => error "Malformed context: expected type theory, Proof.context, Context.generic") | |
| 174 | end; | |
| 60935 | 175 | in context1 |> eval_setup thread_name index SML end; | 
| 60897 | 176 | |
| 177 | in | |
| 178 | ||
| 179 | fun eval thread_name index SML txt1 txt2 = | |
| 180 | let | |
| 181 | val (toks1, toks2) = apply2 (ML_Lex.read_source SML o Input.string) (txt1, txt2); | |
| 182 | val context = eval_context thread_name index SML toks1; | |
| 60904 | 183 |   in Context.setmp_thread_data (SOME context) (evaluate {SML = SML, verbose = true}) toks2 end;
 | 
| 60897 | 184 | |
| 185 | fun print_vals thread_name index SML txt = | |
| 186 | let | |
| 60935 | 187 | val toks = ML_Lex.read_source SML (Input.string txt) | 
| 188 | val context = eval_context thread_name index SML toks; | |
| 60897 | 189 | val space = ML_Debugger.debug_name_space (the_debug_state thread_name index); | 
| 190 | ||
| 191 | fun print x = | |
| 62387 
ad3eb2889f9a
support for polyml-git ec49a49972c5 (branch FixedPrecisionInt);
 wenzelm parents: 
61886diff
changeset | 192 | Pretty.from_ML | 
| 62504 | 193 | (ML_Pretty.from_polyml | 
| 194 | (ML_Name_Space.displayVal (x, FixedInt.fromInt (ML_Options.get_print_depth ()), space))); | |
| 60897 | 195 | fun print_all () = | 
| 61886 | 196 | #allVal (ML_Debugger.debug_local_name_space (the_debug_state thread_name index)) () | 
| 60924 
610794dff23c
tuned signature, in accordance to sortBy in Scala;
 wenzelm parents: 
60904diff
changeset | 197 | |> sort_by #1 |> map (Pretty.item o single o print o #2) | 
| 60897 | 198 | |> Pretty.chunks |> Pretty.string_of |> writeln_message; | 
| 199 | in Context.setmp_thread_data (SOME context) print_all () end; | |
| 60862 | 200 | |
| 201 | end; | |
| 60749 | 202 | |
| 60765 | 203 | |
| 60869 | 204 | |
| 60891 | 205 | (** debugger loop **) | 
| 60857 | 206 | |
| 207 | local | |
| 60765 | 208 | |
| 60842 | 209 | fun debugger_state thread_name = | 
| 210 | Output.protocol_message (Markup.debugger_state thread_name) | |
| 60856 | 211 | [get_debugging () | 
| 60842 | 212 | |> map (fn st => | 
| 62516 | 213 | (Position.properties_of (Exn_Properties.position_of_location (ML_Debugger.debug_location st)), | 
| 60842 | 214 | ML_Debugger.debug_function st)) | 
| 215 | |> let open XML.Encode in list (pair properties string) end | |
| 216 | |> YXML.string_of_body]; | |
| 217 | ||
| 60857 | 218 | fun debugger_command thread_name = | 
| 219 | (case get_input thread_name of | |
| 60931 | 220 | [] => (continue (); false) | 
| 221 | | ["continue"] => (continue (); false) | |
| 222 | | ["step"] => (step (); false) | |
| 223 | | ["step_over"] => (step_over (); false) | |
| 224 | | ["step_out"] => (step_out (); false) | |
| 60857 | 225 | | ["eval", index, SML, txt1, txt2] => | 
| 226 | (error_wrapper (fn () => | |
| 227 | eval thread_name (Markup.parse_int index) (Markup.parse_bool SML) txt1 txt2); true) | |
| 60897 | 228 | | ["print_vals", index, SML, txt] => | 
| 229 | (error_wrapper (fn () => | |
| 230 | print_vals thread_name (Markup.parse_int index) (Markup.parse_bool SML) txt); true) | |
| 60857 | 231 | | bad => | 
| 232 | (Output.system_message | |
| 233 |         ("Debugger: bad input " ^ ML_Syntax.print_list ML_Syntax.print_string bad); true));
 | |
| 234 | ||
| 60889 | 235 | in | 
| 236 | ||
| 60842 | 237 | fun debugger_loop thread_name = | 
| 60885 
d8d51f956f05
report final debugger_state more robustly, e.g. after interrupt;
 wenzelm parents: 
60884diff
changeset | 238 | uninterruptible (fn restore_attributes => fn () => | 
| 
d8d51f956f05
report final debugger_state more robustly, e.g. after interrupt;
 wenzelm parents: 
60884diff
changeset | 239 | let | 
| 
d8d51f956f05
report final debugger_state more robustly, e.g. after interrupt;
 wenzelm parents: 
60884diff
changeset | 240 | fun loop () = | 
| 
d8d51f956f05
report final debugger_state more robustly, e.g. after interrupt;
 wenzelm parents: 
60884diff
changeset | 241 | (debugger_state thread_name; if debugger_command thread_name then loop () else ()); | 
| 
d8d51f956f05
report final debugger_state more robustly, e.g. after interrupt;
 wenzelm parents: 
60884diff
changeset | 242 | val result = Exn.capture (restore_attributes with_debugging) loop; | 
| 
d8d51f956f05
report final debugger_state more robustly, e.g. after interrupt;
 wenzelm parents: 
60884diff
changeset | 243 | val _ = debugger_state thread_name; | 
| 
d8d51f956f05
report final debugger_state more robustly, e.g. after interrupt;
 wenzelm parents: 
60884diff
changeset | 244 | in Exn.release result end) (); | 
| 60765 | 245 | |
| 60857 | 246 | end; | 
| 247 | ||
| 60765 | 248 | |
| 60869 | 249 | |
| 250 | (** protocol commands **) | |
| 60765 | 251 | |
| 252 | val _ = | |
| 253 | Isabelle_Process.protocol_command "Debugger.init" | |
| 60889 | 254 | (fn [] => | 
| 60891 | 255 | (init_input (); | 
| 60889 | 256 | ML_Debugger.on_breakpoint | 
| 257 | (SOME (fn (_, break) => | |
| 60932 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 258 | if not (is_debugging ()) andalso (! break orelse is_break () orelse is_stepping ()) | 
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 259 | then | 
| 61556 | 260 | (case Standard_Thread.get_name () of | 
| 60889 | 261 | SOME thread_name => debugger_loop thread_name | 
| 262 | | NONE => ()) | |
| 60891 | 263 | else ())))); | 
| 60889 | 264 | |
| 265 | val _ = | |
| 266 | Isabelle_Process.protocol_command "Debugger.exit" | |
| 60891 | 267 | (fn [] => (ML_Debugger.on_breakpoint NONE; exit_input ())); | 
| 60765 | 268 | |
| 269 | val _ = | |
| 60932 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 270 | Isabelle_Process.protocol_command "Debugger.break" | 
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 271 | (fn [b] => set_break (Markup.parse_bool b)); | 
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 272 | |
| 
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
 wenzelm parents: 
60931diff
changeset | 273 | val _ = | 
| 60878 
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
 wenzelm parents: 
60871diff
changeset | 274 | Isabelle_Process.protocol_command "Debugger.breakpoint" | 
| 60880 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 275 | (fn [node_name, id0, breakpoint0, breakpoint_state0] => | 
| 60878 
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
 wenzelm parents: 
60871diff
changeset | 276 | let | 
| 60880 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 277 | val id = Markup.parse_int id0; | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 278 | val breakpoint = Markup.parse_int breakpoint0; | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 279 | val breakpoint_state = Markup.parse_bool breakpoint_state0; | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 280 | |
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 281 |         fun err () = error ("Bad exec for command " ^ Markup.print_int id);
 | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 282 | in | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 283 | (case Document.command_exec (Document.state ()) node_name id of | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 284 | SOME (eval, _) => | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 285 | if Command.eval_finished eval then | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 286 | let | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 287 | val st = Command.eval_result_state eval; | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 288 | val ctxt = Toplevel.presentation_context_of st | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 289 | handle Toplevel.UNDEF => err (); | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 290 | in | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 291 | (case ML_Env.get_breakpoint (Context.Proof ctxt) breakpoint of | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 292 | SOME (b, _) => b := breakpoint_state | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 293 | | NONE => err ()) | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 294 | end | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 295 | else err () | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 296 | | NONE => err ()) | 
| 
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
 wenzelm parents: 
60878diff
changeset | 297 | end); | 
| 60878 
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
 wenzelm parents: 
60871diff
changeset | 298 | |
| 
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
 wenzelm parents: 
60871diff
changeset | 299 | val _ = | 
| 60765 | 300 | Isabelle_Process.protocol_command "Debugger.input" | 
| 60842 | 301 | (fn thread_name :: msg => input thread_name msg); | 
| 60765 | 302 | |
| 60749 | 303 | end; |