The debugger is a tool attached with the Visual Studio IDE that works in the background and inspects execution of the program. It holds a database of all the debug-related information and stores it in a program database file. The Visual Studio debugger stores the checksum of the file associated with the debugger, its full path, the line number, and the number of characters of the file. When we create a breakpoint in the file, the information gets stored on the PDB file or sometimes in the memory, and this will get evaluated when the program is running under the debug mode.
The debugger cannot work in certain situations when the debug information related to the file is invalid (when file checksum fails) or does not exist. For instance, say you have compiled the executable in a different source other than the one you are debugging with. In this case, Visual Studio is capable of determining whether the source code and the process running are the same.
Now let's look at how to take advantage of breakpoints in the Visual Studio IDE.
Changing the execution step while debugging
The Visual Studio debugger is very smart and capable of doing a lot of things. You can change the execution path directly inside the debugger by not executing a certain line of the code or executing a certain line more than once.
While you are at the breakpoint, you can right-click on any line inside the IDE and select Set Next Statement or use Ctrl + Shift + F10 to move the debugger to that line, as shown in the following screenshot:
You can also drag the cursor of the current line using the mouse to move the cursor to any line in the code.
If you choose Run to Cursor from the right-click menu instead of Set Next Statement, the debugger will execute the lines in between and stop at the line that you select. If the line you select does not belong to the following execution of code, it will finish execution in regular course.
When working with a large project, there are situations when you need to label your breakpoints for better management and categorization such that you can enable/disable a certain group of breakpoints depending on your requirements. Let's put some breakpoints on the code and start debugging using F5 from the keyboard. The program will stop at the line where the first breakpoint is set, as shown in the following screenshot. Navigate to Debug | Windows | Breakpoints or press Ctrl + D + B.
A window will appear that will show all the breakpoints you have set in the code. In the preceding screenshot, you can see TestClassLabel. Now, let us right-click either on the breakpoint in the code or in the Tool window and select Edit labels, as shown in the following screenshot:
The command will pop up the Edit breakpoint labels dialog box, which lists all the labels and allows you to create a new label. Initially, it will be blank, but as you create more labels, the list will fill up. Once you have created the labels, you can choose any of the labels associated with the current breakpoint. You can also choose multiple breakpoints to select a label. Once you have selected the label for all the breakpoints individually, you can search in the Search box of the Breakpoints tool window to filter breakpoints from the list.
It is important to note that labels play a very important role in naming a breakpoint. When dealing with large projects, it will be really easy to manage breakpoints with labels to identify exactly which breakpoint it shows. The label is an alias to a breakpoint.
The breakpoint window also allows you to enable/disable an existing breakpoint. For instance, you can uncheck a breakpoint to disable the breakpoint in the actual code. Even though the breakpoint exists in the code, it is marked as disabled, and hence the program will not halt at that point.
The preceding screenshot shows how to disable a breakpoint from the right-click menu. It can also be accessed using Ctrl + F9.
Adding a condition in breakpoints
Breakpoints can hold conditions and often come in handy when used in the iteration of a list or numeric value. Putting a halt inside an iteration means redundant breakpoints, and when the problem arises on a certain index of the loop, it is very hard to catch the exact value of the index. Conditions can help in putting a break only when a certain precondition is met. The conditional break allows placing an expression based on the current context and breaks only when the condition is met.
Right-click on the breakpoint either in the tool window or on the sidebar pane where the red breakpoint icon is shown and select a condition, as shown in the following screenshot:
You can declare a local variable using the Watch window and use it in the conditions. The IDE validates the condition and stores it in the PDB file. The textbox that appears is also capable of showing the IntelliSense menu inside it to enhance condition writing.
The two radio buttons indicate what needs to be considered from the condition. If the condition returns a Boolean value, we choose the option Is true; otherwise, we choose Has changed, where the condition can be anything.
Note that after choosing a condition, the red breakpoint icon will show a plus symbol, which indicates that it is conditional.
Visual Studio allows you to import/export breakpoints. If many developers are working on the same code, you can share the breakpoints that you have created in one project with others or save it. So, if you have breakpoints organized with labels, everything is exported to a file that can be used later, as shown:
The file is actually an XML file, which produces XML content of all the breakpoints. A breakpoint, once exported, can be imported back again by choosing the Import Breakpoint button on the Breakpoint tool window.
Note
Remember, breakpoints are created inside a .suo
(Solution User Option) file, but the actual program halts are written inside the PDB file. Thus, while running the program from inside vshost
, if PDB is not found, the file cannot hit the breakpoints even though the breakpoints exist on the IDE.
A breakpoint hit counter is used to determine how many times a breakpoint has been hit on a particular line. Sometimes, it is needed to halt a program only when a certain number of breakpoints have already been hit. Consider a situation where you are iterating with a large number of loops. In these cases, we can configure the breakpoint to stop only when the number of hits it encounters reaches a threshold, as shown in the following screenshot:
In the preceding screenshot, you can see the breakpoint hit count box, which is opened by right-clicking on the breakpoint and selecting Hit Count. The default setting is Break always but we can configure it to hit on a counter or a multiple of a counter, or even greater than equal to a counter.
Adding tracepoints while debugging
As a default, when a breakpoint is hit, it will halt the execution of the program and pause it until we run the program again. But when the application is pretty large, you may want to continue the program and either run a macro when the breakpoint is hit or invoke a trace method. The breakpoint in such cases is called a tracepoint. To open the tracepoint window, right-click on a breakpoint and select When Hit.
Notice that we print the function name, thread ID, and thread name when the breakpoint is hit. The tracepoints can print items on the output window based on the options listed on the window. Keywords such as $ADDRESS, $CALLER, and so on specified on the window provide macros that evaluate the specific context. After you place a tracepoint, the breakpoint identifier in the left-hand side of the screen will appear as a red diamond instead of a circle.
If you choose Continue execution, it means the macro that we selected will be executed without stopping the code execution.
Breakpoint filters allow you to specify any additional criteria of the breakpoint. With filters applied, you can specify the breakpoint to hit only for a specific machine, or a specific process or a thread. This is often required if you are executing your program several times with a different process ID each time and want to debug only on a certain process ID.
In the preceding screenshot, we filter the breakpoints based on the name of the computer and the process ID. The search box allows you to type in text and, based on the input, the window will filter all the breakpoints that match the labels of the searched string.
Note
The information about the breakpoint is stored inside the .suo
files in the IDE, but the implementation is produced when MSBuild builds the project. While debugging an application, if the source code isn't available but PDB is, the IDE opens the code disassembly to break.