blob: a5b4e7e363d5ba0dc4e72523eefa542f821e97d5 [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
104 #----------called by a FrameProxy----------
105
Chui Tey5d2af632002-05-26 13:36:41 +0000106 def frame_attr(self, fid, name):
107 frame = frametable[fid]
108 return getattr(frame, name)
109
110 def frame_globals(self, fid):
111 frame = frametable[fid]
112 dict = frame.f_globals
113 did = id(dict)
114 dicttable[did] = dict
115 return did
116
117 def frame_locals(self, fid):
118 frame = frametable[fid]
119 dict = frame.f_locals
120 did = id(dict)
121 dicttable[did] = dict
122 return did
123
124 def frame_code(self, fid):
125 frame = frametable[fid]
126 code = frame.f_code
127 cid = id(code)
128 codetable[cid] = code
129 return cid
130
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000131 #----------called by a CodeProxy----------
132
Chui Tey5d2af632002-05-26 13:36:41 +0000133 def code_name(self, cid):
134 code = codetable[cid]
135 return code.co_name
136
137 def code_filename(self, cid):
138 code = codetable[cid]
139 return code.co_filename
140
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000141 #----------called by a DictProxy----------
142
Chui Tey5d2af632002-05-26 13:36:41 +0000143 def dict_keys(self, did):
144 dict = dicttable[did]
145 return dict.keys()
146
147 def dict_item(self, did, key):
148 dict = dicttable[did]
149 value = dict[key]
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000150 value = repr(value)
151# try:
152# # Test for picklability
153# import cPickle
154# pklstr = cPickle.dumps(value)
155# except:
156# print >>sys.__stderr__, "** dict_item pickle failed: ", value
157# raise
158# #value = None
Chui Tey5d2af632002-05-26 13:36:41 +0000159 return value
160
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000161#----------end class IdbAdapter----------
162
163
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000164def start_debugger(conn, gui_adap_oid):
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000165 """Start the debugger and its RPC link in the Python subprocess
166
167 Start the subprocess side of the split debugger and set up that side of the
168 RPC link by instantiating the GUIProxy, Idle debugger, and IdbAdapter
169 objects and linking them together. Register the IdbAdapter to handle RPC
170 requests from the split Debugger GUI via the IdbProxy.
171
172 """
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000173 gui_proxy = GUIProxy(conn, gui_adap_oid)
174 idb = Debugger.Idb(gui_proxy)
175 idb_adap = IdbAdapter(idb)
176 idb_adap_oid = "idb_adapter"
177 conn.register(idb_adap_oid, idb_adap)
178 return idb_adap_oid
Chui Tey5d2af632002-05-26 13:36:41 +0000179
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000180
181#=======================================
182#
183# In the IDLE process:
184
Chui Tey5d2af632002-05-26 13:36:41 +0000185
186class FrameProxy:
187
188 def __init__(self, conn, fid):
189 self._conn = conn
190 self._fid = fid
191 self._oid = "idb_adapter"
192 self._dictcache = {}
193
194 def __getattr__(self, name):
195 if name[:1] == "_":
196 raise AttributeError, name
197 if name == "f_code":
198 return self._get_f_code()
199 if name == "f_globals":
200 return self._get_f_globals()
201 if name == "f_locals":
202 return self._get_f_locals()
203 return self._conn.remotecall(self._oid, "frame_attr",
204 (self._fid, name), {})
205
206 def _get_f_code(self):
207 cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
208 return CodeProxy(self._conn, self._oid, cid)
209
210 def _get_f_globals(self):
211 did = self._conn.remotecall(self._oid, "frame_globals",
212 (self._fid,), {})
213 return self._get_dict_proxy(did)
214
215 def _get_f_locals(self):
216 did = self._conn.remotecall(self._oid, "frame_locals",
217 (self._fid,), {})
218 return self._get_dict_proxy(did)
219
220 def _get_dict_proxy(self, did):
221 if self._dictcache.has_key(did):
222 return self._dictcache[did]
223 dp = DictProxy(self._conn, self._oid, did)
224 self._dictcache[did] = dp
225 return dp
226
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000227
Chui Tey5d2af632002-05-26 13:36:41 +0000228class CodeProxy:
229
230 def __init__(self, conn, oid, cid):
231 self._conn = conn
232 self._oid = oid
233 self._cid = cid
234
235 def __getattr__(self, name):
236 if name == "co_name":
237 return self._conn.remotecall(self._oid, "code_name",
238 (self._cid,), {})
239 if name == "co_filename":
240 return self._conn.remotecall(self._oid, "code_filename",
241 (self._cid,), {})
242
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000243
Chui Tey5d2af632002-05-26 13:36:41 +0000244class DictProxy:
245
246 def __init__(self, conn, oid, did):
247 self._conn = conn
248 self._oid = oid
249 self._did = did
250
251 def keys(self):
252 return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})
253
254 def __getitem__(self, key):
255 return self._conn.remotecall(self._oid, "dict_item",
256 (self._did, key), {})
257
258 def __getattr__(self, name):
259 ##print >>sys.__stderr__, "failed DictProxy.__getattr__:", name
260 raise AttributeError, name
261
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000262
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000263class GUIAdapter:
Chui Tey5d2af632002-05-26 13:36:41 +0000264
265 def __init__(self, conn, gui):
266 self.conn = conn
267 self.gui = gui
268
269 def interaction(self, message, fid, iid):
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000270 ##print "interaction: (%s, %s, %s)" % (`message`,`fid`, `iid`)
Chui Tey5d2af632002-05-26 13:36:41 +0000271 frame = FrameProxy(self.conn, fid)
272 info = None # XXX for now
273 self.gui.interaction(message, frame, info)
274
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000275
Chui Tey5d2af632002-05-26 13:36:41 +0000276class IdbProxy:
277
278 def __init__(self, conn, oid):
279 self.oid = oid
280 self.conn = conn
281
282 def call(self, methodname, *args, **kwargs):
283 ##print "call %s %s %s" % (methodname, args, kwargs)
284 value = self.conn.remotecall(self.oid, methodname, args, kwargs)
285 ##print "return %s" % `value`
286 return value
287
288 def run(self, cmd, locals):
289 # Ignores locals on purpose!
290 self.call("run", cmd)
291
292 def get_stack(self, frame, tb):
293 stack, i = self.call("get_stack", frame._fid, None)
294 stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
295 return stack, i
296
297 def set_continue(self):
298 self.call("set_continue")
299
300 def set_step(self):
301 self.call("set_step")
302
303 def set_next(self, frame):
304 self.call("set_next", frame._fid)
305
306 def set_return(self, frame):
307 self.call("set_return", frame._fid)
308
309 def set_quit(self):
310 self.call("set_quit")
311
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000312 def set_break(self, filename, lineno):
313 msg = self.call("set_break", filename, lineno)
314 return msg
315
316 def clear_break(self, filename, lineno):
317 msg = self.call("clear_break", filename, lineno)
318
Chui Tey5d2af632002-05-26 13:36:41 +0000319def start_remote_debugger(conn, pyshell):
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000320 """Start the subprocess debugger, initialize the debugger GUI and RPC link
321
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000322 Request the RPCServer start the Python subprocess debugger and link. Set
323 up the Idle side of the split debugger by instantiating the IdbProxy,
324 Debugger GUI, and Debugger GUIAdapter objects and linking them together.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000325
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000326 Register the GUIAdapter to handle debugger GUI interaction requests coming
327 from the subprocess debugger via the GUIProxy.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000328
329 The IdbAdapter will pass execution and environment requests coming from the
330 Idle debugger GUI to the subprocess debugger via the IdbProxy.
331
332 """
333 gui_adap_oid = "gui_adapter"
334 idb_adap_oid = conn.remotecall("exec", "start_the_debugger",\
335 (gui_adap_oid,), {})
336 idb_proxy = IdbProxy(conn, idb_adap_oid)
337 gui = Debugger.Debugger(pyshell, idb_proxy)
338 gui_adap = GUIAdapter(conn, gui)
339 conn.register(gui_adap_oid, gui_adap)
Chui Tey5d2af632002-05-26 13:36:41 +0000340 return gui