From 7108ee075bddb8425305ed93bc1992ca8fe0c6cb Mon Sep 17 00:00:00 2001 From: Stefan Huber Date: Sat, 7 Jun 2008 23:21:36 +0200 Subject: [PATCH] Major change: Discovered the vte.Terminal class and moved to that one! - Client I/O Window - Debugger I/O Window - Basic commands to debugger --- ClientIOWindow.py | 32 ++++++ DbgTerminal.py | 262 ---------------------------------------------- DbgWindow.py | 140 +++++++++++++++++++++++++ GdbWindow.py | 66 ++++++++++++ 4 files changed, 238 insertions(+), 262 deletions(-) create mode 100755 ClientIOWindow.py delete mode 100755 DbgTerminal.py create mode 100755 DbgWindow.py create mode 100755 GdbWindow.py diff --git a/ClientIOWindow.py b/ClientIOWindow.py new file mode 100755 index 0000000..87f045e --- /dev/null +++ b/ClientIOWindow.py @@ -0,0 +1,32 @@ +#!/usr/bin/python +#shuber, 2008-06-04 + +__author__ = "shuber" + + +import gtk +import vte + + +class ClientIOWindow (gtk.Window): + + + def __init__(self, parent, pty_master): + + #Set up GTK stuff + gtk.Window.__init__(self) + self.set_screen(parent.get_screen()) + self.connect("destroy", lambda *w: gtk.main_quit()) + + + #Set title and add terminal + self.set_title("Client I/O") + self.terminal = vte.Terminal() + self.add(self.terminal) + + #Set the pty to client + self.terminal.set_pty(pty_master) + + #Show the window + self.show_all() + diff --git a/DbgTerminal.py b/DbgTerminal.py deleted file mode 100755 index 88064db..0000000 --- a/DbgTerminal.py +++ /dev/null @@ -1,262 +0,0 @@ -#!/usr/bin/python - - -import select -import string -import sys -import thread -import threading -import time -import os -import pty -import Queue - - -class DbgTerminal: - - #Reply cache from debugger - dbgreplyqueue = Queue.Queue() - - - def __init__ (self, binary, gdbReadCallback, childReadCallback): - - #Set some members - self.gdbReadCallback = gdbReadCallback - self.childReadCallback = childReadCallback - self.binary = binary - self.stopped = False - - #Connect to sub-process - self.__connect() - - def __connect( self ): - - #This function handles readings from the debugger - def gdbCb(str): - self.gdbReadCallback(str) - self.dbgreplyqueue.put(str) - - #Open pseudo-terminal where to-be-debugged process reads/writes to - self.ptymaster, self.ptyslave = pty.openpty() - self.childout = os.fdopen(self.ptymaster, "r", 0) - self.childin = os.fdopen(self.ptymaster, "w", 0) - - #Call gdb and get in- and out-streams to/from gdb - cmd = self.getCommand(self.binary) - self.gdbin, self.gdbout = os.popen4( cmd, bufsize=0) - - #Set up a reading thread to gdb output - self.gdbReadThread = self.ReadThread(self.gdbout, gdbCb) - self.gdbReadThread.start() - - #Set up a reading thread to childs output - self.childReadThread = self.ReadThread(self.childout, self.childReadCallback) - self.childReadThread.start() - - #Set up tty gdb-childs process - self.sendSetTTY(os.ttyname(self.ptyslave)) - - - - def getDbgReply(self): - raise NotImplementedError() - - - def iseof( self ): - """Check if terminal is closed already""" - return self.gdbReadThread.eventFin.isSet() - - def stop( self ): - - if not self.stopped: - - self.stopped = True - - #Finish the reading-thread - self.gdbReadThread.fin = True - self.childReadThread.fin = True - self.gdbReadThread.eventFin.wait(1) - self.childReadThread.eventFin.wait(1) - - - def getCommand( self, binary ): - """Get the command to execute""" - raise NotImplementedError() - - def sendBreak(self, file, lineno): - raise NotImplementedError() - - def sendContinue(self): - raise NotImplementedError() - - def sendRun(self): - raise NotImplementedError() - - def sendInspectVar(self, var): - raise NotImplementedError() - - def sendInspectExpr(self, expr): - raise NotImplementedError() - - def sendSetTTY(self, ttyname): - raise NotImplementedError() - - def sendQuit(self): - raise NotImplementedError() - - - class ReadThread (threading.Thread): - """Thread which reads from sub-process output""" - - def __init__( self, stream, callback, sleep=0.1): - self.stream = stream - self.fin = False - self.callback = callback - self.sleep = sleep - self.eventFin = threading.Event() - threading.Thread.__init__(self) - - def run(self): - - try: - while True: - #Wait until data is available - rlist, wlist, xlist = select.select([self.stream], [], [], self.sleep) - - #If we should finish, finish - if self.fin: - break - - #Got new data - if len(rlist) > 0: - fd = rlist[0] - str = fd.read(1) - #Call callbacks - self.callback(str) - except: - pass - - #Set the finished event - self.eventFin.set() - - - - -class GdbTerminal (DbgTerminal): - - gdbreply = "" - - - def getCommand( self, binary ): - return "gdb --fullname %s" % (binary,) - - def sendBreak(self, file, lineno): - self.gdbin.write("break %s:%d\n" % (file, lineno)) - - def sendContinue(self): - self.gdbin.write("cont\n") - - def sendRun(self): - self.gdbin.write("run\n") - - def sendInspectVar(self, var): - self.sendInspectExpr(var) - - def sendInspectExpr(self, expr): - self.gdbin.write("print %s\n" % (expr,)) - - def sendSetTTY(self, ttyname): - self.gdbin.write("set inferior-tty %s\n" % (ttyname,)) - - def sendQuit(self): - self.gdbin.write("quit\n") - DbgTerminal.stop(self) - - def getDbgReply(self, timeout=None): - - while True: - splits = self.gdbreply.split("\n") - - #Need more data: If there is a single (gdb) entry, then - #there are at least two splits - if len(splits) <= 1: - try: - self.gdbreply += self.dbgreplyqueue.get(True, timeout) - except Queue.Empty: - return None - #Yeah there is data! - else: - self.gdbreply = string.join(splits[1:], "(gdb)") - return string.strip(splits[0]) - - def flushDbgReply(self): - - try: - self.gdbreply = "" - #Remove all elements from queue - while True: - self.dbgreplyqueue.get(False) - except Queue.Empty: - pass - -if __name__ == "__main__": - - def tostdout(str): - sys.stdout.write(str) - sys.stdout.flush() - - try: - - term = GdbTerminal( "./main", tostdout, tostdout) - term.sendBreak("main.cpp", 13) - term.sendBreak("main.cpp", 14) - term.sendRun() - term.childin.write("1\n"); - term.childin.write("2\n"); - - time.sleep(0.2) - term.flushDbgReply() - term.sendInspectVar("a+b") - - term.sendContinue() - term.sendInspectVar("a+b") - term.sendContinue() - - - time.sleep(1) - - print "reply >>>", term.getDbgReply(1) - print "reply >>>", term.getDbgReply(1) - print "reply >>>", term.getDbgReply(1) - print "reply >>>", term.getDbgReply(1) - print "reply >>>", term.getDbgReply(1) - print "reply >>>", term.getDbgReply(1) - print "reply >>>", term.getDbgReply(1) - print "reply >>>", term.getDbgReply(1) - print "reply >>>", term.getDbgReply(1) - print "reply >>>", term.getDbgReply(1) - - - while not term.iseof(): - - cmd = sys.stdin.readline() - - if term.iseof(): - break - - if cmd == "quit\n": - term.sendQuit() - else: - term.gdbin.write(cmd) - - except Exception, e: - print e - except: - pass - - - print "stopping..." - term.stop() - - - diff --git a/DbgWindow.py b/DbgWindow.py new file mode 100755 index 0000000..6f46c6c --- /dev/null +++ b/DbgWindow.py @@ -0,0 +1,140 @@ +#!/usr/bin/python +#shuber, 2008-06-04 + +__author__ = "shuber" + + +import gtk +import pty +import string +import time +import threading +import vte + +import ClientIOWindow + + +class DbgWindow (gtk.Window): + + clientIOWnd = None + lastrow = 0 + history = [] + + + + def __init__(self, clientCmd): + + #Set up some members + self.clientCmd = clientCmd + + #Set up GTK stuff + gtk.Window.__init__(self) + self.connect("destroy", lambda *w: gtk.main_quit()) + + #Set title and add terminal + self.set_title("Debugger I/O") + self.terminal = vte.Terminal() + self.terminal.history = [] + self.terminal.history_length = 5 + self.add(self.terminal) + + #Set up terminal window and initialize debugger + self.terminal.connect("child-exited", lambda *w: gtk.main_quit()) + self.terminal.connect("cursor-moved", self.contents_changed) + self._initializeDbg() + + #Show the window + self.show_all() + + + def _initializeDbg(self): + + #Start debugger + self.terminal.fork_command( self.getDbgCommand(), self.getDbgArgv()) + + #Open pseudo-terminal where to-be-debugged process reads/writes to + self.client_ptymaster, self.client_ptyslave = pty.openpty() + self.setDbgPty(self.client_ptyslave) + + + def contents_changed(self, term): + c,r = term.get_cursor_position() + + if self.lastrow < r: + text = self.terminal.get_text_range(self.lastrow,0,r-1,-1,lambda *w:True) + self.history += string.split(text, "\n") + self.lastrow = r + + def waitForDbgNewline(self): + r = self.lastrow + while not self.lastrow > r: + gtk.main_iteration() + + def waitForDbgRx(self, rx): + while True: + start = len(self.history) + gtk.main_iteration() + + for line in self.history[start:]: + if rx.search(line): + return line + + + def getDbgCommand(self): + return self.getDbgArgv()[0]; + + def getDbgArgv(self): + raise NotImplementedError() + + def setDbgPty(self, pty): + raise NotImplementedError() + + def setDbgRun(self): + raise NotImplementedError() + + def setDbgQuit(self): + raise NotImplementedError() + + def setDbgContinue(self): + raise NotImplementedError() + + def setDbgBreakpoint(self, file, lineno): + raise NotImplementedError() + + def getDbgExpression(self, expr): + raise NotImplementedError() + + def getDbgLastLine(self): + if len(self.history) == 0: + return None + + return self.history[-1] + + def feed_dbg(self, text): + self.terminal.feed_child(text) + + + def showClientIOWindow(self): + if not self.clientIOWnd: + self.clientIOWnd = ClientIOWindow.ClientIOWindow(self, self.client_ptymaster) + + + + +def launchDebugger(wnd): + + wnd.showClientIOWindow() + + wnd.setDbgBreakpoint("main.cpp", 15) + wnd.setDbgRun() + res = wnd.getDbgExpression("a") + print "Result = ", res + + wnd.setDbgQuit() + + gtk.main() + + + + + diff --git a/GdbWindow.py b/GdbWindow.py new file mode 100755 index 0000000..a5baaa8 --- /dev/null +++ b/GdbWindow.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +#shuber, 2008-06-04 + +__author__ = "shuber" + + +import gtk +import os +import re +import string +import sys +import time + +import DbgWindow + + +class GdbWindow (DbgWindow.DbgWindow): + + + def __init__(self, clientCmd): + DbgWindow.DbgWindow.__init__(self, clientCmd) + + def getDbgArgv(self): + return ["gdb", "--fullname", string.split(self.clientCmd)[0]] + + def setDbgPty(self, pty): + ttyname = os.ttyname(pty) + self.feed_dbg("set inferior-tty %s\n" % (ttyname,)) + + def setDbgRun(self): + argv = string.join(string.split(self.clientCmd)[1:]) + self.feed_dbg("run " + argv + "\n") + + def setDbgQuit(self): + self.feed_dbg("quit\n") + self.waitForDbgNewline() + self.feed_dbg("y\n"); + + def setDbgContinue(self): + self.feed_dbg("cont\n"); + + def setDbgBreakpoint(self, file, lineno): + self.feed_dbg("break %s:%d\n" % (file, lineno)) + + def getDbgExpression(self, expr): + + self.waitForDbgNewline() + self.feed_dbg("print " + expr + "\n") + + rx = re.compile("^\$[1-9][0-9]* = .*$") + response = self.waitForDbgRx(rx) + + split = string.split(response, "=") + return string.join(split[1:], "=").strip() + + + +if __name__ == "__main__": + + + gdbwnd = GdbWindow(string.join(sys.argv[1:])) + DbgWindow.launchDebugger(gdbwnd) + + + + -- 2.30.2