blob: 33072393192017c57b1417df2260c6c632a439c4 [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
24import rpc
25import Debugger
26
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000027debugging = 0
28
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +000029#=======================================
30#
31# In the PYTHON subprocess:
Chui Tey5d2af632002-05-26 13:36:41 +000032
33frametable = {}
34dicttable = {}
35codetable = {}
36
37def wrap_frame(frame):
38 fid = id(frame)
39 frametable[fid] = frame
40 return fid
41
42def wrap_info(info):
43 if info is None:
44 return None
45 else:
46 return None # XXX for now
47
48class GUIProxy:
49
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000050 def __init__(self, conn, gui_adap_oid):
Chui Tey5d2af632002-05-26 13:36:41 +000051 self.conn = conn
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000052 self.oid = gui_adap_oid
Chui Tey5d2af632002-05-26 13:36:41 +000053
54 def interaction(self, message, frame, info=None):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +000055 # calls rpc.SocketIO.remotecall() via run.MyHandler instance
Chui Tey5d2af632002-05-26 13:36:41 +000056 self.conn.remotecall(self.oid, "interaction",
57 (message, wrap_frame(frame), wrap_info(info)),
58 {})
59
60class IdbAdapter:
61
62 def __init__(self, idb):
63 self.idb = idb
64
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +000065 #----------called by an IdbProxy----------
66
Chui Tey5d2af632002-05-26 13:36:41 +000067 def set_step(self):
68 self.idb.set_step()
69
70 def set_quit(self):
71 self.idb.set_quit()
72
73 def set_continue(self):
74 self.idb.set_continue()
75
76 def set_next(self, fid):
77 frame = frametable[fid]
78 self.idb.set_next(frame)
79
80 def set_return(self, fid):
81 frame = frametable[fid]
82 self.idb.set_return(frame)
83
84 def get_stack(self, fid, tbid):
85 ##print >>sys.__stderr__, "get_stack(%s, %s)" % (`fid`, `tbid`)
86 frame = frametable[fid]
87 tb = None # XXX for now
88 stack, i = self.idb.get_stack(frame, tb)
89 ##print >>sys.__stderr__, "get_stack() ->", stack
90 stack = [(wrap_frame(frame), k) for frame, k in stack]
91 ##print >>sys.__stderr__, "get_stack() ->", stack
92 return stack, i
93
94 def run(self, cmd):
95 import __main__
96 self.idb.run(cmd, __main__.__dict__)
97
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +000098 def set_break(self, filename, lineno):
99 msg = self.idb.set_break(filename, lineno)
100 return msg
101
102 def clear_break(self, filename, lineno):
103 msg = self.idb.clear_break(filename, lineno)
104
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000105 def clear_all_file_breaks(self, filename):
106 msg = self.idb.clear_all_file_breaks(filename)
107
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000108 #----------called by a FrameProxy----------
109
Chui Tey5d2af632002-05-26 13:36:41 +0000110 def frame_attr(self, fid, name):
111 frame = frametable[fid]
112 return getattr(frame, name)
113
114 def frame_globals(self, fid):
115 frame = frametable[fid]
116 dict = frame.f_globals
117 did = id(dict)
118 dicttable[did] = dict
119 return did
120
121 def frame_locals(self, fid):
122 frame = frametable[fid]
123 dict = frame.f_locals
124 did = id(dict)
125 dicttable[did] = dict
126 return did
127
128 def frame_code(self, fid):
129 frame = frametable[fid]
130 code = frame.f_code
131 cid = id(code)
132 codetable[cid] = code
133 return cid
134
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000135 #----------called by a CodeProxy----------
136
Chui Tey5d2af632002-05-26 13:36:41 +0000137 def code_name(self, cid):
138 code = codetable[cid]
139 return code.co_name
140
141 def code_filename(self, cid):
142 code = codetable[cid]
143 return code.co_filename
144
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000145 #----------called by a DictProxy----------
146
Chui Tey5d2af632002-05-26 13:36:41 +0000147 def dict_keys(self, did):
148 dict = dicttable[did]
149 return dict.keys()
150
151 def dict_item(self, did, key):
152 dict = dicttable[did]
153 value = dict[key]
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000154 value = repr(value)
Chui Tey5d2af632002-05-26 13:36:41 +0000155 return value
156
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000157#----------end class IdbAdapter----------
158
159
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000160def start_debugger(rpchandler, gui_adap_oid):
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000161 """Start the debugger and its RPC link in the Python subprocess
162
163 Start the subprocess side of the split debugger and set up that side of the
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000164 RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000165 objects and linking them together. Register the IdbAdapter with the
166 RPCServer to handle RPC requests from the split debugger GUI via the
167 IdbProxy.
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000168
169 """
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000170 gui_proxy = GUIProxy(rpchandler, gui_adap_oid)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000171 idb = Debugger.Idb(gui_proxy)
172 idb_adap = IdbAdapter(idb)
173 idb_adap_oid = "idb_adapter"
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000174 rpchandler.register(idb_adap_oid, idb_adap)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000175 return idb_adap_oid
Chui Tey5d2af632002-05-26 13:36:41 +0000176
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000177
178#=======================================
179#
180# In the IDLE process:
181
Chui Tey5d2af632002-05-26 13:36:41 +0000182
183class FrameProxy:
184
185 def __init__(self, conn, fid):
186 self._conn = conn
187 self._fid = fid
188 self._oid = "idb_adapter"
189 self._dictcache = {}
190
191 def __getattr__(self, name):
192 if name[:1] == "_":
193 raise AttributeError, name
194 if name == "f_code":
195 return self._get_f_code()
196 if name == "f_globals":
197 return self._get_f_globals()
198 if name == "f_locals":
199 return self._get_f_locals()
200 return self._conn.remotecall(self._oid, "frame_attr",
201 (self._fid, name), {})
202
203 def _get_f_code(self):
204 cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
205 return CodeProxy(self._conn, self._oid, cid)
206
207 def _get_f_globals(self):
208 did = self._conn.remotecall(self._oid, "frame_globals",
209 (self._fid,), {})
210 return self._get_dict_proxy(did)
211
212 def _get_f_locals(self):
213 did = self._conn.remotecall(self._oid, "frame_locals",
214 (self._fid,), {})
215 return self._get_dict_proxy(did)
216
217 def _get_dict_proxy(self, did):
218 if self._dictcache.has_key(did):
219 return self._dictcache[did]
220 dp = DictProxy(self._conn, self._oid, did)
221 self._dictcache[did] = dp
222 return dp
223
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000224
Chui Tey5d2af632002-05-26 13:36:41 +0000225class CodeProxy:
226
227 def __init__(self, conn, oid, cid):
228 self._conn = conn
229 self._oid = oid
230 self._cid = cid
231
232 def __getattr__(self, name):
233 if name == "co_name":
234 return self._conn.remotecall(self._oid, "code_name",
235 (self._cid,), {})
236 if name == "co_filename":
237 return self._conn.remotecall(self._oid, "code_filename",
238 (self._cid,), {})
239
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000240
Chui Tey5d2af632002-05-26 13:36:41 +0000241class DictProxy:
242
243 def __init__(self, conn, oid, did):
244 self._conn = conn
245 self._oid = oid
246 self._did = did
247
248 def keys(self):
249 return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})
250
251 def __getitem__(self, key):
252 return self._conn.remotecall(self._oid, "dict_item",
253 (self._did, key), {})
254
255 def __getattr__(self, name):
256 ##print >>sys.__stderr__, "failed DictProxy.__getattr__:", name
257 raise AttributeError, name
258
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000259
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000260class GUIAdapter:
Chui Tey5d2af632002-05-26 13:36:41 +0000261
262 def __init__(self, conn, gui):
263 self.conn = conn
264 self.gui = gui
265
266 def interaction(self, message, fid, iid):
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000267 ##print "interaction: (%s, %s, %s)" % (`message`,`fid`, `iid`)
Chui Tey5d2af632002-05-26 13:36:41 +0000268 frame = FrameProxy(self.conn, fid)
269 info = None # XXX for now
270 self.gui.interaction(message, frame, info)
271
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000272
Chui Tey5d2af632002-05-26 13:36:41 +0000273class IdbProxy:
274
275 def __init__(self, conn, oid):
276 self.oid = oid
277 self.conn = conn
278
279 def call(self, methodname, *args, **kwargs):
280 ##print "call %s %s %s" % (methodname, args, kwargs)
281 value = self.conn.remotecall(self.oid, methodname, args, kwargs)
282 ##print "return %s" % `value`
283 return value
284
285 def run(self, cmd, locals):
286 # Ignores locals on purpose!
287 self.call("run", cmd)
288
289 def get_stack(self, frame, tb):
290 stack, i = self.call("get_stack", frame._fid, None)
291 stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
292 return stack, i
293
294 def set_continue(self):
295 self.call("set_continue")
296
297 def set_step(self):
298 self.call("set_step")
299
300 def set_next(self, frame):
301 self.call("set_next", frame._fid)
302
303 def set_return(self, frame):
304 self.call("set_return", frame._fid)
305
306 def set_quit(self):
307 self.call("set_quit")
308
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000309 def set_break(self, filename, lineno):
310 msg = self.call("set_break", filename, lineno)
311 return msg
312
313 def clear_break(self, filename, lineno):
314 msg = self.call("clear_break", filename, lineno)
315
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000316 def clear_all_file_breaks(self, filename):
317 msg = self.call("clear_all_file_breaks", filename)
318
319
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000320def start_remote_debugger(rpcclt, pyshell):
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000321 """Start the subprocess debugger, initialize the debugger GUI and RPC link
322
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000323 Request the RPCServer start the Python subprocess debugger and link. Set
324 up the Idle side of the split debugger by instantiating the IdbProxy,
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000325 debugger GUI, and debugger GUIAdapter objects and linking them together.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000326
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000327 Register the GUIAdapter with the RPCClient to handle debugger GUI
328 interaction requests coming from the subprocess debugger via the GUIProxy.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000329
330 The IdbAdapter will pass execution and environment requests coming from the
331 Idle debugger GUI to the subprocess debugger via the IdbProxy.
332
333 """
334 gui_adap_oid = "gui_adapter"
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000335 idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000336 (gui_adap_oid,), {})
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000337 idb_proxy = IdbProxy(rpcclt, idb_adap_oid)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000338 gui = Debugger.Debugger(pyshell, idb_proxy)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000339 gui_adap = GUIAdapter(rpcclt, gui)
340 rpcclt.register(gui_adap_oid, gui_adap)
Chui Tey5d2af632002-05-26 13:36:41 +0000341 return gui
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000342
343def close_remote_debugger(rpcclt):
344 """Shut down subprocess debugger and Idle side of debugger RPC link
345
346 Request that the RPCServer shut down the subprocess debugger and link.
347 Unregister the GUIAdapter, which will cause a GC on the Idle process
348 debugger and RPC link objects. (The second reference to the debugger GUI
349 is deleted in PyShell.close_remote_debugger().)
350
351 """
352 idb_adap_oid = "idb_adapter"
353 rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})
354 gui_adap_oid = "gui_adapter"
355 rpcclt.unregister(gui_adap_oid)