Debugging Aid For Production Code

This is a bit of code we use to manage inline debugging code. We use a global variable "::DEBUG" to set the debugging state, when ::DEBUG < 0, the procedure just returns without doing anything.

The procedure addLogEntry is not provided here. It is a very elaborate set of procs which provide custom trace information for our system -- replace it with your own handler.

 ## ******************************************************** 
 ##
 ## Name: debugPuts
 ##
 ## Description:
 ## Used to make optional puts's and addLogEntry's based on
 ## debugging level. setting ::DEBUG > 1 turns on putsing to
 ## stderr.
 ## An optional special handler may be specified as a third
 ## command line argument.
 ##
 ## Parameters:
 ## msg     - text to be displayed/logged
 ## level   - optional level modifier
 ## handler - optional special handler 
 ##           (not the default addLogEntry)
 ## Usage:
 ##
 ## Comments:
 ## Convenience routine for making debugging switchable.
 ## returns in 25 microseconds if ::DEBUG < 0.
 ## If ::DEBUG < 0 then the debug level cannot be
 ## used to cause debugging output.

 proc debugPuts { { msg "" } { level 0 } { handler addLogEntry } } {
     
      if { $::DEBUG < 0 || ! [ string length $msg ] } { return {} }
      
      ;## get global debug level if none is provided
      if { ! $level } { set level $::DEBUG }
     
      ;## if debugging is on...
      if { $level } {
         if { [ catch {
            uplevel $handler [ list $msg ]
         } err ] } {
            puts stderr $msg
         }
         if { $level > 1 } {
            puts stderr $msg
         }
      }   
 }
 ## ********************************************************

CLL - I would suggest the following to keep the production code as fast as possible..

if {$::DEBUG < 0} {
  proc debugPuts {{msg {}} {level 0} {handler {}}} {
    # body of the above proc
  }
} else {
  proc debugPuts {{msg {}} {level 0} {handler {}}} {return}
}

In the original example, every single call to debugPuts (even when debugging is totally off) requires a conditional check. This can slow things down tremendously. With this second example there are ZERO conditional checks when debugging is turned off. It does lack the advantage of enabling easy run-time debug mode switching. However, that could be easily taken care of by putting all of the above code into a single proc, calling it once to initialize, and then calling it thereafter whenever debugging state changes.


"tremendously" is a bit harsh... 15 vs. 20 microseconds per call is not exactly tremendous (how often are you going to call a logging function??). -PSE

MS: in tcl8.4, the following is much faster as [debugPuts] avoids the call/return sequence (new bytecode compiler optimisation)

   if {$::DEBUG < 0} {
      proc debugPuts {{msg {}} {level 0} {handler {}}} {
      # body of the above proc
      }
   } else {
      proc debugPuts args {}
   }

On a related note, see also the implementation of ::control::assert in tcllib


While nitpicking: The call to...

 if { [ regexp {^$} $handler] }

...changed to...

 if { [string length $handler] }

...which does conceptually exactly the same (both do not check on whitespace) saves 2/3 of execution cost (on my machine, 22 vs. 7 micros ... WHEEEEEE SCNR) -Martin

Not sure, but "if {$handler ne ""}" might be even quicker for Tcl 8.4... -jcw


Well, since this is getting so much UNDESERVED attantion today, I revisited the code. -PSE


-Martin:

to JCW: I assume most people have still 8.3, that's why I prefer 8.3 constructions for now (in fact I still have 8.3 *hum*) - there "if {$handler != ""}" in fact even is faster...

to PSE: Now if handler is not set people will get bogus

 bad level ""

messages. Imho you should either adjust the errorInfo in that case, or revert to checking if handler (regexp...|string length...|!=...)

Okay okay, I stop it now ...


Oh, you might want to have a look on Errx, too. -Martin


Instead of inventing your own high performance debug logging system, take a look at the logger package from the tcllib. It provides fast hierarchical logging and introspection of the logging system, using the empty procedure trick above for maximum performance.