blob: 84a51a5b5b8b6b2c471d1459d52239f685436b46 [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):
55 self.conn.remotecall(self.oid, "interaction",
56 (message, wrap_frame(frame), wrap_info(info)),
57 {})
58
59class IdbAdapter:
60
61 def __init__(self, idb):
62 self.idb = idb
63
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +000064 #----------called by an IdbProxy----------
65
Chui Tey5d2af632002-05-26 13:36:41 +000066 def set_step(self):
67 self.idb.set_step()
68
69 def set_quit(self):
70 self.idb.set_quit()
71
72 def set_continue(self):
73 self.idb.set_continue()
74
75 def set_next(self, fid):
76 frame = frametable[fid]
77 self.idb.set_next(frame)
78
79 def set_return(self, fid):
80 frame = frametable[fid]
81 self.idb.set_return(frame)
82
83 def get_stack(self, fid, tbid):
84 ##print >>sys.__stderr__, "get_stack(%s, %s)" % (`fid`, `tbid`)
85 frame = frametable[fid]
86 tb = None # XXX for now
87 stack, i = self.idb.get_stack(frame, tb)
88 ##print >>sys.__stderr__, "get_stack() ->", stack
89 stack = [(wrap_frame(frame), k) for frame, k in stack]
90 ##print >>sys.__stderr__, "get_stack() ->", stack
91 return stack, i
92
93 def run(self, cmd):
94 import __main__
95 self.idb.run(cmd, __main__.__dict__)
96
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +000097 def set_break(self, filename, lineno):
98 msg = self.idb.set_break(filename, lineno)
99 return msg
100
101 def clear_break(self, filename, lineno):
102 msg = self.idb.clear_break(filename, lineno)
103
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000104 def clear_all_file_breaks(self, filename):
105 msg = self.idb.clear_all_file_breaks(filename)
106
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000107 #----------called by a FrameProxy----------
108
Chui Tey5d2af632002-05-26 13:36:41 +0000109 def frame_attr(self, fid, name):
110 frame = frametable[fid]
111 return getattr(frame, name)
112
113 def frame_globals(self, fid):
114 frame = frametable[fid]
115 dict = frame.f_globals
116 did = id(dict)
117 dicttable[did] = dict
118 return did
119
120 def frame_locals(self, fid):
121 frame = frametable[fid]
122 dict = frame.f_locals
123 did = id(dict)
124 dicttable[did] = dict
125 return did
126
127 def frame_code(self, fid):
128 frame = frametable[fid]
129 code = frame.f_code
130 cid = id(code)
131 codetable[cid] = code
132 return cid
133
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000134 #----------called by a CodeProxy----------
135
Chui Tey5d2af632002-05-26 13:36:41 +0000136 def code_name(self, cid):
137 code = codetable[cid]
138 return code.co_name
139
140 def code_filename(self, cid):
141 code = codetable[cid]
142 return code.co_filename
143
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000144 #----------called by a DictProxy----------
145
Chui Tey5d2af632002-05-26 13:36:41 +0000146 def dict_keys(self, did):
147 dict = dicttable[did]
148 return dict.keys()
149
150 def dict_item(self, did, key):
151 dict = dicttable[did]
152 value = dict[key]
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000153 value = repr(value)
Chui Tey5d2af632002-05-26 13:36:41 +0000154 return value
155
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000156#----------end class IdbAdapter----------
157
158
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000159def start_debugger(conn, gui_adap_oid):
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000160 """Start the debugger and its RPC link in the Python subprocess
161
162 Start the subprocess side of the split debugger and set up that side of the
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000163 RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000164 objects and linking them together. Register the IdbAdapter to handle RPC
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000165 requests from the split debugger GUI via the IdbProxy.
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000166
167 """
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000168 gui_proxy = GUIProxy(conn, gui_adap_oid)
169 idb = Debugger.Idb(gui_proxy)
170 idb_adap = IdbAdapter(idb)
171 idb_adap_oid = "idb_adapter"
172 conn.register(idb_adap_oid, idb_adap)
173 return idb_adap_oid
Chui Tey5d2af632002-05-26 13:36:41 +0000174
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000175
176#=======================================
177#
178# In the IDLE process:
179
Chui Tey5d2af632002-05-26 13:36:41 +0000180
181class FrameProxy:
182
183 def __init__(self, conn, fid):
184 self._conn = conn
185 self._fid = fid
186 self._oid = "idb_adapter"
187 self._dictcache = {}
188
189 def __getattr__(self, name):
190 if name[:1] == "_":
191 raise AttributeError, name
192 if name == "f_code":
193 return self._get_f_code()
194 if name == "f_globals":
195 return self._get_f_globals()
196 if name == "f_locals":
197 return self._get_f_locals()
198 return self._conn.remotecall(self._oid, "frame_attr",
199 (self._fid, name), {})
200
201 def _get_f_code(self):
202 cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
203 return CodeProxy(self._conn, self._oid, cid)
204
205 def _get_f_globals(self):
206 did = self._conn.remotecall(self._oid, "frame_globals",
207 (self._fid,), {})
208 return self._get_dict_proxy(did)
209
210 def _get_f_locals(self):
211 did = self._conn.remotecall(self._oid, "frame_locals",
212 (self._fid,), {})
213 return self._get_dict_proxy(did)
214
215 def _get_dict_proxy(self, did):
216 if self._dictcache.has_key(did):
217 return self._dictcache[did]
218 dp = DictProxy(self._conn, self._oid, did)
219 self._dictcache[did] = dp
220 return dp
221
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000222
Chui Tey5d2af632002-05-26 13:36:41 +0000223class CodeProxy:
224
225 def __init__(self, conn, oid, cid):
226 self._conn = conn
227 self._oid = oid
228 self._cid = cid
229
230 def __getattr__(self, name):
231 if name == "co_name":
232 return self._conn.remotecall(self._oid, "code_name",
233 (self._cid,), {})
234 if name == "co_filename":
235 return self._conn.remotecall(self._oid, "code_filename",
236 (self._cid,), {})
237
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000238
Chui Tey5d2af632002-05-26 13:36:41 +0000239class DictProxy:
240
241 def __init__(self, conn, oid, did):
242 self._conn = conn
243 self._oid = oid
244 self._did = did
245
246 def keys(self):
247 return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})
248
249 def __getitem__(self, key):
250 return self._conn.remotecall(self._oid, "dict_item",
251 (self._did, key), {})
252
253 def __getattr__(self, name):
254 ##print >>sys.__stderr__, "failed DictProxy.__getattr__:", name
255 raise AttributeError, name
256
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000257
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000258class GUIAdapter:
Chui Tey5d2af632002-05-26 13:36:41 +0000259
260 def __init__(self, conn, gui):
261 self.conn = conn
262 self.gui = gui
263
264 def interaction(self, message, fid, iid):
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000265 ##print "interaction: (%s, %s, %s)" % (`message`,`fid`, `iid`)
Chui Tey5d2af632002-05-26 13:36:41 +0000266 frame = FrameProxy(self.conn, fid)
267 info = None # XXX for now
268 self.gui.interaction(message, frame, info)
269
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000270
Chui Tey5d2af632002-05-26 13:36:41 +0000271class IdbProxy:
272
273 def __init__(self, conn, oid):
274 self.oid = oid
275 self.conn = conn
276
277 def call(self, methodname, *args, **kwargs):
278 ##print "call %s %s %s" % (methodname, args, kwargs)
279 value = self.conn.remotecall(self.oid, methodname, args, kwargs)
280 ##print "return %s" % `value`
281 return value
282
283 def run(self, cmd, locals):
284 # Ignores locals on purpose!
285 self.call("run", cmd)
286
287 def get_stack(self, frame, tb):
288 stack, i = self.call("get_stack", frame._fid, None)
289 stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
290 return stack, i
291
292 def set_continue(self):
293 self.call("set_continue")
294
295 def set_step(self):
296 self.call("set_step")
297
298 def set_next(self, frame):
299 self.call("set_next", frame._fid)
300
301 def set_return(self, frame):
302 self.call("set_return", frame._fid)
303
304 def set_quit(self):
305 self.call("set_quit")
306
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000307 def set_break(self, filename, lineno):
308 msg = self.call("set_break", filename, lineno)
309 return msg
310
311 def clear_break(self, filename, lineno):
312 msg = self.call("clear_break", filename, lineno)
313
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000314 def clear_all_file_breaks(self, filename):
315 msg = self.call("clear_all_file_breaks", filename)
316
317
Chui Tey5d2af632002-05-26 13:36:41 +0000318def start_remote_debugger(conn, pyshell):
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000319 """Start the subprocess debugger, initialize the debugger GUI and RPC link
320
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000321 Request the RPCServer start the Python subprocess debugger and link. Set
322 up the Idle side of the split debugger by instantiating the IdbProxy,
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000323 debugger GUI, and debugger GUIAdapter objects and linking them together.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000324
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000325 Register the GUIAdapter to handle debugger GUI interaction requests coming
326 from the subprocess debugger via the GUIProxy.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000327
328 The IdbAdapter will pass execution and environment requests coming from the
329 Idle debugger GUI to the subprocess debugger via the IdbProxy.
330
331 """
332 gui_adap_oid = "gui_adapter"
333 idb_adap_oid = conn.remotecall("exec", "start_the_debugger",\
334 (gui_adap_oid,), {})
335 idb_proxy = IdbProxy(conn, idb_adap_oid)
336 gui = Debugger.Debugger(pyshell, idb_proxy)
337 gui_adap = GUIAdapter(conn, gui)
338 conn.register(gui_adap_oid, gui_adap)
Chui Tey5d2af632002-05-26 13:36:41 +0000339 return gui