blob: d8662bbd96ebb3a6f926a6725315c6261f42ed7d [file] [log] [blame]
Chui Tey5d2af632002-05-26 13:36:41 +00001"""Support for remote Python debugging.
2
3Some ASCII art to describe the structure:
4
5 IN PYTHON SUBPROCESS # IN IDLE PROCESS
6 #
7 # oid='gui_adapter'
8 +----------+ # +------------+ +-----+
9 | GUIProxy |--remote#call-->| GUIAdapter |--calls-->| GUI |
10+-----+--calls-->+----------+ # +------------+ +-----+
11| Idb | # /
12+-----+<-calls--+------------+ # +----------+<--calls-/
13 | IdbAdapter |<--remote#call--| IdbProxy |
14 +------------+ # +----------+
15 oid='idb_adapter' #
16
17The purpose of the Proxy and Adapter classes is to translate certain
18arguments and return values that cannot be transported through the RPC
19barrier, in particular frame and traceback objects.
20
21"""
22
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000023import types
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000024from idlelib import rpc
25from idlelib import Debugger
Chui Tey5d2af632002-05-26 13:36:41 +000026
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000027debugging = 0
28
Kurt B. Kaiser63857a42002-09-05 02:31:20 +000029idb_adap_oid = "idb_adapter"
30gui_adap_oid = "gui_adapter"
31
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +000032#=======================================
33#
34# In the PYTHON subprocess:
Chui Tey5d2af632002-05-26 13:36:41 +000035
36frametable = {}
37dicttable = {}
38codetable = {}
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000039tracebacktable = {}
Chui Tey5d2af632002-05-26 13:36:41 +000040
41def wrap_frame(frame):
42 fid = id(frame)
43 frametable[fid] = frame
44 return fid
45
46def wrap_info(info):
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000047 "replace info[2], a traceback instance, by its ID"
Chui Tey5d2af632002-05-26 13:36:41 +000048 if info is None:
49 return None
50 else:
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000051 traceback = info[2]
52 assert isinstance(traceback, types.TracebackType)
53 traceback_id = id(traceback)
54 tracebacktable[traceback_id] = traceback
55 modified_info = (info[0], info[1], traceback_id)
56 return modified_info
Chui Tey5d2af632002-05-26 13:36:41 +000057
58class GUIProxy:
59
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000060 def __init__(self, conn, gui_adap_oid):
Chui Tey5d2af632002-05-26 13:36:41 +000061 self.conn = conn
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000062 self.oid = gui_adap_oid
Chui Tey5d2af632002-05-26 13:36:41 +000063
64 def interaction(self, message, frame, info=None):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +000065 # calls rpc.SocketIO.remotecall() via run.MyHandler instance
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000066 # pass frame and traceback object IDs instead of the objects themselves
Chui Tey5d2af632002-05-26 13:36:41 +000067 self.conn.remotecall(self.oid, "interaction",
68 (message, wrap_frame(frame), wrap_info(info)),
69 {})
70
71class IdbAdapter:
72
73 def __init__(self, idb):
74 self.idb = idb
75
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +000076 #----------called by an IdbProxy----------
77
Chui Tey5d2af632002-05-26 13:36:41 +000078 def set_step(self):
79 self.idb.set_step()
80
81 def set_quit(self):
82 self.idb.set_quit()
83
84 def set_continue(self):
85 self.idb.set_continue()
86
87 def set_next(self, fid):
88 frame = frametable[fid]
89 self.idb.set_next(frame)
90
91 def set_return(self, fid):
92 frame = frametable[fid]
93 self.idb.set_return(frame)
94
95 def get_stack(self, fid, tbid):
Chui Tey5d2af632002-05-26 13:36:41 +000096 frame = frametable[fid]
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000097 if tbid is None:
98 tb = None
99 else:
100 tb = tracebacktable[tbid]
Chui Tey5d2af632002-05-26 13:36:41 +0000101 stack, i = self.idb.get_stack(frame, tb)
Chui Tey5d2af632002-05-26 13:36:41 +0000102 stack = [(wrap_frame(frame), k) for frame, k in stack]
Chui Tey5d2af632002-05-26 13:36:41 +0000103 return stack, i
104
105 def run(self, cmd):
106 import __main__
107 self.idb.run(cmd, __main__.__dict__)
108
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000109 def set_break(self, filename, lineno):
110 msg = self.idb.set_break(filename, lineno)
111 return msg
112
113 def clear_break(self, filename, lineno):
114 msg = self.idb.clear_break(filename, lineno)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000115 return msg
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000116
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000117 def clear_all_file_breaks(self, filename):
118 msg = self.idb.clear_all_file_breaks(filename)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000119 return msg
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000120
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000121 #----------called by a FrameProxy----------
122
Chui Tey5d2af632002-05-26 13:36:41 +0000123 def frame_attr(self, fid, name):
124 frame = frametable[fid]
125 return getattr(frame, name)
126
127 def frame_globals(self, fid):
128 frame = frametable[fid]
129 dict = frame.f_globals
130 did = id(dict)
131 dicttable[did] = dict
132 return did
133
134 def frame_locals(self, fid):
135 frame = frametable[fid]
136 dict = frame.f_locals
137 did = id(dict)
138 dicttable[did] = dict
139 return did
140
141 def frame_code(self, fid):
142 frame = frametable[fid]
143 code = frame.f_code
144 cid = id(code)
145 codetable[cid] = code
146 return cid
147
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000148 #----------called by a CodeProxy----------
149
Chui Tey5d2af632002-05-26 13:36:41 +0000150 def code_name(self, cid):
151 code = codetable[cid]
152 return code.co_name
153
154 def code_filename(self, cid):
155 code = codetable[cid]
156 return code.co_filename
157
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000158 #----------called by a DictProxy----------
159
Chui Tey5d2af632002-05-26 13:36:41 +0000160 def dict_keys(self, did):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000161 raise NotImplemented("dict_keys not public or pickleable")
162## dict = dicttable[did]
163## return dict.keys()
164
165 ### Needed until dict_keys is type is finished and pickealable.
166 ### Will probably need to extend rpc.py:SocketIO._proxify at that time.
167 def dict_keys_list(self, did):
Chui Tey5d2af632002-05-26 13:36:41 +0000168 dict = dicttable[did]
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000169 return list(dict.keys())
Chui Tey5d2af632002-05-26 13:36:41 +0000170
171 def dict_item(self, did, key):
172 dict = dicttable[did]
173 value = dict[key]
Georg Brandl1a3284e2007-12-02 09:40:06 +0000174 value = repr(value) ### can't pickle module 'builtins'
Chui Tey5d2af632002-05-26 13:36:41 +0000175 return value
176
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000177#----------end class IdbAdapter----------
178
179
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000180def start_debugger(rpchandler, gui_adap_oid):
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000181 """Start the debugger and its RPC link in the Python subprocess
182
183 Start the subprocess side of the split debugger and set up that side of the
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000184 RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000185 objects and linking them together. Register the IdbAdapter with the
186 RPCServer to handle RPC requests from the split debugger GUI via the
187 IdbProxy.
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000188
189 """
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000190 gui_proxy = GUIProxy(rpchandler, gui_adap_oid)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000191 idb = Debugger.Idb(gui_proxy)
192 idb_adap = IdbAdapter(idb)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000193 rpchandler.register(idb_adap_oid, idb_adap)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000194 return idb_adap_oid
Chui Tey5d2af632002-05-26 13:36:41 +0000195
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000196
197#=======================================
198#
199# In the IDLE process:
200
Chui Tey5d2af632002-05-26 13:36:41 +0000201
202class FrameProxy:
203
204 def __init__(self, conn, fid):
205 self._conn = conn
206 self._fid = fid
207 self._oid = "idb_adapter"
208 self._dictcache = {}
209
210 def __getattr__(self, name):
211 if name[:1] == "_":
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000212 raise AttributeError(name)
Chui Tey5d2af632002-05-26 13:36:41 +0000213 if name == "f_code":
214 return self._get_f_code()
215 if name == "f_globals":
216 return self._get_f_globals()
217 if name == "f_locals":
218 return self._get_f_locals()
219 return self._conn.remotecall(self._oid, "frame_attr",
220 (self._fid, name), {})
221
222 def _get_f_code(self):
223 cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
224 return CodeProxy(self._conn, self._oid, cid)
225
226 def _get_f_globals(self):
227 did = self._conn.remotecall(self._oid, "frame_globals",
228 (self._fid,), {})
229 return self._get_dict_proxy(did)
230
231 def _get_f_locals(self):
232 did = self._conn.remotecall(self._oid, "frame_locals",
233 (self._fid,), {})
234 return self._get_dict_proxy(did)
235
236 def _get_dict_proxy(self, did):
Guido van Rossum811c4e02006-08-22 15:45:46 +0000237 if did in self._dictcache:
Chui Tey5d2af632002-05-26 13:36:41 +0000238 return self._dictcache[did]
239 dp = DictProxy(self._conn, self._oid, did)
240 self._dictcache[did] = dp
241 return dp
242
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000243
Chui Tey5d2af632002-05-26 13:36:41 +0000244class CodeProxy:
245
246 def __init__(self, conn, oid, cid):
247 self._conn = conn
248 self._oid = oid
249 self._cid = cid
250
251 def __getattr__(self, name):
252 if name == "co_name":
253 return self._conn.remotecall(self._oid, "code_name",
254 (self._cid,), {})
255 if name == "co_filename":
256 return self._conn.remotecall(self._oid, "code_filename",
257 (self._cid,), {})
258
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000259
Chui Tey5d2af632002-05-26 13:36:41 +0000260class DictProxy:
261
262 def __init__(self, conn, oid, did):
263 self._conn = conn
264 self._oid = oid
265 self._did = did
266
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000267## def keys(self):
268## return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})
269
270 # 'temporary' until dict_keys is a pickleable built-in type
Chui Tey5d2af632002-05-26 13:36:41 +0000271 def keys(self):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000272 return self._conn.remotecall(self._oid,
273 "dict_keys_list", (self._did,), {})
Chui Tey5d2af632002-05-26 13:36:41 +0000274
275 def __getitem__(self, key):
276 return self._conn.remotecall(self._oid, "dict_item",
277 (self._did, key), {})
278
279 def __getattr__(self, name):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000280 ##print("*** Failed DictProxy.__getattr__:", name)
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000281 raise AttributeError(name)
Chui Tey5d2af632002-05-26 13:36:41 +0000282
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000283
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000284class GUIAdapter:
Chui Tey5d2af632002-05-26 13:36:41 +0000285
286 def __init__(self, conn, gui):
287 self.conn = conn
288 self.gui = gui
289
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000290 def interaction(self, message, fid, modified_info):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000291 ##print("*** Interaction: (%s, %s, %s)" % (message, fid, modified_info))
Chui Tey5d2af632002-05-26 13:36:41 +0000292 frame = FrameProxy(self.conn, fid)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000293 self.gui.interaction(message, frame, modified_info)
Chui Tey5d2af632002-05-26 13:36:41 +0000294
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000295
Chui Tey5d2af632002-05-26 13:36:41 +0000296class IdbProxy:
297
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000298 def __init__(self, conn, shell, oid):
Chui Tey5d2af632002-05-26 13:36:41 +0000299 self.oid = oid
300 self.conn = conn
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000301 self.shell = shell
Chui Tey5d2af632002-05-26 13:36:41 +0000302
303 def call(self, methodname, *args, **kwargs):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000304 ##print("*** IdbProxy.call %s %s %s" % (methodname, args, kwargs))
Chui Tey5d2af632002-05-26 13:36:41 +0000305 value = self.conn.remotecall(self.oid, methodname, args, kwargs)
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000306 ##print("*** IdbProxy.call %s returns %r" % (methodname, value))
Chui Tey5d2af632002-05-26 13:36:41 +0000307 return value
308
309 def run(self, cmd, locals):
310 # Ignores locals on purpose!
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000311 seq = self.conn.asyncqueue(self.oid, "run", (cmd,), {})
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000312 self.shell.interp.active_seq = seq
Chui Tey5d2af632002-05-26 13:36:41 +0000313
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000314 def get_stack(self, frame, tbid):
315 # passing frame and traceback IDs, not the objects themselves
316 stack, i = self.call("get_stack", frame._fid, tbid)
Chui Tey5d2af632002-05-26 13:36:41 +0000317 stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
318 return stack, i
319
320 def set_continue(self):
321 self.call("set_continue")
322
323 def set_step(self):
324 self.call("set_step")
325
326 def set_next(self, frame):
327 self.call("set_next", frame._fid)
328
329 def set_return(self, frame):
330 self.call("set_return", frame._fid)
331
332 def set_quit(self):
333 self.call("set_quit")
334
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000335 def set_break(self, filename, lineno):
336 msg = self.call("set_break", filename, lineno)
337 return msg
338
339 def clear_break(self, filename, lineno):
340 msg = self.call("clear_break", filename, lineno)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000341 return msg
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000342
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000343 def clear_all_file_breaks(self, filename):
344 msg = self.call("clear_all_file_breaks", filename)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000345 return msg
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000346
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000347def start_remote_debugger(rpcclt, pyshell):
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000348 """Start the subprocess debugger, initialize the debugger GUI and RPC link
349
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000350 Request the RPCServer start the Python subprocess debugger and link. Set
351 up the Idle side of the split debugger by instantiating the IdbProxy,
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000352 debugger GUI, and debugger GUIAdapter objects and linking them together.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000353
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000354 Register the GUIAdapter with the RPCClient to handle debugger GUI
355 interaction requests coming from the subprocess debugger via the GUIProxy.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000356
357 The IdbAdapter will pass execution and environment requests coming from the
358 Idle debugger GUI to the subprocess debugger via the IdbProxy.
359
360 """
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000361 global idb_adap_oid
362
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000363 idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000364 (gui_adap_oid,), {})
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000365 idb_proxy = IdbProxy(rpcclt, pyshell, idb_adap_oid)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000366 gui = Debugger.Debugger(pyshell, idb_proxy)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000367 gui_adap = GUIAdapter(rpcclt, gui)
368 rpcclt.register(gui_adap_oid, gui_adap)
Chui Tey5d2af632002-05-26 13:36:41 +0000369 return gui
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000370
371def close_remote_debugger(rpcclt):
372 """Shut down subprocess debugger and Idle side of debugger RPC link
373
374 Request that the RPCServer shut down the subprocess debugger and link.
375 Unregister the GUIAdapter, which will cause a GC on the Idle process
376 debugger and RPC link objects. (The second reference to the debugger GUI
377 is deleted in PyShell.close_remote_debugger().)
378
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000379 """
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000380 close_subprocess_debugger(rpcclt)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000381 rpcclt.unregister(gui_adap_oid)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000382
383def close_subprocess_debugger(rpcclt):
384 rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})
385
386def restart_subprocess_debugger(rpcclt):
387 idb_adap_oid_ret = rpcclt.remotecall("exec", "start_the_debugger",\
388 (gui_adap_oid,), {})
389 assert idb_adap_oid_ret == idb_adap_oid, 'Idb restarted with different oid'