step out button
[pygdb.git] / DbgTerminal.py
1 #!/usr/bin/python
2 #shuber, 2008-06-04
3
4 __author__ = "shuber"
5
6
7 import gobject
8 import gtk
9 import os
10 import pango
11 import pty
12 import re
13 import string
14 import sys
15 import time
16 import threading
17 import vte
18
19 import ClientIOTerminal
20
21
22
23 class DbgTerminal (vte.Terminal):
24
25 def __init__(self, clientCmd):
26
27 vte.Terminal.__init__(self)
28
29 #Set members
30 self.childpid = None
31 self.history = [""]
32 self.isactive = True
33 self.lastc, self.lastr = 0,0
34 self.gotActiveCallback = []
35 self.gotInactiveCallback = []
36 self.activityChanged = None
37
38 #Start debugger
39 self.clientCmd = clientCmd
40 #Open pseudo-terminal where to-be-debugged process reads/writes to
41 self.client_ptymaster, self.client_ptyslave = pty.openpty()
42
43 #Set up terminal window and initialize debugger
44 self.connect("cursor-moved", self.contents_changed)
45 self.connect("child-exited", quitHandler)
46 gobject.timeout_add(50, self.checkActivityChanged)
47
48 #font description
49 fontdesc = pango.FontDescription("monospace 9")
50 self.set_font(fontdesc)
51
52
53 def initialize(self):
54 self.childpid = self.fork_command( self.getCommand(), self.getArgv())
55 self.waitForPrompt(0)
56 self.setPty(self.client_ptyslave)
57
58 def stopDbg(self):
59
60 if self.childpid != None:
61 #9=KILL, 15=TERM
62 os.kill(self.childpid, 15);
63 self.childpid = None
64
65 def getClientExecuteable(self):
66 return string.split(self.clientCmd)[0]
67
68
69 def toAbsPath(self, path):
70 """convert path to an absolute path relative to the client
71 executable we debug."""
72
73 #Current working dir
74 pwd = os.getcwd() + "/"
75
76 #executeable path
77 client = self.getClientExecuteable()
78 client = relToAbsPath(pwd, client)
79
80 return relToAbsPath(client, path)
81
82
83 def checkActivityChanged(self):
84
85 try:
86
87 #There was activity
88 if self.activityChanged != None:
89
90 res = self.activityChanged
91 self.activityChanged = None
92
93 status, param = res
94 if self.isActive():
95 print "got active: ", res
96 for cb in self.gotActiveCallback:
97 cb(status, param)
98 else:
99 print "got inactive: ", res
100 for cb in self.gotInactiveCallback:
101 cb(status, param)
102 except Exception, e:
103 print e
104
105 return True
106
107
108
109 def contents_changed(self, term):
110 assert( self.getHistoryLen()>0 )
111
112 c,r = term.get_cursor_position()
113 text = self.get_text_range(self.lastr,0,r,c,lambda *w:True)
114 self.lastc, self.lastr = c,r
115
116 #Remove annoying \n at the end
117 assert(text[-1] == "\n")
118 text = text[:-1]
119
120 #Get the lines and remove empty lines
121 lines = string.split(text, "\n")
122
123 #Remove the incomplete line
124 len = max(0,self.getHistoryLen())
125 self.history[-1] = lines[0]
126 self.history += lines[1:]
127
128
129 #Check if activity status has been changed
130 for i in range(len, self.getHistoryLen()):
131 line = self.history[i]
132
133 res = self.testForInactivity(i)
134 if res != None:
135 while self.activityChanged != None:
136 print "wait for pending activity"
137 gtk.main_iteration()
138
139 self.setActive(False)
140 self.activityChanged = res
141
142 res = self.testForActivity(i)
143 if res != None:
144 while self.activityChanged != None:
145 print "wait for pending activity"
146 gtk.main_iteration()
147
148 self.setActive(True)
149 self.activityChanged = res
150
151
152
153
154 def waitForNewline(self):
155 l = self.getHistoryLen()
156 while not self.getHistoryLen() > l:
157 gtk.main_iteration()
158
159 def getHistoryLen(self):
160 return len(self.history)
161
162 def waitForRx(self, pat, start):
163
164 rx = re.compile(pat)
165 curr = start
166 while True:
167 assert( curr>=start )
168 for no in range(curr, self.getHistoryLen()):
169 line = self.history[no]
170 if rx.search(line):
171 return no, line
172
173 #Do not forget the last line
174 curr = max(start,self.getHistoryLen()-1)
175 lr, lc = self.lastr, self.lastc
176
177 while (self.lastr, self.lastc) == (lr,lc):
178 gtk.main_iteration()
179
180
181 def getCommand(self):
182 return self.getArgv()[0];
183
184 def getArgv(self):
185 raise NotImplementedError()
186
187 def setPty(self, pty):
188 raise NotImplementedError()
189
190 def setRun(self):
191 raise NotImplementedError()
192
193 def setContinue(self):
194 raise NotImplementedError()
195
196 def setStepover(self):
197 raise NotImplementedError()
198
199 def setStepin(self):
200 raise NotImplementedError()
201
202 def setStepout(self):
203 raise NotImplementedError()
204
205 def setQuit(self):
206 raise NotImplementedError()
207
208 def setBreakpoint(self, file, lineno, condition=False):
209 raise NotImplementedError()
210
211 def delBreakpoint(self, breakpoint):
212 raise NotImplementedError()
213
214 def getExpression(self, expr):
215 raise NotImplementedError()
216
217 def listCodeSnippet(self):
218 raise NotImplementedError()
219
220 def getBacktrace(self):
221 raise NotImplementedError()
222
223 def waitForPrompt(self, his):
224 raise NotImplementedError()
225
226 def testForActivity(self, his):
227 raise NotImplementedError()
228
229 def testForInactivity(self, his):
230 raise NotImplementedError()
231
232 def setActive(self, isactive):
233 self.isactive = isactive
234
235 def isActive(self):
236 return self.isactive
237
238
239
240 def quitHandler(*w):
241 try:
242 gtk.main_quit()
243 except:
244 pass
245
246
247 def updateVim():
248 os.system('gvim --servername pygdb --remote-send "<ESC> :GDBLoadConfig<CR>"')
249
250
251 def relToAbsPath(absfile, relfile):
252 """When an absfile is given and a relfile is given by
253 relative paths relative to absfile, determine the abs
254 path of relfile"""
255
256 #Get directories except for "." parts
257 relsplit = filter(lambda x: x!=".", string.split(relfile, os.sep))
258 #Get the directories of absfile withouth the trailing filename
259 abssplit = string.split(absfile, os.sep)[:-1]
260
261 #Determine number of ".." and remove them
262 up=0
263 while relsplit[0] == "..":
264 up += 1
265 del relsplit[0]
266 del abssplit[-1]
267
268 return string.join(abssplit + relsplit, os.sep)
269
270
271 class DbgWindow (gtk.Window):
272
273 clientIOWnd = None
274
275
276 def __init__(self, terminal):
277
278 #Set up some members
279 self.terminal = terminal
280
281 #Set up GTK stuff
282 gtk.Window.__init__(self)
283 self.connect("destroy", quitHandler)
284
285 #Set title and add terminal
286 self.set_title("Debugger I/O")
287 self.terminal.history = []
288 self.terminal.history_length = 5
289 self.add(self.terminal)
290
291 #Show the window
292 self.show_all()
293
294 def toggleClientIOWindow(self):
295 if not self.clientIOWnd:
296 self.clientIOWnd = ClientIOTerminal.ClientIOWindow(self, \
297 self.terminal.client_ptymaster)
298 else:
299 self.clientIOWnd.destroy()
300 self.clientIOWnd = None
301
302 def isClientIOWindowExisting(self):
303 return self.clientIOWnd != None
304
305
306