blob: 01a3bd25998fc04ba1bc4acc7814f5b9d0fbf142 [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
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040024from idlelib import debugger
Chui Tey5d2af632002-05-26 13:36:41 +000025
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000026debugging = 0
27
Kurt B. Kaiser63857a42002-09-05 02:31:20 +000028idb_adap_oid = "idb_adapter"
29gui_adap_oid = "gui_adapter"
30
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +000031#=======================================
32#
33# In the PYTHON subprocess:
Chui Tey5d2af632002-05-26 13:36:41 +000034
35frametable = {}
36dicttable = {}
37codetable = {}
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000038tracebacktable = {}
Chui Tey5d2af632002-05-26 13:36:41 +000039
40def wrap_frame(frame):
41 fid = id(frame)
42 frametable[fid] = frame
43 return fid
44
45def wrap_info(info):
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000046 "replace info[2], a traceback instance, by its ID"
Chui Tey5d2af632002-05-26 13:36:41 +000047 if info is None:
48 return None
49 else:
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000050 traceback = info[2]
51 assert isinstance(traceback, types.TracebackType)
52 traceback_id = id(traceback)
53 tracebacktable[traceback_id] = traceback
54 modified_info = (info[0], info[1], traceback_id)
55 return modified_info
Chui Tey5d2af632002-05-26 13:36:41 +000056
57class GUIProxy:
58
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000059 def __init__(self, conn, gui_adap_oid):
Chui Tey5d2af632002-05-26 13:36:41 +000060 self.conn = conn
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000061 self.oid = gui_adap_oid
Chui Tey5d2af632002-05-26 13:36:41 +000062
63 def interaction(self, message, frame, info=None):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +000064 # calls rpc.SocketIO.remotecall() via run.MyHandler instance
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000065 # pass frame and traceback object IDs instead of the objects themselves
Chui Tey5d2af632002-05-26 13:36:41 +000066 self.conn.remotecall(self.oid, "interaction",
67 (message, wrap_frame(frame), wrap_info(info)),
68 {})
69
70class IdbAdapter:
71
72 def __init__(self, idb):
73 self.idb = idb
74
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +000075 #----------called by an IdbProxy----------
76
Chui Tey5d2af632002-05-26 13:36:41 +000077 def set_step(self):
78 self.idb.set_step()
79
80 def set_quit(self):
81 self.idb.set_quit()
82
83 def set_continue(self):
84 self.idb.set_continue()
85
86 def set_next(self, fid):
87 frame = frametable[fid]
88 self.idb.set_next(frame)
89
90 def set_return(self, fid):
91 frame = frametable[fid]
92 self.idb.set_return(frame)
93
94 def get_stack(self, fid, tbid):
Chui Tey5d2af632002-05-26 13:36:41 +000095 frame = frametable[fid]
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000096 if tbid is None:
97 tb = None
98 else:
99 tb = tracebacktable[tbid]
Chui Tey5d2af632002-05-26 13:36:41 +0000100 stack, i = self.idb.get_stack(frame, tb)
Terry Jan Reedy038c16b2015-05-15 23:03:17 -0400101 stack = [(wrap_frame(frame2), k) for frame2, k in stack]
Chui Tey5d2af632002-05-26 13:36:41 +0000102 return stack, i
103
104 def run(self, cmd):
105 import __main__
106 self.idb.run(cmd, __main__.__dict__)
107
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000108 def set_break(self, filename, lineno):
109 msg = self.idb.set_break(filename, lineno)
110 return msg
111
112 def clear_break(self, filename, lineno):
113 msg = self.idb.clear_break(filename, lineno)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000114 return msg
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000115
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000116 def clear_all_file_breaks(self, filename):
117 msg = self.idb.clear_all_file_breaks(filename)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000118 return msg
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000119
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000120 #----------called by a FrameProxy----------
121
Chui Tey5d2af632002-05-26 13:36:41 +0000122 def frame_attr(self, fid, name):
123 frame = frametable[fid]
124 return getattr(frame, name)
125
126 def frame_globals(self, fid):
127 frame = frametable[fid]
128 dict = frame.f_globals
129 did = id(dict)
130 dicttable[did] = dict
131 return did
132
133 def frame_locals(self, fid):
134 frame = frametable[fid]
135 dict = frame.f_locals
136 did = id(dict)
137 dicttable[did] = dict
138 return did
139
140 def frame_code(self, fid):
141 frame = frametable[fid]
142 code = frame.f_code
143 cid = id(code)
144 codetable[cid] = code
145 return cid
146
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000147 #----------called by a CodeProxy----------
148
Chui Tey5d2af632002-05-26 13:36:41 +0000149 def code_name(self, cid):
150 code = codetable[cid]
151 return code.co_name
152
153 def code_filename(self, cid):
154 code = codetable[cid]
155 return code.co_filename
156
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000157 #----------called by a DictProxy----------
158
Chui Tey5d2af632002-05-26 13:36:41 +0000159 def dict_keys(self, did):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000160 raise NotImplemented("dict_keys not public or pickleable")
161## dict = dicttable[did]
162## return dict.keys()
163
164 ### Needed until dict_keys is type is finished and pickealable.
165 ### Will probably need to extend rpc.py:SocketIO._proxify at that time.
166 def dict_keys_list(self, did):
Chui Tey5d2af632002-05-26 13:36:41 +0000167 dict = dicttable[did]
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000168 return list(dict.keys())
Chui Tey5d2af632002-05-26 13:36:41 +0000169
170 def dict_item(self, did, key):
171 dict = dicttable[did]
172 value = dict[key]
Georg Brandl1a3284e2007-12-02 09:40:06 +0000173 value = repr(value) ### can't pickle module 'builtins'
Chui Tey5d2af632002-05-26 13:36:41 +0000174 return value
175
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000176#----------end class IdbAdapter----------
177
178
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000179def start_debugger(rpchandler, gui_adap_oid):
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000180 """Start the debugger and its RPC link in the Python subprocess
181
182 Start the subprocess side of the split debugger and set up that side of the
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000183 RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000184 objects and linking them together. Register the IdbAdapter with the
185 RPCServer to handle RPC requests from the split debugger GUI via the
186 IdbProxy.
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000187
188 """
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000189 gui_proxy = GUIProxy(rpchandler, gui_adap_oid)
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400190 idb = debugger.Idb(gui_proxy)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000191 idb_adap = IdbAdapter(idb)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000192 rpchandler.register(idb_adap_oid, idb_adap)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000193 return idb_adap_oid
Chui Tey5d2af632002-05-26 13:36:41 +0000194
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000195
196#=======================================
197#
198# In the IDLE process:
199
Chui Tey5d2af632002-05-26 13:36:41 +0000200
201class FrameProxy:
202
203 def __init__(self, conn, fid):
204 self._conn = conn
205 self._fid = fid
206 self._oid = "idb_adapter"
207 self._dictcache = {}
208
209 def __getattr__(self, name):
210 if name[:1] == "_":
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000211 raise AttributeError(name)
Chui Tey5d2af632002-05-26 13:36:41 +0000212 if name == "f_code":
213 return self._get_f_code()
214 if name == "f_globals":
215 return self._get_f_globals()
216 if name == "f_locals":
217 return self._get_f_locals()
218 return self._conn.remotecall(self._oid, "frame_attr",
219 (self._fid, name), {})
220
221 def _get_f_code(self):
222 cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
223 return CodeProxy(self._conn, self._oid, cid)
224
225 def _get_f_globals(self):
226 did = self._conn.remotecall(self._oid, "frame_globals",
227 (self._fid,), {})
228 return self._get_dict_proxy(did)
229
230 def _get_f_locals(self):
231 did = self._conn.remotecall(self._oid, "frame_locals",
232 (self._fid,), {})
233 return self._get_dict_proxy(did)
234
235 def _get_dict_proxy(self, did):
Guido van Rossum811c4e02006-08-22 15:45:46 +0000236 if did in self._dictcache:
Chui Tey5d2af632002-05-26 13:36:41 +0000237 return self._dictcache[did]
238 dp = DictProxy(self._conn, self._oid, did)
239 self._dictcache[did] = dp
240 return dp
241
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000242
Chui Tey5d2af632002-05-26 13:36:41 +0000243class CodeProxy:
244
245 def __init__(self, conn, oid, cid):
246 self._conn = conn
247 self._oid = oid
248 self._cid = cid
249
250 def __getattr__(self, name):
251 if name == "co_name":
252 return self._conn.remotecall(self._oid, "code_name",
253 (self._cid,), {})
254 if name == "co_filename":
255 return self._conn.remotecall(self._oid, "code_filename",
256 (self._cid,), {})
257
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000258
Chui Tey5d2af632002-05-26 13:36:41 +0000259class DictProxy:
260
261 def __init__(self, conn, oid, did):
262 self._conn = conn
263 self._oid = oid
264 self._did = did
265
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000266## def keys(self):
267## return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})
268
269 # 'temporary' until dict_keys is a pickleable built-in type
Chui Tey5d2af632002-05-26 13:36:41 +0000270 def keys(self):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000271 return self._conn.remotecall(self._oid,
272 "dict_keys_list", (self._did,), {})
Chui Tey5d2af632002-05-26 13:36:41 +0000273
274 def __getitem__(self, key):
275 return self._conn.remotecall(self._oid, "dict_item",
276 (self._did, key), {})
277
278 def __getattr__(self, name):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000279 ##print("*** Failed DictProxy.__getattr__:", name)
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000280 raise AttributeError(name)
Chui Tey5d2af632002-05-26 13:36:41 +0000281
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000282
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000283class GUIAdapter:
Chui Tey5d2af632002-05-26 13:36:41 +0000284
285 def __init__(self, conn, gui):
286 self.conn = conn
287 self.gui = gui
288
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000289 def interaction(self, message, fid, modified_info):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000290 ##print("*** Interaction: (%s, %s, %s)" % (message, fid, modified_info))
Chui Tey5d2af632002-05-26 13:36:41 +0000291 frame = FrameProxy(self.conn, fid)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000292 self.gui.interaction(message, frame, modified_info)
Chui Tey5d2af632002-05-26 13:36:41 +0000293
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000294
Chui Tey5d2af632002-05-26 13:36:41 +0000295class IdbProxy:
296
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000297 def __init__(self, conn, shell, oid):
Chui Tey5d2af632002-05-26 13:36:41 +0000298 self.oid = oid
299 self.conn = conn
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000300 self.shell = shell
Chui Tey5d2af632002-05-26 13:36:41 +0000301
302 def call(self, methodname, *args, **kwargs):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000303 ##print("*** IdbProxy.call %s %s %s" % (methodname, args, kwargs))
Chui Tey5d2af632002-05-26 13:36:41 +0000304 value = self.conn.remotecall(self.oid, methodname, args, kwargs)
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000305 ##print("*** IdbProxy.call %s returns %r" % (methodname, value))
Chui Tey5d2af632002-05-26 13:36:41 +0000306 return value
307
308 def run(self, cmd, locals):
309 # Ignores locals on purpose!
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000310 seq = self.conn.asyncqueue(self.oid, "run", (cmd,), {})
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000311 self.shell.interp.active_seq = seq
Chui Tey5d2af632002-05-26 13:36:41 +0000312
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000313 def get_stack(self, frame, tbid):
314 # passing frame and traceback IDs, not the objects themselves
315 stack, i = self.call("get_stack", frame._fid, tbid)
Chui Tey5d2af632002-05-26 13:36:41 +0000316 stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
317 return stack, i
318
319 def set_continue(self):
320 self.call("set_continue")
321
322 def set_step(self):
323 self.call("set_step")
324
325 def set_next(self, frame):
326 self.call("set_next", frame._fid)
327
328 def set_return(self, frame):
329 self.call("set_return", frame._fid)
330
331 def set_quit(self):
332 self.call("set_quit")
333
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000334 def set_break(self, filename, lineno):
335 msg = self.call("set_break", filename, lineno)
336 return msg
337
338 def clear_break(self, filename, lineno):
339 msg = self.call("clear_break", filename, lineno)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000340 return msg
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000341
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000342 def clear_all_file_breaks(self, filename):
343 msg = self.call("clear_all_file_breaks", filename)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000344 return msg
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000345
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000346def start_remote_debugger(rpcclt, pyshell):
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000347 """Start the subprocess debugger, initialize the debugger GUI and RPC link
348
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000349 Request the RPCServer start the Python subprocess debugger and link. Set
350 up the Idle side of the split debugger by instantiating the IdbProxy,
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000351 debugger GUI, and debugger GUIAdapter objects and linking them together.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000352
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000353 Register the GUIAdapter with the RPCClient to handle debugger GUI
354 interaction requests coming from the subprocess debugger via the GUIProxy.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000355
356 The IdbAdapter will pass execution and environment requests coming from the
357 Idle debugger GUI to the subprocess debugger via the IdbProxy.
358
359 """
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000360 global idb_adap_oid
361
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000362 idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000363 (gui_adap_oid,), {})
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000364 idb_proxy = IdbProxy(rpcclt, pyshell, idb_adap_oid)
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400365 gui = debugger.Debugger(pyshell, idb_proxy)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000366 gui_adap = GUIAdapter(rpcclt, gui)
367 rpcclt.register(gui_adap_oid, gui_adap)
Chui Tey5d2af632002-05-26 13:36:41 +0000368 return gui
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000369
370def close_remote_debugger(rpcclt):
371 """Shut down subprocess debugger and Idle side of debugger RPC link
372
373 Request that the RPCServer shut down the subprocess debugger and link.
374 Unregister the GUIAdapter, which will cause a GC on the Idle process
375 debugger and RPC link objects. (The second reference to the debugger GUI
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400376 is deleted in pyshell.close_remote_debugger().)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000377
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000378 """
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000379 close_subprocess_debugger(rpcclt)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000380 rpcclt.unregister(gui_adap_oid)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000381
382def close_subprocess_debugger(rpcclt):
383 rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})
384
385def restart_subprocess_debugger(rpcclt):
386 idb_adap_oid_ret = rpcclt.remotecall("exec", "start_the_debugger",\
387 (gui_adap_oid,), {})
388 assert idb_adap_oid_ret == idb_adap_oid, 'Idb restarted with different oid'
Terry Jan Reedyee5ef302018-06-15 18:20:55 -0400389
390
391if __name__ == "__main__":
392 from unittest import main
393 main('idlelib.idle_test.test_debugger', verbosity=2, exit=False)