added functions for getting dbg replies
[pygdb.git] / DbgTerminal.py
1 #!/usr/bin/python
2
3
4 import select
5 import string
6 import sys
7 import thread
8 import threading
9 import time
10 import os
11 import pty
12 import Queue
13
14
15 class DbgTerminal:
16
17 #Reply cache from debugger
18 dbgreplyqueue = Queue.Queue()
19
20
21 def __init__ (self, binary, gdbReadCallback, childReadCallback):
22
23 #Set some members
24 self.gdbReadCallback = gdbReadCallback
25 self.childReadCallback = childReadCallback
26 self.binary = binary
27 self.stopped = False
28
29 #Connect to sub-process
30 self.__connect()
31
32 def __connect( self ):
33
34 #This function handles readings from the debugger
35 def gdbCb(str):
36 self.gdbReadCallback(str)
37 self.dbgreplyqueue.put(str)
38
39 #Open pseudo-terminal where to-be-debugged process reads/writes to
40 self.ptymaster, self.ptyslave = pty.openpty()
41 self.childout = os.fdopen(self.ptymaster, "r", 0)
42 self.childin = os.fdopen(self.ptymaster, "w", 0)
43
44 #Call gdb and get in- and out-streams to/from gdb
45 cmd = self.getCommand(self.binary)
46 self.gdbin, self.gdbout = os.popen4( cmd, bufsize=0)
47
48 #Set up a reading thread to gdb output
49 self.gdbReadThread = self.ReadThread(self.gdbout, gdbCb)
50 self.gdbReadThread.start()
51
52 #Set up a reading thread to childs output
53 self.childReadThread = self.ReadThread(self.childout, self.childReadCallback)
54 self.childReadThread.start()
55
56 #Set up tty gdb-childs process
57 self.sendSetTTY(os.ttyname(self.ptyslave))
58
59
60
61 def getDbgReply(self):
62 raise NotImplementedError()
63
64
65 def iseof( self ):
66 """Check if terminal is closed already"""
67 return self.gdbReadThread.eventFin.isSet()
68
69 def stop( self ):
70
71 if not self.stopped:
72
73 self.stopped = True
74
75 #Finish the reading-thread
76 self.gdbReadThread.fin = True
77 self.childReadThread.fin = True
78 self.gdbReadThread.eventFin.wait(1)
79 self.childReadThread.eventFin.wait(1)
80
81
82 def getCommand( self, binary ):
83 """Get the command to execute"""
84 raise NotImplementedError()
85
86 def sendBreak(self, file, lineno):
87 raise NotImplementedError()
88
89 def sendContinue(self):
90 raise NotImplementedError()
91
92 def sendRun(self):
93 raise NotImplementedError()
94
95 def sendInspectVar(self, var):
96 raise NotImplementedError()
97
98 def sendInspectExpr(self, expr):
99 raise NotImplementedError()
100
101 def sendSetTTY(self, ttyname):
102 raise NotImplementedError()
103
104 def sendQuit(self):
105 raise NotImplementedError()
106
107
108 class ReadThread (threading.Thread):
109 """Thread which reads from sub-process output"""
110
111 def __init__( self, stream, callback, sleep=0.1):
112 self.stream = stream
113 self.fin = False
114 self.callback = callback
115 self.sleep = sleep
116 self.eventFin = threading.Event()
117 threading.Thread.__init__(self)
118
119 def run(self):
120
121 try:
122 while True:
123 #Wait until data is available
124 rlist, wlist, xlist = select.select([self.stream], [], [], self.sleep)
125
126 #If we should finish, finish
127 if self.fin:
128 break
129
130 #Got new data
131 if len(rlist) > 0:
132 fd = rlist[0]
133 str = fd.read(1)
134 #Call callbacks
135 self.callback(str)
136 except:
137 pass
138
139 #Set the finished event
140 self.eventFin.set()
141
142
143
144
145 class GdbTerminal (DbgTerminal):
146
147 gdbreply = ""
148
149
150 def getCommand( self, binary ):
151 return "gdb --fullname %s" % (binary,)
152
153 def sendBreak(self, file, lineno):
154 self.gdbin.write("break %s:%d\n" % (file, lineno))
155
156 def sendContinue(self):
157 self.gdbin.write("cont\n")
158
159 def sendRun(self):
160 self.gdbin.write("run\n")
161
162 def sendInspectVar(self, var):
163 self.sendInspectExpr(var)
164
165 def sendInspectExpr(self, expr):
166 self.gdbin.write("print %s\n" % (expr,))
167
168 def sendSetTTY(self, ttyname):
169 self.gdbin.write("set inferior-tty %s\n" % (ttyname,))
170
171 def sendQuit(self):
172 self.gdbin.write("quit\n")
173 DbgTerminal.stop(self)
174
175 def getDbgReply(self, timeout=None):
176
177 while True:
178 splits = self.gdbreply.split("\n")
179
180 #Need more data: If there is a single (gdb) entry, then
181 #there are at least two splits
182 if len(splits) <= 1:
183 try:
184 self.gdbreply += self.dbgreplyqueue.get(True, timeout)
185 except Queue.Empty:
186 return None
187 #Yeah there is data!
188 else:
189 self.gdbreply = string.join(splits[1:], "(gdb)")
190 return string.strip(splits[0])
191
192 def flushDbgReply(self):
193
194 try:
195 self.gdbreply = ""
196 #Remove all elements from queue
197 while True:
198 self.dbgreplyqueue.get(False)
199 except Queue.Empty:
200 pass
201
202 if __name__ == "__main__":
203
204 def tostdout(str):
205 sys.stdout.write(str)
206 sys.stdout.flush()
207
208 try:
209
210 term = GdbTerminal( "./main", tostdout, tostdout)
211 term.sendBreak("main.cpp", 13)
212 term.sendBreak("main.cpp", 14)
213 term.sendRun()
214 term.childin.write("1\n");
215 term.childin.write("2\n");
216
217 time.sleep(0.2)
218 term.flushDbgReply()
219 term.sendInspectVar("a+b")
220
221 term.sendContinue()
222 term.sendInspectVar("a+b")
223 term.sendContinue()
224
225
226 time.sleep(1)
227
228 print "reply >>>", term.getDbgReply(1)
229 print "reply >>>", term.getDbgReply(1)
230 print "reply >>>", term.getDbgReply(1)
231 print "reply >>>", term.getDbgReply(1)
232 print "reply >>>", term.getDbgReply(1)
233 print "reply >>>", term.getDbgReply(1)
234 print "reply >>>", term.getDbgReply(1)
235 print "reply >>>", term.getDbgReply(1)
236 print "reply >>>", term.getDbgReply(1)
237 print "reply >>>", term.getDbgReply(1)
238
239
240 while not term.iseof():
241
242 cmd = sys.stdin.readline()
243
244 if term.iseof():
245 break
246
247 if cmd == "quit\n":
248 term.sendQuit()
249 else:
250 term.gdbin.write(cmd)
251
252 except Exception, e:
253 print e
254 except:
255 pass
256
257
258 print "stopping..."
259 term.stop()
260
261
262