<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE TIP SYSTEM "http://www.tcl.tk/cgi-bin/tct/tip/tipxml.dtd">
<!-- Converted at Thu May 23 21:45:24 GMT 2013 -->
<!-- TIP AutoGenerator - written by Donal K. Fellows -->

<TIP number='290'>
<header><title>Registration of Custom Error Handler Scripts</title><author address="mailto:ecky-l@web.de">Eckhard Lehmann</author><author address="mailto:lvirden@yahoo.com">Larry W. Virden</author><status type='project' state='draft' tclversion="8.7" vote='prior'>$Revision: 1.13 $</status><history></history><created day='29' month='oct' year='2006' /><keyword>Tcl error trap</keyword></header>
<abstract>This TIP proposes the possibility to register a custom command as error and exception handler.</abstract>
<body><section title="Rationale">
<para>Errors are thrown in the Tcl interpreter through the <emph style="bold">error</emph> command or from a C extension that returns TCL_ERROR. When an error is thrown, the global <emph style="italic">errorInfo</emph> variable is filled with rudimentary stacktrace information and the error message itself. The global <emph style="italic">errorCode</emph> variable can contain an error code if this is provided by the command that has thrown the error.</para>
<para>Errors can be caught with the <emph style="bold">catch</emph> command. In this case, the <emph style="italic">errorInfo</emph> 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</para>
<para>The information in <emph style="italic">errorInfo</emph> is, in some simple cases, useful for reproducing and tracking down the error source and fixing the problem. In more complicated cases however, <emph style="italic">errorInfo</emph> does not include enough information to successfully reproduce the error - information about the application&apos;s state is missing.</para>
<para>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 <emph style="bold">error</emph> 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 <emph style="italic">return TCL_ERROR</emph>.</para>
<para>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:</para>
<itemize><item.i><para>registering a <emph style="italic">breakpoint</emph> command that stops execution at the error position and opens a console for introspection</para></item.i><item.i><para>registering a more advanced (Tk) debugger that opens on error for introspection.</para></item.i><item.i><para>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).</para></item.i></itemize>
</section>
<section title="Alternatives">
<enumerate><item.e index='1'><para>Overwriting of the <emph style="bold">error</emph> command As stated abovem, this works only for errors generated from Tcl code, whereas it does not work for errors from C extensions.</para></item.e><item.e index='2'><para>Leavestep execution traces In Tcl &gt;=8.4 it is possible to register execution traces on <emph style="italic">leavestep</emph> 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 <emph style="italic">each</emph> 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 <emph style="bold">catch</emph> by this way, so it is complicated or impossible to react on caught errors.</para></item.e></enumerate>
<para>It might be possible to get part or most of the proposed functionality by <emph style="italic">trace add execution leavestep</emph> or overwriting of <emph style="bold">error</emph>. 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.</para>
</section>
<section title="Specification">
<para>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 &quot;catch&quot;es that occure during execution.</para>
<para>The registration command is responsible for:</para>
<itemize><item.i><para>register the command for caught and/or uncaught exceptions</para></item.i><item.i><para>retrieve the currently registered command</para></item.i><item.i><para>change the execution details (caught and/or uncaught errors) and the command</para></item.i><item.i><para>unregister the command and thus get back to the current behaviour in error cases</para></item.i></itemize>
<para>Since the functionality is very similar to the family of <emph style="bold">trace</emph> commands, the proposed registration command is an extension to trace:</para>
<para><emph style="bold">trace set exception ?-caught? ?-uncaught? ?command?</emph>: Registration and modification</para>
<para>The arguments <emph style="italic">-caught</emph> and/or <emph style="italic">-uncaught</emph> to <emph style="italic">trace set exception</emph> modify the run conditions for the currently registered handler (run on caught/uncaught errors). With the <emph style="italic">command</emph> argument, this is set as the new handler. The return code and result of the command that was executing is appended to the registered <emph style="italic">command</emph>. So, the real call is: <emph style="italic">command code result</emph>.</para>
<para><emph style="bold">trace info exception</emph>: Information about the current handler</para>
<para>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.</para>
<para><emph style="bold">trace unset exception</emph>: Remove registerred handler</para>
<para>Any previously set error handler is unregistered.</para>
<para>The <emph style="italic">command</emph> that is registered will quell the error if it returns normal (return code 0). If the script returns abnormal, it&apos;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.</para>
<para>The changes in the execution engine should be done so that:</para>
<itemize><item.i><para>existing functionality is not disturbed</para></item.i><item.i><para>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</para></item.i><item.i><para>the <emph style="italic">::errorInfo</emph> and <emph style="italic">::errorCode</emph> variables are updated to contain the error information <emph style="bold">that is available in the current callframe</emph>. This information must be updated before the custom command is run, so that it is accessible from there.</para></item.i></itemize>
<para>The innermost function that is called on Tcl code execution is <emph style="italic">TclEvalObjvInternal()</emph>. It is called from others to execute a command and returns the code that the executed command returned. It&apos;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 <emph style="italic">TclEvalObjvInternal()</emph>. The catch level can be determined either from <emph style="italic">TclExecuteByteCode()</emph> or from <emph style="italic">Tcl_CatchObjCmd()</emph> directly.</para>
<para>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.</para>
</section>
<section title="Reference Implementation">
<para>A reference implementation is available from sourceforge as a patch against Tcl 8.5a5 [<url ref="http://sourceforge.net/support/tracker.php?aid=1587317"/>].</para>
</section>
<section title="Usage Example">
<para>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 <emph style="bold">debug-repl</emph> and is available (with a short discussion on the topic) on [<url ref="http://lambda-the-ultimate.org/node/1544#comment-18446"/>]:</para>
<verbatim><vline encoding='base64'>cGFja2FnZSBwcm92aWRlIGRlYnVnIDEuMA==</vline><vline encoding='base64'></vline><vline encoding='base64'>cHJvYyB1cCB7fSB7</vline><vline encoding='base64'>ICAgIHVwbGV2ZWwgMiB7</vline><vline encoding='base64'>ICAgICAgICBicmVha3BvaW50</vline><vline encoding='base64'>ICAgIH0=</vline><vline encoding='base64'>fQ==</vline><vline encoding='base64'></vline><vline encoding='base64'>cHJvYyBkb3duIHt9IHs=</vline><vline encoding='base64'>ICAgIHJldHVybiAtY29kZSBjb250aW51ZQ==</vline><vline encoding='base64'>fQ==</vline><vline encoding='base64'></vline><vline encoding='base64'>cHJvYyBicmVha3BvaW50IHtjb2RlIHJlc3VsdH0gew==</vline><vline encoding='base64'>ICAgIGlmIHskY29kZSA+IDF9IHs=</vline><vline encoding='base64'>ICAgICAgICByZXR1cm4gLWNvZGUgJGNvZGUgJHJlc3VsdA==</vline><vline encoding='base64'>ICAgIH0=</vline><vline encoding='base64'>ICAgIHNldCBjbWQgIiI=</vline><vline encoding='base64'>ICAgIHNldCBsZXZlbCBbZXhwciB7W2luZm8gbGV2ZWxdLTF9XQ==</vline><vline encoding='base64'>ICAgIHNldCBwcm9tcHQgIkRlYnVnICgkbGV2ZWwpICUgIg==</vline><vline encoding='base64'>ICAgIHdoaWxlIHsxfSB7</vline><vline encoding='base64'>ICAgICAgICBwdXRzIC1ub25ld2xpbmUgJHByb21wdA==</vline><vline encoding='base64'>ICAgICAgICBmbHVzaCBzdGRvdXQ=</vline><vline encoding='base64'>ICAgICAgICBnZXRzIHN0ZGluIGxpbmU=</vline><vline encoding='base64'>ICAgICAgICBhcHBlbmQgY21kICRsaW5lXG4=</vline><vline encoding='base64'>ICAgICAgICBpZiB7W2luZm8gY29tcGxldGUgJGNtZF19IHs=</vline><vline encoding='base64'>ICAgICAgICAgICAgc2V0IGNvZGUgW2NhdGNoIHt1cGxldmVsICMkbGV2ZWwgJGNtZH0gcmVzdWx0XQ==</vline><vline encoding='base64'>ICAgICAgICAgICAgaWYgeyRjb2RlID09IDAgJiYgW3N0cmluZyBsZW5ndGggJHJlc3VsdF19IHs=</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgIHB1dHMgc3Rkb3V0ICRyZXN1bHQ=</vline><vline encoding='base64'>ICAgICAgICAgICAgfSBlbHNlaWYgeyRjb2RlID09IDN9IHs=</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgIGJyZWFr</vline><vline encoding='base64'>ICAgICAgICAgICAgfSBlbHNlaWYgeyRjb2RlID09IDR9IHs=</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICMgY29udGludWU=</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgIHJldHVybg==</vline><vline encoding='base64'>ICAgICAgICAgICAgfSBlbHNlIHs=</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgIHB1dHMgc3RkZXJyICRyZXN1bHQ=</vline><vline encoding='base64'>ICAgICAgICAgICAgfQ==</vline><vline encoding='base64'>ICAgICAgICAgICAgc2V0IHByb21wdCAiRGVidWcgKCRsZXZlbCkgJSAi</vline><vline encoding='base64'>ICAgICAgICAgICAgc2V0IGNtZCAiIg==</vline><vline encoding='base64'>ICAgICAgICB9IGVsc2Ugew==</vline><vline encoding='base64'>ICAgICAgICAgICAgc2V0IHByb21wdCAiICAgICI=</vline><vline encoding='base64'>ICAgICAgICB9</vline><vline encoding='base64'>ICAgIH0=</vline><vline encoding='base64'>fQ==</vline></verbatim>
<para>To use it for uncaught errors, the <emph style="italic">breakpoint</emph> procedure can be registered as error command:</para>
<verbatim><vline encoding='base64'>cGFja2FnZSByZSBkZWJ1Zw==</vline><vline encoding='base64'>dHJhY2Ugc2V0IGV4Y2VwdGlvbiAtdW5jYXVnaHQgYnJlYWtwb2ludA==</vline></verbatim>
<para>When an error raises, <emph style="italic">breakpoint</emph> is called in the current callframe and Tcl introspection commands like <emph style="bold">info vars</emph> etc. can be used to get information about the program state.</para>
</section>
<section title="Copyright">
<para>This document has been placed in the public domain.</para>
</section>
</body></TIP>
