[Arjen Markus] (4 october 2010) I thought I'd give it a try - a Tcl/Tk GUI for the GNU debugger ([gdb]). Just to see what it takes. Well, it turned out to be both simpler and more complex than I thought at first: * You need [Expect] to control gdb - simply a pipe won't do it. * gdb produces different output depending on the command you gave - you need to match all of them * Making the user-interface intuitive enough will require hiding some peculiarities of gdb, like that you need the run command to start the program. The script below is far from complete and I would not even claim it to be useful, but it was a nice experiment anyway. ---- ====== # guigdb.tcl -- # Attempt to create a GUI for the GNU debugger # # (just a first experiment!) # # Note: # We need Expect because gdb takes over the standard # input. # package require Expect # findSourceFile -- # Find the name of the source file # # Arguments: # None # # Returns: # Nothing # # Side effects: # Show the source file # proc findSourceFile {} { expect -re {\(gdb\)} { if { [regexp {file (.+), line ([0-9]+)} $expect_out(buffer) ==> filename lineno] } { showCode $filename $lineno } } } # findLineno -- # Find the current line number and highlight # # Arguments: # None # # Returns: # Nothing # # Side effects: # Show the current line highlighted # proc findLineno {} { global currentFile expect -re {\(gdb\)} { if { [regexp {Breakpoint [0-9]+.*:([0-9]+)} $expect_out(buffer) ==> lineno] } { showCode $currentFile $lineno } elseif { [regexp {\(\) at (.*):([0-9]+)} $expect_out(buffer) ==> filename lineno] } { showCode $filename $lineno } elseif { [regexp {^[^0-9]*([0-9]+)} $expect_out(buffer) ==> lineno] } { showCode $currentFile $lineno } } } # showCode -- # Show the source file # # Arguments: # filename Name of the file to show # lineno Line number to highlight # # Returns: # Nothing # # Side effects: # Show the complete file # proc showCode {filename lineno} { global codeWindow global currentFile global currentLine if { $filename != $currentFile } { $codeWindow delete 1.0 end set infile [open $filename] $codeWindow insert 1.0 [read $infile] close $infile set currentLine "" } if { $currentLine != "" } { $codeWindow tag remove highlight $currentLine.0 [expr {$currentLine+1}].0 } set currentLine $lineno $codeWindow tag add highlight $currentLine.0 [expr {$currentLine+1}].0 $codeWindow see $currentLine.0 } # setupWindow -- # Set up the main window # # Arguments: # None # # Returns: # Nothing # # Side effects: # The main window is filled with a menu bar # and two text widgets # proc setupWindow {} { global codeWindow global commandWindow set codeWindow [text .codeWindow -yscrollcommand [list .yscroll set] -height 30 -xscrollcommand [list .xscroll set]] set yscroll [scrollbar .yscroll -command [list .codeWindow yview]] set xscroll [scrollbar .xscroll -command [list .codeWindow xview] -orient horizontal] set commandWindow [text .commandWindow -yscrollcommand [list .cscroll set] -height 10] set cscroll [scrollbar .cscroll -command [list .commandWindow yview]] set commandFrame [frame .frame] set command [entry .frame.command -textvariable ::cmd] set commandButton [button .frame.send -text "Command" -command runCommand -width 10] set stepButton [button .frame.step -text "Step" -command {runCommand s} -width 10] set stepOverButton [button .frame.stepover -text "Step over" -command {runCommand n} -width 10] set continueButton [button .frame.continue -text "Continue" -command {runCommand c} -width 10] grid $codeWindow $yscroll -sticky news grid $xscroll -sticky news grid $commandWindow $cscroll -sticky news grid $commandFrame - -sticky news grid $command $commandButton $stepButton $stepOverButton $continueButton -sticky news $codeWindow tag configure highlight -background lightblue } # runGdb -- # Start the debugger for the specified program # # Arguments: # exename Name of the executable to run # # Returns: # Nothing # proc runGdb {exename} { global spawn_id ;# Really required by Expect! exp_spawn gdb $exename showResult exp_send "break main\r" findSourceFile #console show showResult } # showResult -- # Read the output from the debugger and show it # # Arguments: # None # # Returns: # Nothing # # Side effects: # Show the output in the output window # proc showResult {} { global commandWindow expect -re {\(gdb\)} { $commandWindow insert end "$expect_out(buffer)\n" $commandWindow see end } timeout { $commandWindow insert end "Timeout!" $commandWindow see end } #console show #parray ::expect_out } # runCommand -- # Send a command to the debugger # # Arguments: # command Command to run (optional) # # Returns: # Nothing # # Side effects: # Send the command to the debugger - whatever # comes back will be shown or analysed # # Uses the global variable "cmd" # proc runCommand {{command {}}} { global gdb global spawn_id global cmd if { $command != "" } { set cmd $command } exp_send "$cmd\r" switch -- $cmd { "s" - "n" - "c" { findLineno } default { showResult } } set cmd "" } # main -- # Bring up the GUI and run the debugger # set currentFile "" setupWindow runGdb [lindex $argv 1] ====== <>Category GUI|Category Toys