\documentclass[]{article}
\usepackage{amsmath,graphicx,supertabular,hyperref,tabularx,ifthen}
\title{TIP \#290: Registration of Custom Error Handler Scripts}
\date{October 29, 2006}
\author{Eckhard Lehmann, Larry W. Virden}
\urlstyle{sf}
\setlength{\parskip}{1ex}
\setlength{\parindent}{0pt}
\def\tipversion$#1${\texttt{\$#1\$}}
\def\tiplangle#1{\ensuremath{<}}
\def\tiprangle#1{\ensuremath{>}}
\def\tipbar#1{\ensuremath{|}}
\def\tipmail#1#2{\(\langle\){\small\expandafter\url{#1@#2}}\(\rangle\)}
\ifx\pdfoutput\undefined
\newcommand{\tipimage}[2]{\typeout{Make sure you download #1.eps}\ifthenelse{\lengthtest{0.8\textwidth>#2}\and\lengthtest{0pt<#2}}{\includegraphics{#1.eps}}{\includegraphics[width=0.8\textwidth]{#1.eps}}}
\newcommand{\tipxref}[1]{}
\newcommand{\tipxrefend}{}
\else
\newcommand{\tipimage}[2]{\typeout{Make sure you create #1.pdf}\ifthenelse{\lengthtest{0.8\textwidth>#2}\and\lengthtest{0pt<#2}}{\includegraphics{#1.pdf}}{\includegraphics[width=0.8\textwidth]{#1.pdf}}}
\pdfcatalog{/PageMode /UseOutlines}
\newcommand{\tipxref}[1]{\pdfannotlink attr {/C [0.5 0.5 1.0] /Border [0 0 1]} goto name {#1}}
\newcommand{\tipxrefend}{\pdfendlink}
\fi
\newenvironment{tipabstract}{\begin{abstract}}{\end{abstract}}
\begin{document}\maketitle
\begin{center}\begin{tabularx}{\linewidth}{|r@{: }X|}\hline
\textbf{TIP \#290}&\textbf{Registration of Custom Error Handler Scripts}\\\hline
Author&

Eckhard Lehmann \tipmail{ecky-l}{web.de}
\par
Larry W. Virden \tipmail{lvirden}{yahoo.com}
\\
Created&Sunday, \(\text{29}^{\text{th}}\) October 2006\\
Type&Project\\
Tcl Version&\(8.7\)\\
State&Draft\\
Vote&Pending\\
Version&\tipversion$Revision: 1.13 $\\
Keywords&Tcl, error, trap\\
Post History&\\
\hline\end{tabularx}\end{center}
\thispagestyle{empty}\pagestyle{empty}
\begin{tipabstract}

This TIP proposes the possibility to register a custom command as error and exception handler.
\end{tipabstract}
\tableofcontents\setcounter{page}{0}\clearpage\pagestyle{plain}

\section{Rationale}

Errors are thrown in the Tcl interpreter through the \textbf{error} command or from a C extension that returns TCL\_ERROR. When an error is thrown, the global \textit{errorInfo} variable is filled with rudimentary stacktrace information and the error message itself. The global \textit{errorCode} variable can contain an error code if this is provided by the command that has thrown the error.

Errors can be caught with the \textbf{catch} command. In this case, the \textit{errorInfo} variable is still filled with the information mentioned above, but the error is not presented to the interpreter. If the error is not caught, it is presented to the interpreter and the execution of the current code is aborted immediately

The information in \textit{errorInfo} is, in some simple cases, useful for reproducing and tracking down the error source and fixing the problem. In more complicated cases however, \textit{errorInfo} does not include enough information to successfully reproduce the error --- information about the application's state is missing.

In other languages such as LISP, this problem is addressed by stopping the execution at the position where the error was thrown (preserving the current callframe) and presenting the developer with a console that enables him to introspect the running program. Although Tcl has very good introspection capabilities, it is not possible to use them in an error case, because the execution just aborts and the stacktrace is unwound at once. For errors generated with the \textbf{error} command, it is possible to overwrite this command and provide more advanced functionality, but this is not possible if errors are generated in C code by \textit{return TCL\_ERROR}.

The proposed implementation addresses this problem by a custom error command that is executed whenever an error occurs in the execution of Tcl code. This opens a range of implementation possibilities for error handling, for instance:
\begin{itemize}
\item{}
registering a \textit{breakpoint} command that stops execution at the error position and opens a console for introspection
\item{}
registering a more advanced (Tk) debugger that opens on error for introspection.
\item{}
registering a command that captures the state of each call-frame up to the one where the error was thrown and writes that state to a file. This file can later be debugged (with an appropriate tool).
\end{itemize}

\section{Alternatives}
\begin{enumerate}
\item{}
Overwriting of the \textbf{error} command As stated abovem, this works only for errors generated from Tcl code, whereas it does not work for errors from C extensions.
\setcounter{enumi}{1}
\item{}
Leavestep execution traces In Tcl \tiprangle{}=8.4 it is possible to register execution traces on \textit{leavestep} to commands. To implement part of the proposed functionality it would be possible to add a leavestep trace procedure, which --- after each command --- checks for the return code and acts accordingly. The problem with this approach is, that the procedure is called after \textit{each} command, independent from whether it returned an error or not. This can slow down the program execution more or less significantly. Furthermore, it is not easy if at all possible to capture occurences of \textbf{catch} by this way, so it is complicated or impossible to react on caught errors.
\end{enumerate}

It might be possible to get part or most of the proposed functionality by \textit{trace add execution leavestep} or overwriting of \textbf{error}. But it is the responsibility of the core language (even more when it is a dynamic language) to provide customizable and advanced error handling. The TIP proposes in this direction.

\section{Specification}

The implementation consists of two parts: a registration command for the custom command and a place where the handler is called. For this to work, there are some minor changes necessary to the Tcl execution engine and to the Interp structure. For running the handler on caught and or uncaught errors (depending on how the user wants to have it) it is necessary to capture the current level of ``catch''es that occure during execution.

The registration command is responsible for:
\begin{itemize}
\item{}
register the command for caught and/or uncaught exceptions
\item{}
retrieve the currently registered command
\item{}
change the execution details (caught and/or uncaught errors) and the command
\item{}
unregister the command and thus get back to the current behaviour in error cases
\end{itemize}

Since the functionality is very similar to the family of \textbf{trace} commands, the proposed registration command is an extension to trace:

\textbf{trace set exception ?-caught? ?-uncaught? ?command?}: Registration and modification

The arguments \textit{-caught} and/or \textit{-uncaught} to \textit{trace set exception} modify the run conditions for the currently registered handler (run on caught/uncaught errors). With the \textit{command} argument, this is set as the new handler. The return code and result of the command that was executing is appended to the registered \textit{command}. So, the real call is: \textit{command code result}.

\textbf{trace info exception}: Information about the current handler

This command returns a list with the elements \{-caught y/n -uncaught y/n script\}, where caught and uncaught flags are specified and script is the currently registered handler.

\textbf{trace unset exception}: Remove registerred handler

Any previously set error handler is unregistered.

The \textit{command} that is registered will quell the error if it returns normal (return code 0). If the script returns abnormal, it's return code is returned to the interpreter. Errors inside the handler are not trapped by the script again, rather they are presented to the interpreter as usual --- otherwise this would result in an endless loop.

The changes in the execution engine should be done so that:
\begin{itemize}
\item{}
existing functionality is not disturbed
\item{}
the call frame is preserved after the error occured --- thus the custom command is run in the same level as where the error was thrown
\item{}
the \textit{::errorInfo} and \textit{::errorCode} variables are updated to contain the error information \textbf{that is available in the current callframe}. This information must be updated before the custom command is run, so that it is accessible from there.
\end{itemize}

The innermost function that is called on Tcl code execution is \textit{TclEvalObjvInternal()}. It is called from others to execute a command and returns the code that the executed command returned. It's the best place to trigger the error handler execution, but whether errors are catched (catchLevel) must be present at this time. Therefore, this level is stored in the current Interp* from within the callers of \textit{TclEvalObjvInternal()}. The catch level can be determined either from \textit{TclExecuteByteCode()} or from \textit{Tcl\_CatchObjCmd()} directly.

The errorInfo and errorCode variables are set directly before the handler is run. This ensures that they are updated properly. Eventually registerred traces on this variable are handled as usual, before the custom error command is executed.

\section{Reference Implementation}

A reference implementation is available from sourceforge as a patch against Tcl 8.5a5 [\url{http://sourceforge.net/support/tracker.php?aid=1587317}].

\section{Usage Example}

Here is a sample procedure that can be used to stop execution on error (return code 1) and introspect the program using stdin/stdout. Other return codes are returned together with the previous result. The procedure was mainly implemented by Neil Madden as \textbf{debug-repl} and is available (with a short discussion on the topic) on [\url{http://lambda-the-ultimate.org/node/1544\#comment-18446}]:
\begingroup\small\begin{verbatim}
package provide debug 1.0

proc up {} {
    uplevel 2 {
        breakpoint
    }
}

proc down {} {
    return -code continue
}

proc breakpoint {code result} {
    if {$code > 1} {
        return -code $code $result
    }
    set cmd ""
    set level [expr {[info level]-1}]
    set prompt "Debug ($level) % "
    while {1} {
        puts -nonewline $prompt
        flush stdout
        gets stdin line
        append cmd $line\n
        if {[info complete $cmd]} {
            set code [catch {uplevel #$level $cmd} result]
            if {$code == 0 && [string length $result]} {
                puts stdout $result
            } elseif {$code == 3} {
                break
            } elseif {$code == 4} {
                # continue
                return
            } else {
                puts stderr $result
            }
            set prompt "Debug ($level) % "
            set cmd ""
        } else {
            set prompt "    "
        }
    }
}
\end{verbatim}
\endgroup

To use it for uncaught errors, the \textit{breakpoint} procedure can be registered as error command:
\begingroup\small\begin{verbatim}
package re debug
trace set exception -uncaught breakpoint
\end{verbatim}
\endgroup

When an error raises, \textit{breakpoint} is called in the current callframe and Tcl introspection commands like \textbf{info vars} etc. can be used to get information about the program state.

\section{Copyright}

This document has been placed in the public domain.

\section{Colophon}

\textit{TIP AutoGenerator --- written by Donal K. Fellows}
\end{document}
