blob: 8c71a217461baec17a8d1f8d331714d9b906b961 [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
Florent Xiclunad630c042010-04-02 07:24:52 +000024from idlelib import Debugger
Chui Tey5d2af632002-05-26 13:36:41 +000025
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000026debugging = 0
27
Kurt B. Kaiser63857a42002-09-05 02:31:20 +000028idb_adap_oid = "idb_adapter"
29gui_adap_oid = "gui_adapter"
30
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +000031#=======================================
32#
33# In the PYTHON subprocess:
Chui Tey5d2af632002-05-26 13:36:41 +000034
35frametable = {}
36dicttable = {}
37codetable = {}
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000038tracebacktable = {}
Chui Tey5d2af632002-05-26 13:36:41 +000039
40def wrap_frame(frame):
41 fid = id(frame)
42 frametable[fid] = frame
43 return fid
44
45def wrap_info(info):
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000046 "replace info[2], a traceback instance, by its ID"
Chui Tey5d2af632002-05-26 13:36:41 +000047 if info is None:
48 return None
49 else:
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000050 traceback = info[2]
51 assert isinstance(traceback, types.TracebackType)
52 traceback_id = id(traceback)
53 tracebacktable[traceback_id] = traceback
54 modified_info = (info[0], info[1], traceback_id)
55 return modified_info
Chui Tey5d2af632002-05-26 13:36:41 +000056
57class GUIProxy:
58
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000059 def __init__(self, conn, gui_adap_oid):
Chui Tey5d2af632002-05-26 13:36:41 +000060 self.conn = conn
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +000061 self.oid = gui_adap_oid
Chui Tey5d2af632002-05-26 13:36:41 +000062
63 def interaction(self, message, frame, info=None):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +000064 # calls rpc.SocketIO.remotecall() via run.MyHandler instance
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000065 # pass frame and traceback object IDs instead of the objects themselves
Chui Tey5d2af632002-05-26 13:36:41 +000066 self.conn.remotecall(self.oid, "interaction",
67 (message, wrap_frame(frame), wrap_info(info)),
68 {})
69
70class IdbAdapter:
71
72 def __init__(self, idb):
73 self.idb = idb
74
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +000075 #----------called by an IdbProxy----------
76
Chui Tey5d2af632002-05-26 13:36:41 +000077 def set_step(self):
78 self.idb.set_step()
79
80 def set_quit(self):
81 self.idb.set_quit()
82
83 def set_continue(self):
84 self.idb.set_continue()
85
86 def set_next(self, fid):
87 frame = frametable[fid]
88 self.idb.set_next(frame)
89
90 def set_return(self, fid):
91 frame = frametable[fid]
92 self.idb.set_return(frame)
93
94 def get_stack(self, fid, tbid):
Walter Dörwald70a6b492004-02-12 17:35:32 +000095 ##print >>sys.__stderr__, "get_stack(%r, %r)" % (fid, tbid)
Chui Tey5d2af632002-05-26 13:36:41 +000096 frame = frametable[fid]
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +000097 if tbid is None:
98 tb = None
99 else:
100 tb = tracebacktable[tbid]
Chui Tey5d2af632002-05-26 13:36:41 +0000101 stack, i = self.idb.get_stack(frame, tb)
102 ##print >>sys.__stderr__, "get_stack() ->", stack
Terry Jan Reedy28489252015-05-15 23:03:11 -0400103 stack = [(wrap_frame(frame2), k) for frame2, k in stack]
Chui Tey5d2af632002-05-26 13:36:41 +0000104 ##print >>sys.__stderr__, "get_stack() ->", stack
105 return stack, i
106
107 def run(self, cmd):
108 import __main__
109 self.idb.run(cmd, __main__.__dict__)
110
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000111 def set_break(self, filename, lineno):
112 msg = self.idb.set_break(filename, lineno)
113 return msg
114
115 def clear_break(self, filename, lineno):
116 msg = self.idb.clear_break(filename, lineno)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000117 return msg
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000118
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000119 def clear_all_file_breaks(self, filename):
120 msg = self.idb.clear_all_file_breaks(filename)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000121 return msg
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000122
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000123 #----------called by a FrameProxy----------
124
Chui Tey5d2af632002-05-26 13:36:41 +0000125 def frame_attr(self, fid, name):
126 frame = frametable[fid]
127 return getattr(frame, name)
128
129 def frame_globals(self, fid):
130 frame = frametable[fid]
131 dict = frame.f_globals
132 did = id(dict)
133 dicttable[did] = dict
134 return did
135
136 def frame_locals(self, fid):
137 frame = frametable[fid]
138 dict = frame.f_locals
139 did = id(dict)
140 dicttable[did] = dict
141 return did
142
143 def frame_code(self, fid):
144 frame = frametable[fid]
145 code = frame.f_code
146 cid = id(code)
147 codetable[cid] = code
148 return cid
149
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000150 #----------called by a CodeProxy----------
151
Chui Tey5d2af632002-05-26 13:36:41 +0000152 def code_name(self, cid):
153 code = codetable[cid]
154 return code.co_name
155
156 def code_filename(self, cid):
157 code = codetable[cid]
158 return code.co_filename
159
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000160 #----------called by a DictProxy----------
161
Chui Tey5d2af632002-05-26 13:36:41 +0000162 def dict_keys(self, did):
163 dict = dicttable[did]
164 return dict.keys()
165
166 def dict_item(self, did, key):
167 dict = dicttable[did]
168 value = dict[key]
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000169 value = repr(value)
Chui Tey5d2af632002-05-26 13:36:41 +0000170 return value
171
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000172#----------end class IdbAdapter----------
173
174
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000175def start_debugger(rpchandler, gui_adap_oid):
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000176 """Start the debugger and its RPC link in the Python subprocess
177
178 Start the subprocess side of the split debugger and set up that side of the
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000179 RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000180 objects and linking them together. Register the IdbAdapter with the
181 RPCServer to handle RPC requests from the split debugger GUI via the
182 IdbProxy.
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000183
184 """
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000185 gui_proxy = GUIProxy(rpchandler, gui_adap_oid)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000186 idb = Debugger.Idb(gui_proxy)
187 idb_adap = IdbAdapter(idb)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000188 rpchandler.register(idb_adap_oid, idb_adap)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000189 return idb_adap_oid
Chui Tey5d2af632002-05-26 13:36:41 +0000190
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000191
192#=======================================
193#
194# In the IDLE process:
195
Chui Tey5d2af632002-05-26 13:36:41 +0000196
197class FrameProxy:
198
199 def __init__(self, conn, fid):
200 self._conn = conn
201 self._fid = fid
202 self._oid = "idb_adapter"
203 self._dictcache = {}
204
205 def __getattr__(self, name):
206 if name[:1] == "_":
207 raise AttributeError, name
208 if name == "f_code":
209 return self._get_f_code()
210 if name == "f_globals":
211 return self._get_f_globals()
212 if name == "f_locals":
213 return self._get_f_locals()
214 return self._conn.remotecall(self._oid, "frame_attr",
215 (self._fid, name), {})
216
217 def _get_f_code(self):
218 cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
219 return CodeProxy(self._conn, self._oid, cid)
220
221 def _get_f_globals(self):
222 did = self._conn.remotecall(self._oid, "frame_globals",
223 (self._fid,), {})
224 return self._get_dict_proxy(did)
225
226 def _get_f_locals(self):
227 did = self._conn.remotecall(self._oid, "frame_locals",
228 (self._fid,), {})
229 return self._get_dict_proxy(did)
230
231 def _get_dict_proxy(self, did):
Benjamin Peterson6e3dbbd2009-10-09 22:15:50 +0000232 if did in self._dictcache:
Chui Tey5d2af632002-05-26 13:36:41 +0000233 return self._dictcache[did]
234 dp = DictProxy(self._conn, self._oid, did)
235 self._dictcache[did] = dp
236 return dp
237
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000238
Chui Tey5d2af632002-05-26 13:36:41 +0000239class CodeProxy:
240
241 def __init__(self, conn, oid, cid):
242 self._conn = conn
243 self._oid = oid
244 self._cid = cid
245
246 def __getattr__(self, name):
247 if name == "co_name":
248 return self._conn.remotecall(self._oid, "code_name",
249 (self._cid,), {})
250 if name == "co_filename":
251 return self._conn.remotecall(self._oid, "code_filename",
252 (self._cid,), {})
253
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000254
Chui Tey5d2af632002-05-26 13:36:41 +0000255class DictProxy:
256
257 def __init__(self, conn, oid, did):
258 self._conn = conn
259 self._oid = oid
260 self._did = did
261
262 def keys(self):
263 return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})
264
265 def __getitem__(self, key):
266 return self._conn.remotecall(self._oid, "dict_item",
267 (self._did, key), {})
268
269 def __getattr__(self, name):
270 ##print >>sys.__stderr__, "failed DictProxy.__getattr__:", name
271 raise AttributeError, name
272
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000273
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000274class GUIAdapter:
Chui Tey5d2af632002-05-26 13:36:41 +0000275
276 def __init__(self, conn, gui):
277 self.conn = conn
278 self.gui = gui
279
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000280 def interaction(self, message, fid, modified_info):
281 ##print "interaction: (%s, %s, %s)" % (message, fid, modified_info)
Chui Tey5d2af632002-05-26 13:36:41 +0000282 frame = FrameProxy(self.conn, fid)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000283 self.gui.interaction(message, frame, modified_info)
Chui Tey5d2af632002-05-26 13:36:41 +0000284
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000285
Chui Tey5d2af632002-05-26 13:36:41 +0000286class IdbProxy:
287
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000288 def __init__(self, conn, shell, oid):
Chui Tey5d2af632002-05-26 13:36:41 +0000289 self.oid = oid
290 self.conn = conn
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000291 self.shell = shell
Chui Tey5d2af632002-05-26 13:36:41 +0000292
293 def call(self, methodname, *args, **kwargs):
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000294 ##print "**IdbProxy.call %s %s %s" % (methodname, args, kwargs)
Chui Tey5d2af632002-05-26 13:36:41 +0000295 value = self.conn.remotecall(self.oid, methodname, args, kwargs)
Walter Dörwald70a6b492004-02-12 17:35:32 +0000296 ##print "**IdbProxy.call %s returns %r" % (methodname, value)
Chui Tey5d2af632002-05-26 13:36:41 +0000297 return value
298
299 def run(self, cmd, locals):
300 # Ignores locals on purpose!
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000301 seq = self.conn.asyncqueue(self.oid, "run", (cmd,), {})
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000302 self.shell.interp.active_seq = seq
Chui Tey5d2af632002-05-26 13:36:41 +0000303
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000304 def get_stack(self, frame, tbid):
305 # passing frame and traceback IDs, not the objects themselves
306 stack, i = self.call("get_stack", frame._fid, tbid)
Chui Tey5d2af632002-05-26 13:36:41 +0000307 stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
308 return stack, i
309
310 def set_continue(self):
311 self.call("set_continue")
312
313 def set_step(self):
314 self.call("set_step")
315
316 def set_next(self, frame):
317 self.call("set_next", frame._fid)
318
319 def set_return(self, frame):
320 self.call("set_return", frame._fid)
321
322 def set_quit(self):
323 self.call("set_quit")
324
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000325 def set_break(self, filename, lineno):
326 msg = self.call("set_break", filename, lineno)
327 return msg
328
329 def clear_break(self, filename, lineno):
330 msg = self.call("clear_break", filename, lineno)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000331 return msg
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000332
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000333 def clear_all_file_breaks(self, filename):
334 msg = self.call("clear_all_file_breaks", filename)
Kurt B. Kaiserf50d0f92002-07-03 03:55:43 +0000335 return msg
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000336
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000337def start_remote_debugger(rpcclt, pyshell):
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000338 """Start the subprocess debugger, initialize the debugger GUI and RPC link
339
Kurt B. Kaiser669f4c32002-06-20 04:01:47 +0000340 Request the RPCServer start the Python subprocess debugger and link. Set
341 up the Idle side of the split debugger by instantiating the IdbProxy,
Kurt B. Kaiser83118c62002-06-24 17:03:37 +0000342 debugger GUI, and debugger GUIAdapter objects and linking them together.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000343
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000344 Register the GUIAdapter with the RPCClient to handle debugger GUI
345 interaction requests coming from the subprocess debugger via the GUIProxy.
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000346
347 The IdbAdapter will pass execution and environment requests coming from the
348 Idle debugger GUI to the subprocess debugger via the IdbProxy.
349
350 """
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000351 global idb_adap_oid
352
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000353 idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000354 (gui_adap_oid,), {})
Kurt B. Kaiserbc286132003-01-25 21:33:40 +0000355 idb_proxy = IdbProxy(rpcclt, pyshell, idb_adap_oid)
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000356 gui = Debugger.Debugger(pyshell, idb_proxy)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000357 gui_adap = GUIAdapter(rpcclt, gui)
358 rpcclt.register(gui_adap_oid, gui_adap)
Chui Tey5d2af632002-05-26 13:36:41 +0000359 return gui
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000360
361def close_remote_debugger(rpcclt):
362 """Shut down subprocess debugger and Idle side of debugger RPC link
363
364 Request that the RPCServer shut down the subprocess debugger and link.
365 Unregister the GUIAdapter, which will cause a GC on the Idle process
366 debugger and RPC link objects. (The second reference to the debugger GUI
367 is deleted in PyShell.close_remote_debugger().)
368
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +0000369 """
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000370 close_subprocess_debugger(rpcclt)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000371 rpcclt.unregister(gui_adap_oid)
Kurt B. Kaiser63857a42002-09-05 02:31:20 +0000372
373def close_subprocess_debugger(rpcclt):
374 rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})
375
376def restart_subprocess_debugger(rpcclt):
377 idb_adap_oid_ret = rpcclt.remotecall("exec", "start_the_debugger",\
378 (gui_adap_oid,), {})
379 assert idb_adap_oid_ret == idb_adap_oid, 'Idb restarted with different oid'