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