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:
The script below is far from complete and I would not even claim it to be useful, but it was a nice experiment anyway.
(BTW, I was unaware of Insight, which is another Tcl/Tk GUI for gdb - probably much more capable than the one below)
# 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]