Tracepoints, Conditional Breakpoints, and Function Breakpoints

We've all been there: we step through our code with breakpoints and it works just fine, but run it without ever hitting a breakpoint and our code explodes in a fiery ball of enigmatic failure. Perhaps the failure only happens after the 1000th call of a method, when a variable is set to a specific value, or the value of a variable changes. These bugs can be hard to investigate without actually modifying the software that has the bug, which then means you are no longer debugging the same software that had the bug and might mean the bug disappears1.

Thankfully, Visual Studio has our backs on tracking down some of these more obscure bugs. Visual Studio allows us to modify our breakpoints to only break on certain conditions (like the 5th loop iteration, or when a file is open), to output text to the debug window, or to just output text and not actually break into our code. We can even create breakpoints that break on any function that matches a name we provide, just in case you don't even know which code it's actually calling.

Though I discuss the 2015 experience, the features themselves have been around for quite some time. I would not be surprised if this were the first time you had heard about these breakpoint settings, they have always been somewhat hidden away from the primary workflow. Even now, with the updated user experience, they are not obvious unless you go exploring.  If you want to see how to use them in your variant of Visual Studio, or get more information on breakpoints in 2015, MSDN has you covered (2003|2005|2008|2010|2012|2013|2015).

Conditions and Actions

Floating breakpoint toolbar

Let's begin by taking a look at adding conditions and tracepoints. When you add a breakpoint to a line of code, either by using the F9 keyboard shortcut or left-clicking in the code margin, a little toolbar appears to the upper right of the cursor2 . The toolbar has two icons: the first is called Settings…, where all the cool stuff lives; the second is called Disable Breakpoint, which is very useful if you have customized the breakpoint3. If you click the Settings… button, you will see an inline dialog with two checkboxes; Conditions and Actions4.

If you check the first box, Conditions, you will be presented with various fields for specifying a condition under which the breakpoint fires. There are three types of condition;

  1. Conditional Expression
  2. Hit Count
  3. Filter
Adding a condition to a breakpoint
Adding a condition to a breakpoint

Conditional Expression conditions allow you to specify a condition based on variables within your code. You can break on when a specific condition is met, or when a condition changes (this allows you to break when a variable changes value, for example).

Hit Count conditions allow you to break once after the breakpoint has been hit a specific number of times (such as on the fifth index of an array in a loop), every time after the breakpoint has been hit a specific number of times, or every time the hit count is a multiple of a specific number (like every other hit, or once every five hits).

Filter conditions allow you to specify filters based on process, thread, and machine names, and process and thread identifiers.

Conditional breakpointYou can add multiple conditions to a breakpoint, which all have to match for the breakpoint to fire. When a condition is applied to a breakpoint, the red circle will have a white plus symbol inside it.

Adding output text to a breakpoint
Adding output text to a breakpoint

If you check the Actions box, you can specify text to be outputted when the breakpoint fires. By default, a checkbox named Continue Execution will be checked because, usually, if specifying output text, you want a tracepoint rather than a breakpoint. However, if you want to break and output text, you can uncheck this additional checkbox.

Non-breaking breakpoint (aka tracepoint)When a breakpoint is set to continue execution, the red circle changes into a red diamond. If a condition is also applied, the diamond has a white cross Conditional non-breaking breakpoint (aka tracepoint)in it.

If you use the Oz-code extension for Visual Studio, tracepoints are given some additional support with a quick action to add a tracepoint and a tracepoint viewer that shows you just tracepoints.

Function Breakpoints

Adding a function breakpoint
Adding a function breakpoint

So far, we've looked at traditional breakpoints that are set on specific lines of code. Function breakpoints are set against function names. To add a function point, use the Visual Studio menu to go to Debug→New Breakpoint→Function Breakpoint…5. Selecting this will show a dialog where you can specify the function name (qualifying it as you require), and the language to which the breakpoint applies. You can also specify conditions and actions as with any other breakpoint.

In Conclusion…

Visual Studio is a complex development environment, which unfortunately leads to some of its cooler features being harder to find. I hope you find this introduction to breakpoint superpowers useful, if you do or if you have more Visual Studio debugging tips, I'd love to hear from you in the comments.

Today's featured image is "Debugging the Computer" by Jitze Couperus. The image is licensed under CC BY 2.0. Other than being resized, the image has not been modified.

  1. yay, fixed it… []
  2. This also appears if you hover over an existing breakpoint in the margin []
  3. Deleting the breakpoint would also delete any customization, but disabling does not []
  4. This dialog can also be reached by right-clicking the breakpoint and choosing either Conditions… or Actions… or by hitting Alt+F9, C; the only difference here is that one of the two checkboxes will get checked automatically []
  5. You can also add one via the keyboard with Alt+F9, B or the New… dropdown in the Breakpoints tool window []

Debugging in LINQPad

If you have been reading my blog over the last few months you will no doubt be aware that I am a regular user of LINQPad. I do not have any commercial involvement with LINQPad nor its creators, I just really like it. Recently, I decided to try out the latest release, which adds integrated debugging to the already feature rich tool. This amazingly powerful new feature adds yet another reason why this application should be in every developer's arsenal, regardless of experience and ability (it is a great learning tool for students). Here is a brief overview of this new feature, which is available with the premium license (currently on sale for $85 at time of writing; it may not be the case as you are reading this).

When running the latest LINQPad, the debugging feature adds some new buttons to the familiar toolbar. All the debugging features are available for both statement and program-based queries in C#, VB, and F# (not expressions or SQL languages). The first new button is the `Pause` button, also known as `Break`. This works as you might expect, pausing the current code execution. The other two are to specify how exceptions should be handled, informing  the debugger to break on unhandled exceptions and when exceptions are thrown. Breakpoints can be added by clicking in the margin to the left of the code or pressing `F9` when the caret is on the desired line.  When a breakpoint is active on a line, it is indicated as a large red circle. For those who regularly use Visual Studio, the breakpoint and general debugger experience will be familiar.

Pressing `F5` will run the query (or selected lines) as usual, but now, any breakpoints set on executing lines will cause the code to break. At this point, LINQPad will reveal some familar and not-so-familiar tools for debugging the code. General status information is displayed at the bottom of the LINQPad window, showing things like whether the code is executing or paused, whether the debugger is attached or not, and the process ID.

The next code statement to execute is highlighted in the code with a yellow arrow in the margin (in this case, overlaid on the breakpoint circle), and the code highlighted in yellow. In the lower left portion of the screen, we can see local variables and executing threads. We can also set up our own watches as necessary. Any objects in the `Locals` and `Watch` tabs can be expanded using the `+` glyph to reveal their constituent values. As in Visual Studio, these tabs allow the expansion of just-in-time LINQ queries so you can delve into the deep dark secrets of your code. However, you can also take advantage of LINQPad's fantastic dump feature and dump any value out to the `Results` tab on the right. If you want to control how far down the object graph a dump will go, you can modify the `Dump Depth` using the `+` and `-` controls in the column header.

The `Dump` output for the `range` variable
The `Dump` output for the `range` variable
Specifying the depth of the dump
Specifying the depth of the dump

For more information on LINQPad and its many features, check out the LINQPad website (http://linqpad.net). In my opinion, whether you use the free version or one of the paid upgrades, you will have one of the best coding utilities available for .NET.