Tuesday, November 1, 2011

The Zenith of Tracing

Zenith used to make TVs, and they may still; I haven't shopped for a TV in years.  Back in The Good Old Days (tm), Zenith had an advertising slogan:  "The quality is built in, not bolted on".  The implication, for those who could read between the lines, is that quality can't be either forgotten at the factory or removed at a later date.  I have the same attitude when it comes to tracing and debugging.

How many times have you seen this in a REXX program:

/* REXX   --  a program to do some stuff.   */
/*  Trace("R")   */
How many times have you written something like that?

This is an invitation to maintainers to de-comment that line when they need to trace the flow of the program.  This is "bolting quality on", not "building it in", and there are a host of problems that it generates.

First, de-commenting that line constitutes "changing the program".  Yes, it's a small change and (I admit) unlikely to have any noticeable effects beyond the fact that the program now runs in trace-mode.  The (philosophical) point here is that you are not really running the program that failed, you're running a program that was changed after it failed.

Then there are other, less philosophical and much more serious, problems:  are you changing the production copy or something else?  If you're changing the production copy, will everyone running this "in production" suddenly find themselves accidentally (and surprisingly) producing reams of output?  If it's not the production copy, are you sure it was an exact duplicate of the version that failed?  Where is your test copy stored?  Is the concatenation sequence the same for your test copy as for the failing version?  Are you sure?  What about called subroutines?  Will your version pick up true copies of all necessary subroutines?  After all, the failure might have been in a subroutine and caused by bad data being passed back to the caller.

So many problems.  One solution.  Build the trace capability in, don't bolt it on.  As with oh-so-many-things, there is more than one way to skin this cat.  All that's necessary is to make it clear, and to do it consistently, and to design it so that the program doesn't have to be changed in order to start a TRACE.  Did I just say "parm"?  Yes, I believe I did.

/* REXX    -- a program to do some stuff
*/ arg argline
address TSO
arg parms "((" opts            /* 'opts' are rightward of a double-open-paren */
parse var opts "TRACE"   tv  . /* looking for "TRACE ?r" or similar           */
parse value tv "O" with tv .   /* if tv is empty, it becomes 'O'              */
rc     = trace(tv)             /* set whatever trace-value was loaded         */
You now can turn on TRACE with any form of TRACE without any change to the program.  You will be running the program that failed, not a copy of it.  Instead of calling it as
TSO BLAH43M for Monday using custfile4 crossfoot subtotals
you issue instead
TSO BLAH43M for Monday using custfile4 crossfoot subtotals (( trace ?r

You can, of course, get much fancier than this, but it's not strictly necessary unless you have an articulable reason.  I do get fancier than this (see REXXSKEL on my REXX page) because much of the parsing code I use is pre-packaged and thus doesn't have to be coded for every routine I write.  Whether you choose 'simple' or 'elaborate', it really doesn't matter.  Just no more de-commenting TRACE commands, okay?  Please?

No comments:

Post a Comment