Debugging Basics: Breakpoints
A breakpoint is a signal that tells the debugger to temporarily suspend execution of your program at a certain point. When execution is suspended at a breakpoint, your program is said to be in break mode. Entering break mode does not terminate or end the execution of your program. Execution can be resumed at any time.
You can think of break mode as being like a timeout. All the elements remain, functions, variables, and objects remain in memory, for example, but their movements and activities are suspended. During break mode, you can examine their positions and states to look for violations or bugs. You can make adjustments to the program while in break mode. You can change the value of a variable, for example. You can move the execution point, which changes the statement that will be executed next when execution resumes. In C++, C#, and Visual Basic, you can even make changes to the code itself while in break mode using a powerful feature called Edit and Continue.
Breakpoints provide a powerful tool that enables you to suspend execution where and when you need to. Rather than stepping through your code line-by-line or instruction-by-instruction, you can allow your program to run until it hits a breakpoint, then start to debug. This speeds up the debugging process enormously. Without this ability, it would be virtually impossible to debug a large program.
Many programming languages have statements or constructs that suspend execution and put your program into break mode. Visual Basic, for example, has the Stop statement. Breakpoints differ from these statements because breakpoints are not actual source code that has to be added to your program. You do not type a breakpoint statement into a source window. You request a breakpoint through the debugger interface, and the debugger sets it for you. To insert a line breakpoint, click in the gray margin next to the line where you want to set the breakpoint. More complex breakpoints can be handled through a full-featured Breakpoints window.
Breakpoints have many advantages over debugging constructs such as the Visual Basic Stop statement. Breakpoints can be deleted or changed without having to change your program's source code. Because breakpoints are not statements, they never produce any extra code when you build a release version of your program. If you use Stop statements in your program, you need to manually remove the Stop statements before building the release version, or use conditionals, as shown here:
#If DEBUG Then Stop #End If
If you want to disable a Stop statement temporarily, you need to locate the statement in your source code and comment it out:
This is no problem if you only have one Stop statement. If you are debugging a large program with lots of Stop statements, however, searching for all of the statements and commenting each one out can be time consuming. With breakpoints, you can select and disable or enable any or all of your breakpoints from the Breakpoints window.
Finally, breakpoints have a big advantage over Stop statements in their flexibility. A Stop statement causes execution to break on the source line where the statement is located. You can set a breakpoint on a source line that does the same thing. Alternatively, you can also set a breakpoint on a function or a memory address, which cannot be done with Stop statements. In addition to these location breakpoints, the Visual Studio debugger offers data breakpoints for native only. A data breakpoint is set on a global or local variable, rather than a location in the code. Setting a data breakpoint causes execution to break when the value of that variable changes.
To provide even greater flexibility, the Visual Studio debugger enables you to set properties that modify the behavior of a breakpoint:
Hit Count enables you to determine how many times the breakpoint is hit before the debugger breaks execution. By default, the debugger breaks execution every time the breakpoint is hit. You can set a hit count to tell the debugger to break every two times the breakpoint is hit, or every 10 times, or every 512 times, any number you choose. Hit counts can be useful because some bugs do not appear the first time your program executes a loop, calls a function, or accesses a variable. Sometimes, the bug might not show up until the 100th or 1000th iteration. To debug such a problem, you can set a breakpoint with a hit count of 100 or 1000.
Condition is an expression that determines whether the breakpoint is hit or skipped. When the debugger reaches the breakpoint, it will evaluate the condition. The breakpoint will be hit only if the condition is satisfied. You can use a condition with a location breakpoint to stop at a specified location only when a certain condition is true. Suppose you are debugging a banking program, for example, where the account balance is never allowed to go below zero. You might set breakpoints at certain locations in the code and attach condition like balance < 0 to each one. When you run the program, execution will break at those locations only when the balance is less than zero. You can examine variables and program state at the first breakpoint location, then continue execution to the second breakpoint location, and so on.
Action specifies something that should occur when the breakpoint is hit. By default, the debugger breaks execution, but you can choose to print a message or run a Visual Studio macro instead. If you choose to print a message instead of breaking, the breakpoint has an effect very similar to a Trace statement. This method of using breakpoints is called tracepoints.
Filter provides a way to specify a process or thread for the breakpoint.
One particularly useful technique is setting breakpoints in the Call Stack window. Using the Call Stack window, you can set a breakpoint on a particular function call. This can be especially useful when you are debugging a recursive function, a function that calls itself. If you break execution after a certain number of calls, you can use the Call Stack window to set a breakpoint on a previous call that has not yet returned. The debugger will encounter that breakpoint and break execution on the way out of the current calls.