summary history branches tags files
commit:0578060223c30dd830a4006afe2f57273f2b5b72
author:mrmekon
committer:mrmekon
date:Thu Nov 8 19:32:10 2012 -0500
parents:5fc8ff47372372aba77b2c81927127e19734b64e
Cleanup, documentation, and new debug command.
diff --git a/README.md b/README.md
line changes: +2/-1
index faa4e3a..123add0
--- a/README.md
+++ b/README.md
@@ -29,6 +29,7 @@ I wrote this because mdb.sh is currently incomplete, and nearly unusable, so loa
     * step (step target -- currently StepOver only)
     * print (print information about something -- currently only Program Counter)
     * help (list possible commands, or display specific command's help)
+    * debug (drop to a Python debugger, so you can debug while you debug.)
     * quit
 * Readline input handling supports up/down keys, and history.
 * Address-to-Source-Line conversions when stepping.
@@ -63,7 +64,7 @@ PICdb> continue
 Breakpoint 1: Stopped at 0x9D00B982 (MainDemo.c:534)
 PICdb> step
 PC: 0x9D00B982  (MainDemo.c:534)  (LW V0, -32416(S0))
-PICdb> print pc
+PICdb> print $pc
 PC: 0x9D00B9AC
 PICdb> continue
 Breakpoint 0: Stopped at 0x9D00B8C8 (MainDemo.c:431)

diff --git a/picdb.py b/picdb.py
line changes: +99/-22
index cf702d4..c80dab7
--- a/picdb.py
+++ b/picdb.py
@@ -1,25 +1,29 @@
+import os
 import sys
+import pdb
+import bdb
 import time
 import string
 import signal
-from select import select
+import operator
 
 from mdb.picdebugger import picdebugger
 
 class CommandHandler:
     def __init__(self, quitCB):
-        self.quitCB = quitCB
         self.dbg = picdebugger()
+        self._quitCB = quitCB
         self._commandMap = {
-            "connect": {'fn': self.cmdConnect, 'help': "Conects to a PIC target."},
-            "load": {'fn': self.cmdLoad, 'help': "Load ELF file onto target."},
-            "step": {'fn': self.cmdStep, 'help': "Step over next source line."},
-            "quit": {'fn': self.cmdQuit, 'help': "Quits this program."},
-            "help": {'fn': self.cmdHelp, 'help': "Displays this help."},
-            "break": {'fn': self.cmdBreak},
-            "continue": {'fn': self.cmdContinue},
-            "print": {'fn': self.cmdPrint},
-            "breakpoints": {'fn': self.cmdBreakpoints},
+        "connect": {'fn': self.cmdConnect, 'help': "Conects to a PIC target."},
+        "load": {'fn': self.cmdLoad, 'help': "Load ELF file onto target."},
+        "step": {'fn': self.cmdStep, 'help': "Step over next source line."},
+        "quit": {'fn': self.cmdQuit, 'help': "Quits this program."},
+        "help": {'fn': self.cmdHelp, 'help': "Displays this help."},
+        "debug": {'fn': self.cmdDebug, 'help': "Drop to Python console."},
+        "break": {'fn': self.cmdBreak, 'help': "Set breakpoint."},
+        "continue": {'fn': self.cmdContinue, 'help': "Continue running target."},
+        "print": {'fn': self.cmdPrint, 'help': "Print variable."},
+        "breakpoints": {'fn': self.cmdBreakpoints, 'help': "List breakpoints."},
         }
         
     def cmdConnect(self, args):
@@ -37,21 +41,54 @@ ex: connect PIC32MX150F128B
         self.dbg.connect()
 
     def cmdPrint(self, args):
-        if args.lower() == "pc":
+        '''
+Prints a variable or register.
+Usage: print <variable or register>
+Supported registers:
+ * $pc
+'''
+        if args.lower() == "$pc":
             print "PC: 0x%X" % self.dbg.getPC()
+
+    def cmdDebug(self, args):
+        '''
+Drops to a Python prompt for debug-level introspection.
+Usage: debug
+'''
+        pdb.set_trace()
             
     def cmdLoad(self, args):
+        '''
+Load an ELF file onto target board.
+Usage: load <file>
+<file> can be a full absolute path, or relative to the working directory.
+'''
         self.dbg.load(args)
         self.dbg.reset()
 
     def cmdBreak(self, args):
+        '''
+Set a breakpoint
+Usage: break <address>
+<address> is a memory address specified in decimal, or hexadecimal with an '0x'
+prefix.
+'''
         addr = int(args,0)
         self.dbg.setBreakpoint(addr)
 
     def cmdBreakpoints(self, args):
+        '''
+List all set breakpoints.  Outputs a numbered list of breakpoints, their memory
+address, their source file and line, and an asterisk if they are enabled.
+Usage: breakpoints
+'''
         self.dbg.listBreakpoints()
         
     def cmdContinue(self, args):
+        '''
+Continue running target from current PC.
+Usage: continue
+'''
         self.dbg.run()
         self.dbg.waitForHalt()
 
@@ -68,16 +105,28 @@ ex: connect PIC32MX150F128B
                                              pc,file,line)
 
     def cmdStep(self, args):
