TIP 348: Substituted 'errorstack' / 'traceback'

Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2017 Conference, Houston/TX, US, Oct 16-20
Send your abstracts to tclconference@googlegroups.com
by Aug 21.
Author:         Alexandre Ferrieux <alexandre.ferrieux@gmail.com>
State:          Final
Type:           Project
Vote:           Done
Created:        26-Feb-2009
Keywords:       Tcl,debugging
Tcl-Version:    8.6


This TIP proposes to add an errorstack options dict entry and associated info subcommand, giving a "substituted" traceback similar to Python's or gdb's ones.


The ::errorInfo variable is a valuable tool for debugging; however, it yields mostly static information regarding each level of procedure call, as it only gives the static text (extracted from the source) at the call site. This leads to frustrating situations, like when an error occurs at a deep level of a recursive function, ::errorInfo repeatedly reporting "f $x [foo [bar]]" or similar un-substituted commands. In other languages, the traceback is more useful in that it contains the actual values passed as arguments.

Proposed Change

This TIP proposes to create an -errorstack options dictionary entry, and an associated info errorstack ?interp? command returning a list containing the [info level 0] lists of command-and-args at each stack frame level at the time of error unwinding.


In a natural implementation, its construction is analogous to that of ::errorInfo, which is built incrementally, one level at a time while popping back from the error site. The only differences are that:

  1. dynamic arglists (from [info level 0]) are stored,

  2. the result is a true list built by (the equivalent of) lappend, and

  3. the granularity is coarser than with ::errorInfo since there is just one element per stack frame level (and not for intervening while or foreach constructs) and no adornment like "... while executing ..."

Measurements show that the performance hit of maintaining the error stack is small, being under 5% for realistic error stack sizes, and hits only the error cases. To achieve this speed, the list-growing is done carefully, reusing earlier optimizations made on list operations for the in-place case, so that in routine use the allocated Tcl_Obj, internal representation, and element array storage backing the list never change. This way, even code heavily relying on catch across dozens of stack frame levels won't noticeably suffer from the error stack machinery.


The error stack is an even-sized list alternating tokens and parameters. Tokens are currently either CALL or UP, but other values may be introduced in the future; CALL indicates a procedure call, and its parameter is the corresponding [info level 0]; UP' indicates a shift in variable frames generated by uplevel or similar, and applies to the previous CALL item:

CALL {foo a} UP 1 CALL {bar b} CALL {baz c} UP 2 CALL {gnu d} CALL {gnats e}

The above value indicates that [foo a] was called by [bar b] through [uplevel 1]. Similarly, [baz c] was itself lifted to toplevel by an [uplevel 2] in [gnu d].

Reference Implementation

The reference implementation can be found on SourceForgehttps://sourceforge.net/support/tracker.php?aid=2868499 .

An earlier, extremely slow, pure-Tcl proof of concept based on write traces on ::errorInfo can be found on the Wikihttp://wiki.tcl.tk/traceback .


This document has been placed in the public domain.