Ah, just a quick diversion before I talk about debug code and when you should have it and when you might not want it. Something interesting happened this past weekend that rather amused me and I don’t mean seeing WALL-E though that was a fine movie.
A student in Germany tried to hack Digital Looking Glass’s website using a rather uninspired directory traversal weakness. It might have worked if I was using the sort of host that he thought that I was and I was unpatched. I can’t claim to be surprised by the attack though. Hacking a security company’s website is a bit like smashing a sixer in conkers. Not today, my friend. Also, you might want to cover your tracks a bit better next time.
So, debug code. It used to be that this was one of the main debugging tools available and sometimes the only one. Trace statements displaying some information were peppered through the problematic area of the program and were included in the program’s output. We did this on mainframes, we did this on DOS and we didn’t do it quite as often in Windows. “printf” was replaced by “OutputDebugString” or “Debug.Print” and we still did business in much the same way though there were other options and it was a less popular choice. Often the information was limited to “Entering procX1” and “Leaving ProcX1”. The developer tools and some of the debuggers would display the debug text. There was a debug stream that was often piped to null – but easy enough for the tool to hook. Once programs were working, the debug statements were removed.
If you are debugging an application today, you will still see bits of debug spew in the debugger. If you are a fan of the SysInternals tools (uh, the Microsoft tools, I mean) then you may be familiar with DebugView which lets you capture the debug output. Try it on some published programs and there is a good chance that you will see debug information being pushed to a unregarded stream.
What are the good and bad points of leaving the debug code in place?
The good point is that you can get some idea of what is going wrong without reaching for the debugger and walking through some very complex structures in CDB or whatever your tool of choice happens to be.
The downsides are multiple. The first is that it bulks out the code and requires much more code to be in the working set. Given that processors are much faster than memory and loads faster than disk, this is not a good thing.
The second is that you are revealing information about the internals of your program that might be of use to someone reverse engineering the code. I have seen malware that still had trace statements.
Thirdly but not least, the trace statement may also display information that should be confidential. Imagine that you had an application that accessed medical records. The program knows that Patient John Q. Smith is HIV positive but no-one except his clinician should have access to this information – a subset is presented to other authorised users such as the person who books the patient appointments. If your debug statement shows the whole record then you have just revealed information to a user who had no right to that data. This is regarded as a very bad thing I am told.
All that said, debug code can be handy if you are sure that they don’t compromise you unacceptably as I mentioned when I was talking about object brokers last week.
If you are going to have debug code, you have a few options.
Just put it in there with no means to disable it without an edit. This is simple but inefficient.
Use conditional compilation to create a version with and without the debugging code. This isn’t a bad option. The downside is that you need to swap over the binary when you want to debug and that means restarting the application/service. If you do go this route, you probably want to use a debug switch other than DEBUG to enable the tracing because you want to alter the behaviour of the application as little as possible.
Have the debug code in there but skipped via a flag. This can work very well. Ideally, you would have something like a registry key that is checked every few minutes and use this to turn on or off the logging. Of course, it is best to have a Boolean flag which you set and check rather than reading the key each time since registry reads are not cheap. A Boolean will pipeline very nicely indeed.
Any debug output will slow the application down, of course. Does this matter? Probably but maybe not as much as you would imagine. If the application spends most of its life waiting on a database, it doesn’t much matter if you give it a little more work to do that has nothing to do with accessing the database. Debug statements which access the database are another thing though – not least because they will alter the timing enough for it to be a problem in many cases.
All in all, a technique which has some uses but not a solution to all things. Of course, nothing ever is.
Mark Long, Digital Looking Glass.