Back in the 1950s, many people were still optimistic about the possibility of creating a machine endowed with human intelligence. In 1950, English mathematician Alan Turing proposed a thought experiment to test whether a machine was intelligent. His idea was that anybody who wanted to verify a computer program's intelligence would be able to interrogate both the program in question and a human being via computer links. If after asking a series of questions, the interrogator was unable to distinguish between the human and the program, the program might legitimately be considered intelligent. This experiment had several drawbacks, the main one being that it is very difficult to devise the right type of questions. The interrogator would forever be devising new questions and wondering about the answers to the current ones.
This question-and-answer process is remarkably similar to what happens during program testing. A tester devises a number of inputs (equivalent to asking a series of questions) and then carefully examines the output (listens to the computer's answers). And like Turing's experiment, this type of black-box testing has the same drawbacks. The tester simply can't be sure whether he or she is asking the right questions or when enough questions have been asked to be reasonably sure that the program is functioning correctly.
What a debugger allows you to do is dive below the surface. No longer do you have to be satisfied with your original questions. You can observe your program's inner workings, redirect your questions in midflight to examine new issues raised by watching the effect of your original questions on the code, and be much more aware of which questions are important. Unlike a psychiatrist, who can never be sure whether a patient is sane, using a source-level debugger means that you will have a much better probability of being able to evaluate the sanity of your program.
Visual Basic 6's source-level debugger has three debugging windows as part of the IDE.
- The Immediate (or Debug) window is still here, with all the familiar abilities, such as being able to execute single-line statements or subroutines.
- The Locals window is rather cool. It displays the name, value, and data type of each variable declared in the current procedure. It can also show properties. You can change the value of any variable or property merely by clicking on it and then typing the new value. This can save a lot of time during debugging.
- The Watches window also saves you some time, allowing you to watch a variable's value without having to type any statements into the Immediate window. You can easily edit the value of any Watch expression you've set or the Watch expression itself by clicking on it, just as you can in the Locals window.
One technique that many programmers have found useful when working with this type of interactive debugger is to build debugging hooks directly into their programs. These hooks, usually in the form of functions or subroutines, can be executed directly from the Immediate window when in break mode. An example might be a routine that walks any array passed to it and prints out its contents, as shown here:
Public Sub DemonstrateDebugHook() Dim saTestArray(1 to 4) As Integer saTestArray(1) = "Element one" saTestArray(2) = "Element two" saTestArray(3) = "Element three" saTestArray(4) = "Element four" Stop End Sub Public Sub WalkArray(ByVal vntiArray As Variant) Dim nLoop As Integer ' Check that we really have an array. Debug.Assert IsArray(vntiArray) ' Print the array type and number of elements. Debug.Print "Array is of type " & TypeName(vntiArray) nLoop = UBound(vntiArray) - LBound(vntiArray) + 1 Debug.Print "Array has " & CStr(nLoop) & " elements"" ' Walk the array, and print its elements. For nLoop = LBound(vntiArray) To UBound(vntiArray) Debug.Print "Element " & CStr(nLoop) & " contains:" _ & vntiArray(nLoop) Next nLoop End Sub
When you run this code, Visual Basic will go into break mode when it hits the Stop statement placed in DemonstrateDebugHook. You can then use the Immediate window to type:
This debugging hook will execute and show you all the required information about any array passed to it.
The array is received as a Variant so that any array type can be handled and the array can be passed by value. Whole arrays can't be passed by value in their natural state. These types of debugging hooks placed in a general debug class or module can be extremely useful, both for you and for any programmers who later have to modify or debug your code.
Exercising all the paths
Another effective way to use the debugger is to step through all new or modified code to exercise all the data paths contained within one or more procedures. You can do this quickly, often in a single test run. Every program has code that gets executed only once in a very light blue moon, usually code that handles special conditions or errors. Being able to reset the debugger to a particular source statement, change some data to force the traversal of another path, and then continue program execution from that point gives you a great deal of testing power.
' This code will work fine - until nDiv is zero. If nDiv > 0 And nAnyNumber / nDiv > 1 Then DoSomething Else DoSomethingElse End If
When I first stepped through the above code while testing another programmer's work, nDiv had the value of 1. I stepped through to the End If statement-everything looked fine. Then I used the Locals window to edit the nDiv variable and change it to zero, set the debugger to execute the first line again, and of course the program crashed. (Visual Basic doesn't short-circuit this sort of expression evaluation. No matter what the value of nDiv, the second expression on the line will always be evaluated.) This ability to change data values and thereby follow all the code paths through a procedure is invaluable in detecting bugs that might otherwise take a long time to appear.