# Tcl Programming/Debugging

Tcl itself is quite a good teacher. Don't be afraid to do something wrong - it will most often deliver a helpful error message. When tclsh is called with no arguments, it starts in an interactive mode and displays a "%" prompt. The user types something in and sees what comes out: either the result or an error message.
Trying isolated test cases interactively, and pasting the command into the editor when satisfied, can greatly reduce debugging time (there is no need to restart the application after every little change - just make sure it's the right one, before restarting.)

### A quick tour

Here's a commented session transcript:
OK, so we're supposed to type in a command. Although it doesn't look so, here's one:
Interactive tclsh tries to guess what we mean, and "hi" is the unambiguous prefix of the "history" command, whose results we see here. Another command worth remembering is "info":
The error message tells us there should be at least one option, and optionally more arguments.
Another helpful error: "option" is not an option, but the valid ones are listed. To get information about commands, it makes sense to type the following:
Oh my, quite many... How many?
Now for a more practical task - let's let Tcl compute the value of Pi.
Hm.. can we have that with more precision?
Back to the first try, where "hello" was an invalid command. Let's just create a valid one:
Silently acknowledged. Now testing:

### Errors are exceptions

What in Tcl is called error is in fact more like an exception in other languages - you can deliberately raise an error, and also catch errors. Examples:
One reason for errors can be an undefined command name. One can use this playfully, together with catch, as in the following example of a multi-loop break, that terminates the two nested loops when a matrix element is empty:
The throw command does not exist in normal Tcl, so it throws an error, which is caught by the catch around the outer loop.

#### The errorInfo variable

This global variable provided by Tcl contains the last error message and the traceback of the last error. Silly example:
If no error has occurred yet, errorInfo will contain the empty string.

#### The errorCode variable

In addition, there is the errorCode variable that returns a list of up to three elements:
* category (POSIX, ARITH, ...)
* abbreviated code for the last error
* human-readable error text
Examples:

### Tracing procedure calls

For a quick overview how some procedures are called, and when, and what do they return, and when, the trace execution is a valuable tool. Let's take the following factorial function as example:
We need to supply a handler that will be called with different numbers of arguments (two on enter, four on leave). Here's a very simple one:
Now we instruct the interpreter to trace enter and leave of fac:
Let's test it with the factorial of 7:
which gives, on stdout:
So we can see how recursion goes down to 1, then returns in backward order, stepwise building up the final result. The 0 that comes as second word in "leave" lines is the return status, 0 being TCL_OK.

### Stepping through a procedure

To find out how exactly a proc works (and what goes wrong where), you can also register commands to be called before and after a command inside a procedure is called (going down transitively to all called procs). You can use the following step and interact procedures for this:
The above code gives the following transcript when sourced into a tclsh:

### Debugging

The simplest way to inspect why something goes wrong is inserting a puts command before the place where it happens. Say if you want to see the values of variables x and y, just insert
(if the string argument contains no spaces, it needs not be quoted). The output will go to stdout - the console from where you started the script. On Windows or Mac, you might need to add the command
to get the substitute console Tcl creates for you, when no real one is present.
If at some time you want to see details of what your program does, and at others not, you can define and redefine a dputs command that either calls puts or does nothing:
For more debugging comfort, add the proc interact from above to your code, and put a call to interact before the place where the error happens. Some useful things to do at such a debugging prompt:

### Assertions

Checking data for certain conditions is a frequent operation in coding. Absolutely intolerable conditions can just throw an error:
Where the error occurred is evident from ::errorInfo, which will look a bit clearer (no mention of the error command) if you code
If you don't need hand-crafted error messages, you can factor such checks out to an assert command:
Use cases look like this:
Note that the condition is reverted - as "assert" means roughly "take for granted", the positive case is specified, and the error is raised if it is not satisfied.
Tests for internal conditions (that do not depend on external data) can be used during development, and when the coder is sure they are bullet-proof to always succeed, (s)he can turn them off centrally in one place by defining
This way, assertions are compiled to no bytecode at all, and can remain in the source code as a kind of documentation.
If assertions are tested, it only happens at the position where they stand in the code. Using a trace, it is also possible to specify a condition once, and have it tested whenever a variable's value changes:
The ";#" at the end of the trace causes the additional arguments name element op, that are appended to the command prefix when a trace fires, to be ignored as a comment.
Testing:
The error message isn't as clear as could be, because the [llength $list] is already substituted in it. But I couldn't find an easy solution to that quirk in this breakfast fun project - backslashing the $condition in the assertt code sure didn't help. Better ideas welcome.
To make the assertion condition more readable, we could quote the condition one more time,i.e
In this case,when trace trigger fires, the argument for assert is {[llength $list]<10}.
In any case, these few lines of code give us a kind of bounds checking - the size of Tcl's data structures is in principle only bounded by the available virtual memory, but runaway loops may be harder to debug, compared to a few assertt calls for suspicious variables:
or
Tcllib has a control::assert with more bells and whistles.

### A tiny testing framework

Bugs happen. The earlier found, the easier for the coder, so the golden rule "Test early. Test often" should really be applied.
One easy way is adding self-tests to a file of Tcl code. When the file is loaded as part of a library, just the proc definitions are executed. If however you feed this file directly to a tclsh, that fact is detected, and the "e.g." calls are executed. If the result is not the one expected, this is reported on stdout; and in the end, you even get a little statistics.
Here's a file that implements and demonstrates "e.g.":

### Guarded proc

In more complex Tcl software, it may happen that a procedure is defined twice with different body and/or args, causing hard-to-track errors. The Tcl command proc itself doesn't complain if it is called with an existing name. Here is one way to add this functionality. Early in your code, you overload the proc command like this:
From the time that is sourced, any attempt to override a proc name will be reported to stderr (on Win-wish, it would show on the console in red). You may make it really strict by adding an "exit" after the "puts stderr ...", or throw an error.
Known feature: proc names with wildcards will run into this trap, e.g.
will always lead to a complaint because "*" fits any proc name. Fix (some regsub magic on 'name') left as an exercise.

### Windows wish console

While on Unixes, the standard channels stdin, stdout, and stderr are the same as the terminal you started wish from, a Windows wish doesn't typically have these standard channels (and is mostly started with double-click anyway). To help this, a console was added that takes over the standard channels (stderr even coming in red, stdin in blue). The console is normally hidden, but can be brought up with the command
You can also use the partially documented "console" command. "console eval <script>" evals the given script in the Tcl interpreter that manages the console. The console's text area is actually a text widget created in this interpreter. For example:
will change the font of the console to "Times". Since the console is a Tk text widget, you can use all text widget commands and options on it (for example, changing colors, bindings...).
tells you more about the console widget: it is a toplevel with children .menu, .console (text), and .sb (scrollbar). You can resize the whole thing with
where $W and $H are dimensions in character cells (default 80x24), but $X and $Y are in pixels.
And more again: you can even add widgets to the console - try
The button appears between the text widget and the scroll bar, and looks and does as expected. There is also a way back: the main interpreter is visible in the console interpreter under the name, consoleinterp.

### Remote debugging

Here's a simple experiment on how to connect two Tcl processes so that one (call it "debugger") can inspect and control the other ("debuggee"). Both must have an event loop running (which is true when Tk runs, or when started with e.g. vwait forever).
As this goes over a socket connection, the two processes could be on different hosts and operating systems (though I've so far tested only the localhost variety). Use at your own risk, of course... :^)
The "debuggee" contains in my experiments the following code, in addition to its own:
The "debugger" in this version (remo.tcl) runs only on Windows in a wish, as it needs a console, but you could modify it to avoid these restrictions:
Now from remo you can call any Tcl command in the "debuggee", where it is executed in global scope, so in particular you can inspect (and modify) global variables. But you could also redefine procs on the fly, or whatever tickles your fancy... Examples from a remo session, showing that the two have different pids, how errors are reported, and that quoting is different from normal (needs more work):