::dmh::dbDebug and Related Tcl Commands

NAME

dbDebug - Debugging of Tcl Interpreters

SYNOPSIS

package require dmh 1.4

Debugger Interpreter Commands

::dmh::dbGUI

::dmh::dbDebugInit ?wantGUI?

::dmh::dbPuts text

Target Interpreter Commands

dbBreak
::dmh::dbBreak

dbDebug ?boolean?
::dmh::dbDebug ?boolean?

dbDumpLocals
::dmh::dbDumpLocals

::dmh::dbGUI

dbListUpdate ?boolean?
::dmh::dbListUpdate ?boolean?

dbPuts text
::dmh::dbPuts text

dbStackDump
::dmh::dbStackDump

dbStepMode ?boolean?
::dmh::dbStepMode ?boolean?


DESCRIPTION

These commands provide a traditional code debugger for Tcl interpreters that can be used on demand to provide stepping, display of local data values, breakpoints, and execution abort. Debugging is dynamically enabled or disabled. When debugging is not enabled, the target interpreter runs at full execution speed. When debugging is enabled, a callback is executed in the debugger interpreter for every code step, which enables traced execution, interactive stepping, or pacing of the number of executed steps per centisecond. There is no requirement to modify or instrument Tcl code to enable debugging. However, it is possible to imbed debug commands in code to control debugging or to add information to the debugger GUI.

When interactive debugging is desired, execute ::dmh::dbGUI in the target interpreter.  The command creates a child debugger interpreter if needed, and tells the debugger to bring up a Graphical User Interface (GUI).  If the debugger interpreter has not been created, the command executes ::dmh::dbDebugInit to create one.  The application can call ::dmh::dbDebugInit directly instead of calling ::dmh::dbGUI if there is a desire to initialize and use debugging features without creating the GUI.  Calling ::dmh::dbDebugInit adds the db* commands to the global namespace so that they are callable without the ::dmh:: prefix.  A user who has instrumented code containing global namespace debug commands such as dbBreak and dbPuts needs to call ::dmh::dbDebugInit directly or indirectly to avoid unknown command errors.  

Here is a rundown on the most important features of the GUI:

checkbox Enable Debug Features in Target
This checkbox executes ::dmh::dbDebug 1|0 in the target interpreter to turn on or off code tracing callbacks. When code tracing callbacks are enabled, the ::dmh::dbCallback procedure is called from dmh package C code for every Tcl execution step of the target.  The callback executes in the debugger interpreter.

button Inspect Target
If the Target supports the Tk send command or the Windows DDE-based send command, a button is featured that starts the inspect application with a command line argument for the target.   Inspect is used routinely when developing Tcl/Tk applications.  The program has the ability to interactively display and modify a running application without needing code instrumentation or application changes. 

checkbox Update Fields
This checkbox controls whether the latest code statement, stack level, and token are displayed in label fields. It can be a slight speedup to not display these fields.

checkbox Trace Steps (near bottom on the right of the display list)
This checkbox controls whether the latest code statements and stack level are added to the scrolling display list.

buttons dumpLocals, stackDump, and errorInfo
The dumpLocals button displays the local variable values of the target when stepping through code. If the target is idle, the button displays global data items. The display is added to the scrolling display list.

The stackDump button displays the execution call stack of the current target code step. If the target is idle, the call stack is empty. The information is added to the scrolling display list. The dbStackDump procedure call can be edited into the target source code to display the call stack of the procedure invocation on the debugger GUI window.

The errorInfo button displays the value of the target's errorInfo global variable.  This variable value caches the latest error message.  A side effect of the stackDump action is to clear the target's errorInfo value.  Would-be errors trapped in catch statements also change the errorInfo value.

checkbox Step Mode and buttons Step (In), Step Over, Step Out, and Continue
The Step Mode checkbox controls whether stepping is active. When enabled, the user should also check the Update Fields and/or Trace Steps checkboxes to see the dialog refresh during stepping. The dbStepMode command can be edited into the target source code to have the running code enable or disable step mode.  The command can be used without an argument to test whether stepping is active.

Pressing Step (In) when stepping is active causes the current code step to execute. If updating or tracing are enabled, the user sees the dialog refresh to show the next code step.

Pressing Step Over when stepping is active causes the current code step to execute. Stepping resumes at the next code statement which is at the same stack level. In other words, this step choice will skip over descending into a procedure call or descending into loop code.

Pressing Step Out when stepping is active causes the current code step to execute. Stepping resumes at the next code statement which is above the current stack level. In other words, this step choice will go to the next statement outside of a loop or return from a current procedure call.

Pressing Continue when stepping is active causes an exit from step mode. The target executes until a breakpoint is encountered, the Abort checkbox is checked, or execution completes normally. When debugging a GUI application you can see all manner of mouse events as the pointer moves over the application windows.

