[Richard Suchenwirth] 2002-12-17 - This is my first experiment with the command [trace]s introduced in Tcl 8.4: step through certain [proc]s, 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, 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]. <> Debugging | Arts and crafts of Tcl-Tk programming