February 15,1999
Sherlock is a portable tool for C, C++ and Objective-C programmers. Sherlock creates a general framework for adding conditional code that can be controlled without recompiling your program. This framework naturally lends itself to a wide variety of tasks, including focused tracing of code, gathering and reporting statistics and creating watchpoints. Sherlock features support each of these activities.
Sherlock allows you to execute arbitrary actions based on the settings of switches. Actions may be composed of arbitrary pieces of code, including blocks. Actions are executed only if their associated switch is set. Each action has its own switch. Actions may share switches in a completely general way. Switches are represented as strings or string variables. Switch settings are external to your program. You set switches without altering or recompiling your program. Settings may be altered at any time before or during the execution of your program. As a result, actions define pieces of conditional code that can be activated at any time before or during the execution of your program.
Sherlock tests switch settings very quickly. You can insert a huge number of actions into your program without significantly degrading your program's performance. Sherlock's actions and switches are encapsulated in C macros. The SPP utility program automatically inserts Sherlock macros in your program. It is easy to completely disable Sherlock after debugging is complete, typically without changing your program in any way.
The entire Sherlock system, including the SPP, SDEL and SDIF programs, was placed in the public domain on June 15, 1991, by its author, Edward K. Ream. Sherlock may be used for any commercial or non-commercial purpose.
You may get the Macintosh 3.0 version of Sherlock from me. Email me if you would like a free copy. You can get the portable version of Sherlock for the cost of media from The C/C++ Users Group. The portable version of Sherlock is Volume 355; the Mac v2.0 is Volume 462. Finally, Sherlock has nothing to do with Apple's new searching tool.
Sherlock is my most misunderstood creation. I invented Sherlock in 1986. By now Sherlock should have become a part of every C programmer's repertoire. It should be discussed in Microsoft's excellent books, Code Complete and Writing Solid Code. Sherlock should be widely distributed. None of these happy events has happened. I hope this new documentation will help Sherlock gain the recognition it deserves.
Let me acknowledge here the essential role of today's superb debuggers. I always use a debugger when I write code; I single-step through the new code, looking for discrepancies between what I expect and what the debugger shows me. This process catches virtually all coding blunders. Nevertheless, Sherlock has an important role in testing complex programs. Indeed, the more complex the program, the more useful Sherlock is.
I have used both Sherlock and debuggers in every significant project that I have undertaken since 1986. These projects have included SPP, two compilers, an assembler, a linker, a Unix-like file system, and Leo, an outlining text editor that supports Donald E. Knuth's literate programming. Leo's code runs to more than 800 typeset pages. In all these projects, I have found Sherlock to be more than merely useful; time after time Sherlock has proven itself to be essential.
How can Sherlock be so important? Because Sherlock creates a fast, general and convenient framework for adding code that should lie dormant until needed. Fast, because it is often faster to discern patterns in printed dumps than to find those same patterns while single stepping. General, because actions can contain virtually any kind of code. Convenient, because you can enable dormant code without changing or recompiling your program in any way. My experience demonstrates that Sherlock encourages novel and effective programming methods. The following paragraphs discuss several of these new methods.
Tracing: Sherlock macros allow you to print the contents of any data at will. You can leave tracing macros in your code without drowning in output or significantly slowing your program. You can afford to invest in sophisticated tracing because you don't have to rip those traces out of your code as soon as they are no longer immediately needed. It is reasonable, indeed natural, to leave dormant traces in your code on the off chance that they may be useful later.
For example, while developing Leo on the Macintosh, I developed traces that would slowly highlight various parts of the screen while the screen was being redrawn. These traces also produced printed output so that I could relate what was happening on the screen to the functions being executed. It would have been out of the question to develop these routines had it not been practical and convenient to have them be dormant most of the time.
Testing: Sherlock is often useful when single-stepping would disrupt the program under test. For instance, I use Sherlock to trace through "mouse-down" code. Such code is extremely difficult to examine using a debugger because the debugger can intercept the very events that the code is attempting to handle.
With Sherlock you can use, on a selective basis, assertions and other tests that would be far too expensive to use routinely. For instance, one of my compilers contained memory allocation routines that could, if asked, check all allocated memory any time any memory was created or destroyed. Such checks were fabulously expensive; it was out of the question to routinely enable them during debugging. Sherlock provided a convenient way of activating such checks as needed.
More than mere convenience was involved, however. Memory problems often involve pointer bugs, and the symptoms of pointer bugs usually change if any change is made to the program under test. The ability to enable such code at will, without having to disturb the program under test, is a huge advantage in such situations.
Yes, it does take some effort to insert Sherlock macros into code. That effort pays off handsomely in the long run. Yes, you could activate dormant code without using Sherlock. How? By defining global tracing variables and somehow setting those tracing variables before or during execution. Sherlock provides a simpler, more flexible and more powerful alternative.