package require dmh 1.4
::dmh::dbGUI
::dmh::dbDebugInit ?wantGUI?
::dmh::dbPuts text
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?
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:
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.
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.
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.
The rc and Result display fields update to show the execution result of sourcing the file.
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.