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.