debug with trace

iu2 2007-01-24

Seeking for a simple debug scheme, I've managed to do it using tclsh (must have stdin..). Actually now it doesn't seem to me so simple... ;-)

  # break point - pause execution
  proc bp {} {
    interp alias {} tracecmd {} tracecmd1
  }

 # continue execution
  proc cont {} {
    interp alias {} tracecmd {} tracecmd2
  }

  # used after a bp command  
  proc tracecmd1 {cmd op} {
    puts $cmd
    puts -nonewline "[lindex [info level -1] 0]> "
    gets stdin a
    while {$a ne ""} {if {[catch {uplevel 1 puts \[$a\]} err]} {puts $err}
      gets stdin a
    }
  }

  # used before a bp command or after a cont command
  proc tracecmd2 {cmd op} {
    if {$cmd eq "bp"} {
      interp alias {} tracecmd {} tracecmd1
    }
  }

  interp alias {} tracecmd {} tracecmd2

I tried it on this

  proc func1 {v} {
    func2 10
    foreach x {1 2 3 4 5} {
    }
    bp
    for {set i 0} {$i < $v} {incr i} {
      puts i=$i
    }
  }

  proc func2 {a} {
    puts {This is func2}
    puts var=$a
  }

Of cource

  trace add execution func1 enterstep tracecmd

must be added before the func1 or whatever debugged function.

To debug this I invoked tclsh, then sourced the code with func1, func2 and the debug procs. Then I called func1 via tclsh. func1 runs until the bp. Pressing Enter continues to the next command/line. cont and then Enter continues execution until end of proc or until next bp. Any other input is evaluated so variables can be inspected, changed, other procs can be called, etc..

trace add execution func1 enterstep tracecmd must be added before the debugged function. This line can be converted to a simple proc, of course.

A different approach to replace the trace add... line: Here is the traceMe proc which when put on the first line of any proc, makes it tracable. This seems dangerous but worked with my simple example

  # if this is the first line in a function, make it tracable
  proc traceMe {} {
    set funcname [lindex [info level -1] 0]
    # redefine the function, to not include the traceMe command
    proc $funcname [info args $funcname] [join [lrange [split [info body $funcname] \n] 1 end] \n]
    # put a trace, will take effect only on next invocation
    uplevel #0 [list trace add execution $funcname enterstep tracecmd]
    # re-invoke
    uplevel 2 [info level -1]
    # cancel original incovation
    return -code return {}
  }

Now func1 can be rewritten

  proc func1 {v} {
    traceMe
    func2 10
    foreach x {1 2 3 4 5} {
    }
    bp
    for {set i 0} {$i < $v} {incr i} {
      puts i=$i
    }
  }

I wonder if I could call it debug with grace...