-
Notifications
You must be signed in to change notification settings - Fork 13
Stack Trace
Tcl 8.6 stack traces are similar to but simpler than TCL 7.6 stack traces, as described below. However, a number of things (such as loop bodies) are no longer included. In particular, Tcl_AddErrorInfo is still used
to add the error info to the stack trace. The focus now seems to be on proc
invocations, and on code that is
called outside of the normal flow of control, e.g., namespace unknown
handlers, etc.
Command/Function | Module | Line | What | |
---|---|---|---|---|
A | "for" | tclCmdAH.c | 2684 | ("for" initial command) |
A | "for" | tclCmdAH.c | 2801 | ("for" loop-end command) |
B | SortCompare | tclCmdIL.c | 4303 | (-compare command) |
D | "dict incr" | tclDictObj.c | 2120 | (reading increment) |
D | "dict filter" | tclDictObj.c | 3043 | ("dict filter" filter script key variable) |
D | "dict filter" | tclDictObj.c | 3050 | ("dict filter" filter script value variable) |
C | "dict update" | tclDictObj.c | 3223 | (body of "dict update") |
C | "dict with" | tclDictObj.c | 3372 | (body of "dict with") |
B | ensembles | tclEnsemble.c | 2302 | ... ensemble unknown subcommand ... |
B | ensembles | tclEnsemble.c | 2340 | ... ensemble unknown subcommand ... |
B | ensembles | tclEnsemble.c | 2346 | (ensemble unknown subcommand handler) |
D | IncrObj | tclExecute.c | 1884 | (reading increment) |
D | IncrObj | tclExecute.c | 1932 | (reading increment) |
D | TEBCresume | tclExecute.c | 3816 | (reading value of variable to increment) |
B | TimeLimitCallback | tclInterp.c | 4002 | (while waiting for event) |
D | GetOpenModeEx | tclIOUtil.c | 1608 | access mode errors |
B | Prompt | tclMain.c | 911 | (script that generates a prompt) |
B | "package require" | tclPkg.c | 445 | ("package unknown" script) |
D | "proc" | tclProc.c | 168 | (creating proc "$name") |
B | "after" | tclTimer.c | 1193 | ("after" script) |
D | IncrObjVar2 | tclVar.c | 2095 | (reading value of variable to increment) |
Notes:
- A: Stack trace in existing code base that should be added.
- B: Cases that should be handled if the relevant feature is ever added to Molt.
- C: Execution of a command body. Why should these be included when loop bodies aren't?
- D: Cases that add context, but that don't seem necessary.
My plan is to support A now, B whenever it's appropriate, and C (with misgivings).
- A TCL C function can detect an error or propagate an error.
- It knows the difference by the ERR_ALREADY_LOGGED interpreter flag.
- Either way, it calls `Tcl_AddErrorInfo()`` to save a stack trace entry.
- The entry can vary depending on whether this is a newly detected error or a propagated error.
In `Tcl_Eval()``, for example, a detection entry looks like this:
while executing
"{some TCL command} ..."
and a propagation entry looks like this:
invoked from within
"{the calling TCL command} ..."
The command is elided and the ellipsis included only if the command is too long.
The Tcl_AddErrorInfo
function does this:
- On a newly detected error, it clears the TCL
errorInfo
variable and sets theerrorCode
toNONE
if it isn't already set.- Note: it's setting the actual TCL variables.
- Then, either way it appends the message to the current value of
errorInfo
.
Thus, errorInfo
gets built up with the error at the top and the ultimate caller at the
bottom.
The natural thing is to augment the MoltReturn data
, and particularly the ResultCode::Error
enum. At present, the Error
constant has one argument, a String. In addition or instead it
should take a struct, ErrorInfo, which can accumulate stack trace info for later programmatic
query (unsupported in TCL 7.6). We also need to update the errorInfo
variable; and it may
be appropriate to do that as we go, as now; otherwise it needs to be done when the error is
finally caught.
TCL's error handling evolved over many years and versions. Should we use an errorInfo variable? Should we provide a command to return the most recent error? Should we require that you catch it? Perhaps errorInfo should be the property of the molt-shell, rather than molt itself?
Some notes:
- Many functions appear to simply AddErrorInfo and return TCL_ERROR or -1 without setting the actual interp result. Molt would probably use the same mechanism for both kinds.
Command/Function | Module | Line | What |
---|---|---|---|
AfterProc | tclEvent.c | 1870 | ("after" script) |
InterpProc | tclProc.c | 510 | (procedure "{name}" line %d) |
SortCompareProc | tclCmdIL.c | 1428 | (converting list element...) |
SortCompareProc | tclCmdIL.c | 1443 | (converting list element...) |
SortCompareProc | tclCmdIL.c | 1468 | (user-defined comparison command) |
Tcl_BackgroundError | tclEvent.c | 1255 | "", Setting up for background error |
Tcl_Eval | tclBasic.c | 1486 | "while executing", "invoked from within" |
Tcl_EvalFile | tclIOUtil.c | 421 | (file "{name}" line %d) |
Tcl_GetOpenMode | tclIOUtil.c | 256 | "while processing open access modes" |
Tcl_Main | tclMain.c | 158 | Appends "" before reading errorInfo |
Tcl_ParseVar | tclParse.c | 1333 | (parsing index for array "...") |
Tcl_PkgRequire | tclPkg.c | 203 | ("package ifneeded" script) |
Tcl_PkgRequire | tclPkg.c | 203 | ("package unknown" script) |
"case" | tclCmdAH.c | n/a | "case" is obsolete. |
"error" | tclCmdAH.c | 393 | The error message. |
"eval" | tclCmdAH.c | 454 | "eval" body line %d |
"for" | tclCmdAH.c | 1162 | "for" initial command |
"for" | tclCmdAH.c | 1179 | "for" body line %d |
"for" | tclCmdAH.c | 1188 | "for" loop-end command |
"foreach" | tclCmdAH.c | 1330 | "foreach" body line %d |
"incr" | tclCmdIL.c | 196 | (reading value of variable to increment) |
"incr" | tclCmdIL.c | 206 | (reading increment) |
"interp", slaves | tclInterp.c | n/a | Won't be implementing this any time soon. |
"load" | tclLoad.c | n/a | Dynamic loading isn't a thing in Rust. |
"switch" | tclCmdMZ.c | 1702 | (... arm line %d) |
"time" | tclCmdMZ.c | 1768 | ("time" body line %d) |
"uplevel" | tclProc.c | 313 | ("uplevel" body line %d) |
"while" | tclCmdMZ.c | 2104 | ("while" body line %d) |