+        '''
+Step target over one line of source.
+Usage: step
+'''
         self.dbg.step()
         
     def cmdQuit(self, args):
-        self.quitCB()
+        '''
+Quit debugger.
+Usage: quit
+'''
+        self._quitCB()
         
     def cmdHelp(self, args):
+        '''
+Request list of commands, or help on a specific command.
+Usage: help [command]
+'''
         if len(args) == 0:
             print "Type 'help <topic>' for help."
             print
-            for x,info in self._commandMap.iteritems():
+            for x,info in sorted(self._commandMap.iteritems(),key=operator.itemgetter(0)):
                 line = x.ljust(20)
                 if info.has_key('help'):
                     line += info['help']
@@ -95,24 +144,39 @@ class CommandInterpreter:
     def __init__(self):
         self.running = False
         self._handler = CommandHandler(self.stopInputLoop)
+        signal.signal(signal.SIGINT, self.sigIntHandler)
 
     def stopInputLoop(self):
+        '''Set main loop to stop running.'''
         self.running = False
-        signal.alarm(1)
+
+    def _cleanShutdown(self):
+        '''Disconnect from debugger and quit.'''
+        print "Exiting!"
+        self._handler.dbg.disconnect()
+        sys.exit(0) # this will interrupt raw_input()
+
+    def sigIntHandler(self, sig, frame):
+        '''Quit cleanly on ^C.'''
+        self.stopInputLoop()
+        self._cleanShutdown()
 
     def _displayPrompt(self):
+        '''Display the debugger prompt.'''
         sys.stdout.write("PICdb> ")
         sys.stdout.flush()
         
     def _readUserInput(self):
+        '''Wait for (and return) input from user.  EOF terminates.'''
         try:
             user_input = raw_input()
             return user_input.strip()
         except EOFError:
-            self.running = False
+            self.stopInputLoop()
             return ""
 
     def _stringStartsWithCmd(self, str, cmd):
+        '''Determine if string from user starts with a known command.'''
         matches = False
         n = len(cmd)
         m = len(str)
@@ -120,10 +184,10 @@ class CommandInterpreter:
             if m == n or (m > n and str[n] not in string.ascii_letters):
                 matches = True
         return matches
+
     
-    def runInputLoop(self):
-        signal.signal(signal.SIGINT, lambda x,y: self.stopInputLoop())
-        self.running = True
+    def _mainLoop(self):
+        '''Main loop listening for commands from user.'''
         while self.running:
             self._displayPrompt()
             user_input = self._readUserInput()
@@ -133,8 +197,21 @@ class CommandInterpreter:
                 if self._stringStartsWithCmd(user_input, cmd):
                     info['fn'](user_input[len(cmd):].strip())
         print
-        self._handler.dbg.disconnect()
+        
+    
+    def run(self):
+        '''Run debugger.'''
+        self.running = True
+        # Wrap the whole main loop in an outer loop here so we can catch
+        # exceptions from the pdb debugger.
+        while self.running:
+            try:
+                self._mainLoop()
+            except bdb.BdbQuit:
+                pass # pdb quit, but we're still runnin'
+        self._cleanShutdown()
 
-interp = CommandInterpreter()
-interp.runInputLoop()
+if __name__ == "__main__":
+    interp = CommandInterpreter()
+    interp.run()