blob: e5b95b4e6ba785fb09d2f73a2e256ab1d721752f [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
23import sys
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000024import types
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +000025from idlelib import rpc
26from idlelib import Debugger
Chui Tey5d2af632002-05-26 13:36:41 +000027
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000028debugging = 0
29
Kurt B. Kaiser63857a42002-09-05 02:31:20 +000030idb_adap_oid = "idb_adapter"
31gui_adap_oid = "gui_adapter"
32
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +000033#=======================================
34#
35# In the PYTHON subprocess:
Chui Tey5d2af632002-05-26 13:36:41 +000036
37frametable = {}
38dicttable = {}
39codetable = {}
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000040tracebacktable = {}
Chui Tey5d2af632002-05-26 13:36:41 +000041
42def wrap_frame(frame):
43 fid = id(frame)
44 frametable[fid] = frame
45 return fid
46
47def wrap_info(info):
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000048 "replace info[2], a traceback instance, by its ID"
Chui Tey5d2af632002-05-26 13:36:41 +000049 if info is None:
50 return None
51 else:
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000052 traceback = info[2]
53 assert isinstance(traceback, types.TracebackType)
54 traceback_id = id(traceback)
55 tracebacktable[traceback_id] = traceback
56 modified_info = (info[0], info[1], traceback_id)
57 return modified_info
Chui Tey5d2af632002-05-26 13:36:41 +000058
59class GUIProxy:
60
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000061 def __init__(self, conn, gui_adap_oid):
Chui Tey5d2af632002-05-26 13:36:41 +000062 self.conn = conn
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000063 self.oid = gui_adap_oid
Chui Tey5d2af632002-05-26 13:36:41 +000064
65 def interaction(self, message, frame, info=None):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +000066 # calls rpc.SocketIO.remotecall() via run.MyHandler instance
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000067 # pass frame and traceback object IDs instead of the objects themselves
Chui Tey5d2af632002-05-26 13:36:41 +000068 self.conn.remotecall(self.oid, "interaction",
69 (message, wrap_frame(frame), wrap_info(info)),
70 {})
71
72class IdbAdapter:
73
74 def __init__(self, idb):
75 self.idb = idb
76
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +000077 #----------called by an IdbProxy----------
78
Chui Tey5d2af632002-05-26 13:36:41 +000079 def set_step(self):
80 self.idb.set_step()
81
82 def set_quit(self):
83 self.idb.set_quit()
84
85 def set_continue(self):
86 self.idb.set_continue()
87
88 def set_next(self, fid):
89 frame = frametable[fid]
90 self.idb.set_next(frame)
91
92 def set_return(self, fid):
93 frame = frametable[fid]
94 self.idb.set_return(frame)
95
96 def get_stack(self, fid, tbid):
Chui Tey5d2af632002-05-26 13:36:41 +000097 frame = frametable[fid]
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000098 if tbid is None:
99 tb = None
100 else:
101 tb = tracebacktable[tbid]
Chui Tey5d2af632002-05-26 13:36:41 +0000102 stack, i = self.idb.get_stack(frame, tb)
Chui Tey5d2af632002-05-26 13:36:41 +0000103 stack = [(wrap_frame(frame), k) for frame, k in stack]
Chui Tey5d2af632002-05-26 13:36:41 +0000104 return stack, i
105
106 def run(self, cmd):
107 import __main__
108 self.idb.run(cmd, __main__.__dict__)
109
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000110 def set_break(self, filename, lineno):
111 msg = self.idb.set_break(filename, lineno)
112 return msg
113
114 def clear_break(self, filename, lineno):
115 msg = self.idb.clear_break(filename, lineno)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000116 return msg
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000117
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000118 def clear_all_file_breaks(self, filename):
119 msg = self.idb.clear_all_file_breaks(filename)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000120 return msg
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000121
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000122 #----------called by a FrameProxy----------
123
Chui Tey5d2af632002-05-26 13:36:41 +0000124 def frame_attr(self, fid, name):
125 frame = frametable[fid]
126 return getattr(frame, name)
127
128 def frame_globals(self, fid):
129 frame = frametable[fid]
130 dict = frame.f_globals
131 did = id(dict)
132 dicttable[did] = dict
133 return did
134
135 def frame_locals(self, fid):
136 frame = frametable[fid]
137 dict = frame.f_locals
138 did = id(dict)
139 dicttable[did] = dict
140 return did
141
142 def frame_code(self, fid):
143 frame = frametable[fid]
144 code = frame.f_code
145 cid = id(code)
146 codetable[cid] = code
147 return cid
148
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000149 #----------called by a CodeProxy----------
150
Chui Tey5d2af632002-05-26 13:36:41 +0000151 def code_name(self, cid):
152 code = codetable[cid]
153 return code.co_name
154
155 def code_filename(self, cid):
156 code = codetable[cid]
157 return code.co_filename
158
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000159 #----------called by a DictProxy----------
160
Chui Tey5d2af632002-05-26 13:36:41 +0000161 def dict_keys(self, did):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000162 raise NotImplemented("dict_keys not public or pickleable")
163## dict = dicttable[did]
164## return dict.keys()
165
166 ### Needed until dict_keys is type is finished and pickealable.
167 ### Will probably need to extend rpc.py:SocketIO._proxify at that time.
168 def dict_keys_list(self, did):
Chui Tey5d2af632002-05-26 13:36:41 +0000169 dict = dicttable[did]
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000170 return list(dict.keys())
Chui Tey5d2af632002-05-26 13:36:41 +0000171
172 def dict_item(self, did, key):
173 dict = dicttable[did]
174 value = dict[key]
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000175 value = repr(value) ### can't pickle module '__builtin__'
Chui Tey5d2af632002-05-26 13:36:41 +0000176 return value
177
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000178#----------end class IdbAdapter----------
179
180
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000181def start_debugger(rpchandler, gui_adap_oid):
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000182 """Start the debugger and its RPC link in the Python subprocess
183
184 Start the subprocess side of the split debugger and set up that side of the
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000185 RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000186 objects and linking them together. Register the IdbAdapter with the
187 RPCServer to handle RPC requests from the split debugger GUI via the
188 IdbProxy.
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000189
190 """
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000191 gui_proxy = GUIProxy(rpchandler, gui_adap_oid)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000192 idb = Debugger.Idb(gui_proxy)
193 idb_adap = IdbAdapter(idb)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000194 rpchandler.register(idb_adap_oid, idb_adap)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000195 return idb_adap_oid
Chui Tey5d2af632002-05-26 13:36:41 +0000196
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000197
198#=======================================
199#
200# In the IDLE process:
201
Chui Tey5d2af632002-05-26 13:36:41 +0000202
203class FrameProxy:
204
205 def __init__(self, conn, fid):
206 self._conn = conn
207 self._fid = fid
208 self._oid = "idb_adapter"
209 self._dictcache = {}
210
211 def __getattr__(self, name):
212 if name[:1] == "_":
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000213 raise AttributeError(name)
Chui Tey5d2af632002-05-26 13:36:41 +0000214 if name == "f_code":
215 return self._get_f_code()
216 if name == "f_globals":
217 return self._get_f_globals()
218 if name == "f_locals":
219 return self._get_f_locals()
220 return self._conn.remotecall(self._oid, "frame_attr",
221 (self._fid, name), {})
222
223 def _get_f_code(self):
224 cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
225 return CodeProxy(self._conn, self._oid, cid)
226
227 def _get_f_globals(self):
228 did = self._conn.remotecall(self._oid, "frame_globals",
229 (self._fid,), {})
230 return self._get_dict_proxy(did)
231
232 def _get_f_locals(self):
233 did = self._conn.remotecall(self._oid, "frame_locals",
234 (self._fid,), {})
235 return self._get_dict_proxy(did)
236
237 def _get_dict_proxy(self, did):
Guido van Rossum811c4e02006-08-22 15:45:46 +0000238 if did in self._dictcache:
Chui Tey5d2af632002-05-26 13:36:41 +0000239 return self._dictcache[did]
240 dp = DictProxy(self._conn, self._oid, did)
241 self._dictcache[did] = dp
242 return dp
243
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000244
Chui Tey5d2af632002-05-26 13:36:41 +0000245class CodeProxy:
246
247 def __init__(self, conn, oid, cid):
248 self._conn = conn
249 self._oid = oid
250 self._cid = cid
251
252 def __getattr__(self, name):
253 if name == "co_name":
254 return self._conn.remotecall(self._oid, "code_name",
255 (self._cid,), {})
256 if name == "co_filename":
257 return self._conn.remotecall(self._oid, "code_filename",
258 (self._cid,), {})
259
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000260
Chui Tey5d2af632002-05-26 13:36:41 +0000261class DictProxy:
262
263 def __init__(self, conn, oid, did):
264 self._conn = conn
265 self._oid = oid
266 self._did = did
267
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000268## def keys(self):
269## return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})
270
271 # 'temporary' until dict_keys is a pickleable built-in type
Chui Tey5d2af632002-05-26 13:36:41 +0000272 def keys(self):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000273 return self._conn.remotecall(self._oid,
274 "dict_keys_list", (self._did,), {})
Chui Tey5d2af632002-05-26 13:36:41 +0000275
276 def __getitem__(self, key):
277 return self._conn.remotecall(self._oid, "dict_item",
278 (self._did, key), {})
279
280 def __getattr__(self, name):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000281 ##print("*** Failed DictProxy.__getattr__:", name)
Kurt B. Kaiserad667422007-08-23 01:06:15 +0000282 raise AttributeError(name)
Chui Tey5d2af632002-05-26 13:36:41 +0000283
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000284
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000285class GUIAdapter:
Chui Tey5d2af632002-05-26 13:36:41 +0000286
287 def __init__(self, conn, gui):
288 self.conn = conn
289 self.gui = gui
290
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000291 def interaction(self, message, fid, modified_info):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000292 ##print("*** Interaction: (%s, %s, %s)" % (message, fid, modified_info))
Chui Tey5d2af632002-05-26 13:36:41 +0000293 frame = FrameProxy(self.conn, fid)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000294 self.gui.interaction(message, frame, modified_info)
Chui Tey5d2af632002-05-26 13:36:41 +0000295
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000296
Chui Tey5d2af632002-05-26 13:36:41 +0000297class IdbProxy:
298
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000299 def __init__(self, conn, shell, oid):
Chui Tey5d2af632002-05-26 13:36:41 +0000300 self.oid = oid
301 self.conn = conn
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000302 self.shell = shell
Chui Tey5d2af632002-05-26 13:36:41 +0000303
304 def call(self, methodname, *args, **kwargs):
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000305 ##print("*** IdbProxy.call %s %s %s" % (methodname, args, kwargs))
Chui Tey5d2af632002-05-26 13:36:41 +0000306 value = self.conn.remotecall(self.oid, methodname, args, kwargs)
Kurt B. Kaisercf3c42172007-08-29 18:44:24 +0000307 ##print("*** IdbProxy.call %s returns %r" % (methodname, value))
Chui Tey5d2af632002-05-26 13:36:41 +0000308 return value
309
310 def run(self, cmd, locals):
311 # Ignores locals on purpose!
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000312 seq = self.conn.asyncqueue(self.oid, "run", (cmd,), {})
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000313 self.shell.interp.active_seq = seq
Chui Tey5d2af632002-05-26 13:36:41 +0000314
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000315 def get_stack(self, frame, tbid):
316 # passing frame and traceback IDs, not the objects themselves
317 stack, i = self.call("get_stack", frame._fid, tbid)
Chui Tey5d2af632002-05-26 13:36:41 +0000318 stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
319 return stack, i
320
321 def set_continue(self):
322 self.call("set_continue")
323
324 def set_step(self):
325 self.call("set_step")
326
327 def set_next(self, frame):
328 self.call("set_next", frame._fid)
329
330 def set_return(self, frame):
331 self.call("set_return", frame._fid)
332
333 def set_quit(self):
334 self.call("set_quit")
335
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000336 def set_break(self, filename, lineno):
337 msg = self.call("set_break", filename, lineno)
338 return msg
339
340 def clear_break(self, filename, lineno):
341 msg = self.call("clear_break", filename, lineno)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000342 return msg
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000343
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000344 def clear_all_file_breaks(self, filename):
345 msg = self.call("clear_all_file_breaks", filename)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000346 return msg
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000347
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000348def start_remote_debugger(rpcclt, pyshell):
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000349 """Start the subprocess debugger, initialize the debugger GUI and RPC link
350
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000351 Request the RPCServer start the Python subprocess debugger and link. Set
352 up the Idle side of the split debugger by instantiating the IdbProxy,
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000353 debugger GUI, and debugger GUIAdapter objects and linking them together.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000354
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000355 Register the GUIAdapter with the RPCClient to handle debugger GUI
356 interaction requests coming from the subprocess debugger via the GUIProxy.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000357
358 The IdbAdapter will pass execution and environment requests coming from the
359 Idle debugger GUI to the subprocess debugger via the IdbProxy.
360
361 """
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000362 global idb_adap_oid
363
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000364 idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000365 (gui_adap_oid,), {})
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000366 idb_proxy = IdbProxy(rpcclt, pyshell, idb_adap_oid)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000367 gui = Debugger.Debugger(pyshell, idb_proxy)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000368 gui_adap = GUIAdapter(rpcclt, gui)
369 rpcclt.register(gui_adap_oid, gui_adap)
Chui Tey5d2af632002-05-26 13:36:41 +0000370 return gui
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000371
372def close_remote_debugger(rpcclt):
373 """Shut down subprocess debugger and Idle side of debugger RPC link
374
375 Request that the RPCServer shut down the subprocess debugger and link.
376 Unregister the GUIAdapter, which will cause a GC on the Idle process
377 debugger and RPC link objects. (The second reference to the debugger GUI
378 is deleted in PyShell.close_remote_debugger().)
379
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000380 """
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000381 close_subprocess_debugger(rpcclt)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000382 rpcclt.unregister(gui_adap_oid)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000383
384def close_subprocess_debugger(rpcclt):
385 rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})
386
387def restart_subprocess_debugger(rpcclt):
388 idb_adap_oid_ret = rpcclt.remotecall("exec", "start_the_debugger",\
389 (gui_adap_oid,), {})
390 assert idb_adap_oid_ret == idb_adap_oid, 'Idb restarted with different oid'