checkbox Enable Breakpoint and entryfield Code Start
A breakpoint is set by typing a command name or partial name into the entryfield and checking the checkbox. When the current code statement starts with the same characters as the entered data, step mode is enabled. If you need better control over breakpoints, you can use the inspect application to dynamically edit target Tcl procedures and add dbBreak statement(s). Another useful edit is to add dbPuts statements to display diagnostic messages or selected variable values.

checkbox Abort
Pressing this checkbox causes the current target execution to end with an error. The checkbox is automatically unchecked after one code termination. Now here is a fine point to remember. If the target is busy executing and not dispatching events, and the Pace checkbox is not checked, the debugging GUI is not responsive to the user pressing the Abort checkbox, so you cannot interrupt the target. If you are debugging a situation where the target may become unresponsive, have the Pace checkbox checked so that the debugger GUI stays responsive.

checkbox Pace and entyfield Max Steps per Centisecond
Checking the Pace checkbox cause the Tcl update command to execute with every code callback. This enables window events to be dispatched and the application stays responsive to the debug GUI user. When the Max Steps entryfield has a non-zero integer value, the value becomes the maximum number of callbacks executed by the target in each 10 millisecond interval.

Pacing is useful if the target interpreter executes Tcl code without servicing the event loop; the debug GUI can still respond to the user and interrupt the target.  However, certain properly written applications such as the Hume Tcl/Tk SECS apps and inspect rely on after idle and after 0 to not execute until their own application code dispatches events.  This assumption is broken when a debugger interpreter causes unexpected event dispatching in the target.  Inspect can be used with target interpreters, and the Hume SECS applications can be debugged successfully by unchecking the Pace checkbox.

entryfield Source File and buttons Choose... and Source
These features are used to enter or select a Tcl source code file, and cause it to be sourced into the target interpreter by pressing the Source button. If you are revising a file in an editor, it is convenient to press the Source button to load in the latest changes after each file save.

The rc and Result display fields update to show the execution result of sourcing the file.

entryfield Tcl Code and the Eval button
Tcl code can be entered and caused to execute in the target interpreter by pressing the Eval button. Notice that there is a command history feature of using the cursor Up key to display a popup menu of prior commands, and selecting one for re-execution.  Moving the cursor down is a fast way to clear the entryfield.  Pressing the Enter key when the keyboard focus is on the entryfield is equivalent to pressing the Eval button.

The rc and Result display fields update to show the execution result when the command completes.  Optionally the execution results are appended to the scrolling display if the Trace Results checkbox is checked.

If you are looking for an interesting procedure to step execute, here is a square root solver using the Newton-Raphson method. The invocation "sqrt 3" converges in a few passes but "sqrt 3 0" runs forever and needs to be aborted.

# a fun algorithm to step
# sqrt 3 0 loops forever so use the Abort feature
proc sqrt {v {tol 2.2e-16}} {
    if { $v < 0 } { 
        return [sqrt [expr {-$v}]]i
        }
    set x [expr {$v/2.0}]
    while {1} {
       set xn [expr {$x - ($x*$x - $v)/(2*$x)}]
       if { abs(($x - $xn)/$xn) <= $tol } break
       set x $xn
       #dbPuts x=$x
       }
   return $x
   }

Here are other procedures to explore Step, Pace, and Abort debug features.

proc alwaysBusy {{max 100000}} {
    # be sure to enable debugging before starting this
    # and have step mode checked!
    # target code can turn on debugging:
    dbDebug 1
    dbBreak
    set msg "Press Step (In) to get inside the loop"
    for {set i 0} {$i < $max} {incr i} {
        set msg "alwaysBusy $i"
        }
    return $msg
    }

# you can have debug print statements for the debugger dbGUI which are
# harmless when not debugging
proc notBusy {{max 100000}} {
    set msg "Press Step (In) to get inside the loop"
    for {set i 0} {$i < $max} {incr i} {
        set msg "notBusy $i"
dbPuts "msg=$msg"
        break
        }
    return $msg
    }

# An app can test whether debug callbacks are active
# and do things differently
proc maybeBusy {{max 100000}} {
    set msg "Press Step (In) to get inside the loop"
    for {set i 0} {$i < $max} {incr i} {
        set msg "maybeBusy $i"
        if {[dbDebug]} {
            dbPuts "msg=$msg"
            break
            }
        }
    return $msg
    }

This breakpoint debugger is a new creative development of Hume Integration Software as of April 2020 and is integrated into the Hume Datahub SDK licensed software.  The features support Tcl 8.5 and newer versions, but are not designed for the debugging of multi-threaded Tcl applications.  As an example of the pitfalls of threaded applications, we note that the Tcl Thread Package man page description of the thread::send command has a 7 line code snippet with two synchronization flaws.

AUTHOR

Ed Hume, Hume Integration Software

KEYWORDS

abort breakpoint debug debugging inspect interp