Richard Suchenwirth 2002-12-17 - This is my first experiment with the command traces introduced in Tcl 8.4: step through certain procs, so that every command inside the body is displayed before execution, and its result after, while allowing to execute arbitrary Tcl commands at such step positions. This is of course a powerful debugging tool
The code below requires stdin/out, so under Windows is best run from a tclsh. At the stepping prompt, <Return> lets you advance through the code. What troubled me on my 8.4.0/W95 installation at home is that for all but the first command, leavestep traces fired even before enterstep traces (with empty string result) - wonder whether that's a bug (same on 8.4.1/W2k)... Also, when I provoked an error with the uncommented kaboodle command below, stepping continued into unknown, which was interesting to observe but not what the doctor ordered.
proc stepping name { trace add exec $name enterstep enterStep trace add exec $name leavestep leaveStep } proc noStepping name { trace remove exec $name enterstep enterStep trace remove exec $name leavestep leaveStep } proc enterStep {cmd _} {uplevel 1 [list bp "before $cmd"]} proc leaveStep {cmd code result _} { uplevel 1 [list bp "result ($code):$result<=$cmd"] }
Reusable breakpoint handler from A minimal debugger
proc bp s { if {[string length $s]>50} {set s [string range $s 0 49]...} while 1 { puts -nonewline "$s > " flush stdout gets stdin line if {$line==""} break catch {uplevel 1 $line} res puts $res } }
Testing...
if {[file tail [info script]] == [file tail $argv0]} { proc foo x { puts "This is foo" set i 0 while {$i < $x} { puts "i=$i" incr i } #kaboodle puts "That was foo" } stepping foo foo 3 }
Sample session:
before puts {This is foo} > This is foo result (0):<=puts {This is foo} > result (0):<=set i 0 > before set i 0 > result (0):0<=set i 0 > result (0):<=while {$i < $x} { puts "i... > before while {$i < $x} { puts "i=$i" ... > result (0):<=puts i=0 > info locals x i result (0):<=puts i=0 > set x 3 result (0):<=puts i=0 > before puts i=0 > i=0 result (0):<=puts i=0 > result (0):<=incr i > before incr i > result (0):1<=incr i > result (0):<=puts i=1 > before puts i=1 > i=1 result (0):<=puts i=1 > result (0):<=incr i > before incr i > result (0):2<=incr i > result (0):<=puts i=2 > before puts i=2 > info pa 8.4.1 before puts i=2 > i=2 result (0):<=puts i=2 > result (0):<=incr i > before incr i > result (0):3<=incr i > result (0):<=while {$i < $x} { puts "i... > result (0):<=puts {That was foo} > before puts {That was foo} > That was foo result (0):<=puts {That was foo} >
The early leavestep is indeed a bug, registered at SF (655645). Until it gets fixed, here is a workaround that uses a global variable to suppress calls to leaveStep before the matching enterStep was called:
proc stepping name { trace add exec $name enterstep enterStep trace add exec $name leavestep leaveStep if {![info exists ::Entered]} {set ::Entered ""} ;# Workaround } proc enterStep {cmd _} { set ::Entered $cmd ;# Workaround uplevel 1 [list bp "before $cmd"] } proc leaveStep {cmd code result _} { if {$cmd eq $::Entered} { ;# Workaround uplevel 1 [list bp "result ($code):$result<=$cmd"] } }
AM I have used the above technique to create A basic debugger that works under tclsh and wish, even under Windows.
RS A more recent take is under Steppin' out again.