blob: fa234d17c17bebdc0ba48bbff625271d4830be3a [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
Chui Tey5d2af632002-05-26 13:36:41 +000024import rpc
25import Debugger
26
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):
Walter Dörwald70a6b492004-02-12 17:35:32 +000096 ##print >>sys.__stderr__, "get_stack(%r, %r)" % (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)
103 ##print >>sys.__stderr__, "get_stack() ->", stack
104 stack = [(wrap_frame(frame), k) for frame, k in stack]
105 ##print >>sys.__stderr__, "get_stack() ->", stack
106 return stack, i
107
108 def run(self, cmd):
109 import __main__
110 self.idb.run(cmd, __main__.__dict__)
111
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000112 def set_break(self, filename, lineno):
113 msg = self.idb.set_break(filename, lineno)
114 return msg
115
116 def clear_break(self, filename, lineno):
117 msg = self.idb.clear_break(filename, lineno)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000118 return msg
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000119
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000120 def clear_all_file_breaks(self, filename):
121 msg = self.idb.clear_all_file_breaks(filename)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000122 return msg
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000123
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000124 #----------called by a FrameProxy----------
125
Chui Tey5d2af632002-05-26 13:36:41 +0000126 def frame_attr(self, fid, name):
127 frame = frametable[fid]
128 return getattr(frame, name)
129
130 def frame_globals(self, fid):
131 frame = frametable[fid]
132 dict = frame.f_globals
133 did = id(dict)
134 dicttable[did] = dict
135 return did
136
137 def frame_locals(self, fid):
138 frame = frametable[fid]
139 dict = frame.f_locals
140 did = id(dict)
141 dicttable[did] = dict
142 return did
143
144 def frame_code(self, fid):
145 frame = frametable[fid]
146 code = frame.f_code
147 cid = id(code)
148 codetable[cid] = code
149 return cid
150
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000151 #----------called by a CodeProxy----------
152
Chui Tey5d2af632002-05-26 13:36:41 +0000153 def code_name(self, cid):
154 code = codetable[cid]
155 return code.co_name
156
157 def code_filename(self, cid):
158 code = codetable[cid]
159 return code.co_filename
160
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000161 #----------called by a DictProxy----------
162
Chui Tey5d2af632002-05-26 13:36:41 +0000163 def dict_keys(self, did):
164 dict = dicttable[did]
165 return dict.keys()
166
167 def dict_item(self, did, key):
168 dict = dicttable[did]
169 value = dict[key]
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000170 value = repr(value)
Chui Tey5d2af632002-05-26 13:36:41 +0000171 return value
172
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000173#----------end class IdbAdapter----------
174
175
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000176def start_debugger(rpchandler, gui_adap_oid):
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000177 """Start the debugger and its RPC link in the Python subprocess
178
179 Start the subprocess side of the split debugger and set up that side of the
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000180 RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000181 objects and linking them together. Register the IdbAdapter with the
182 RPCServer to handle RPC requests from the split debugger GUI via the
183 IdbProxy.
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000184
185 """
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000186 gui_proxy = GUIProxy(rpchandler, gui_adap_oid)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000187 idb = Debugger.Idb(gui_proxy)
188 idb_adap = IdbAdapter(idb)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000189 rpchandler.register(idb_adap_oid, idb_adap)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000190 return idb_adap_oid
Chui Tey5d2af632002-05-26 13:36:41 +0000191
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000192
193#=======================================
194#
195# In the IDLE process:
196
Chui Tey5d2af632002-05-26 13:36:41 +0000197
198class FrameProxy:
199
200 def __init__(self, conn, fid):
201 self._conn = conn
202 self._fid = fid
203 self._oid = "idb_adapter"
204 self._dictcache = {}
205
206 def __getattr__(self, name):
207 if name[:1] == "_":
208 raise AttributeError, name
209 if name == "f_code":
210 return self._get_f_code()
211 if name == "f_globals":
212 return self._get_f_globals()
213 if name == "f_locals":
214 return self._get_f_locals()
215 return self._conn.remotecall(self._oid, "frame_attr",
216 (self._fid, name), {})
217
218 def _get_f_code(self):
219 cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
220 return CodeProxy(self._conn, self._oid, cid)
221
222 def _get_f_globals(self):
223 did = self._conn.remotecall(self._oid, "frame_globals",
224 (self._fid,), {})
225 return self._get_dict_proxy(did)
226
227 def _get_f_locals(self):
228 did = self._conn.remotecall(self._oid, "frame_locals",
229 (self._fid,), {})
230 return self._get_dict_proxy(did)
231
232 def _get_dict_proxy(self, did):
233 if self._dictcache.has_key(did):
234 return self._dictcache[did]
235 dp = DictProxy(self._conn, self._oid, did)
236 self._dictcache[did] = dp
237 return dp
238
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000239
Chui Tey5d2af632002-05-26 13:36:41 +0000240class CodeProxy:
241
242 def __init__(self, conn, oid, cid):
243 self._conn = conn
244 self._oid = oid
245 self._cid = cid
246
247 def __getattr__(self, name):
248 if name == "co_name":
249 return self._conn.remotecall(self._oid, "code_name",
250 (self._cid,), {})
251 if name == "co_filename":
252 return self._conn.remotecall(self._oid, "code_filename",
253 (self._cid,), {})
254
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000255
Chui Tey5d2af632002-05-26 13:36:41 +0000256class DictProxy:
257
258 def __init__(self, conn, oid, did):
259 self._conn = conn
260 self._oid = oid
261 self._did = did
262
263 def keys(self):
264 return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})
265
266 def __getitem__(self, key):
267 return self._conn.remotecall(self._oid, "dict_item",
268 (self._did, key), {})
269
270 def __getattr__(self, name):
271 ##print >>sys.__stderr__, "failed DictProxy.__getattr__:", name
272 raise AttributeError, name
273
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000274
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000275class GUIAdapter:
Chui Tey5d2af632002-05-26 13:36:41 +0000276
277 def __init__(self, conn, gui):
278 self.conn = conn
279 self.gui = gui
280
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000281 def interaction(self, message, fid, modified_info):
282 ##print "interaction: (%s, %s, %s)" % (message, fid, modified_info)
Chui Tey5d2af632002-05-26 13:36:41 +0000283 frame = FrameProxy(self.conn, fid)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000284 self.gui.interaction(message, frame, modified_info)
Chui Tey5d2af632002-05-26 13:36:41 +0000285
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000286
Chui Tey5d2af632002-05-26 13:36:41 +0000287class IdbProxy:
288
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000289 def __init__(self, conn, shell, oid):
Chui Tey5d2af632002-05-26 13:36:41 +0000290 self.oid = oid
291 self.conn = conn
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000292 self.shell = shell
Chui Tey5d2af632002-05-26 13:36:41 +0000293
294 def call(self, methodname, *args, **kwargs):
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000295 ##print "**IdbProxy.call %s %s %s" % (methodname, args, kwargs)
Chui Tey5d2af632002-05-26 13:36:41 +0000296 value = self.conn.remotecall(self.oid, methodname, args, kwargs)
Walter Dörwald70a6b492004-02-12 17:35:32 +0000297 ##print "**IdbProxy.call %s returns %r" % (methodname, value)
Chui Tey5d2af632002-05-26 13:36:41 +0000298 return value
299
300 def run(self, cmd, locals):
301 # Ignores locals on purpose!
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000302 seq = self.conn.asyncqueue(self.oid, "run", (cmd,), {})
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000303 self.shell.interp.active_seq = seq
Chui Tey5d2af632002-05-26 13:36:41 +0000304
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000305 def get_stack(self, frame, tbid):
306 # passing frame and traceback IDs, not the objects themselves
307 stack, i = self.call("get_stack", frame._fid, tbid)
Chui Tey5d2af632002-05-26 13:36:41 +0000308 stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
309 return stack, i
310
311 def set_continue(self):
312 self.call("set_continue")
313
314 def set_step(self):
315 self.call("set_step")
316
317 def set_next(self, frame):
318 self.call("set_next", frame._fid)
319
320 def set_return(self, frame):
321 self.call("set_return", frame._fid)
322
323 def set_quit(self):
324 self.call("set_quit")
325
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000326 def set_break(self, filename, lineno):
327 msg = self.call("set_break", filename, lineno)
328 return msg
329
330 def clear_break(self, filename, lineno):
331 msg = self.call("clear_break", filename, lineno)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000332 return msg
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000333
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000334 def clear_all_file_breaks(self, filename):
335 msg = self.call("clear_all_file_breaks", filename)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000336 return msg
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000337
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000338def start_remote_debugger(rpcclt, pyshell):
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000339 """Start the subprocess debugger, initialize the debugger GUI and RPC link
340
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000341 Request the RPCServer start the Python subprocess debugger and link. Set
342 up the Idle side of the split debugger by instantiating the IdbProxy,
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000343 debugger GUI, and debugger GUIAdapter objects and linking them together.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000344
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000345 Register the GUIAdapter with the RPCClient to handle debugger GUI
346 interaction requests coming from the subprocess debugger via the GUIProxy.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000347
348 The IdbAdapter will pass execution and environment requests coming from the
349 Idle debugger GUI to the subprocess debugger via the IdbProxy.
350
351 """
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000352 global idb_adap_oid
353
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000354 idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000355 (gui_adap_oid,), {})
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000356 idb_proxy = IdbProxy(rpcclt, pyshell, idb_adap_oid)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000357 gui = Debugger.Debugger(pyshell, idb_proxy)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000358 gui_adap = GUIAdapter(rpcclt, gui)
359 rpcclt.register(gui_adap_oid, gui_adap)
Chui Tey5d2af632002-05-26 13:36:41 +0000360 return gui
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000361
362def close_remote_debugger(rpcclt):
363 """Shut down subprocess debugger and Idle side of debugger RPC link
364
365 Request that the RPCServer shut down the subprocess debugger and link.
366 Unregister the GUIAdapter, which will cause a GC on the Idle process
367 debugger and RPC link objects. (The second reference to the debugger GUI
368 is deleted in PyShell.close_remote_debugger().)
369
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000370 """
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000371 close_subprocess_debugger(rpcclt)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000372 rpcclt.unregister(gui_adap_oid)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000373
374def close_subprocess_debugger(rpcclt):
375 rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})
376
377def restart_subprocess_debugger(rpcclt):
378 idb_adap_oid_ret = rpcclt.remotecall("exec", "start_the_debugger",\
379 (gui_adap_oid,), {})
380 assert idb_adap_oid_ret == idb_adap_oid, 'Idb restarted with different oid'