author | wenzelm |
Fri, 01 Apr 2022 23:19:12 +0200 | |
changeset 75394 | 42267c650205 |
parent 75393 | 87ebf5a50283 |
child 75440 | 39011d0d2128 |
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 |
||
75393 | 13 |
object Debugger { |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
14 |
/* thread context */ |
60834 | 15 |
|
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
16 |
case object Update |
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
17 |
|
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
18 |
sealed case class Debug_State(pos: Position.T, function: String) |
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
19 |
type Threads = SortedMap[String, List[Debug_State]] |
60842 | 20 |
|
75393 | 21 |
sealed case class Context(thread_name: String, debug_states: List[Debug_State], index: Int = 0) { |
61010 | 22 |
def size: Int = debug_states.length + 1 |
23 |
def reset: Context = copy(index = 0) |
|
71601 | 24 |
def select(i: Int): Context = copy(index = i + 1) |
61010 | 25 |
|
26 |
def thread_state: Option[Debug_State] = debug_states.headOption |
|
27 |
||
28 |
def stack_state: Option[Debug_State] = |
|
29 |
if (1 <= index && index <= debug_states.length) |
|
30 |
Some(debug_states(index - 1)) |
|
31 |
else None |
|
32 |
||
61014 | 33 |
def debug_index: Option[Int] = |
61010 | 34 |
if (stack_state.isDefined) Some(index - 1) |
35 |
else if (debug_states.nonEmpty) Some(0) |
|
36 |
else None |
|
37 |
||
38 |
def debug_state: Option[Debug_State] = stack_state orElse thread_state |
|
61014 | 39 |
def debug_position: Option[Position.T] = debug_state.map(_.pos) |
61010 | 40 |
|
41 |
override def toString: String = |
|
42 |
stack_state match { |
|
43 |
case None => thread_name |
|
44 |
case Some(d) => d.function |
|
45 |
} |
|
46 |
} |
|
47 |
||
48 |
||
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
49 |
/* debugger state */ |
61016 | 50 |
|
60835 | 51 |
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
|
52 |
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
|
53 |
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
|
54 |
active_breakpoints: Set[Long] = Set.empty, // explicit breakpoint state |
61016 | 55 |
threads: Threads = SortedMap.empty, // thread name ~> stack of debug states |
61014 | 56 |
focus: Map[String, Context] = Map.empty, // thread name ~> focus |
75393 | 57 |
output: Map[String, Command.Results] = Map.empty // thread name ~> output messages |
58 |
) { |
|
60932
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
59 |
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
|
60 |
|
65223
844c067bc3d4
more robust startup, despite remaining race condition of debugger.is_active vs. session.is_ready;
wenzelm
parents:
65222
diff
changeset
|
61 |
def is_active: Boolean = active > 0 |
60898 | 62 |
def inc_active: State = copy(active = active + 1) |
63 |
def dec_active: State = copy(active = active - 1) |
|
60876 | 64 |
|
75393 | 65 |
def toggle_breakpoint(breakpoint: Long): (Boolean, State) = { |
60876 | 66 |
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
|
67 |
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
|
68 |
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
|
69 |
(active_breakpoints1(breakpoint), copy(active_breakpoints = active_breakpoints1)) |
60876 | 70 |
} |
71 |
||
60842 | 72 |
def get_thread(thread_name: String): List[Debug_State] = |
73 |
threads.getOrElse(thread_name, Nil) |
|
74 |
||
75393 | 75 |
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
|
76 |
val threads1 = |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
77 |
if (debug_states.nonEmpty) threads + (thread_name -> debug_states) |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
78 |
else threads - thread_name |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
79 |
val focus1 = |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
80 |
focus.get(thread_name) match { |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
81 |
case Some(c) if debug_states.nonEmpty => |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
82 |
focus + (thread_name -> Context(thread_name, debug_states)) |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
83 |
case _ => focus - thread_name |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
84 |
} |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
85 |
copy(threads = threads1, focus = focus1) |
7ce030f14aa9
reset focus after thread update (with new debug_states);
wenzelm
parents:
61018
diff
changeset
|
86 |
} |
60842 | 87 |
|
61014 | 88 |
def set_focus(c: Context): State = copy(focus = focus + (c.thread_name -> c)) |
89 |
||
60842 | 90 |
def get_output(thread_name: String): Command.Results = |
91 |
output.getOrElse(thread_name, Command.Results.empty) |
|
92 |
||
93 |
def add_output(thread_name: String, entry: Command.Results.Entry): State = |
|
94 |
copy(output = output + (thread_name -> (get_output(thread_name) + entry))) |
|
95 |
||
96 |
def clear_output(thread_name: String): State = |
|
97 |
copy(output = output - thread_name) |
|
60834 | 98 |
} |
99 |
||
60902 | 100 |
|
101 |
/* protocol handler */ |
|
102 |
||
75393 | 103 |
class Handler(session: Session) extends Session.Protocol_Handler { |
65222
fb8253564483
more robust debugger initialization, e.g. required for GUI components before actual session startup;
wenzelm
parents:
65219
diff
changeset
|
104 |
val debugger: Debugger = new Debugger(session) |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
105 |
|
75393 | 106 |
private def debugger_state(msg: Prover.Protocol_Output): Boolean = { |
60842 | 107 |
msg.properties match { |
108 |
case Markup.Debugger_State(thread_name) => |
|
73563 | 109 |
val msg_body = Symbol.decode_yxml_failsafe(msg.text) |
75393 | 110 |
val debug_states = { |
60842 | 111 |
import XML.Decode._ |
65344
b99283eed13c
clarified YXML vs. symbol encoding: operate on whole message;
wenzelm
parents:
65223
diff
changeset
|
112 |
list(pair(properties, string))(msg_body).map({ |
60842 | 113 |
case (pos, function) => Debug_State(pos, function) |
114 |
}) |
|
115 |
} |
|
65222
fb8253564483
more robust debugger initialization, e.g. required for GUI components before actual session startup;
wenzelm
parents:
65219
diff
changeset
|
116 |
debugger.update_thread(thread_name, debug_states) |
60842 | 117 |
true |
118 |
case _ => false |
|
119 |
} |
|
120 |
} |
|
121 |
||
75393 | 122 |
private def debugger_output(msg: Prover.Protocol_Output): Boolean = { |
60830 | 123 |
msg.properties match { |
60835 | 124 |
case Markup.Debugger_Output(thread_name) => |
73563 | 125 |
Symbol.decode_yxml_failsafe(msg.text) match { |
60834 | 126 |
case List(XML.Elem(Markup(name, props @ Markup.Serial(i)), body)) => |
65218 | 127 |
val message = XML.Elem(Markup(Markup.message(name), props), body) |
73031
f93f0597f4fb
clarified signature: absorb XZ.Cache into XML.Cache;
wenzelm
parents:
72212
diff
changeset
|
128 |
debugger.add_output(thread_name, i -> session.cache.elem(message)) |
60834 | 129 |
true |
130 |
case _ => false |
|
131 |
} |
|
60830 | 132 |
case _ => false |
133 |
} |
|
134 |
} |
|
135 |
||
72212 | 136 |
override val functions = |
65219 | 137 |
List( |
71601 | 138 |
Markup.DEBUGGER_STATE -> debugger_state, |
139 |
Markup.DEBUGGER_OUTPUT -> debugger_output) |
|
60749 | 140 |
} |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
141 |
} |
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
142 |
|
75393 | 143 |
class Debugger private(session: Session) { |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
144 |
/* debugger state */ |
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
145 |
|
65223
844c067bc3d4
more robust startup, despite remaining race condition of debugger.is_active vs. session.is_ready;
wenzelm
parents:
65222
diff
changeset
|
146 |
private val state = Synchronized(Debugger.State()) |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
147 |
|
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
148 |
private val delay_update = |
71704 | 149 |
Delay.first(session.output_delay) { |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
150 |
session.debugger_updates.post(Debugger.Update) |
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
151 |
} |
60765 | 152 |
|
153 |
||
154 |
/* protocol commands */ |
|
155 |
||
75393 | 156 |
def update_thread(thread_name: String, debug_states: List[Debugger.Debug_State]): Unit = { |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
157 |
state.change(_.update_thread(thread_name, debug_states)) |
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
158 |
delay_update.invoke() |
60910 | 159 |
} |
160 |
||
75393 | 161 |
def add_output(thread_name: String, entry: Command.Results.Entry): Unit = { |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
162 |
state.change(_.add_output(thread_name, entry)) |
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
163 |
delay_update.invoke() |
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
164 |
} |
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
165 |
|
65223
844c067bc3d4
more robust startup, despite remaining race condition of debugger.is_active vs. session.is_ready;
wenzelm
parents:
65222
diff
changeset
|
166 |
def is_active(): Boolean = session.is_ready && state.value.is_active |
844c067bc3d4
more robust startup, despite remaining race condition of debugger.is_active vs. session.is_ready;
wenzelm
parents:
65222
diff
changeset
|
167 |
|
75393 | 168 |
def ready(): Unit = { |
65223
844c067bc3d4
more robust startup, despite remaining race condition of debugger.is_active vs. session.is_ready;
wenzelm
parents:
65222
diff
changeset
|
169 |
if (is_active()) |
844c067bc3d4
more robust startup, despite remaining race condition of debugger.is_active vs. session.is_ready;
wenzelm
parents:
65222
diff
changeset
|
170 |
session.protocol_command("Debugger.init") |
844c067bc3d4
more robust startup, despite remaining race condition of debugger.is_active vs. session.is_ready;
wenzelm
parents:
65222
diff
changeset
|
171 |
} |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
172 |
|
60910 | 173 |
def init(): Unit = |
75394 | 174 |
state.change { st => |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
175 |
val st1 = st.inc_active |
65223
844c067bc3d4
more robust startup, despite remaining race condition of debugger.is_active vs. session.is_ready;
wenzelm
parents:
65222
diff
changeset
|
176 |
if (session.is_ready && !st.is_active && st1.is_active) |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
177 |
session.protocol_command("Debugger.init") |
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
178 |
st1 |
75394 | 179 |
} |
60889 | 180 |
|
60910 | 181 |
def exit(): Unit = |
75394 | 182 |
state.change { st => |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
183 |
val st1 = st.dec_active |
65223
844c067bc3d4
more robust startup, despite remaining race condition of debugger.is_active vs. session.is_ready;
wenzelm
parents:
65222
diff
changeset
|
184 |
if (session.is_ready && st.is_active && !st1.is_active) |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
185 |
session.protocol_command("Debugger.exit") |
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
186 |
st1 |
75394 | 187 |
} |
60876 | 188 |
|
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
189 |
def is_break(): Boolean = state.value.break |
75393 | 190 |
def set_break(b: Boolean): Unit = { |
75394 | 191 |
state.change { st => |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
192 |
val st1 = st.set_break(b) |
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
193 |
session.protocol_command("Debugger.break", b.toString) |
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
194 |
st1 |
75394 | 195 |
} |
60932
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
196 |
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
|
197 |
} |
13ee73f57c85
allow to break running threads at next possible breakpoint (simplified version of former option, see f3039309702e);
wenzelm
parents:
60912
diff
changeset
|
198 |
|
75393 | 199 |
def active_breakpoint_state(breakpoint: Long): Option[Boolean] = { |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
200 |
val st = state.value |
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
201 |
if (st.is_active) Some(st.active_breakpoints(breakpoint)) else None |
60876 | 202 |
} |
203 |
||
60882 | 204 |
def breakpoint_state(breakpoint: Long): Boolean = |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
205 |
state.value.active_breakpoints(breakpoint) |
60882 | 206 |
|
75393 | 207 |
def toggle_breakpoint(command: Command, breakpoint: Long): Unit = { |
75394 | 208 |
state.change { st => |
75393 | 209 |
val (breakpoint_state, st1) = st.toggle_breakpoint(breakpoint) |
210 |
session.protocol_command( |
|
211 |
"Debugger.breakpoint", |
|
212 |
Symbol.encode(command.node_name.node), |
|
213 |
Document_ID(command.id), |
|
214 |
Value.Long(breakpoint), |
|
215 |
Value.Boolean(breakpoint_state)) |
|
216 |
st1 |
|
75394 | 217 |
} |
60878
1f0d2bbcf38b
added action to toggle breakpoints (on editor side);
wenzelm
parents:
60876
diff
changeset
|
218 |
} |
60876 | 219 |
|
75393 | 220 |
def status(focus: Option[Debugger.Context]): (Debugger.Threads, List[XML.Tree]) = { |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
221 |
val st = state.value |
61018 | 222 |
val output = |
223 |
focus match { |
|
224 |
case None => Nil |
|
225 |
case Some(c) => |
|
226 |
(for { |
|
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
227 |
(thread_name, results) <- st.output |
61018 | 228 |
if thread_name == c.thread_name |
229 |
(_, tree) <- results.iterator |
|
230 |
} yield tree).toList |
|
231 |
} |
|
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
232 |
(st.threads, output) |
61018 | 233 |
} |
61011 | 234 |
|
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
235 |
def focus(): List[Debugger.Context] = state.value.focus.toList.map(_._2) |
75393 | 236 |
def set_focus(c: Debugger.Context): Unit = { |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
237 |
state.change(_.set_focus(c)) |
61008 | 238 |
delay_update.invoke() |
239 |
} |
|
60875 | 240 |
|
60854 | 241 |
def input(thread_name: String, msg: String*): Unit = |
73565 | 242 |
session.protocol_command_args("Debugger.input", thread_name :: msg.toList) |
60854 | 243 |
|
60899 | 244 |
def continue(thread_name: String): Unit = input(thread_name, "continue") |
60869 | 245 |
def step(thread_name: String): Unit = input(thread_name, "step") |
246 |
def step_over(thread_name: String): Unit = input(thread_name, "step_over") |
|
247 |
def step_out(thread_name: String): Unit = input(thread_name, "step_out") |
|
60856 | 248 |
|
75393 | 249 |
def clear_output(thread_name: String): Unit = { |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
250 |
state.change(_.clear_output(thread_name)) |
60902 | 251 |
delay_update.invoke() |
60901 | 252 |
} |
253 |
||
75393 | 254 |
def eval(c: Debugger.Context, SML: Boolean, context: String, expression: String): Unit = { |
75394 | 255 |
state.change { st => |
61014 | 256 |
input(c.thread_name, "eval", c.debug_index.getOrElse(0).toString, |
61010 | 257 |
SML.toString, Symbol.encode(context), Symbol.encode(expression)) |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
258 |
st.clear_output(c.thread_name) |
75394 | 259 |
} |
60902 | 260 |
delay_update.invoke() |
60856 | 261 |
} |
60897 | 262 |
|
75393 | 263 |
def print_vals(c: Debugger.Context, SML: Boolean, context: String): Unit = { |
61014 | 264 |
require(c.debug_index.isDefined) |
61010 | 265 |
|
75394 | 266 |
state.change { st => |
61014 | 267 |
input(c.thread_name, "print_vals", c.debug_index.getOrElse(0).toString, |
61010 | 268 |
SML.toString, Symbol.encode(context)) |
65213
51c0f094dc02
proper local debugger state, depending on session;
wenzelm
parents:
63805
diff
changeset
|
269 |
st.clear_output(c.thread_name) |
75394 | 270 |
} |
60902 | 271 |
delay_update.invoke() |
60897 | 272 |
} |
60749 | 273 |
} |