author | wenzelm |
Mon, 04 Jan 2016 20:34:34 +0100 | |
changeset 62051 | c3c871b509d9 |
parent 61556 | 0d4ee4168e41 |
child 63805 | c272680df665 |
permissions | -rw-r--r-- |
60749 | 1 |
/* Title: Pure/Tools/debugger.scala |
2 |
Author: Makarius |
|
3 |
||
4 |
Interactive debugger for Isabelle/ML. |
|
5 |
*/ |
|
6 |
||
7 |
package isabelle |
|
8 |
||
9 |
||
61016 | 10 |
import scala.collection.immutable.SortedMap |
11 |
||
12 |
||
60749 | 13 |
object Debugger |
14 |
{ |
|
61010 | 15 |
/* context */ |
60834 | 16 |
|
60842 | 17 |
sealed case class Debug_State( |
18 |
pos: Position.T, |
|
19 |
function: String) |
|
20 |
||
61010 | 21 |
sealed case class Context(thread_name: String, debug_states: List[Debug_State], index: Int = 0) |
22 |
{ |
|
23 |
def size: Int = debug_states.length + 1 |
|
24 |
def reset: Context = copy(index = 0) |
|
25 |
def select(i: Int) = copy(index = i + 1) |
|
26 |
||
27 |
def thread_state: Option[Debug_State] = debug_states.headOption |
|
28 |
||
29 |
def stack_state: Option[Debug_State] = |
|
30 |
if (1 <= index && index <= debug_states.length) |
|
31 |
Some(debug_states(index - 1)) |
|
32 |
else None |
|
33 |
||
61014 | 34 |
def debug_index: Option[Int] = |
61010 | 35 |
if (stack_state.isDefined) Some(index - 1) |
36 |
else if (debug_states.nonEmpty) Some(0) |
|
37 |
else None |
|
38 |
||
39 |
def debug_state: Option[Debug_State] = stack_state orElse thread_state |
|
61014 | 40 |
def debug_position: Option[Position.T] = debug_state.map(_.pos) |
61010 | 41 |
|
42 |
override def toString: String = |
|
43 |
stack_state match { |
|
44 |
case None => thread_name |
|
45 |
case Some(d) => d.function |
|
46 |
} |
|
47 |
} |
|
48 |
||
49 |
||
50 |
/* global state */ |
|
51 |
||
61016 | 52 |
type Threads = SortedMap[String, List[Debug_State]] |
53 |
||
60835 | 54 |
sealed case class State( |
60932
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
55 |
session: Session = new Session(Resources.empty), // implicit session |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
56 |
active: Int = 0, // active views |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
57 |
break: Boolean = false, // break at next possible breakpoint |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
58 |
active_breakpoints: Set[Long] = Set.empty, // explicit breakpoint state |
61016 | 59 |
threads: Threads = SortedMap.empty, // thread name ~> stack of debug states |
61014 | 60 |
focus: Map[String, Context] = Map.empty, // thread name ~> focus |
60834 | 61 |
output: Map[String, Command.Results] = Map.empty) // thread name ~> output messages |
62 |
{ |
|
60842 | 63 |
def set_session(new_session: Session): State = |
62051 | 64 |
{ |
65 |
session.stop() |
|
60842 | 66 |
copy(session = new_session) |
62051 | 67 |
} |
60842 | 68 |
|
60932
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
69 |
def set_break(b: Boolean): State = copy(break = b) |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
70 |
|
60910 | 71 |
def is_active: Boolean = active > 0 && session.is_ready |
60898 | 72 |
def inc_active: State = copy(active = active + 1) |
73 |
def dec_active: State = copy(active = active - 1) |
|
60876 | 74 |
|
60880
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
75 |
def toggle_breakpoint(breakpoint: Long): (Boolean, State) = |
60876 | 76 |
{ |
77 |
val active_breakpoints1 = |
|
60880
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
78 |
if (active_breakpoints(breakpoint)) active_breakpoints - breakpoint |
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
79 |
else active_breakpoints + breakpoint |
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
80 |
(active_breakpoints1(breakpoint), copy(active_breakpoints = active_breakpoints1)) |
60876 | 81 |
} |
82 |
||
60842 | 83 |
def get_thread(thread_name: String): List[Debug_State] = |
84 |
threads.getOrElse(thread_name, Nil) |
|
85 |
||
86 |
def update_thread(thread_name: String, debug_states: List[Debug_State]): State = |
|
61019
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
87 |
{ |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
88 |
val threads1 = |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
89 |
if (debug_states.nonEmpty) threads + (thread_name -> debug_states) |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
90 |
else threads - thread_name |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
91 |
val focus1 = |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
92 |
focus.get(thread_name) match { |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
93 |
case Some(c) if debug_states.nonEmpty => |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
94 |
focus + (thread_name -> Context(thread_name, debug_states)) |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
95 |
case _ => focus - thread_name |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
96 |
} |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
97 |
copy(threads = threads1, focus = focus1) |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
98 |
} |
60842 | 99 |
|
61014 | 100 |
def set_focus(c: Context): State = copy(focus = focus + (c.thread_name -> c)) |
101 |
||
60842 | 102 |
def get_output(thread_name: String): Command.Results = |
103 |
output.getOrElse(thread_name, Command.Results.empty) |
|
104 |
||
105 |
def add_output(thread_name: String, entry: Command.Results.Entry): State = |
|
106 |
copy(output = output + (thread_name -> (get_output(thread_name) + entry))) |
|
107 |
||
108 |
def clear_output(thread_name: String): State = |
|
109 |
copy(output = output - thread_name) |
|
60834 | 110 |
} |
111 |
||
112 |
private val global_state = Synchronized(State()) |
|
113 |
||
114 |
||
60902 | 115 |
/* update events */ |
60749 | 116 |
|
60900 | 117 |
case object Update |
60749 | 118 |
|
60902 | 119 |
private val delay_update = |
61556 | 120 |
Standard_Thread.delay_first(global_state.value.session.output_delay) { |
60902 | 121 |
global_state.value.session.debugger_updates.post(Update) |
122 |
} |
|
123 |
||
124 |
||
125 |
/* protocol handler */ |
|
126 |
||
60749 | 127 |
class Handler extends Session.Protocol_Handler |
128 |
{ |
|
60842 | 129 |
private def debugger_state(prover: Prover, msg: Prover.Protocol_Output): Boolean = |
130 |
{ |
|
131 |
msg.properties match { |
|
132 |
case Markup.Debugger_State(thread_name) => |
|
60863 | 133 |
val msg_body = |
134 |
YXML.parse_body_failsafe(Symbol.decode(UTF8.decode_permissive(msg.bytes))) |
|
60842 | 135 |
val debug_states = |
136 |
{ |
|
137 |
import XML.Decode._ |
|
138 |
list(pair(properties, Symbol.decode_string))(msg_body).map({ |
|
139 |
case (pos, function) => Debug_State(pos, function) |
|
140 |
}) |
|
141 |
} |
|
142 |
global_state.change(_.update_thread(thread_name, debug_states)) |
|
60900 | 143 |
delay_update.invoke() |
60842 | 144 |
true |
145 |
case _ => false |
|
146 |
} |
|
147 |
} |
|
148 |
||
60830 | 149 |
private def debugger_output(prover: Prover, msg: Prover.Protocol_Output): Boolean = |
150 |
{ |
|
151 |
msg.properties match { |
|
60835 | 152 |
case Markup.Debugger_Output(thread_name) => |
60834 | 153 |
val msg_body = |
154 |
prover.xml_cache.body( |
|
60863 | 155 |
YXML.parse_body_failsafe(Symbol.decode(UTF8.decode_permissive(msg.bytes)))) |
60834 | 156 |
msg_body match { |
157 |
case List(XML.Elem(Markup(name, props @ Markup.Serial(i)), body)) => |
|
158 |
val message = XML.Elem(Markup(Markup.message(name), props), body) |
|
60835 | 159 |
global_state.change(_.add_output(thread_name, i -> message)) |
60900 | 160 |
delay_update.invoke() |
60834 | 161 |
true |
162 |
case _ => false |
|
163 |
} |
|
60830 | 164 |
case _ => false |
165 |
} |
|
166 |
} |
|
167 |
||
168 |
val functions = |
|
60842 | 169 |
Map( |
170 |
Markup.DEBUGGER_STATE -> debugger_state _, |
|
171 |
Markup.DEBUGGER_OUTPUT -> debugger_output _) |
|
60749 | 172 |
} |
60765 | 173 |
|
174 |
||
175 |
/* protocol commands */ |
|
176 |
||
60898 | 177 |
def is_active(): Boolean = global_state.value.is_active |
60889 | 178 |
|
60910 | 179 |
def init_session(session: Session) |
180 |
{ |
|
181 |
global_state.change(state => |
|
182 |
{ |
|
183 |
val state1 = state.set_session(session) |
|
184 |
if (!state.session.is_ready && state1.session.is_ready && state1.is_active) |
|
185 |
state1.session.protocol_command("Debugger.init") |
|
186 |
state1 |
|
187 |
}) |
|
188 |
} |
|
189 |
||
190 |
def init(): Unit = |
|
60889 | 191 |
global_state.change(state => |
192 |
{ |
|
60898 | 193 |
val state1 = state.inc_active |
194 |
if (!state.is_active && state1.is_active) |
|
195 |
state1.session.protocol_command("Debugger.init") |
|
60889 | 196 |
state1 |
197 |
}) |
|
198 |
||
60910 | 199 |
def exit(): Unit = |
60889 | 200 |
global_state.change(state => |
201 |
{ |
|
60898 | 202 |
val state1 = state.dec_active |
203 |
if (state.is_active && !state1.is_active) |
|
204 |
state1.session.protocol_command("Debugger.exit") |
|
60889 | 205 |
state1 |
206 |
}) |
|
60876 | 207 |
|
60932
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
208 |
def is_break(): Boolean = global_state.value.break |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
209 |
def set_break(b: Boolean) |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
210 |
{ |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
211 |
global_state.change(state => { |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
212 |
val state1 = state.set_break(b) |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
213 |
state1.session.protocol_command("Debugger.break", b.toString) |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
214 |
state1 |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
215 |
}) |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
216 |
delay_update.invoke() |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
217 |
} |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
218 |
|
60882 | 219 |
def active_breakpoint_state(breakpoint: Long): Option[Boolean] = |
60876 | 220 |
{ |
60898 | 221 |
val state = global_state.value |
60912 | 222 |
if (state.is_active) Some(state.active_breakpoints(breakpoint)) else None |
60876 | 223 |
} |
224 |
||
60882 | 225 |
def breakpoint_state(breakpoint: Long): Boolean = |
60898 | 226 |
global_state.value.active_breakpoints(breakpoint) |
60882 | 227 |
|
60880
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
228 |
def toggle_breakpoint(command: Command, breakpoint: Long) |
60878
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
wenzelm
parents:
60876
diff
changeset
|
229 |
{ |
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
wenzelm
parents:
60876
diff
changeset
|
230 |
global_state.change(state => |
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
wenzelm
parents:
60876
diff
changeset
|
231 |
{ |
60880
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
232 |
val (breakpoint_state, state1) = state.toggle_breakpoint(breakpoint) |
60878
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
wenzelm
parents:
60876
diff
changeset
|
233 |
state1.session.protocol_command( |
60880
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
234 |
"Debugger.breakpoint", |
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
235 |
Symbol.encode(command.node_name.node), |
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
236 |
Document_ID(command.id), |
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
237 |
Properties.Value.Long(breakpoint), |
fa958e24ff24
set breakpoint state on ML side, relying on stable situation within the PIDE editing queue;
wenzelm
parents:
60878
diff
changeset
|
238 |
Properties.Value.Boolean(breakpoint_state)) |
60878
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
wenzelm
parents:
60876
diff
changeset
|
239 |
state1 |
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
wenzelm
parents:
60876
diff
changeset
|
240 |
}) |
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
wenzelm
parents:
60876
diff
changeset
|
241 |
} |
60876 | 242 |
|
61018 | 243 |
def status(focus: Option[Context]): (Threads, List[XML.Tree]) = |
244 |
{ |
|
245 |
val state = global_state.value |
|
246 |
val output = |
|
247 |
focus match { |
|
248 |
case None => Nil |
|
249 |
case Some(c) => |
|
250 |
(for { |
|
251 |
(thread_name, results) <- state.output |
|
252 |
if thread_name == c.thread_name |
|
253 |
(_, tree) <- results.iterator |
|
254 |
} yield tree).toList |
|
255 |
} |
|
256 |
(state.threads, output) |
|
257 |
} |
|
61011 | 258 |
|
61014 | 259 |
def focus(): List[Context] = global_state.value.focus.toList.map(_._2) |
260 |
def set_focus(c: Context) |
|
61008 | 261 |
{ |
61014 | 262 |
global_state.change(_.set_focus(c)) |
61008 | 263 |
delay_update.invoke() |
264 |
} |
|
60875 | 265 |
|
60854 | 266 |
def input(thread_name: String, msg: String*): Unit = |
60898 | 267 |
global_state.value.session.protocol_command("Debugger.input", (thread_name :: msg.toList):_*) |
60854 | 268 |
|
60899 | 269 |
def continue(thread_name: String): Unit = input(thread_name, "continue") |
60869 | 270 |
def step(thread_name: String): Unit = input(thread_name, "step") |
271 |
def step_over(thread_name: String): Unit = input(thread_name, "step_over") |
|
272 |
def step_out(thread_name: String): Unit = input(thread_name, "step_out") |
|
60856 | 273 |
|
60901 | 274 |
def clear_output(thread_name: String) |
275 |
{ |
|
276 |
global_state.change(_.clear_output(thread_name)) |
|
60902 | 277 |
delay_update.invoke() |
60901 | 278 |
} |
279 |
||
61010 | 280 |
def eval(c: Context, SML: Boolean, context: String, expression: String) |
60856 | 281 |
{ |
60896 | 282 |
global_state.change(state => { |
61014 | 283 |
input(c.thread_name, "eval", c.debug_index.getOrElse(0).toString, |
61010 | 284 |
SML.toString, Symbol.encode(context), Symbol.encode(expression)) |
285 |
state.clear_output(c.thread_name) |
|
60896 | 286 |
}) |
60902 | 287 |
delay_update.invoke() |
60856 | 288 |
} |
60897 | 289 |
|
61010 | 290 |
def print_vals(c: Context, SML: Boolean, context: String) |
60897 | 291 |
{ |
61014 | 292 |
require(c.debug_index.isDefined) |
61010 | 293 |
|
60897 | 294 |
global_state.change(state => { |
61014 | 295 |
input(c.thread_name, "print_vals", c.debug_index.getOrElse(0).toString, |
61010 | 296 |
SML.toString, Symbol.encode(context)) |
297 |
state.clear_output(c.thread_name) |
|
60897 | 298 |
}) |
60902 | 299 |
delay_update.invoke() |
60897 | 300 |
} |
60749 | 301 |
} |