Favorite debugging techniques applicable to Tcl

Frank Pilhofer's Reload, Refresh, ..., David Welton's endless reloads, Jeffrey Hobbs' use of "fresh" tkcon tabs [L1 ], as well as Keith Vetter's console resets (Restarting a tkcon application)

KJN adds: the c.l.t thread [1] is a Tcl-URL! announcement followed by a discussion about debugging. It is a good read, but to save you trawling the entire thread, Jeff's comment was:

"Now that tkcon has tabbed consoles, I'd say the easiest is to create another tab, delete the current tab, and go on with what you were doing - pristine state (modulo tkcon basics) guaranteed! :) "


RS routinely puts into Tk-provided scripts under development these two lines:

bind . <Escape> {exec wish $argv0 &; exit}
catch {bind . <F1> {console show}} ;# Windows only

With an interactive console, the following shortcut is often helpful:

interp alias {} ? {} set errorInfo

Another little helper to display canvas x/y coordinates in the title bar:

bind .c <Motion> {wm title . %x/%y}

LES: Hmm... I have this:

frame $::w.wframe
text $::w.wframe.textw
scrollbar $::w.wframe.sbar

Then I bind it:

bind $::w.wframe.textw <Motion> {wm title $::w "Position:  %x/%y" }

It works, but only so far as I move the mouse around the text widget. It won't work when I move it over the scroll or title bar. Certainly because I made a binding for the text widget only. Instead, I should bind the mouse movement to the frame, because it contains everything, right?

bind $::w.wframe <Motion> {wm title $::w "Position:  %x/%y" }

Wrong. Now it only works when I move the mouse over the scroll bar. But isn't the text widget contained in the frame too? What is wrong with my assumption?

And I guess it is impossible to detect mouse movement over the title bar?


Robert Heller and tclguy explain how to use gdb constructively with Tcl-based applications in a clt thread [L2 ].


LV 2007-09-25: Okay, here's a debugging situation that needs a technique. Hopefully it is obvious to readers that the small coherent example I provide here is merely to allow illustration of the type of problem, and that the real world examples of this are not so trivial. Thus, responses such as just read the code and look for the bug, while certainly a valid approach, may involve reading tens of thousands of lines of code, extensions, etc. and thus the location of this sort of problem could use a bit of help.

Assume this small coherent example of tcl code:

proc novar {} {
    return $abc
}

proc a {} {
    return [novar]
}

proc b {} {
    return [a]
}
% b
can't read "abc": no such variable

What would be useful here is some coding technique or trick that would change that simple error message into a stack trace that would get the developer looking into the appropriate proc.

In an ideal situation, Tcl would just generate that stack trace by default.

Lars H: I usually just type

set errorInfo

when I encounter this situation. That outputs the information you're asking for, does it not?

LV I either didn't know that, or had forgotten that in my old age. Indeed, what I get if I remember is:

% b
can't read "abc": no such variable
% set errorInfo
can't read "abc": no such variable
   while executing
"return $abc"
   (procedure "novar" line 2)
   invoked from within
"novar"
   (procedure "a" line 2)
   invoked from within
"a"
   (procedure "b" line 2)
   invoked from within
"b"

Thanks!

2007-09-26 UKo: And what's more: This really is an artificial example, cause in the real world you have these commands in a script and execute them with tclsh. And this gives you the full stack trace at once. So the default behaviour is just as you want it to be. Only in the really limited tclsh commandline you have to go the extra step of explicitly printing the stack trace.

JH: As noted on the tkcon page, tkcon's hot errors functionality handles this for you graphically, going all the way to show you the source of the proc in a dig-down style.


Lars H: A useful procedure for use with trace is

proc putslist {args} {puts $args}

since you can then go

trace add execution someProc {enter enterstep leave leavestep} putslist

(or some subset of those operations) and get a raw dump of what someProc does. Unless the amount of trace data is large, it's more troublesome to format the trace data than it is to interpret this raw trace. It's also a bit troublesome for more ambitious tracing that the enter* and leave* operations have different syntaxes for the tracing command, hence the use of a catch-all args argument.