| author | wenzelm |
| Tue, 11 Aug 2015 13:50:59 +0200 | |
| changeset 60888 | 35d85fd89fc1 |
| parent 60887 | 9d8b244234ab |
| child 60889 | 7f210887cc4e |
| 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 |
|
27 |
(Markup.debugger_output (Simple_Thread.the_name ())) |
|
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 => |
|
36 |
if Exn.is_interrupt exn then reraise exn |
|
37 |
else error_message (Runtime.exn_message exn); |
|
38 |
||
| 60830 | 39 |
|
|
60888
35d85fd89fc1
eliminated cancel operation: disrupts normal evaluation of thread;
wenzelm
parents:
60887
diff
changeset
|
40 |
(* thread input *) |
| 60765 | 41 |
|
|
60888
35d85fd89fc1
eliminated cancel operation: disrupts normal evaluation of thread;
wenzelm
parents:
60887
diff
changeset
|
42 |
val thread_input = |
|
35d85fd89fc1
eliminated cancel operation: disrupts normal evaluation of thread;
wenzelm
parents:
60887
diff
changeset
|
43 |
Synchronized.var "Debugger.state" (Symtab.empty: string list Queue.T Symtab.table); |
| 60765 | 44 |
|
| 60842 | 45 |
fun input thread_name msg = |
|
60888
35d85fd89fc1
eliminated cancel operation: disrupts normal evaluation of thread;
wenzelm
parents:
60887
diff
changeset
|
46 |
Synchronized.change thread_input |
|
35d85fd89fc1
eliminated cancel operation: disrupts normal evaluation of thread;
wenzelm
parents:
60887
diff
changeset
|
47 |
(Symtab.map_default (thread_name, Queue.empty) (Queue.enqueue msg)); |
| 60887 | 48 |
|
| 60842 | 49 |
fun get_input thread_name = |
|
60888
35d85fd89fc1
eliminated cancel operation: disrupts normal evaluation of thread;
wenzelm
parents:
60887
diff
changeset
|
50 |
Synchronized.guarded_access thread_input (fn input => |
| 60842 | 51 |
(case Symtab.lookup input thread_name of |
| 60765 | 52 |
NONE => NONE |
53 |
| SOME queue => |
|
54 |
let |
|
55 |
val (msg, queue') = Queue.dequeue queue; |
|
56 |
val input' = |
|
| 60842 | 57 |
if Queue.is_empty queue' then Symtab.delete_safe thread_name input |
58 |
else Symtab.update (thread_name, queue') input; |
|
|
60888
35d85fd89fc1
eliminated cancel operation: disrupts normal evaluation of thread;
wenzelm
parents:
60887
diff
changeset
|
59 |
in SOME (msg, input') end)); |
| 60765 | 60 |
|
61 |
||
| 60869 | 62 |
|
63 |
(** thread state **) |
|
64 |
||
65 |
(* stack frame during debugging *) |
|
| 60856 | 66 |
|
67 |
local |
|
68 |
val tag = Universal.tag () : ML_Debugger.state list Universal.tag; |
|
69 |
in |
|
| 60765 | 70 |
|
| 60856 | 71 |
fun get_debugging () = the_default [] (Thread.getLocal tag); |
72 |
val is_debugging = not o null o get_debugging; |
|
| 60765 | 73 |
|
| 60856 | 74 |
fun with_debugging e = |
75 |
setmp_thread_data tag (get_debugging ()) (ML_Debugger.state (Thread.self ())) e (); |
|
| 60765 | 76 |
|
| 60749 | 77 |
end; |
78 |
||
| 60858 | 79 |
fun the_debug_state thread_name index = |
80 |
(case get_debugging () of |
|
81 |
[] => error ("Missing debugger information for thread " ^ quote thread_name)
|
|
82 |
| states => |
|
83 |
if index < 0 orelse index >= length states then |
|
84 |
error ("Bad debugger stack index " ^ signed_string_of_int index ^ " for thread " ^
|
|
85 |
quote thread_name) |
|
86 |
else nth states index); |
|
87 |
||
88 |
||
| 60869 | 89 |
(* flags for single-stepping *) |
| 60765 | 90 |
|
| 60869 | 91 |
datatype stepping = Stepping of bool * int; (*stepping enabled, stack depth limit*) |
92 |
||
93 |
local |
|
94 |
val tag = Universal.tag () : stepping Universal.tag; |
|
95 |
in |
|
96 |
||
97 |
fun get_stepping () = the_default (Stepping (false, ~1)) (Thread.getLocal tag); |
|
98 |
fun put_stepping stepping = Thread.setLocal (tag, Stepping stepping); |
|
99 |
||
100 |
end; |
|
101 |
||
102 |
fun is_stepping () = |
|
103 |
let |
|
104 |
val stack = ML_Debugger.state (Thread.self ()); |
|
105 |
val Stepping (stepping, depth) = get_stepping (); |
|
106 |
in stepping andalso (depth < 0 orelse length stack <= depth) end; |
|
107 |
||
108 |
fun step_stepping () = put_stepping (true, ~1); |
|
109 |
fun step_over_stepping () = put_stepping (true, length (get_debugging ())); |
|
110 |
fun step_out_stepping () = put_stepping (true, length (get_debugging ()) - 1); |
|
111 |
fun continue_stepping () = put_stepping (false, ~1); |
|
112 |
||
113 |
||
114 |
(** eval ML **) |
|
| 60765 | 115 |
|
| 60862 | 116 |
local |
117 |
||
118 |
val context_attempts = |
|
119 |
map ML_Lex.read |
|
120 |
["Context.set_thread_data (SOME (Context.Theory ML_context))", |
|
121 |
"Context.set_thread_data (SOME (Context.Proof ML_context))", |
|
122 |
"Context.set_thread_data (SOME ML_context)"]; |
|
123 |
||
124 |
fun evaluate {SML, verbose} =
|
|
125 |
ML_Context.eval |
|
126 |
{SML = SML, exchange = false, redirect = false, verbose = verbose,
|
|
127 |
writeln = writeln_message, warning = warning_message} |
|
128 |
Position.none; |
|
129 |
||
130 |
in |
|
131 |
||
| 60858 | 132 |
fun eval thread_name index SML txt1 txt2 = |
133 |
let |
|
| 60862 | 134 |
val (toks1, toks2) = apply2 (ML_Lex.read_source SML o Input.string) (txt1, txt2); |
| 60858 | 135 |
|
| 60862 | 136 |
val evaluate_verbose = evaluate {SML = SML, verbose = true};
|
| 60858 | 137 |
val context1 = ML_Context.the_generic_context () |
138 |
|> Context_Position.set_visible_generic false |
|
| 60871 | 139 |
|> Config.put_generic ML_Options.debugger false |
| 60862 | 140 |
|> ML_Env.add_name_space {SML = SML}
|
141 |
(ML_Debugger.debug_name_space (the_debug_state thread_name index)); |
|
142 |
val context2 = |
|
143 |
if SML orelse forall (fn Antiquote.Text tok => ML_Lex.is_improper tok | _ => false) toks1 |
|
144 |
then context1 |
|
145 |
else |
|
146 |
let |
|
147 |
val context' = context1 |
|
148 |
|> ML_Context.exec (fn () => evaluate_verbose (ML_Lex.read "val ML_context = " @ toks1)); |
|
149 |
fun try_exec toks = |
|
150 |
try (ML_Context.exec (fn () => evaluate {SML = false, verbose = false} toks)) context';
|
|
151 |
in |
|
152 |
(case get_first try_exec context_attempts of |
|
153 |
SOME context2 => context2 |
|
154 |
| NONE => error "Malformed context: expected type theory, Proof.context, Context.generic") |
|
155 |
end; |
|
156 |
in Context.setmp_thread_data (SOME context2) evaluate_verbose toks2 end; |
|
157 |
||
158 |
end; |
|
| 60749 | 159 |
|
| 60765 | 160 |
|
| 60869 | 161 |
|
162 |
(** debugger entry point **) |
|
| 60857 | 163 |
|
164 |
local |
|
| 60765 | 165 |
|
| 60842 | 166 |
fun debugger_state thread_name = |
167 |
Output.protocol_message (Markup.debugger_state thread_name) |
|
| 60856 | 168 |
[get_debugging () |
| 60842 | 169 |
|> map (fn st => |
170 |
(Position.properties_of (Exn_Properties.position_of (ML_Debugger.debug_location st)), |
|
171 |
ML_Debugger.debug_function st)) |
|
172 |
|> let open XML.Encode in list (pair properties string) end |
|
173 |
|> YXML.string_of_body]; |
|
174 |
||
| 60857 | 175 |
fun debugger_command thread_name = |
176 |
(case get_input thread_name of |
|
| 60869 | 177 |
["step"] => (step_stepping (); false) |
178 |
| ["step_over"] => (step_over_stepping (); false) |
|
179 |
| ["step_out"] => (step_out_stepping (); false) |
|
180 |
| ["continue"] => (continue_stepping (); false) |
|
| 60857 | 181 |
| ["eval", index, SML, txt1, txt2] => |
182 |
(error_wrapper (fn () => |
|
183 |
eval thread_name (Markup.parse_int index) (Markup.parse_bool SML) txt1 txt2); true) |
|
184 |
| bad => |
|
185 |
(Output.system_message |
|
186 |
("Debugger: bad input " ^ ML_Syntax.print_list ML_Syntax.print_string bad); true));
|
|
187 |
||
| 60842 | 188 |
fun debugger_loop thread_name = |
|
60885
d8d51f956f05
report final debugger_state more robustly, e.g. after interrupt;
wenzelm
parents:
60884
diff
changeset
|
189 |
uninterruptible (fn restore_attributes => fn () => |
|
d8d51f956f05
report final debugger_state more robustly, e.g. after interrupt;
wenzelm
parents:
60884
diff
changeset
|
190 |
let |
|
d8d51f956f05
report final debugger_state more robustly, e.g. after interrupt;
wenzelm
parents:
60884
diff
changeset
|
191 |
fun loop () = |
|
d8d51f956f05
report final debugger_state more robustly, e.g. after interrupt;
wenzelm
parents:
60884
diff
changeset
|
192 |
(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
|
193 |
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
|
194 |
val _ = debugger_state thread_name; |
|
d8d51f956f05
report final debugger_state more robustly, e.g. after interrupt;
wenzelm
parents:
60884
diff
changeset
|
195 |
in Exn.release result end) (); |
| 60765 | 196 |
|
| 60857 | 197 |
in |
198 |
||
| 60765 | 199 |
fun init () = |
200 |
ML_Debugger.on_breakpoint |
|
| 60869 | 201 |
(SOME (fn (_, break) => |
| 60887 | 202 |
if not (is_debugging ()) andalso (! break orelse is_stepping ()) andalso |
| 60869 | 203 |
Options.default_bool @{system_option ML_debugger_active}
|
204 |
then |
|
205 |
(case Simple_Thread.get_name () of |
|
|
60888
35d85fd89fc1
eliminated cancel operation: disrupts normal evaluation of thread;
wenzelm
parents:
60887
diff
changeset
|
206 |
SOME thread_name => debugger_loop thread_name |
| 60869 | 207 |
| NONE => ()) |
208 |
else ())); |
|
| 60765 | 209 |
|
| 60857 | 210 |
end; |
211 |
||
| 60765 | 212 |
|
| 60869 | 213 |
|
214 |
(** protocol commands **) |
|
| 60765 | 215 |
|
216 |
val _ = |
|
217 |
Isabelle_Process.protocol_command "Debugger.init" |
|
218 |
(fn [] => init ()); |
|
219 |
||
220 |
val _ = |
|
|
60878
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
wenzelm
parents:
60871
diff
changeset
|
221 |
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
|
222 |
(fn [node_name, id0, breakpoint0, breakpoint_state0] => |
|
60878
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
wenzelm
parents:
60871
diff
changeset
|
223 |
let |
|
60880
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
224 |
val id = Markup.parse_int id0; |
|
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
225 |
val breakpoint = Markup.parse_int breakpoint0; |
|
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
226 |
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:
60878
diff
changeset
|
227 |
|
|
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
228 |
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:
60878
diff
changeset
|
229 |
in |
|
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
230 |
(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
|
231 |
SOME (eval, _) => |
|
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
232 |
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
|
233 |
let |
|
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
234 |
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
|
235 |
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
|
236 |
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
|
237 |
in |
|
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
238 |
(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
|
239 |
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
|
240 |
| NONE => err ()) |
|
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
241 |
end |
|
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
242 |
else err () |
|
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
243 |
| NONE => err ()) |
|
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
244 |
end); |
|
60878
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
wenzelm
parents:
60871
diff
changeset
|
245 |
|
|
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
wenzelm
parents:
60871
diff
changeset
|
246 |
val _ = |
| 60765 | 247 |
Isabelle_Process.protocol_command "Debugger.input" |
| 60842 | 248 |
(fn thread_name :: msg => input thread_name msg); |
| 60765 | 249 |
|
| 60749 | 250 |
end; |