Version 3 of traceback

Updated 2006-06-27 14:56:49

* A traceback is a precise description of where an error occured, in terms of the call graph of the program.

* It is useful to display and/or store it whenever the error is fairly unexpected or hard to reproduce.

* To do so, the idiom is to

  if {[catch {some code} err]} {
      Log "Argh: err\n$::errorInfo"
  }

or if you're catching from bgerror:

  proc bgerror err {
      Log "Argh: err\n$::errorInfo"
  }

* However useful, ::errorInfo is a bit frustrating in that it only gives the static text of the commands on the call stack. Most notably, it doesn't give the actual values that were passed to the functions in which the error occured.

* To solve this, I like the following hack:

  set ::errorLevel -1
  set ::errorStack {}
  trace add variable ::errorInfo write {
      set __n [info level]

      if {($__n>0)&&($__n!=$::errorLevel)} {
          set ::errorLevel $__n
          set __l [info level 0]
          lappend ::errorStack $__l
      }
  }

It works by exploiting the fact that ::errorInfo is built progressively while climbing back up the stack; hence, each increment occurs within the scope of each level, and there info level 0 gives the local function's actual arguments, regardless of whether the variables holding these args have been modified since function entry or not.

* The code above is crude, to expose the mechanism. It can easily be refined, for example to auto-reset errorLevel and errorStack on subsequent invocations, as suggested by Martin Lemburg:

  if {[string match {*while executing*} $::errorInfo] == 0} {
      set ::errorLevel        -1;
      set ::errorStack        [list];
  }

-Alex


Category Glossary Category Tutorial